/* Laesst JEDE Pruefung laufen und zaehlt mit. WARUM EIN EIGENER LAeUFER und nicht eine Zeile in der Shell: 1. DIE ANZAHL MUSS STIMMEN. Ein gruener Lauf ist kein Beweis, solange nicht auch die Zahl der Pruefungen stimmt -- eine Datei, die still nicht startet, faellt sonst niemandem auf. Hier wird jede Datei einzeln gezaehlt, und die Gesamtzahl der Einzelpruefungen steht am Ende. 2. EIN FEHLSCHLAG DARF DEN LAUF NICHT ABBRECHEN. Sonst sieht man den ersten Fehler und weiss nichts ueber die restlichen zwanzig. 3. DIE DAUER GEHOeRT DAZU. Eine Pruefung, die ploetzlich dreimal so lange braucht, ist meist eine, die auf etwas wartet, das es nicht mehr gibt. */ import { readdirSync } from "node:fs"; import { spawn } from "node:child_process"; import { join } from "node:path"; const ORDNER = "server"; const dateien = readdirSync(ORDNER) .filter((n) => n.startsWith("pruef-") && n.endsWith(".mjs")) .sort(); console.log(`${dateien.length} Pruefdateien gefunden\n`); /* ---------- JEDE PRUEFUNG BEKOMMT EINE FRIST (06.09.2026) --------------- VIERTER GRUND fuer diesen eigenen Laeufer, und er hat mich an einem Tag ZWEIMAL erwischt: Eine Pruefung, die haengt, bringt den GANZEN Lauf zum Stillstand -- ohne Fehlermeldung, ohne FEHL, ohne Absturz. Es steht einfach nichts mehr da. Von aussen sieht "noch nicht fertig" genauso aus wie "haengt fuer immer", und deshalb faellt es erst Stunden spaeter auf. * pruef-alle-wege lief in den Ereignisstrom des Chats und wartete auf ein Ende, das es nie gibt (behoben in der Datei selbst). * pruef-call-kategorien blieb DREI STUNDEN stehen. Sie hatte, anders als pruef-handy, keine eigene Notbremse. Einzelne Dateien nachzuruesten hilft genau so lange, bis jemand die naechste ohne Notbremse schreibt. Deshalb sitzt sie jetzt HIER, wo sie fuer alle gilt -- auch fuer die, die es noch nicht gibt. Und eine ueberschrittene Frist ist ein FEHLER, kein Ueberspringen: Sie wird gezaehlt, gemeldet und mit der letzten Ausgabe der Datei belegt, damit man sieht, wo sie stehengeblieben ist. Ein stiller Stillstand waere das Gegenteil. 600 Sekunden sind grosszuegig bemessen: Die langsamste ehrliche Pruefung im Haus (pruef-barrierefrei-workspace) braucht 190 s. Wer das Dreifache davon ueberschreitet, wartet nicht -- er haengt. */ const FRIST_MS = 600_000; const laufen = (datei) => new Promise((fertig) => { const start = Date.now(); const kind = spawn(process.execPath, [join(ORDNER, datei)], { stdio: ["ignore", "pipe", "pipe"], }); let aus = ""; let abgewuergt = false; kind.stdout.on("data", (d) => { aus += d; }); kind.stderr.on("data", (d) => { aus += d; }); const wecker = setTimeout(() => { abgewuergt = true; /* Erst freundlich, dann bestimmt. Ein Playwright-Browser haengt sonst als Waise weiter und belegt Speicher bis zum Neustart. */ try { kind.kill("SIGTERM"); } catch { /* schon weg */ } setTimeout(() => { try { kind.kill("SIGKILL"); } catch { /* schon weg */ } }, 3000); }, FRIST_MS); kind.on("close", (code) => { clearTimeout(wecker); const sek = Math.round((Date.now() - start) / 100) / 10; /* ==== GROSS ODER KLEIN -- BEIDES ZAEHLT (25.09.2026) ========== Hier stand `(ok|FEHL)`. Acht Pruefdateien schreiben aber „OK" in Grossbuchstaben (pruef-kachelfarben, -livepunkt, -tippziele, -turn-wege, -leerzustand, -nachfrage, -tagesruf, -anruf-klingelt). Fuer sie zaehlte dieser Ausdruck NULL -- und weil „null Pruefungen ist ein Fehler" (die Regel direkt darunter, und sie ist richtig), standen sie Nacht fuer Nacht als ROT in der Notiz, obwohl sie fehlerfrei durchliefen. Exitcode 0, keine FEHL-Zeile, trotzdem rot. Zwei Schaeden auf einmal: Ihre Pruefungen fehlten in der Gesamtzahl (also genau der Zahl, die vor stillen Aussetzern schuetzen soll), und acht dauerhaft rote Eintraege gewoehnen einem das Hinsehen ab. DIE SCHREIBWEISE WIRD NICHT VEREINHEITLICHT, SONDERN GELESEN. Acht Dateien umzuschreiben waere acht Gelegenheiten, etwas kaputtzumachen -- und die neunte Datei, die jemand morgen anlegt, schreibt ohnehin wieder, was sie will. Ein Zaehler, der beide Schreibweisen kennt, braucht niemanden zu erziehen. */ const einzeln = (aus.match(/^\s{2}(ok|OK|FEHL)\s/gm) || []).length; const schlecht = (aus.match(/^\s{2}FEHL\s/gm) || []).length; if (abgewuergt) { aus += `\n\n FEHL DIE PRUEFUNG HING. Nach ${Math.round(FRIST_MS / 1000)} s kam nichts mehr` + " -- abgebrochen.\n Letzte Ausgabe davor steht oben; dort ist sie stehengeblieben." + "\n Haeufigste Ursache: eine Verbindung, die nie endet (Ereignisstrom)," + "\n oder ein warten auf etwas, das nicht kommt.\n"; } fertig({ datei, code: abgewuergt ? 124 : code, sek, /* Die abgewuergte Pruefung zaehlt als fehlgeschlagen, nicht als "keine Pruefung gelaufen" -- das sind zwei verschiedene Sachen, und beide muessen unterscheidbar bleiben. */ einzeln: einzeln + (abgewuergt ? 1 : 0), schlecht: schlecht + (abgewuergt ? 1 : 0), aus, hing: abgewuergt, }); }); }); let gesamt = 0, kaputt = 0, einzelnGesamt = 0, einzelnSchlecht = 0; const fehlerhafte = []; for (const datei of dateien) { const r = await laufen(datei); gesamt++; einzelnGesamt += r.einzeln; einzelnSchlecht += r.schlecht; /* NULL PRUEFUNGEN IST EIN FEHLER, KEIN ERFOLG. Beim ersten Lauf stand hier nur `code === 0 && schlecht === 0` -- und pruef-kopf-messen.mjs kam damit als "ok" durch, obwohl sie ueberhaupt nichts geprueft hat (sie war ein reines Messwerkzeug und endete immer mit 0). Genau so sieht eine Pruefung aus, die immer bestaetigt: gruen, und wertlos. */ const gut = r.code === 0 && r.schlecht === 0 && r.einzeln > 0; if (!gut) { kaputt++; fehlerhafte.push(r); } console.log( `${gut ? " ok " : " FEHL "} ${r.datei.padEnd(30)}` + `${String(r.einzeln).padStart(4)} Pruefungen` + `${r.schlecht ? `, ${r.schlecht} FEHLGESCHLAGEN` : ""}` + ` ${String(r.sek).padStart(6)} s` + (r.einzeln === 0 ? " KEINE PRUEFUNG GELAUFEN" : "")); } console.log("\n" + "=".repeat(64)); console.log(`Dateien: ${gesamt - kaputt} von ${gesamt} in Ordnung`); console.log(`Einzelpunkte: ${einzelnGesamt - einzelnSchlecht} von ${einzelnGesamt} bestanden`); /* Eine Datei ohne eine einzige Pruefung ist ein Fehler, kein Erfolg. */ const leer = dateien.length && einzelnGesamt === 0; if (leer) console.log("ACHTUNG: keine einzige Pruefung ist gelaufen."); for (const r of fehlerhafte) { console.log("\n" + "-".repeat(64)); console.log(`### ${r.datei} (Exitcode ${r.code})`); const zeilen = r.aus.split("\n").filter((z) => /FEHL|Error|error:/.test(z)); console.log(zeilen.slice(0, 12).join("\n") || r.aus.slice(-800)); } console.log("\n" + (kaputt || leer ? "NICHT in Ordnung." : "Alles in Ordnung.")); process.exit(kaputt || leer ? 1 : 0);