import assert from "node:assert/strict"; import { test } from "node:test"; import { absenceDate, absenceToday, canEditOwnAbsence, canManageKitaAbsences, } from "../src/lib/absence-policy"; import { familyInvitationView } from "../src/lib/family-invitation-view"; test("Abwesenheitsdatum akzeptiert echte Kalendertage und lehnt Überläufe ab", () => { assert.equal( absenceDate("2028-02-29").toISOString(), "2028-02-29T00:00:00.000Z", ); for (const value of [ "2026-02-29", "2026-04-31", "2026-13-01", "2026-1-09", "2026-10-09T12:00:00", "invalid", ]) assert.throws(() => absenceDate(value)); }); test("Die Datumsgrenze folgt Berlin auch über Mitternacht und die Zeitumstellung", () => { assert.equal(absenceToday(new Date("2026-10-09T21:59:59Z")), "2026-10-09"); assert.equal(absenceToday(new Date("2026-10-09T22:00:00Z")), "2026-10-10"); assert.equal(absenceToday(new Date("2026-10-25T22:59:59Z")), "2026-10-25"); assert.equal(absenceToday(new Date("2026-10-25T23:00:00Z")), "2026-10-26"); assert.equal(canEditOwnAbsence("2026-10-09", "2026-10-09"), false); assert.equal(canEditOwnAbsence("2026-10-10", "2026-10-09"), true); assert.equal(canManageKitaAbsences("ELTERN"), false); for (const role of ["ADMIN", "KOORDINATOR", "ERZIEHER"]) assert.equal(canManageKitaAbsences(role), true); }); test("Einladungsanzeige unterscheidet Aktivierung, Fehler, Ablauf und laufenden Versand", () => { const now = new Date("2026-10-09T12:00:00Z"); const base = { expiresAt: new Date("2026-10-16T12:00:00Z"), sentAt: null, failed: false, leaseUntil: null, status: "PENDING", }; assert.equal(familyInvitationView(null, null, now).status, "open"); assert.equal( familyInvitationView(null, { ...base, failed: true }, now).status, "failed", ); assert.equal( familyInvitationView(null, { ...base, expiresAt: now }, now).status, "expired", ); assert.equal( familyInvitationView(null, { ...base, sentAt: now }, now).status, "sent", ); assert.equal( familyInvitationView( null, { ...base, leaseUntil: new Date(now.getTime() + 1000) }, now, ).status, "sending", ); assert.equal( familyInvitationView(now, { ...base, failed: true }, now).status, "active", ); });