feat: complete notdienst planning, quick wins and notifications

This commit is contained in:
t.indorf
2026-10-12 15:34:09 +02:00
parent b915857951
commit 00f602a7aa
50 changed files with 2443 additions and 513 deletions
+55
View File
@@ -0,0 +1,55 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import { getNotdienstDays } from "../src/lib/notdienst-days";
const keys = (year: number, month: number, start: string, end: string) =>
getNotdienstDays(year, month, [
{ startDate: new Date(start), endDate: new Date(end) },
]).map((d) => d.toISOString().slice(0, 10));
test("Schließtage schließen inklusive beider Randtage aus", () => {
const days = keys(2030, 11, "2030-11-04T10:00:00Z", "2030-11-06T12:00:00Z");
for (const key of ["2030-11-04", "2030-11-05", "2030-11-06"])
assert.ok(!days.includes(key));
assert.ok(days.includes("2030-11-07"));
assert.ok(days.every((key) => ![0, 6].includes(new Date(key).getUTCDay())));
});
test("Berliner Mitternacht am Monatsanfang zählt zum richtigen Monat", () => {
const days = keys(2030, 11, "2030-10-31T23:00:00Z", "2030-11-01T22:59:00Z");
assert.ok(!days.includes("2030-11-01"));
assert.ok(days.includes("2030-11-04"));
});
test("Sommerzeit und Schließzeiten über Monatsgrenzen", () => {
const days = keys(2030, 7, "2030-06-28T22:00:00Z", "2030-07-02T21:59:00Z");
assert.ok(!days.includes("2030-07-01"));
assert.ok(!days.includes("2030-07-02"));
assert.ok(days.includes("2030-07-03"));
});
test("Überlappende Schließtermine erzeugen keine doppelten Tage", () => {
const closures = [
{ startDate: new Date("2030-11-04"), endDate: new Date("2030-11-05") },
{ startDate: new Date("2030-11-05"), endDate: new Date("2030-11-06") },
];
const days = getNotdienstDays(2030, 11, closures);
assert.equal(new Set(days.map(Number)).size, days.length);
assert.equal(days.length, getNotdienstDays(2030, 11, []).length - 3);
});
test("Berliner Monatswechsel bestimmt Zielmonat und Sperrfrist", async () => {
const { getTargetMonthData } = await import("../src/lib/date-utils");
assert.equal(getTargetMonthData(new Date("2030-10-26T22:00:00Z")).isLocked, true);
const data = getTargetMonthData(new Date("2030-12-31T23:00:00Z"));
assert.equal(data.targetYear, 2031); assert.equal(data.targetMonth, 2);
});
test("Automatische Erinnerung beginnt um 9 Uhr vor der Eintragsfrist", async () => {
const { isNotdienstReminderDue } = await import("../src/lib/date-utils");
assert.equal(isNotdienstReminderDue(7, new Date("2030-10-18T07:00:00Z")), false);
assert.equal(isNotdienstReminderDue(7, new Date("2030-10-19T06:59:00Z")), false);
assert.equal(isNotdienstReminderDue(7, new Date("2030-10-19T07:00:00Z")), true);
assert.equal(isNotdienstReminderDue(0, new Date("2030-10-19T07:00:00Z")), false);
});