test(medications): prove the reported export lands, row for row

The fixture is the export a self-hoster attached, de-identified as it
arrived: 3,395 rows, sixteen medications, three and a half years, two UTC
offsets, three statuses, two units, blank cells in four columns. A
hand-built fixture would have carried one status and one offset and proved
nothing about the file that prompted the work.

The integration pass runs it through the real derivation into a real
Postgres, because the claim is about rows: 3,387 land, 8 are refused because
the file records no decision about them, and imported plus refused equals
every row read. 2,799 of the doses were taken after they were due, which is
the fact that survives only because the scheduled slot and the take are two
columns.

Structural cover so the verdicts cannot rot: every column of both shapes has
exactly one ruling, and every parse-time refusal is spelled the same as a job
skip reason — a reason that exists in only one vocabulary renders as "reason
not recorded", which is a refusal nobody can act on.
This commit is contained in:
Marc-André Bombeck committed 2026-07-26 16:37:10 +02:00
1 parent 11e6402b5a
commit 34a220421c
20 files changed
+5322 -25

No files matched your search

@@ -212,6 +212,10 @@ const EXEMPT_ROUTES: ReadonlyArray<string> = [
"src/app/api/medications/compliance/route.ts",
"src/app/api/medications/intake/route.ts",
"src/app/api/medications/intake/bulk/route.ts",
// The account-wide dose-history import is the same class as the
// per-medication importer below: it writes intake rows and reads its own job.
"src/app/api/medications/intake/dose-history-import/route.ts",
"src/app/api/medications/intake/dose-history-import/[jobId]/status/route.ts",
// NB: `medications/extract` is NOT exempt — it gates on
// `requireAssistantSurface("coach")` (the NL-extraction is an assistant
// surface), so the inventory already counts it as a delegated gate.
@@ -239,14 +239,20 @@ describe("POST /api/medications/[id]/intake/import — durable kickoff", () => {
// A legacy `zaehler` on the first row rides along and decides nothing:
// both keys come from the instant, so two different times can never
// collapse onto one key again.
// This body carries one time per row and nothing to say which slot it
// belonged to, so `scheduledFor` mirrors the take: every dose it
// describes is ad-hoc, anchored on itself. The export-reading route is
// the one with two facts to write.
payload: {
entries: [
{
idempotencyKey: `import-m1-${Date.parse("2026-01-01T07:00:00")}`,
scheduledFor: expect.any(String),
takenAt: expect.any(String),
},
{
idempotencyKey: `import-m1-${Date.parse("2026-01-01T19:00:00")}`,
scheduledFor: expect.any(String),
takenAt: expect.any(String),
},
],