fix(medications): a manual era cannot swallow tracked live history

The manual-era boundary anchors on the medication's creation instead of
the current moment, so a backfilled era can only describe time before
the app was tracking.
This commit is contained in:
Marc-André Bombeck committed 2026-06-11 12:08:41 +02:00
1 parent 5639da4333
commit 44f344bf52
2 files changed
+35 -3

No files matched your search

@@ -233,6 +233,25 @@ describe("POST /api/medications/[id]/schedule-revisions", () => {
expect(json.error).toMatch(/current plan/i);
});
it("422s when no revision exists and the era reaches past createdAt", async () => {
// With zero archived revisions the live plan has covered everything
// since `createdAt` (2026-01-10); a manual era ending inside the
// tracked window would re-score live compliance against the manual
// snapshot. The boundary is createdAt, not "now".
const res = await POST(
postReq({
...VALID_BODY,
validFrom: "2026-01-01T00:00:00.000Z",
validUntil: "2026-02-01T00:00:00.000Z",
}),
ROUTE_CTX,
);
expect(res.status).toBe(422);
const json = await res.json();
expect(json.error).toMatch(/current plan/i);
expect(prisma.medicationScheduleRevision.create).not.toHaveBeenCalled();
});
it("creates a MANUAL revision with a daily snapshot entry", async () => {
vi.mocked(prisma.medicationScheduleRevision.create).mockResolvedValue({
id: "rev-new",
@@ -137,6 +137,14 @@ export const POST = apiHandler(
const guard = await assertMedicationOwnership(id, user.id);
if (guard) return guard;
const med = await prisma.medication.findUnique({
where: { id },
select: { createdAt: true },
});
if (!med) {
return apiError("Medication not found", 404);
}
const { data: body, error: jsonError } = await safeJson(request, {
maxBytes: 16 * 1024,
});
@@ -162,13 +170,18 @@ export const POST = apiHandler(
});
// "Before the current plan": the live era began at the newest
// archived `validUntil`; with no archive it spans up to now, so a
// manual era may cover anything up to this instant.
// archived `validUntil`; with no archive the live plan has covered
// everything since the medication was created (the era splitter
// reads `[newest validUntil, ∞)` — or the whole range when no
// revision exists), so a manual era must end at or before
// `createdAt`. Allowing it to reach "now" would let the manual
// snapshot swallow tracked live history and re-score compliance
// against the wrong plan.
const liveStart = existing.reduce(
(latest, r) => (r.validUntil > latest ? r.validUntil : latest),
new Date(0),
);
const liveBoundary = existing.length > 0 ? liveStart : new Date();
const liveBoundary = existing.length > 0 ? liveStart : med.createdAt;
if (validUntil.getTime() > liveBoundary.getTime()) {
return apiError(
"A manual era must end before the current plan begins",