import { afterEach, describe, expect, it, vi } from "vitest"; import { AUSWEIS_GUELTIG_SEKUNDEN, AUSWEIS_FEHLER_TEXT, ausweisErzeugen, ausweisPruefen, } from "@/lib/dienstausweis"; import { markeErzeugen } from "@/lib/kiosk/anmeldemarke"; /** * Der Dienstausweis weist eine Person aus. Er verdient dieselbe Sorgfalt wie * ein Anmeldeweg — auch wenn er nichts öffnet, denn wer ihn fälschen kann, * kann an einer Absperrung behaupten, jemand anderes zu sein. * * Der interessanteste Test ist der letzte: Anmeldemarke und Ausweis sind mit * demselben Geheimnis signiert. Ohne den Zweck im signierten Teil ginge das * eine als das andere durch — dann wäre der Ausweis ein Anmeldeweg. */ const PERSON = "8f7d3b1e-2c4a-4e6f-9a1b-0c2d4e6f8a1b"; const ORGANISATION = "1a2b3c4d-5e6f-4a8b-9c0d-1e2f3a4b5c6d"; afterEach(() => { vi.useRealTimers(); }); describe("Dienstausweis", () => { it("erkennt den eigenen Ausweis wieder", () => { const ergebnis = ausweisPruefen(ausweisErzeugen(PERSON, ORGANISATION)); expect(ergebnis).toMatchObject({ personId: PERSON, organisationId: ORGANISATION }); expect("fehler" in ergebnis).toBe(false); }); it("nennt den Zeitpunkt der Ausstellung", () => { vi.useFakeTimers(); vi.setSystemTime(new Date("2026-09-04T10:00:00Z")); const ergebnis = ausweisPruefen(ausweisErzeugen(PERSON, ORGANISATION)); expect("erzeugtAm" in ergebnis && ergebnis.erzeugtAm).toEqual( new Date("2026-09-04T10:00:00Z"), ); }); it("weist einen Code ab, an dem jemand gedreht hat", () => { const code = ausweisErzeugen(PERSON, ORGANISATION); const gefaelscht = code.replace(PERSON, "00000000-0000-4000-8000-000000000000"); expect(ausweisPruefen(gefaelscht)).toEqual({ fehler: "verfaelscht" }); }); it("weist eine abgeschnittene Signatur ab", () => { const code = ausweisErzeugen(PERSON, ORGANISATION); expect(ausweisPruefen(code.slice(0, -5))).toEqual({ fehler: "verfaelscht" }); }); it("weist Unsinn ab", () => { expect(ausweisPruefen("")).toEqual({ fehler: "ungueltig" }); expect(ausweisPruefen("hallo")).toEqual({ fehler: "ungueltig" }); expect(ausweisPruefen("a.b.c.d")).toEqual({ fehler: "ungueltig" }); }); it("gilt kurz vor Ablauf noch", () => { vi.useFakeTimers(); const code = ausweisErzeugen(PERSON, ORGANISATION); vi.advanceTimersByTime((AUSWEIS_GUELTIG_SEKUNDEN - 1) * 1000); expect("personId" in ausweisPruefen(code)).toBe(true); }); it("gilt eine Sekunde nach Ablauf nicht mehr", () => { vi.useFakeTimers(); const code = ausweisErzeugen(PERSON, ORGANISATION); vi.advanceTimersByTime((AUSWEIS_GUELTIG_SEKUNDEN + 1) * 1000); expect(ausweisPruefen(code)).toEqual({ fehler: "abgelaufen" }); }); /** * Ein Code aus der Zukunft ist kein gültiger Code. Ohne diese Prüfung * könnte jemand mit einem gefälschten Zeitstempel einen Ausweis bauen, der * beliebig lange gilt — sofern er an das Geheimnis käme. */ it("nimmt keinen Code aus der Zukunft an", () => { vi.useFakeTimers(); vi.setSystemTime(new Date("2026-09-04T12:00:00Z")); const code = ausweisErzeugen(PERSON, ORGANISATION); vi.setSystemTime(new Date("2026-09-04T11:00:00Z")); expect(ausweisPruefen(code)).toEqual({ fehler: "abgelaufen" }); }); it("erzeugt bei jedem Aufruf einen anderen Code", () => { vi.useFakeTimers(); const erster = ausweisErzeugen(PERSON, ORGANISATION); vi.advanceTimersByTime(1000); const zweiter = ausweisErzeugen(PERSON, ORGANISATION); expect(erster).not.toBe(zweiter); }); /** * Der Ausweis darf nicht als Anmeldemarke durchgehen und umgekehrt. Beide * sind mit AUTH_SECRET signiert — was sie trennt, ist der Zweck im * signierten Teil. */ it("nimmt keine Anmeldemarke als Ausweis an", () => { const marke = markeErzeugen(PERSON, ORGANISATION); expect(ausweisPruefen(marke)).toEqual({ fehler: "ungueltig" }); }); it("hat für jeden Fehler einen Satz, den ein Mensch versteht", () => { for (const text of Object.values(AUSWEIS_FEHLER_TEXT)) { expect(text.length).toBeGreaterThan(10); expect(text.endsWith(".")).toBe(true); } }); });