test(labs): integration coverage for biomarker target-range and delete

Drive the biomarker resource route against a real Postgres: PUT persists a
new reference window and rejects an inverted one, and DELETE drops the marker
together with its readings while leaving another user's marker and readings
intact.
This commit is contained in:
Marc-André Bombeck committed 2026-06-28 14:00:55 +02:00
1 parent f17131dbdf
commit 1cb7f0933c
1 file changed
+172
+172
View File
@@ -0,0 +1,172 @@
/**
* Integration suite for the biomarker resource route
* (`/api/biomarkers/{id}`) against a real Postgres.
*
* Covers the two detail-page mutations:
* - PUT adjusts the target range (and rejects an inverted window),
* - DELETE drops the marker together with its readings in one
* userId-narrowed transaction, leaving another user's marker untouched.
*/
import { NextRequest } from "next/server";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { cookieJar } from "./mock-next-headers";
import { getPrismaClient, truncateAllTables } from "./setup";
const USER_ID = "user-biomarker-detail-test";
const OTHER_USER_ID = "user-biomarker-other-test";
vi.mock("next/headers", async () => {
const { cookieJar, headerJar } = await import("./mock-next-headers");
return {
headers: vi.fn(async () => ({
get: (name: string) => headerJar.get(name.toLowerCase()) ?? null,
})),
cookies: vi.fn(async () => ({
get: (name: string) => {
const value = cookieJar.get(name);
return value ? { name, value } : undefined;
},
set: (name: string, value: string) => {
cookieJar.set(name, value);
},
delete: (name: string) => {
cookieJar.delete(name);
},
})),
};
});
vi.mock("@/lib/db-compat", () => ({
ensureDbCompatibility: vi.fn().mockResolvedValue(undefined),
}));
beforeEach(async () => {
await truncateAllTables(getPrismaClient());
cookieJar.clear();
const prisma = getPrismaClient();
await prisma.user.createMany({
data: [
{ id: USER_ID, username: "bm-detail", email: "bm-detail@example.test" },
{
id: OTHER_USER_ID,
username: "bm-other",
email: "bm-other@example.test",
},
],
});
const session = await prisma.session.create({
data: { userId: USER_ID, expiresAt: new Date(Date.now() + 60 * 60 * 1000) },
});
cookieJar.set("healthlog_session", session.id);
});
const params = (id: string) => ({ params: Promise.resolve({ id }) });
function putReq(id: string, body: unknown): NextRequest {
return new NextRequest(`http://localhost/api/biomarkers/${id}`, {
method: "PUT",
headers: { "content-type": "application/json" },
body: JSON.stringify(body),
});
}
function delReq(id: string): NextRequest {
return new NextRequest(`http://localhost/api/biomarkers/${id}`, {
method: "DELETE",
});
}
async function seedMarkerWithReadings(userId: string, name: string) {
const prisma = getPrismaClient();
const marker = await prisma.biomarker.create({
data: { userId, name, unit: "mg/dL", lowerBound: 0, upperBound: 100 },
});
await prisma.labResult.createMany({
data: [
{
userId,
biomarkerId: marker.id,
analyte: name,
unit: "mg/dL",
value: 95,
takenAt: new Date("2026-06-20T08:00:00.000Z"),
},
{
userId,
biomarkerId: marker.id,
analyte: name,
unit: "mg/dL",
value: 88,
takenAt: new Date("2026-05-20T08:00:00.000Z"),
},
],
});
return marker;
}
describe("PUT /api/biomarkers/{id} — target range (real Postgres)", () => {
it("persists an updated reference window", async () => {
const { PUT } = await import("@/app/api/biomarkers/[id]/route");
const marker = await seedMarkerWithReadings(USER_ID, "LDL");
const res = await PUT(
putReq(marker.id, { lowerBound: 10, upperBound: 50 }),
params(marker.id),
);
expect(res.status).toBe(200);
const row = await getPrismaClient().biomarker.findUnique({
where: { id: marker.id },
});
expect(row?.lowerBound).toBe(10);
expect(row?.upperBound).toBe(50);
});
it("422s an inverted target range and leaves the bounds untouched", async () => {
const { PUT } = await import("@/app/api/biomarkers/[id]/route");
const marker = await seedMarkerWithReadings(USER_ID, "HbA1c");
const res = await PUT(
putReq(marker.id, { lowerBound: 80, upperBound: 20 }),
params(marker.id),
);
expect(res.status).toBe(422);
const row = await getPrismaClient().biomarker.findUnique({
where: { id: marker.id },
});
expect(row?.lowerBound).toBe(0);
expect(row?.upperBound).toBe(100);
});
});
describe("DELETE /api/biomarkers/{id} (real Postgres)", () => {
it("drops the marker together with its readings", async () => {
const { DELETE } = await import("@/app/api/biomarkers/[id]/route");
const marker = await seedMarkerWithReadings(USER_ID, "Ferritin");
const res = await DELETE(delReq(marker.id), params(marker.id));
expect(res.status).toBe(200);
const prisma = getPrismaClient();
expect(await prisma.biomarker.count({ where: { id: marker.id } })).toBe(0);
expect(
await prisma.labResult.count({ where: { biomarkerId: marker.id } }),
).toBe(0);
});
it("404s another user's marker and leaves it (and its readings) intact", async () => {
const { DELETE } = await import("@/app/api/biomarkers/[id]/route");
const foreign = await seedMarkerWithReadings(OTHER_USER_ID, "TSH");
const res = await DELETE(delReq(foreign.id), params(foreign.id));
expect(res.status).toBe(404);
const prisma = getPrismaClient();
expect(await prisma.biomarker.count({ where: { id: foreign.id } })).toBe(1);
expect(
await prisma.labResult.count({ where: { biomarkerId: foreign.id } }),
).toBe(2);
});
});