Files
HealthLog/src/lib/validations/__tests__/medication-dose-windows.test.ts
T
Marc-André Bombeck e523a3ae00 feat(medications): validate per-dose dose windows on schedule writes
Accept an optional doseWindows array on the schedule create/update routes:
each entry pins an explicit on-time range [start,end] to one of the
schedule's times-of-day, defaulting to the symmetric grace band when
absent. Reject malformed ranges (bad HH:mm, a timeOfDay outside the
schedule, an inverted span) with a 422 so the persisted window can never
feed the band minter a degenerate range.

Regenerates the OpenAPI contract for the additive field (iOS-safe).
2026-06-08 18:34:37 +02:00

126 lines
3.3 KiB
TypeScript

/**
* v1.15.18 — per-dose configurable on-time window (`doseWindows`) validation.
*
* The schedule schema accepts an optional `[{ timeOfDay, start, end }]` array.
* Each entry must be well-formed HH:mm with `start <= end`, name a real dose
* time of the schedule, and no `timeOfDay` may repeat. Absent leaves the
* default ±1h behaviour unchanged.
*/
import { describe, it, expect } from "vitest";
import { doseWindowEntrySchema, scheduleSchema } from "../medication";
function baseSchedule(over: Record<string, unknown> = {}) {
return {
windowStart: "07:00",
windowEnd: "09:00",
timesOfDay: ["07:00", "19:00"],
rrule: "FREQ=DAILY",
...over,
};
}
describe("doseWindowEntrySchema", () => {
it("accepts a well-formed entry", () => {
expect(
doseWindowEntrySchema.safeParse({
timeOfDay: "07:00",
start: "07:00",
end: "09:00",
}).success,
).toBe(true);
});
it("rejects start > end", () => {
expect(
doseWindowEntrySchema.safeParse({
timeOfDay: "07:00",
start: "12:00",
end: "07:00",
}).success,
).toBe(false);
});
it("rejects a malformed HH:mm", () => {
expect(
doseWindowEntrySchema.safeParse({
timeOfDay: "7:00",
start: "07:00",
end: "09:00",
}).success,
).toBe(false);
expect(
doseWindowEntrySchema.safeParse({
timeOfDay: "07:00",
start: "07:00",
end: "25:00",
}).success,
).toBe(false);
});
it("allows a zero-width window (start === end)", () => {
expect(
doseWindowEntrySchema.safeParse({
timeOfDay: "07:00",
start: "08:00",
end: "08:00",
}).success,
).toBe(true);
});
});
describe("scheduleSchema doseWindows", () => {
it("is optional — a schedule without it validates", () => {
expect(scheduleSchema.safeParse(baseSchedule()).success).toBe(true);
});
it("accepts a window for a real dose time", () => {
const r = scheduleSchema.safeParse(
baseSchedule({
doseWindows: [{ timeOfDay: "07:00", start: "07:00", end: "09:00" }],
}),
);
expect(r.success).toBe(true);
});
it("rejects a window whose timeOfDay is not a scheduled dose time", () => {
const r = scheduleSchema.safeParse(
baseSchedule({
doseWindows: [{ timeOfDay: "13:00", start: "13:00", end: "14:00" }],
}),
);
expect(r.success).toBe(false);
});
it("rejects a duplicate timeOfDay", () => {
const r = scheduleSchema.safeParse(
baseSchedule({
doseWindows: [
{ timeOfDay: "07:00", start: "07:00", end: "08:00" },
{ timeOfDay: "07:00", start: "08:00", end: "09:00" },
],
}),
);
expect(r.success).toBe(false);
});
it("rejects a per-entry start > end", () => {
const r = scheduleSchema.safeParse(
baseSchedule({
doseWindows: [{ timeOfDay: "07:00", start: "12:00", end: "07:00" }],
}),
);
expect(r.success).toBe(false);
});
it("matches a legacy single-time schedule against windowStart", () => {
const r = scheduleSchema.safeParse({
windowStart: "08:00",
windowEnd: "10:00",
// no timesOfDay → effective dose time is windowStart
doseWindows: [{ timeOfDay: "08:00", start: "08:00", end: "11:00" }],
});
expect(r.success).toBe(true);
});
});