/* ===================================================================== Merkt der Waechter, wenn die Sicherung ausfaellt? Die Prüfung selbst muss geprüft werden -- sonst hat man am Ende zwei Dinge, auf die man sich verlässt und von denen keines funktioniert. Getestet wird mit echten Ordnern in einem temporären Verzeichnis, nicht mit nachgebauten Attrappen: Die Funktion soll genau das tun, was sie auf dem Server tut. ===================================================================== */ import { mkdtempSync, mkdirSync, writeFileSync, utimesSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { sicherungZustand, MAX_STUNDEN, MIN_BYTE } from "./server-internal/lib/sicherung-pruefen.js"; let ok = 0, fehl = 0; const pruefe = (name, gut, info) => { if (gut) { ok++; console.log(" ok " + name + (info ? " -> " + info : "")); } else { fehl++; console.log(" FEHL " + name + (info ? " -> " + info : "")); } }; const wurzel = mkdtempSync(join(tmpdir(), "sicherungstest-")); /** Legt eine Sicherung an: Alter in Stunden, Groesse in Byte. */ function baue(name, altStunden, groesse) { const basis = join(wurzel, name); const tag = join(basis, "taeglich", "2026-08-27"); mkdirSync(tag, { recursive: true }); const datei = join(tag, "dogfather-internal.db"); writeFileSync(datei, Buffer.alloc(groesse)); const zeit = new Date(Date.now() - altStunden * 3600000); utimesSync(datei, zeit, zeit); return basis; } console.log("\n=== Der gute Fall ==="); { const r = sicherungZustand(baue("frisch", 2, 800 * 1024)); pruefe("frische Sicherung gilt als heil", r && r.heil === true, r?.grund); } console.log("\n=== Die Ausfaelle, die gemeldet werden muessen ==="); { const r = sicherungZustand(join(wurzel, "gibtesnicht")); pruefe("gar kein Sicherungsordner", r && r.heil === false, r?.grund); } { /* Ordner da, aber keine Laeufe darin. */ const basis = join(wurzel, "leer"); mkdirSync(join(basis, "taeglich"), { recursive: true }); const r = sicherungZustand(basis); pruefe("Ordner vorhanden, aber kein Lauf", r && r.heil === false, r?.grund); } { /* Der Unterordner "taeglich" fehlt ganz -- beim ersten Entwurf lieferte das faelschlich "kein Urteil" statt eines Befunds. */ const basis = join(wurzel, "ohnetaeglich"); mkdirSync(basis, { recursive: true }); const r = sicherungZustand(basis); pruefe("Unterordner taeglich fehlt", r && r.heil === false, r?.grund); } { /* Lauf angelegt, aber die Datenbank fehlt darin. */ const basis = join(wurzel, "ohnedb"); mkdirSync(join(basis, "taeglich", "2026-08-27"), { recursive: true }); const r = sicherungZustand(basis); pruefe("Lauf ohne Datenbankdatei", r && r.heil === false, r?.grund); } { const r = sicherungZustand(baue("alt", MAX_STUNDEN + 3, 800 * 1024)); pruefe("Sicherung zu alt", r && r.heil === false, r?.grund); } { const r = sicherungZustand(baue("winzig", 2, 1024)); pruefe("Sicherung verdaechtig klein", r && r.heil === false, r?.grund); } { const r = sicherungZustand(baue("leerdatei", 2, 0)); pruefe("Sicherung mit 0 Byte", r && r.heil === false, r?.grund); } console.log("\n=== Die Grenze darf nicht zu eng sitzen ==="); { /* Genau 24 Stunden: Das ist der Normalfall kurz vor dem naechsten Lauf und darf auf keinen Fall Alarm ausloesen. Genau hier entstehen die Fehlalarme, die dazu fuehren, dass man die Meldungen abschaltet. */ const r = sicherungZustand(baue("grenzfall", 24, 800 * 1024)); pruefe("24 Stunden alt gilt noch als heil", r && r.heil === true, r?.grund); } { /* Und knapp darueber muss es kippen. */ const r = sicherungZustand(baue("knappdrueber", MAX_STUNDEN + 0.5, 800 * 1024)); pruefe("knapp ueber der Grenze kippt es", r && r.heil === false, r?.grund); } { /* Knapp ueber der Mindestgroesse: darf nicht anschlagen. */ const r = sicherungZustand(baue("knappgross", 2, MIN_BYTE + 1024)); pruefe("knapp ueber der Mindestgroesse ist heil", r && r.heil === true, r?.grund); } console.log("\n=== Der juengste Lauf zaehlt, nicht irgendeiner ==="); { /* Ein alter Lauf daneben darf das Urteil nicht verfaelschen. */ const basis = join(wurzel, "mehrere"); for (const [tag, alt] of [["2026-08-20", 170], ["2026-08-27", 2]]) { const o = join(basis, "taeglich", tag); mkdirSync(o, { recursive: true }); const d = join(o, "dogfather-internal.db"); writeFileSync(d, Buffer.alloc(800 * 1024)); const z = new Date(Date.now() - alt * 3600000); utimesSync(d, z, z); } const r = sicherungZustand(basis); pruefe("alter Lauf daneben stoert nicht", r && r.heil === true, r?.grund); } { /* Umgekehrt: Ist der juengste Lauf alt, hilft ein Ordner mit anderem Namen daneben nicht -- geprueft wird der juengste, nicht der beste. */ const basis = join(wurzel, "nurAlt"); const o = join(basis, "taeglich", "2026-08-01"); mkdirSync(o, { recursive: true }); const d = join(o, "dogfather-internal.db"); writeFileSync(d, Buffer.alloc(800 * 1024)); const z = new Date(Date.now() - 600 * 3600000); utimesSync(d, z, z); const r = sicherungZustand(basis); pruefe("nur ein alter Lauf wird gemeldet", r && r.heil === false, r?.grund); } console.log("\n=== Fremde Ordner werden ignoriert ==="); { /* Im Zielverzeichnis koennen andere Dinge liegen (das Protokoll, ein Ordner "woechentlich"). Nur YYYY-MM-DD darf mitzaehlen. */ const basis = join(wurzel, "fremdes"); mkdirSync(join(basis, "taeglich", "zwischenablage"), { recursive: true }); mkdirSync(join(basis, "taeglich", "2026-08-27"), { recursive: true }); const d = join(basis, "taeglich", "2026-08-27", "dogfather-internal.db"); writeFileSync(d, Buffer.alloc(800 * 1024)); const r = sicherungZustand(basis); pruefe("Ordner ohne Datumsnamen zaehlen nicht mit", r && r.heil === true, r?.grund); } rmSync(wurzel, { recursive: true, force: true }); console.log(`\n===== ${ok} bestanden, ${fehl} fehlgeschlagen =====`); process.exitCode = fehl ? 1 : 0;