mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-07 04:12:04 +00:00
The eight journeys were enumerated and one of them was wired. The other
seven existed as claims in a fixture file, which is the gap the enumeration
was written to close: a browser suite reports what it ran, never what it did
not, and a journey nobody wired reads exactly like a journey that passed.
All eight are wired now, and the list is checked in both directions — an
enumerated journey missing from the wiring fails, not just a wiring entry
naming a journey nobody declared. Before this, a subset was legal, so a
dropped journey and a not-yet-written one looked the same.
A journey names more than one file where more than one file carries it. J1
claims a grant runs through invite, accept, switch, expiry, revoke and a
direct URL; the browser proves the switch, what each level opens and the
direct-URL refusal, and the transitions either side are database facts
proved against real Postgres. Folding that into one file would mean a
browser spec driving expiry through the clock or an integration file
asserting what a page renders, and both are worse tests than the two that
exist.
Three journeys ride Playwright and five ride Vitest, so a green run of this
guard means all eight are claimed and here is which have executed in the
ordinary gate — never that all eight passed. The runner is derived from the
path rather than trusted, so it cannot become a label set to whichever value
made the split read better.
The extractor now reads `it(` as well as `test(`, and the parameterised
`it.each(table)("title")` form alongside the single-call one. It saw neither
before: five of the journeys are proved by Vitest files, every per-file leg
is over a matched set, and an empty match set satisfied them all. The
synthetic spec that proves the parse grew a Vitest half, and it is what
caught the parameterised form being invisible. What the matcher still cannot
see is written into its own doc comment.
180 lines
5.1 KiB
TypeScript
180 lines
5.1 KiB
TypeScript
import { NextRequest } from "next/server";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import { GET as getImportStatus } from "@/app/api/medications/[id]/intake/import/[jobId]/status/route";
|
|
import { ACCOUNT_SELECTOR_HEADER } from "@/lib/api-handler";
|
|
import { hashToken } from "@/lib/auth/hmac";
|
|
import { toJson } from "@/lib/db";
|
|
import {
|
|
_setMedicationIntakeImportPrismaForTests,
|
|
processMedicationIntakeImportJob,
|
|
type MedicationImportProgress,
|
|
} from "@/lib/jobs/medication-intake-import";
|
|
|
|
import { getPrismaClient, truncateAllTables } from "./setup";
|
|
import { cookieJar, headerJar } from "./mock-next-headers";
|
|
|
|
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;
|
|
},
|
|
})),
|
|
};
|
|
});
|
|
|
|
const EMPTY_PROGRESS: MedicationImportProgress = {
|
|
processed: 0,
|
|
total: 0,
|
|
imported: 0,
|
|
skippedByReason: {},
|
|
touchedDays: [],
|
|
rollupProcessed: 0,
|
|
};
|
|
|
|
beforeEach(async () => {
|
|
const prisma = getPrismaClient();
|
|
_setMedicationIntakeImportPrismaForTests(prisma);
|
|
await truncateAllTables(prisma);
|
|
cookieJar.clear();
|
|
headerJar.clear();
|
|
});
|
|
|
|
describe("shared intake import attribution", () => {
|
|
it("[J7-durable-import-actor] retains the record and manager through one durable completion audit", async () => {
|
|
const prisma = getPrismaClient();
|
|
await prisma.user.createMany({
|
|
data: [
|
|
{
|
|
id: "import-record",
|
|
username: "import-record",
|
|
email: "import-record@example.test",
|
|
role: "USER",
|
|
},
|
|
{
|
|
id: "import-manager",
|
|
username: "import-manager",
|
|
email: "import-manager@example.test",
|
|
role: "USER",
|
|
},
|
|
],
|
|
});
|
|
const job = await prisma.medicationIntakeImportJob.create({
|
|
data: {
|
|
recordUserId: "import-record",
|
|
actorUserId: "import-manager",
|
|
medicationId: null,
|
|
payload: toJson({ entries: [] }),
|
|
progress: toJson(EMPTY_PROGRESS),
|
|
} as never,
|
|
});
|
|
|
|
await processMedicationIntakeImportJob(job.id);
|
|
await processMedicationIntakeImportJob(job.id);
|
|
|
|
const [completed, audits] = await Promise.all([
|
|
prisma.medicationIntakeImportJob.findUniqueOrThrow({
|
|
where: { id: job.id },
|
|
}),
|
|
prisma.auditLog.findMany({
|
|
where: {
|
|
userId: "import-record",
|
|
action: "medication.intake.import",
|
|
},
|
|
}),
|
|
]);
|
|
|
|
expect(completed).toMatchObject({
|
|
recordUserId: "import-record",
|
|
actorUserId: "import-manager",
|
|
status: "done",
|
|
});
|
|
expect(audits).toHaveLength(1);
|
|
expect(audits[0]).toMatchObject({
|
|
userId: "import-record",
|
|
actorUserId: "import-manager",
|
|
});
|
|
expect(JSON.parse(audits[0]!.details!)).toEqual({
|
|
jobId: job.id,
|
|
recordUserId: "import-record",
|
|
actorUserId: "import-manager",
|
|
imported: 0,
|
|
skipped: 0,
|
|
total: 0,
|
|
});
|
|
});
|
|
|
|
it("[J7-durable-import-actor] lets the initiating manager poll the record-scoped job", async () => {
|
|
const prisma = getPrismaClient();
|
|
await prisma.user.createMany({
|
|
data: [
|
|
{
|
|
id: "status-record",
|
|
username: "status-record",
|
|
email: "status-record@example.test",
|
|
role: "USER",
|
|
},
|
|
{
|
|
id: "status-manager",
|
|
username: "status-manager",
|
|
email: "status-manager@example.test",
|
|
role: "USER",
|
|
},
|
|
],
|
|
});
|
|
const medication = await prisma.medication.create({
|
|
data: {
|
|
userId: "status-record",
|
|
name: "Status medication",
|
|
dose: "1 unit",
|
|
},
|
|
});
|
|
await prisma.accountGrant.create({
|
|
data: {
|
|
id: "status-manage-grant",
|
|
grantorId: "status-record",
|
|
granteeId: "status-manager",
|
|
access: "MANAGE",
|
|
acceptedAt: new Date(),
|
|
},
|
|
});
|
|
const token = "shared-import-status-token".padEnd(28, "0");
|
|
await prisma.apiToken.create({
|
|
data: {
|
|
userId: "status-manager",
|
|
name: "shared import status",
|
|
tokenHash: hashToken(token),
|
|
permissions: ["*"],
|
|
},
|
|
});
|
|
headerJar.set("authorization", `Bearer ${token}`);
|
|
headerJar.set(ACCOUNT_SELECTOR_HEADER, "status-record");
|
|
const job = await prisma.medicationIntakeImportJob.create({
|
|
data: {
|
|
recordUserId: "status-record",
|
|
actorUserId: "status-manager",
|
|
medicationId: medication.id,
|
|
payload: toJson({ entries: [] }),
|
|
progress: toJson(EMPTY_PROGRESS),
|
|
},
|
|
});
|
|
|
|
const response = await getImportStatus(
|
|
new NextRequest(
|
|
`http://localhost/api/medications/${medication.id}/intake/import/${job.id}/status`,
|
|
{},
|
|
),
|
|
{ params: Promise.resolve({ id: medication.id, jobId: job.id }) },
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect((await response.json()).data.jobId).toBe(job.id);
|
|
});
|
|
});
|