/* ===================================================================== KANN JEDE PRÜFUNG IHREN PORT ÜBERHAUPT BEKOMMEN? ===================================================================== ENTSTANDEN AUS EINEM ABSTURZ, DEN ICH SELBST AUSGELÖST HABE (22./23.09.2026). Die Portnummer einer Prüfung wird aus ihrer STELLE IM ALPHABET abgeleitet (helfer-port.mjs). Das ist eindeutig von der Bauart her — zwei Dateien können nicht dieselbe Stelle haben — und genau deshalb gut. Es hat aber eine Nebenwirkung, die dort auch ausdrücklich steht: Kommt eine neue Prüfung dazu, verschieben sich ALLE Nummern dahinter. In der Nacht zum 23.09. sind sechs neue Prüfdateien entstanden. `pruef-bereiche-lesend` rutschte dadurch auf Port 5040 — und den hält auf diesem Rechner ein Windows-Dienst. Der Lauf endete mit [uncaughtException] Error: listen EACCES 127.0.0.1:5040 also einem Stapelauszug aus node:net, der wie ein Fehler im Code aussieht. Gefunden habe ich es durch Zufall: Ich wollte nur sehen, ob eine andere Änderung etwas kaputt gemacht hat. --------------------------------------------------------------------- WARUM DAS EINE EIGENE PRÜFUNG IST UND KEINE ZEILE IN EINER ANDEREN Weil die Frage nicht „ist mein Code richtig" lautet, sondern „kann hier überhaupt gemessen werden". Das ist der dritte Ausgang, und er betrifft ALLE Prüfungen gleichzeitig. Eine einzelne kann ihn für sich beantworten (das tut `portMussFreiSein` seit dem 23.09. auch); dass der ganze Satz durchläuft, sieht man erst, wenn man den ganzen Satz ansieht. Das dauert unter einer Sekunde. --------------------------------------------------------------------- BELEGT IST ETWAS ANDERES ALS GESPERRT — und nur eines davon ist ein Befund: EACCES Das System gibt den Port nicht her. Das geht nicht vorbei. Hier MUSS der Ausweichport frei sein, sonst ist diese Prüfung auf diesem Rechner nicht lauffähig. EADDRINUSE Jemand hört gerade darauf — meist ein Lauf, der eben zu Ende ging und dessen Server noch schließt. Das geht vorbei, und es als Fehler zu melden wäre eine Warnung, die immer kommt. Deshalb der dritte Ausgang. Der Unterschied ist der ganze Punkt. Würde hier beides gleich behandelt, wäre diese Prüfung im Gesamtlauf regelmäßig rot — und dann liest niemand mehr, wenn sie einmal recht hat. ===================================================================== */ import { readdirSync } from "node:fs"; import { createServer } from "node:net"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; import { notbremse } from "./helfer-notbremse.mjs"; notbremse(60_000, "pruef-ports"); const HIER = dirname(fileURLToPath(import.meta.url)); let fehler = 0, geprueft = 0, offen = 0; const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; }; const unklar = (t) => { offen++; console.log(" -- " + t); }; const { portNummer, AUSWEICHEN, GESPERRT, MESS_BASIS } = await import("./helfer-port.mjs"); console.log("=== Kann jede Pruefung und jede Messung ihren Port bekommen? ==="); console.log(""); /* DIE LISTE WIRD GELESEN, NICHT GEPFLEGT -- und zwar mit derselben Regel wie in helfer-port.mjs selbst: alle `pruef-*.mjs` im Serverordner, sortiert. Eine eigene Liste hier wäre die, die beim Hinzufügen der nächsten Prüfung nicht mitwächst -- also genau der Fehler, den diese Datei verhindern soll. */ const dateien = readdirSync(HIER) .filter((f) => f.startsWith("pruef-") && f.endsWith(".mjs")) .sort(); /* DIE MESSDATEIEN GEHOEREN SEIT DEM 01.10.2026 DAZU. Sie hatten ihre Nummern bis dahin von Hand -- einundzwanzig von 24 sogar ohne jeden Waechter -- und lagen teils mitten im Bereich der Pruefungen. Jetzt leiten sie ab, aus ihrer eigenen Liste, ab MESS_BASIS. Dieser Bereich ist neu, und ob der Rechner ihn hergibt, war eine Annahme, bis er hier durchprobiert wurde. */ const messen = readdirSync(HIER) .filter((f) => f.startsWith("mess-") && f.endsWith(".mjs")) .sort(); ok(dateien.length > 50, `${dateien.length} Pruefdateien gefunden`); ok(messen.length > 10, `${messen.length} Messdateien dazu`); ok(dateien.includes("pruef-ports.mjs"), "diese Datei steht selbst in der Liste -- sonst waere die Regel eine andere" + " als die, nach der die Nummern vergeben werden"); /** Was sagt das Betriebssystem zu diesem Port? */ const lage = (port) => new Promise((fertig) => { const s = createServer(); s.once("error", (f) => fertig(f.code || "FEHLER")); s.once("listening", () => s.close(() => fertig(null))); s.listen(port, "127.0.0.1"); }); /* ===================================================================== 1. DIE ABLEITUNG SELBST ===================================================================== */ console.log("--- Die Nummern ---"); const nummern = []; for (let i = 0; i < dateien.length; i++) { for (const nr of [0, 1]) nummern.push({ datei: dateien[i], nr, port: portNummer(i * 2 + nr) }); } /* IN DIESELBE LISTE, nicht in eine zweite: So erben die Messnummern jeden Abschnitt dieser Datei -- Doppelungen, Sperrliste, die Frage ans System, den Ausweichport. Eine eigene Liste daneben waere die, die beim naechsten Abschnitt vergessen wird. */ for (let i = 0; i < messen.length; i++) { for (const nr of [0, 1]) { nummern.push({ datei: messen[i], nr, port: portNummer(i * 2 + nr, MESS_BASIS) }); } } const wieViele = dateien.length + messen.length; ok(nummern.length === wieViele * 2, `${nummern.length} Nummern fuer ${wieViele} Dateien (je zwei)`); /* DASS SICH DIE BEIDEN BEREICHE NICHT BERUEHREN, steht eine Zeile weiter unten schon als „keine Nummer doppelt" drin. Hier zusaetzlich in Klartext, weil die Zahl das Interessante ist: Wie viele Pruefdateien passen noch dazwischen, bevor es eng wird? */ const hoechstPruef = Math.max(...nummern .filter((x) => x.datei.startsWith("pruef-")).map((x) => x.port)); ok(hoechstPruef < MESS_BASIS, `die Pruefungen enden bei ${hoechstPruef}, die Messungen fangen bei ${MESS_BASIS} an` + ` — Luft fuer ${((MESS_BASIS - hoechstPruef) / 2) | 0} weitere Pruefdateien`); const doppelt = nummern .map((x) => x.port) .filter((p, i, alle) => alle.indexOf(p) !== i); ok(nummern.length > 0 && doppelt.length === 0, `keine Nummer doppelt (${doppelt.length} Ueberschneidungen)`); const inGesperrt = nummern.filter((x) => GESPERRT.has(x.port)); ok(nummern.length > 0 && inGesperrt.length === 0, `keine liegt auf einem gesperrten Port (${inGesperrt.map((x) => x.port).join(", ") || "keine"})`); /* GEGENPROBE ZUR ABLEITUNG: Ein gesperrter Port MUSS uebersprungen werden. Ohne diese Zeile waere „keine liegt auf einem gesperrten" auch dann wahr, wenn die Sperrliste gar nicht angesehen wird. */ { const bekannt = [...GESPERRT].filter((p) => p >= 5000).sort((a, b) => a - b)[0]; if (bekannt === undefined) { unklar("kein gesperrter Port oberhalb von 5000 in der Liste --" + " die Gegenprobe hat nichts, woran sie sich zeigen koennte"); } else { const alle = new Set(nummern.map((x) => x.port)); ok(!alle.has(bekannt), `Gegenprobe: der gesperrte Port ${bekannt} kommt in keiner Nummer vor`); } } /* ===================================================================== 2. UND WAS DAS SYSTEM DAZU SAGT ===================================================================== */ console.log(""); console.log("--- Was der Rechner hergibt ---"); const gesperrt = [], belegt = []; for (const x of nummern) { const l = await lage(x.port); if (l === "EACCES") gesperrt.push(x); else if (l !== null) belegt.push({ ...x, grund: l }); } ok(nummern.length > 0, `${nummern.length} Ports durchprobiert, ${gesperrt.length} vom System gesperrt,` + ` ${belegt.length} gerade belegt`); /* EIN GESPERRTER PORT IST KEIN BEFUND -- solange das Ausweichen greift. `portMussFreiSein` nimmt dann Port + AUSWEICHEN. Was hier geprueft wird, ist genau das: dass dieser Weg offensteht. */ for (const x of gesperrt) { const ersatz = x.port + AUSWEICHEN; const l2 = await lage(ersatz); ok(l2 === null, `${x.datei} (Nr. ${x.nr}): Port ${x.port} ist vom System gesperrt,` + ` der Ausweichport ${ersatz} ist ${l2 === null ? "frei" : "es auch nicht (" + l2 + ")"}`); } if (!gesperrt.length) { ok(true, "kein einziger Port ist vom System gesperrt"); } /* BELEGT GEHT VORBEI. Kein Fehler, sondern der dritte Ausgang: Hier laesst sich gerade nicht sagen, ob die Nummer brauchbar ist. */ for (const x of belegt.slice(0, 5)) { unklar(`${x.datei} (Nr. ${x.nr}): Port ${x.port} ist gerade belegt (${x.grund})` + " -- das geht vorbei, hier ist nichts zu entscheiden"); } if (belegt.length > 5) { unklar(`... und ${belegt.length - 5} weitere belegte Ports`); } /* ===================================================================== 3. DER TEUERSTE FALL: ZWEI LAEUFE GLEICHZEITIG ===================================================================== Sind SEHR viele Ports belegt, laeuft vermutlich ein zweiter Prueflauf. Das ist keine Aussage ueber den Code, aber eine, die man sehen will -- am 06.09.2026 haben zwei gleichzeitige Laeufe im anderen Projekt 16 Fehlschlaege erzeugt, keiner davon echt. ===================================================================== */ if (belegt.length > nummern.length / 4) { unklar(`${belegt.length} von ${nummern.length} Ports sind belegt --` + " laeuft parallel noch ein zweiter Prueflauf? Dessen Ergebnisse" + " und diese hier waeren beide unbrauchbar."); } console.log(""); console.log(`${fehler === 0 ? "BESTANDEN" : "FEHLGESCHLAGEN"}` + ` -- ${geprueft} geprueft, ${fehler} Fehler, ${offen} nicht nachsehbar`); process.exit(fehler === 0 ? 0 : 1);