mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
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.
173 lines
5.3 KiB
TypeScript
173 lines
5.3 KiB
TypeScript
/**
|
|
* 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);
|
|
});
|
|
});
|