mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
test(medications): cover the container kind on the import and refund paths
Hardcoding the container kind in the batch-import consumption path or in the
refund path left the whole suite green — neither had a scenario that read a
clock-less container. Both now do: a back-dated import chunk against a tablet
pack stays IN_USE with no deadline, and a refund that brings units back to a
long-opened pack reads IN_USE rather than expired. The pen counterpart of the
import case is pinned alongside.
The fake client's `updateMany` learns the `id: { in: [...] }` form the batch
claim uses; it only understood a single id before.
The no-runway arm of the low-stock evaluation now has its written-off count
asserted too.
This commit is contained in:
1 parent
7b59388887
commit
d91b7cb57a
2 files changed
+102
-4
No files matched your search
@@ -76,7 +76,14 @@ describe("evaluateMedicationRunway", () => {
|
||||
});
|
||||
|
||||
it("derives NO runway for a schedule-less medication", () => {
|
||||
expect(evaluateMedicationRunway([item(2)], 1, []).runwayDays).toBe(null);
|
||||
const out = evaluateMedicationRunway(
|
||||
[item(2), { state: "EXPIRED", unitsTotal: 30, unitsRemaining: 7 }],
|
||||
1,
|
||||
[],
|
||||
);
|
||||
expect(out.runwayDays).toBe(null);
|
||||
// The no-runway arm still reports the written-off stock.
|
||||
expect(out.expiredUnits).toBe(7);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -19,7 +19,11 @@ vi.mock("@/lib/logging/context", () => ({
|
||||
|
||||
import { Prisma } from "@/generated/prisma/client";
|
||||
import { annotate } from "@/lib/logging/context";
|
||||
import { consumeForIntake, restoreForIntake } from "../consumption";
|
||||
import {
|
||||
consumeForIntake,
|
||||
consumeImportedIntakesBatch,
|
||||
restoreForIntake,
|
||||
} from "../consumption";
|
||||
|
||||
const NOW = new Date("2026-06-10T12:00:00.000Z");
|
||||
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
||||
@@ -98,7 +102,9 @@ function makeClient(state: FakeState) {
|
||||
updateMany: vi.fn(
|
||||
async (args: {
|
||||
where: {
|
||||
id?: string;
|
||||
// The single-event hook narrows by one id; the import batch
|
||||
// claims a whole chunk with `id: { in: [...] }`.
|
||||
id?: string | { in: string[] };
|
||||
userId?: string;
|
||||
medicationId?: string;
|
||||
deletedAt?: null;
|
||||
@@ -110,7 +116,13 @@ function makeClient(state: FakeState) {
|
||||
}) => {
|
||||
const w = args.where;
|
||||
const matches = state.events.filter((e) => {
|
||||
if (w.id !== undefined && e.id !== w.id) return false;
|
||||
if (typeof w.id === "string" && e.id !== w.id) return false;
|
||||
if (
|
||||
w.id !== undefined &&
|
||||
typeof w.id === "object" &&
|
||||
!w.id.in.includes(e.id)
|
||||
)
|
||||
return false;
|
||||
if (w.userId !== undefined && e.userId !== w.userId) return false;
|
||||
if (
|
||||
w.medicationId !== undefined &&
|
||||
@@ -633,6 +645,56 @@ describe("consumeForIntake", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("consumeImportedIntakesBatch", () => {
|
||||
// The import worker's batch path derives state through the same state
|
||||
// machine as the request-time hook, so it has to read the container
|
||||
// kind too — a back-dated import chunk against a tablet pack must not
|
||||
// write the pack off.
|
||||
it("leaves a back-dated import auto-open of a blister IN_USE", async () => {
|
||||
const backdated = new Date(NOW.getTime() - 40 * MS_PER_DAY);
|
||||
const state: FakeState = {
|
||||
unitsPerDose: 1,
|
||||
events: [takenEvent({ takenAt: backdated })],
|
||||
items: [item({ id: "sealed", containerType: "BLISTER" })],
|
||||
};
|
||||
const client = makeClient(state);
|
||||
await consumeImportedIntakesBatch({
|
||||
client: client as never,
|
||||
userId: "user-1",
|
||||
medicationId: "med-1",
|
||||
events: [{ eventId: "evt-1", intakeAt: backdated }],
|
||||
});
|
||||
|
||||
const opened = state.items[0];
|
||||
expect(opened.firstUseAt).toEqual(backdated);
|
||||
expect(opened.state).toBe("IN_USE");
|
||||
expect(opened.expiresAt).toBeNull();
|
||||
expect(opened.unitsRemaining).toBe(9);
|
||||
});
|
||||
|
||||
it("still expires a back-dated import auto-open of a pen", async () => {
|
||||
const backdated = new Date(NOW.getTime() - 40 * MS_PER_DAY);
|
||||
const state: FakeState = {
|
||||
unitsPerDose: 1,
|
||||
events: [takenEvent({ takenAt: backdated })],
|
||||
items: [item({ id: "sealed", containerType: "PEN" })],
|
||||
};
|
||||
const client = makeClient(state);
|
||||
await consumeImportedIntakesBatch({
|
||||
client: client as never,
|
||||
userId: "user-1",
|
||||
medicationId: "med-1",
|
||||
events: [{ eventId: "evt-1", intakeAt: backdated }],
|
||||
});
|
||||
|
||||
const opened = state.items[0];
|
||||
expect(opened.state).toBe("EXPIRED");
|
||||
expect(opened.expiresAt).toEqual(
|
||||
new Date(backdated.getTime() + 30 * MS_PER_DAY),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("restoreForIntake", () => {
|
||||
function restoreArgs(client: ReturnType<typeof makeClient>) {
|
||||
return { client: client as never, userId: "user-1", eventId: "evt-1" };
|
||||
@@ -725,6 +787,35 @@ describe("restoreForIntake", () => {
|
||||
expect(state.items[0].state).toBe("EXPIRED");
|
||||
});
|
||||
|
||||
it("re-derives IN_USE for a refunded blister whose opening is long past", async () => {
|
||||
// Same refund against a tablet pack: the units come back and the
|
||||
// pack is usable again. There is no post-opening window to have
|
||||
// lapsed, so EXPIRED would be wrong.
|
||||
const state: FakeState = {
|
||||
unitsPerDose: 1,
|
||||
events: [
|
||||
takenEvent({
|
||||
inventoryConsumption: [{ itemId: "open", units: 1 }],
|
||||
}),
|
||||
],
|
||||
items: [
|
||||
item({
|
||||
id: "open",
|
||||
containerType: "BLISTER",
|
||||
state: "USED_UP",
|
||||
unitsTotal: 4,
|
||||
unitsRemaining: 0,
|
||||
firstUseAt: new Date(NOW.getTime() - 35 * MS_PER_DAY),
|
||||
}),
|
||||
],
|
||||
};
|
||||
const client = makeClient(state);
|
||||
await restoreForIntake(restoreArgs(client));
|
||||
|
||||
expect(state.items[0].unitsRemaining).toBe(1);
|
||||
expect(state.items[0].state).toBe("IN_USE");
|
||||
});
|
||||
|
||||
it("is a no-op when the row carries no stamp", async () => {
|
||||
const state: FakeState = {
|
||||
unitsPerDose: 1,
|
||||
|
||||
Reference in new issue
Block a user