Filipe, 24.09.2026: "wenn ich bei der einen was mache soll nichts bei
der anderen passieren." -- und heute: "ja los".
GEMESSEN, BEVOR ETWAS ANGEFASST WURDE (jedes Brett x beide Adressen x
vier Rollen):
AGENTUR-Adresse DogFather 18 Bretter
Manager 6 Bretter
Creator 6 Bretter
Modi kommt nicht rein (401)
CREW-Adresse DogFather 18 Bretter
Modi 13 Bretter
Manager kommt nicht rein (401)
Creator kommt nicht rein (401)
Die Anmeldung war also dicht. Durch griff genau EINE Rolle: DogFather.
Er wohnt in beiden Haeusern, und die Riegel in sichtbarEintrag()
fragten nach der ROLLE, nicht nach der Adresse. Auf der Crew-Adresse
stand damit das Brett der Agentur samt Inhalt.
Nachgemessen ist der Durchgriff AELTER als der gestrige Eventkarten-
Umbau -- zweimal gemessen, mit und ohne ihn, gleiches Ergebnis.
RIEGEL 0 in sichtbarEintrag(): Wer auf einer Adresse angemeldet ist,
sieht nur Eintraege dieses Hauses. Er haengt den uebrigen Riegeln
UM, statt in jeden Ausgang geschrieben zu werden -- die Funktion hat
drei Rueckgabepunkte, und der naechste waere sonst wieder offen.
Nachher, dieselbe Messung: DogFather sieht auf der Agenturadresse nur
Agentur-Eintraege, auf der Crew-Adresse nur die des Rudels. Manager,
Creator und Modi unveraendert.
WAS DABEI SCHIEFGING UND WIE ES AUFFIEL
1. Die erste Fassung liess bei `haus IS NULL` den BEREICH entscheiden.
pruef-haus-trennung.mjs wurde sofort rot: "Lunas Live vom Montag"
verschwand von der Agenturadresse. `live`, `technik` und
`community` tragen BEIDES -- die Kacheln von Team Dogi und die
Creator-Akten. Eine Regel, die jedem Brett genau ein Haus zuweist,
kann das nicht. Jetzt bleibt ein Eintrag ohne Haus sichtbar: ein
Eintrag zu viel faellt auf, ein fehlender nicht.
2. Damit NULL kein Dauerloch ist: FUENF von ACHT Stellen, die
Eintraege anlegen, setzten `haus` gar nicht (workspace-video.js,
-content.js, -bewerbung.js, -treff.js, -vorlagen.js). Nachgetragen.
3. Und `person.haus` war dafuer der falsche Massstab: Legt DogFather
ueber die Agenturadresse ein Highlight an, gehoert es trotzdem dem
Rudel -- sonst sieht die Community es nie. pruef-treff.mjs hat das
gefunden (2 Fehler). Neu: hausFuerNeuenEintrag() -- bei den sieben
Brettern des Rudels entscheidet das BRETT, sonst die Adresse.
NEU: pruef-haus-luecke.mjs (12 Pruefungen). Sie sucht die
Einfuege-Stellen im Quelltext und wird rot, sobald eine neunte
dazukommt, die `haus` vergisst -- mit Gegenprobe, dass das Suchmuster
eine solche Stelle auch wirklich erkennt. Ein Kommentar daneben haette
es nicht verhindert; das steht so schon im Projektgedaechtnis.
NEBENBEI: In bereich.js stand seit gestern `|| "Agentur-Events"` als
Rueckfall fuer das Etikett der Vorschau. pruef-treff.mjs verbietet
das zu Recht -- wie ein Brett heisst, haengt am Haus, und der Server
sagt es. Der Rueckfall ist weg; fehlt die Angabe, steht lieber kein
Etikett da als ein falsches.
pruef-treff.mjs nachgezogen (85 -> 86 Pruefungen, nicht weniger): Die
Zusage "DogFather sieht den Beitrag" wird jetzt auf der Crew-Adresse
geprueft, mit Gegenprobe fuer die Agenturadresse.
GEPRUEFT, alle gruen:
pruef-haus-luecke 12 pruef-haus-trennung 100
pruef-crew-adresse 169 pruef-treff 86
pruef-eventkarte 77 pruef-agentur 62
pruef-haus-seiten 38 pruef-eintrag-bild 24
pruef-vorlagen 24 pruef-video, -content, -bewerbung,
-bereiche-lesend: in Ordnung
Co-Authored-By: Claude Opus 5 <[email protected]>
1087 lines
55 KiB
JavaScript
1087 lines
55 KiB
JavaScript
/* =====================================================================
|
|
DER CREW — die Community und ihre sieben Bretter (11.09.2026)
|
|
|
|
Filipe:
|
|
|
|
"eine community rolle und community kategorie. wo die community
|
|
auch dan nur die sieht und die modis rechte hand und ich also
|
|
dogfather sehen die aber auch. die community aber nur die dann."
|
|
|
|
"wieso nur eine kachel fuer die community ich will dass die
|
|
mehrere kacheln haben. mit mehreren optionen."
|
|
|
|
---------------------------------------------------------------------
|
|
DIESE PRUEFUNG MISST IN BEIDE RICHTUNGEN, UND DAS IST DER GANZE PUNKT
|
|
|
|
Eine Trennung kann auf zwei Arten falsch sein, und die zweite ist die
|
|
gefaehrlichere:
|
|
|
|
1. SIE GREIFT NICHT -- die Community sieht Dinge des Hauses. Das
|
|
faellt auf, wenn jemand hinsieht.
|
|
2. SIE GREIFT ZU WEIT -- die Community sieht ihren eigenen Raum
|
|
nicht. Das faellt NICHT auf: Ein leeres Brett sieht aus wie
|
|
"hier ist nichts los", nicht wie ein Fehler. Genau so ist am
|
|
01.09. ein Manager vor einem leeren Aufgabenbrett gestanden und
|
|
hat es nicht gemeldet.
|
|
|
|
Deshalb steht zu jeder Zeile "sieht es nicht" eine Zeile "und der da
|
|
sieht es sehr wohl". Ohne die zweite waere die erste auch dann gruen,
|
|
wenn schlicht gar nichts mehr geht.
|
|
|
|
---------------------------------------------------------------------
|
|
WARUM node:http UND NICHT fetch
|
|
|
|
Das Team und die Community melden sich an verschiedenen Waenden an,
|
|
und welche Wand gilt, entscheidet der Host-Kopf. `fetch` in Node
|
|
setzt den selbst und ignoriert einen gesetzten -- die Anmeldung ginge
|
|
dann an die falsche Wand und scheiterte, ohne dass man saehe warum.
|
|
Dieselbe Falle wie am 10.09. bei den Kategorie-Kanaelen.
|
|
===================================================================== */
|
|
import { mkdtempSync, rmSync, readFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { request as httpAnfrage } from "node:http";
|
|
import { eigenerPort } from "./helfer-port.mjs";
|
|
/* ORTSZEIT, NICHT UTC. `toISOString().slice(0,10)` gibt zwischen
|
|
Mitternacht und zwei Uhr den VORTAG -- ein Test, der nachts einen
|
|
anderen Tag meint als die Anwendung. `pruef-struktur` hat genau
|
|
diese Zeile bei mir gefunden, Minuten nachdem ich sie geschrieben
|
|
hatte. */
|
|
import { tagLokal } from "./helfer-zeit.mjs";
|
|
|
|
const PORT = await eigenerPort(import.meta, "pruef-treff");
|
|
|
|
const ordner = mkdtempSync(join(tmpdir(), "ws-treff-"));
|
|
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
|
process.env.PORT = String(PORT);
|
|
process.env.SITE_ACCESS_SECRET = "lokaler-test";
|
|
process.env.SITE_PUBLIC_LAUNCH_AT = "2020-01-01T00:00:00+01:00";
|
|
|
|
const express = (await import("express")).default;
|
|
const ec = express.response.cookie;
|
|
express.response.cookie = function (n, w, o) { return ec.call(this, n, w, { ...(o || {}), secure: false }); };
|
|
import { notbremse } from "./helfer-notbremse.mjs";
|
|
import { heuteLokal } from "./helfer-tag.mjs";
|
|
await import("./index.js");
|
|
notbremse(240_000, "pruef-treff");
|
|
await new Promise((r) => setTimeout(r, 700));
|
|
|
|
process.on("uncaughtException", (f) => { console.error("ABSTURZ:", f); process.exit(7); });
|
|
process.on("unhandledRejection", (f) => { console.error("ABSTURZ (Versprechen):", f); process.exit(7); });
|
|
|
|
let fehler = 0, geprueft = 0;
|
|
const melde = (t) => console.log(t);
|
|
const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
|
|
|
const CREW = "crew.dogfather-universe.com";
|
|
const HAUS = "workspace.dogfather-universe.com";
|
|
const HAUPT = "dogfather-universe.com";
|
|
|
|
const { DatabaseSync } = await import("node:sqlite");
|
|
const { scryptSync, randomBytes, createHmac } = await import("node:crypto");
|
|
const w = await import("./workspace.js");
|
|
const treffModul = await import("./workspace-treff.js");
|
|
const { BEREICHE } = await import("./workspace-bereiche.js");
|
|
const d = new DatabaseSync(process.env.WORKSPACE_DB);
|
|
const jetzt = new Date().toISOString();
|
|
const schluessel = randomBytes(32).toString("hex");
|
|
d.prepare("INSERT INTO einstellungen (schluessel, wert, geaendert, von)"
|
|
+ " VALUES (?,?,?,NULL) ON CONFLICT(schluessel) DO NOTHING")
|
|
.run("code_kennung_schluessel", schluessel, jetzt);
|
|
function anlegen(name, rolle, code) {
|
|
const salt = randomBytes(16).toString("hex");
|
|
const hash = scryptSync(code, salt, 64, { N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex");
|
|
d.prepare("INSERT INTO personen (name, rolle, code_hash, code_salt, code_n, code_kennung, aktiv, erstellt)"
|
|
+ " VALUES (?,?,?,?,?,?,1,?)")
|
|
.run(name, rolle, hash, salt, 32768, createHmac("sha256", schluessel).update(code).digest("hex"), jetzt);
|
|
return d.prepare("SELECT last_insert_rowid() AS id").get().id;
|
|
}
|
|
const idDogi = anlegen("Filipe", "admin", "CODE-DOGI-0001");
|
|
anlegen("Rieke", "hand", "CODE-HAND-0001");
|
|
anlegen("Frida", "modi", "CODE-MODI-0001");
|
|
anlegen("Mara", "manager", "CODE-MANA-0001");
|
|
anlegen("Luna", "creator", "CODE-CREA-0001");
|
|
const idGast = anlegen("Kessi", "gast", "CODE-GAST-0001");
|
|
|
|
/* EIN EINTRAG IN JEDEM DER SIEBEN BRETTER. Ohne Daten waere jede Zeile
|
|
"sieht nichts" trivial wahr -- der gruene Haken ueber der Null. */
|
|
const rein = d.prepare(
|
|
"INSERT INTO eintraege (bereich, art, titel, text, datum, erstellt, erstellt_von)"
|
|
+ " VALUES (?,?,?,?,?,?,?)");
|
|
const BRETTER = ["anschlag", "ansteht", "treff", "wunsch", "highlight", "regeln", "mitmachen"];
|
|
|
|
/* ==== WAS DER EINZELNEN PERSON GEHOERT, IST KEIN BRETT ==============
|
|
|
|
„Vertraulich melden", „Mein Steckbrief" und seit dem 24.09. auch
|
|
„Support" stehen in der Gruppe „Fuer dich" -- nicht zwischen den
|
|
Brettern. Eine vertrauliche Meldung oder eine kaputte Seite ist eine
|
|
Sache zwischen einem Menschen und der Leitung; zwischen den
|
|
Aushaengen saehe sie aus wie ein oeffentlicher Anschlag.
|
|
|
|
EINE LISTE, ZWEI BENUTZER. Bis heute stand sie zweimal in dieser
|
|
Datei -- einmal bei den Brettern der Community, einmal beim
|
|
Vergleich mit dem Modi. Als Support dazukam, wurde die eine
|
|
nachgezogen und die andere nicht, und die Pruefung meldete „FEHLT:
|
|
Support" fuer etwas, das genau dort steht, wo es hingehoert. Zwei
|
|
Listen fuer dieselbe Sache sind zwei Gelegenheiten, eine davon zu
|
|
vergessen. */
|
|
const PERSOENLICH = ["Vertraulich melden", "Mein Steckbrief", "Support"];
|
|
for (const b of BRETTER) rein.run(b, Object.keys((await import("./workspace-bereiche.js")).BEREICHE[b].arten)[0],
|
|
`CREW-PROBE-${b.toUpperCase()}`, "", heuteLokal(), jetzt, idDogi);
|
|
/* Und einer im Haus, den die Community NICHT sehen darf. */
|
|
rein.run("ideen", "content", "HAUS-GEHEIM-4711", "", heuteLokal(), jetzt, idDogi);
|
|
d.close();
|
|
|
|
/* Die Methode ist ein eigenes Argument, seit es Wege gibt, die PUT
|
|
oder DELETE heissen. Vorher wurde sie aus "gibt es einen Koerper?"
|
|
erraten -- das ging, solange nur GET und POST vorkamen, und haette
|
|
beim ersten PUT stillschweigend ein POST geschickt. Der Server
|
|
haette mit 404 geantwortet, und man sucht den Fehler im Server. */
|
|
function roh(pfad, host, kopf = {}, koerper = null, methode = null) {
|
|
return new Promise((fertig, schief) => {
|
|
const a = httpAnfrage({
|
|
host: "127.0.0.1", port: PORT, path: pfad,
|
|
method: methode || (koerper ? "POST" : "GET"),
|
|
headers: { Host: host, ...(koerper ? { "Content-Type": "application/json" } : {}), ...kopf },
|
|
}, (antwort) => {
|
|
let text = "";
|
|
antwort.on("data", (s) => { text += s; });
|
|
antwort.on("end", () => fertig({ code: antwort.statusCode, text, kopf: antwort.headers }));
|
|
});
|
|
a.on("error", schief);
|
|
if (koerper) a.write(JSON.stringify(koerper));
|
|
a.end();
|
|
});
|
|
}
|
|
async function anmelden(rolle, code, host) {
|
|
/* Die Community bestaetigt beim ersten Mal ihr Alter (seit 15.09.2026).
|
|
Nur fuer 'gast' -- fuer alle anderen muss der Server das Feld gar
|
|
nicht erst sehen. */
|
|
const r = await roh("/workspace/api/anmelden", host, {},
|
|
rolle === "gast" ? { rolle, code, alter_ok: true } : { rolle, code });
|
|
const kekse = [].concat(r.kopf?.["set-cookie"] || []);
|
|
return { keks: kekse.map((c) => c.split(";")[0]).join("; "), code: r.code };
|
|
}
|
|
|
|
/* =======================================================================
|
|
1. DIE WAND: die Community kommt auf genau eine Adresse
|
|
======================================================================= */
|
|
melde("");
|
|
melde("=== 1. Eine Rolle, eine Wand ===");
|
|
const gast = await anmelden("gast", "CODE-GAST-0001", CREW);
|
|
ok(gast.code === 200 && gast.keks.length > 0,
|
|
`die Community kommt auf crew. herein (${gast.code})`);
|
|
|
|
/* AUF DEN ALTEN ADRESSEN NICHT -- und zwar mit derselben Antwort wie
|
|
ein erfundener Code. Ein anderer Fehlercode waere die Auskunft
|
|
"diesen Zugang gibt es, nur nicht hier", und wer das liest, sucht
|
|
weiter. */
|
|
for (const [name, host] of [["workspace.", HAUS], ["der Hauptseite", HAUPT]]) {
|
|
const a = await anmelden("gast", "CODE-GAST-0001", host);
|
|
const b = await anmelden("gast", "VOELLIG-ERFUNDEN-XYZ", host);
|
|
/* NICHT AUF EINEN BESTIMMTEN CODE FESTLEGEN (gemessen 11.09.2026).
|
|
|
|
Hier stand `=== 401`. Auf workspace. stimmt das; auf der
|
|
Hauptseite antwortet der Server 410 -- der Workspace ist von dort
|
|
weggezogen, und "Gone" ist die richtige Antwort fuer JEDEN, der es
|
|
dort noch versucht. Die Pruefung war rot, obwohl genau das Richtige
|
|
passierte.
|
|
|
|
Gemessen wird jetzt, was die Zeile eigentlich behaupten will: Er
|
|
kommt nicht herein, und er kann an der Antwort nicht ablesen, dass
|
|
es seinen Zugang gibt. */
|
|
ok(a.code !== 200 && a.keks === "", `auf ${name} nicht (${a.code})`);
|
|
ok(a.code === b.code, `und zwar mit derselben Antwort wie ein erfundener Code (${a.code}/${b.code})`);
|
|
}
|
|
{
|
|
/* DIE GEGENPROBE: Auf derselben Wand kommt auch das Team herein --
|
|
sonst waere "die Community kommt auf crew. herein" auch dann
|
|
gruen, wenn dort ueberhaupt niemand mehr durchkaeme. */
|
|
const a = await anmelden("admin", "CODE-DOGI-0001", CREW);
|
|
const b = await anmelden("modi", "CODE-MODI-0001", CREW);
|
|
ok(a.code === 200 && b.code === 200,
|
|
`und das Team ebenfalls -- eine Wand fuer beide (${a.code}/${b.code})`);
|
|
|
|
/* UND DIE AGENTUR WEITERHIN NICHT. Dass der Treff hier eingezogen
|
|
ist, hat daran nichts geaendert -- und genau das ist die Stelle,
|
|
an der ein Umbau so etwas versehentlich aufmacht. */
|
|
const c = await anmelden("manager", "CODE-MANA-0001", CREW);
|
|
const d = await anmelden("creator", "CODE-CREA-0001", CREW);
|
|
ok(c.code !== 200 && d.code !== 200,
|
|
`eine Managerin und eine Creatorin aber nicht (${c.code}/${d.code})`);
|
|
}
|
|
|
|
const kDogi = (await anmelden("admin", "CODE-DOGI-0001", HAUS)).keks;
|
|
/* AUF DER MODI-KACHEL (01.10.2026). Hier stand „creator" -- bis zum
|
|
24.09. gab es fuer einen Modi keine eigene Kachel, und der stille
|
|
Zugang liess jede durch. Seit die Kachel bindend ist, scheiterte
|
|
die Anmeldung, und fuenf Pruefungen wurden rot, ohne dass am
|
|
Programm etwas falsch war. */
|
|
const kModi = (await anmelden("modi", "CODE-MODI-0001", CREW)).keks;
|
|
const kCrea = (await anmelden("creator", "CODE-CREA-0001", HAUS)).keks;
|
|
const kMana = (await anmelden("manager", "CODE-MANA-0001", HAUS)).keks;
|
|
ok([kDogi, kModi, kCrea, kMana].every((k) => k.length > 0), "die vier Vergleichsrollen sind angemeldet");
|
|
|
|
/* =======================================================================
|
|
1b. EINE WAND FÜR BEIDE — und keine dritte mehr
|
|
|
|
Bis zum Mittag des 11.09.2026 hatte der Treff eine eigene Adresse,
|
|
eine eigene Wand und ein eigenes Manifest. Filipe hat das verworfen:
|
|
*"das soll keine app für sich sein, das soll in der crew seite
|
|
adaptiert werden."*
|
|
|
|
Was dieser Abschnitt jetzt misst, ist deshalb das Gegenteil von
|
|
vorher — und er misst es, weil ein halber Rückbau die gefährlichste
|
|
Lage wäre: eine Wand, die es nicht mehr geben soll, die aber noch
|
|
als Datei herumliegt und von express.static ausgeliefert wird.
|
|
======================================================================= */
|
|
melde("");
|
|
melde("=== 1b. Eine Wand für beide ===");
|
|
{
|
|
/* DIE DATEIEN DER DRITTEN ADRESSE SIND WEG. Nicht "nicht mehr
|
|
verlinkt" -- weg. Eine Zugangswand, die niemand mehr meint, die
|
|
aber unter ihrem Dateinamen weiterhin herumliegt, ist genau die
|
|
Sorte Rest, die man ein halbes Jahr spaeter findet.
|
|
|
|
GEFRAGT WIRD DIE PLATTE, NICHT DER SERVER (gemessen 11.09.2026).
|
|
Ueber HTTP antwortet eine geloeschte Seite mit 302 auf die
|
|
Startseite -- weil die Schranke davor greift, bevor express.static
|
|
ueberhaupt nachsieht. Das ist im Betrieb genau richtig (eine Seite,
|
|
die es nicht gibt, verhaelt sich wie eine, die man nicht darf),
|
|
macht die Frage "gibt es die Datei noch?" per HTTP aber
|
|
unbeantwortbar: 302 kommt in beiden Faellen. Wer hier 404 erwartet,
|
|
prueft die Schranke und nennt es Dateipruefung. */
|
|
{
|
|
const { existsSync } = await import("node:fs");
|
|
const { fileURLToPath } = await import("node:url");
|
|
/* fileURLToPath statt .pathname mit Handarbeit: Unter Windows
|
|
liefert .pathname "/C:/..." -- mit einem fuehrenden Schraegstrich,
|
|
den existsSync nicht mag. Wer den mit einem Muster wegschneidet,
|
|
hat eine Zeile, die auf Linux nichts tut und auf Windows alles
|
|
entscheidet. Der eingebaute Weg kennt beide Faelle. */
|
|
const ordner = fileURLToPath(new URL("../workspace/", import.meta.url));
|
|
const reste = ["treff-index.html", "treff.webmanifest",
|
|
"assets/img/buehne/treff-gate-1280.webp"]
|
|
.filter((f) => existsSync(ordner + f));
|
|
ok(reste.length === 0,
|
|
`von der dritten Adresse liegt nichts mehr herum${reste.length ? " -- REST: " + reste.join(", ") : ""}`);
|
|
/* GEGENPROBE: Die Pruefung sieht ueberhaupt an der richtigen
|
|
Stelle nach. Ohne sie waere die Zeile darueber auch bei einem
|
|
falschen Pfad gruen -- der gruene Haken ueber der Null. */
|
|
ok(existsSync(ordner + "crew-index.html"),
|
|
"und die Wand von Team Dogi liegt dort, wo gesucht wurde");
|
|
}
|
|
|
|
/* DIE WAND ZEIGT VIER KACHELN. Die Zahl steht nicht als Zahl da,
|
|
sondern kommt aus der Rollenliste des Servers -- wer eine fünfte
|
|
Rolle auf diese Wand lässt, ohne eine Kachel zu bauen, bekommt
|
|
hier eine rote Zeile statt eines stummen Lochs. */
|
|
const wand = await roh("/workspace/", CREW);
|
|
ok(wand.code === 200 && /Team Dogi|Team Dogi/.test(wand.text),
|
|
`die Wand von Team Dogi steht (${wand.code})`);
|
|
const kacheln = [...wand.text.matchAll(/data-rolle="([a-z]+)"/g)].map((m) => m[1]);
|
|
/* ABGELEITET, NICHT GEZAEHLT (berichtigt 21.09.2026).
|
|
----------------------------------------------------------------
|
|
Hier stand `kacheln.length === 4`. Am 21.09. kam die linke Hand
|
|
dazu, und die Zeile wurde rot, obwohl die Wand richtig war.
|
|
|
|
Die eigentliche Aussage ist auch eine andere und wiegt schwerer:
|
|
Die Anmeldung sucht `WHERE rolle = ?`. Eine Rolle OHNE Kachel
|
|
kommt damit ueberhaupt nicht herein -- der Zugang existiert, und
|
|
niemand kann ihn benutzen. Geprueft wird deshalb, dass jede Rolle
|
|
dieser Adresse ihre Kachel hat. Das kann nicht veralten. */
|
|
const sollRollen = [...w.TREFF_ROLLEN];
|
|
const ohneKachel = sollRollen.filter((r) => !kacheln.includes(r));
|
|
ok(ohneKachel.length === 0, ohneKachel.length === 0
|
|
? `jede Rolle dieser Adresse hat ihre Kachel: ${kacheln.join(", ")}`
|
|
: `OHNE KACHEL und damit unerreichbar: ${ohneKachel.join(", ")}`);
|
|
ok(kacheln.includes("gast"), "die Community ist eine davon");
|
|
ok(kacheln[kacheln.length - 1] === "gast",
|
|
"und sie steht zuletzt -- wer täglich kommt, findet oben, was er täglich braucht");
|
|
|
|
/* DER TEXT AUF DER WAND MUSS FÜR BEIDE STIMMEN. Er stand auf
|
|
"Privater Arbeitsbereich für DogFather und die Modis" -- das liest
|
|
ab heute auch die Community, und für sie ist es schlicht falsch. */
|
|
ok(!/Arbeitsbereich für DogFather und die Modis/.test(wand.text),
|
|
"und der Untertitel behauptet nicht mehr, das hier sei nur für das Team");
|
|
|
|
/* DAS MANIFEST GILT JE ADRESSE und kann sich nicht nach der Rolle
|
|
richten. Filipes Entscheidung: ein Name, der für beide passt. */
|
|
const man = await roh("/workspace/app.webmanifest", CREW);
|
|
let name = "(kein JSON)";
|
|
try { name = JSON.parse(man.text).name; } catch { /* bleibt stehen */ }
|
|
ok(name === "DogFather Universe", `die App heißt "${name}" -- für Team und Community`);
|
|
const manHaus = await roh("/workspace/app.webmanifest", HAUS);
|
|
let nameHaus = "(kein JSON)";
|
|
try { nameHaus = JSON.parse(manHaus.text).name; } catch { /* bleibt stehen */ }
|
|
ok(nameHaus !== name, `und auf der Agenturadresse weiterhin etwas anderes ("${nameHaus}")`);
|
|
|
|
/* DIE WAND VON TEAM DOGI BLEIBT AUF DEN ALTEN ADRESSEN UNSICHTBAR.
|
|
404 und keine Weiterleitung: "Gibt es nicht" verrät nichts. */
|
|
const fremd = await roh("/workspace/crew-index.html", HAUS);
|
|
const schraeg = await roh("/workspace//crew-index.html", HAUS);
|
|
ok(fremd.code === 404 && schraeg.code === 404,
|
|
`die Wand gibt es auf workspace. nicht -- auch mit doppeltem Schrägstrich (${fremd.code}/${schraeg.code})`);
|
|
|
|
/* NICHT INDIZIEREN -- als Kopf und nicht nur als <meta>, damit er
|
|
auch für Manifest, Bilder und jede Antwort ohne HTML gilt. */
|
|
ok(/noindex/i.test(wand.kopf?.["x-robots-tag"] || ""),
|
|
`der Kopf sagt noindex (${wand.kopf?.["x-robots-tag"]})`);
|
|
}
|
|
|
|
/* =======================================================================
|
|
2. DIE KACHELN: sieben, und sonst nichts
|
|
======================================================================= */
|
|
melde("");
|
|
melde("=== 2. Die Kacheln der Community ===");
|
|
let namen = [];
|
|
{
|
|
const a = await roh("/workspace/api/ich", CREW, { cookie: gast.keks });
|
|
const j = JSON.parse(a.text);
|
|
/* ==== ABGELEITET STATT ABGESCHRIEBEN (19.09.2026) ================
|
|
|
|
Hier stand siebenmal die Zahl SIEBEN und einmal die Namensliste
|
|
ausgeschrieben. Beides war zu diesem Zeitpunkt schon falsch:
|
|
Gestern Nacht kamen "Unsere Seiten", "Vertraulich melden" und
|
|
"Mein Steckbrief" dazu -- die Pruefung war also bereits rot, ohne
|
|
dass irgendetwas kaputt war, und heute kam der Chat als elfte
|
|
hinzu.
|
|
|
|
Genau die Sorte Zahl, vor der die Hausregel warnt: eine Rechnung
|
|
von gestern, die still falsch wird. Gezaehlt wird ab jetzt gegen
|
|
die Liste, die der Server wirklich fuehrt. Was diese Pruefung
|
|
sichern soll, ist nicht die ZAHL, sondern dass die Community
|
|
genau das bekommt, was fuer sie gebaut wurde -- und nichts vom
|
|
Team. */
|
|
const { bereicheFuer } = await import("./workspace.js");
|
|
const erwartet = bereicheFuer({ rolle: "gast" }).map((k) => k.name);
|
|
ok(Array.isArray(j.bereiche) && j.bereiche.length === erwartet.length,
|
|
`die Community bekommt ${erwartet.length} Kacheln (${j.bereiche?.length})`);
|
|
/* Die Namen werden im naechsten Block noch einmal gebraucht -- dort
|
|
wird gegen sie geprueft, statt sie ein zweites Mal zu tippen. Zwei
|
|
Abschriften derselben sieben Namen waeren zwei Gelegenheiten,
|
|
einen davon zu aendern und den anderen zu vergessen. */
|
|
namen = (j.bereiche || []).map((k) => k.name);
|
|
/* DIE REIHENFOLGE IST TEIL DER AUSSAGE, nicht Zufall (17.09.2026).
|
|
"Das Rudel" steht seit heute VORNE, und zwar aus zwei Gruenden:
|
|
|
|
1. Das Raster hat drei Spalten, seine Kachel ist doppelt breit.
|
|
An dritter Stelle passte sie nicht mehr in die erste Reihe und
|
|
riss ein Loch hinein -- genau das, was Filipe auf dem
|
|
Bildschirmfoto gesehen hat. Eine breite Kachel gehoert an den
|
|
Anfang; steht sie hinten, faellt die Luecke in die Mitte.
|
|
2. Der Treff IST das Herz dieses Bereichs. Die Reihenfolge
|
|
stimmt jetzt auch inhaltlich.
|
|
|
|
Wer sie wieder aendert, aendert beides. `pruef-kachelraster`
|
|
rechnet zusaetzlich nach, ob daraus ein Loch entsteht. */
|
|
ok(namen.join(" | ") === erwartet.join(" | "),
|
|
`und zwar diese: ${namen.join(", ")}`);
|
|
/* UEBER DIE EIGENSCHAFT, NICHT UEBER DEN NAMEN (berichtigt
|
|
21.09.2026). Hier stand `namen[0] === "Das Rudel"`. Am 21.09.
|
|
wurde aus der Kachel die Willkommensseite, und die Zeile wurde
|
|
rot, obwohl das Raster stimmte. Gemeint war nie der Name, sondern:
|
|
die DOPPELT BREITE Kachel steht vorne. Genau das wird jetzt
|
|
gefragt. */
|
|
const breite = bereicheFuer({ rolle: "gast" }).filter((k) => k.gross);
|
|
ok(breite.length === 1, `genau eine Kachel ist doppelt breit (${breite.length})`);
|
|
ok(namen[0] === breite[0]?.name,
|
|
`und sie steht vorne (${namen[0]})`);
|
|
/* NICHTS VOM TEAM. Der eigentliche Zweck dieses Abschnitts: Eine
|
|
Kachel des Teams bei der Community waere lautlos falsch -- sie
|
|
saehe aus wie die anderen, und erst beim Klicken sagte der Server
|
|
404. */
|
|
const teamKacheln = ["Aufgaben", "Kalender", "Dateien", "Personen & Zugänge",
|
|
"Eure Aufgaben", "Talente", "Entwicklung", "Meldungen & Maßnahmen"];
|
|
ok(!namen.some((n) => teamKacheln.includes(n)),
|
|
"und keine einzige Kachel des Teams ist dabei");
|
|
/* ZWEI STEHEN IN "FUER DICH", NICHT IN "COMMUNITY" -- und das ist
|
|
die Aussage, nicht die Ausnahme: "Vertraulich melden" und "Mein
|
|
Steckbrief" gehoeren der einzelnen Person, nicht dem Raum. Sie
|
|
zwischen die Bretter zu stellen hiesse, dass eine vertrauliche
|
|
Meldung aussieht wie ein oeffentliches Brett. */
|
|
/* Siehe PERSOENLICH ganz oben -- eine Liste fuer beide Stellen. */
|
|
const bretter = (j.bereiche || []).filter((k) => !PERSOENLICH.includes(k.name));
|
|
ok(bretter.every((k) => k.gruppe === "Community"),
|
|
`die ${bretter.length} Bretter stehen in der Gruppe „Community“`);
|
|
ok((j.bereiche || []).filter((k) => PERSOENLICH.includes(k.name))
|
|
.every((k) => k.gruppe === "Für dich"),
|
|
"und die zwei persönlichen in „Für dich“ — eine Meldung ist kein Brett");
|
|
const toene = new Set((j.bereiche || []).map((k) => k.ton));
|
|
ok(toene.size === (j.bereiche || []).length,
|
|
`${toene.size} Kacheln, ${toene.size} verschiedene Farben (${[...toene].join(", ")})`);
|
|
ok(j.rolle_name === "Community", `die Rolle heißt „${j.rolle_name}“`);
|
|
}
|
|
{
|
|
/* GEGENPROBE: Das Team sieht sie mit -- sonst waere "sieben Kacheln"
|
|
auch dann gruen, wenn sie sonst niemand bekommt.
|
|
|
|
UND ES SIEHT EINE MEHR (11.09.2026): "Meldungen & Massnahmen". Sie
|
|
ist ausdruecklich KEIN Brett und steht deshalb nicht in
|
|
TREFF_BEREICHE -- sonst waere sie bei der Community gelandet, und
|
|
zwar lautlos: Sie saehe dort aus wie die anderen sieben, und erst
|
|
beim Klicken haette der Server 404 gesagt.
|
|
|
|
Geprueft wird deshalb nicht eine Zahl, sondern die BEZIEHUNG: Die
|
|
sieben der Community sind eine echte Teilmenge dessen, was das Team
|
|
sieht -- und der Unterschied ist genau die Moderationskachel. Eine
|
|
Zahl waere beim naechsten Umbau wieder rot, ohne dass etwas kaputt
|
|
ist; diese Bedingung nicht. */
|
|
const MOD = "Meldungen & Ma\u00dfnahmen";
|
|
const a = JSON.parse((await roh("/workspace/api/ich", CREW, { cookie: kModi })).text);
|
|
const beimModi = (a.bereiche || []).filter((k) => k.gruppe === "Community").map((k) => k.name);
|
|
/* ==== ZWEI KACHELN SIEHT EIN MODI AUSDRUECKLICH NICHT ============
|
|
|
|
Bis zum 18.09. stimmte "ein Modi sieht alle Kacheln der Community
|
|
mit". Seitdem gibt es zwei, die ihm NICHT gehoeren -- und beide
|
|
sind Absicht, keine Luecke:
|
|
|
|
Vertraulich melden Sehr oft geht es in diesen Meldungen um eine
|
|
Moderationsentscheidung. Wer beteiligt ist,
|
|
darf die Beschwerde ueber sich nicht lesen.
|
|
Mein Steckbrief Jeder hat seinen eigenen. "Der von jemand
|
|
anderem" ist hier gar nicht gemeint.
|
|
|
|
Die Pruefung war dadurch seit gestern rot, ohne dass etwas kaputt
|
|
war. Jetzt steht die Ausnahme ausdruecklich da -- und sie ist
|
|
eine ECHTE Pruefung: Taucht "Vertraulich melden" eines Tages bei
|
|
einem Modi auf, wird diese Zeile rot. */
|
|
/* DIESELBE LISTE WIE OBEN. Hier stand eine zweite, und sie kannte
|
|
„Support" nicht -- die Pruefung meldete deshalb, dem Modi fehle
|
|
ein Brett, das gar keines ist. */
|
|
const NUR_COMMUNITY = PERSOENLICH;
|
|
/* Verglichen wird die GRUPPE "Community" auf beiden Seiten -- die
|
|
zwei persoenlichen Kacheln stehen dort ohnehin nicht drin. */
|
|
const sollteSehen = namen.filter((n) => !NUR_COMMUNITY.includes(n));
|
|
const fehlt = sollteSehen.filter((n) => !beimModi.includes(n));
|
|
ok(fehlt.length === 0,
|
|
`ein Modi sieht die ${sollteSehen.length} Bretter der Community mit`
|
|
+ `${fehlt.length ? " -- FEHLT: " + fehlt.join(", ") : ""}`);
|
|
const zuViel = NUR_COMMUNITY.filter((n) => beimModi.includes(n));
|
|
ok(zuViel.length === 0,
|
|
`aber NICHT den vertraulichen Meldeweg${zuViel.length ? " -- DURCH: " + zuViel.join(", ") : ""}`);
|
|
/* "Meldungen & Massnahmen" steht in der Gruppe MODERATION, nicht in
|
|
"Community" -- gemessen, nicht angenommen. Gesucht wird deshalb in
|
|
seiner ganzen Liste. Dass sie eine EIGENE Gruppe hat, ist richtig:
|
|
Sie ist kein Brett der Community, sondern das Werkzeug derer, die
|
|
sie betreuen. */
|
|
const modiAlles = [...(a.bereiche || []), ...(a.bereiche_zusatz || [])]
|
|
.map((k) => k.name);
|
|
ok(modiAlles.includes(MOD) && !namen.includes(MOD),
|
|
`und genau eine mehr, die die Community NICHT hat: ${MOD}`);
|
|
|
|
/* AUF DER TEAM-ADRESSE GEFRAGT, NICHT AUF DER AGENTUR- (21.09.2026).
|
|
|
|
Hier stand `HAUS`, also workspace. -- und die Zeile war richtig,
|
|
solange DogFather dort auch die Bretter des Treffs bekam.
|
|
|
|
Seit dem 21.09. bekommt er sie dort nicht mehr. Filipe: "ich will
|
|
dass du im workspace alles von team dogi weg nimmst ... ich will
|
|
keine mischung mehr von kacheln." Der Treff steht nur noch auf
|
|
seiner eigenen Adresse; von der Agenturseite fuehrt genau EINE
|
|
Tuer hinueber.
|
|
|
|
Die Aussage bleibt dieselbe -- "DogFather sieht dieselben Bretter
|
|
wie die Community" --, sie wird nur dort gemessen, wo diese
|
|
Bretter zuhause sind. Auf der Agenturadresse waere sie ab jetzt
|
|
eine Frage nach etwas, das dort nicht sein soll. */
|
|
const b = JSON.parse((await roh("/workspace/api/ich", CREW, { cookie: kDogi })).text);
|
|
/* AUF crew. STEHEN SIE IN `bereiche`, NICHT IN `bereiche_zusatz`:
|
|
Dort ist der Treff sein Zuhause und keine Zugabe. Gesucht wird
|
|
deshalb in beiden Listen -- so misst die Zeile dasselbe, egal von
|
|
welcher Adresse sie kommt. */
|
|
const beiDogi = [...(b.bereiche || []), ...(b.bereiche_zusatz || [])]
|
|
.filter((k) => k.gruppe === "Community").map((k) => k.name);
|
|
/* DogFather sieht den Meldeweg sehr wohl -- er beantwortet ihn. */
|
|
/* Auch hier: MOD steht in der Gruppe "Moderation", nicht in
|
|
"Community". `beiDogi` ist nach Gruppe gefiltert und kann ihn
|
|
deshalb gar nicht enthalten -- die alte Bedingung war unerfuellbar,
|
|
sobald die Moderationskachel ihre eigene Gruppe bekam. */
|
|
const dogiAlles = [...(b.bereiche || []), ...(b.bereiche_zusatz || [])]
|
|
.map((k) => k.name);
|
|
ok(sollteSehen.every((n) => beiDogi.includes(n)),
|
|
`und DogFather sieht auf seiner Adresse dieselben ${beiDogi.length} Bretter`);
|
|
ok(dogiAlles.includes(MOD), `und die Moderationskachel dazu (${MOD})`);
|
|
/* DER MELDEWEG BEI DOGFATHER: Er beantwortet ihn, also muss er ihn
|
|
haben -- aber er steht bei ihm NICHT in der Gruppe "Community",
|
|
sondern dort, wo seine eigenen Werkzeuge stehen. Gesucht wird
|
|
deshalb in seiner GANZEN Liste, nicht nur in einer Gruppe. */
|
|
/* DER MELDEWEG LEBT AUF DER TEAM-ADRESSE, nicht auf der Agentur-.
|
|
Gemessen: Auf crew. hat DogFather ihn in der Gruppe "Fuer dich";
|
|
auf workspace. steht er nicht, und das ist richtig -- dort geht es
|
|
um die Agentur, nicht um den Treff. Geprueft wird deshalb dort, wo
|
|
er hingehoert. */
|
|
const cDogi = JSON.parse((await roh("/workspace/api/ich", CREW, { cookie: kDogi })).text);
|
|
const dogiAufCrew = [...(cDogi.bereiche || []), ...(cDogi.bereiche_zusatz || [])]
|
|
.map((k) => k.name);
|
|
ok(dogiAufCrew.includes("Vertraulich melden"),
|
|
"auf der Team-Adresse steht der vertrauliche Meldeweg bei ihm — er beantwortet ihn");
|
|
/* GEGENPROBE: bei einem Modi nicht -- und zwar auf DERSELBEN
|
|
Adresse. Ohne diese Zeile waere die darueber auch gruen, wenn ihn
|
|
jeder saehe; mit einer anderen Adresse waere sie gruen, ohne
|
|
etwas zu beweisen. */
|
|
ok(!modiAlles.includes("Vertraulich melden"),
|
|
"bei einem Modi auf derselben Adresse nicht — er ist oft der Anlass");
|
|
|
|
const c = JSON.parse((await roh("/workspace/api/ich", HAUS, { cookie: kMana })).text);
|
|
const beiMana = (c.bereiche_zusatz || []).filter((k) => k.gruppe === "Community");
|
|
ok(beiMana.length === 0, `eine Managerin nicht (${beiMana.length})`);
|
|
|
|
/* UND DIE MODERATIONSKACHEL IST BEI DER COMMUNITY WIRKLICH NICHT DA
|
|
-- nicht nur "nicht in dieser Liste". Die Kachel ist ein Weg; wenn
|
|
der Weg zu ist, muss auch die Tuer zu sein. */
|
|
const d = JSON.parse((await roh("/workspace/api/ich", CREW, { cookie: gast.keks })).text);
|
|
const alleNamen = [...(d.bereiche || []), ...(d.bereiche_zusatz || [])].map((k) => k.name);
|
|
ok(!alleNamen.includes(MOD), `die Community hat keine Moderationskachel (${alleNamen.length} Kacheln)`);
|
|
}
|
|
|
|
/* =======================================================================
|
|
3. DIE BRETTER: was die Community sieht — und was nicht
|
|
======================================================================= */
|
|
melde("");
|
|
melde("=== 3. Die sieben Bretter ===");
|
|
async function brett(b, host, keks) {
|
|
const a = await roh(`/workspace/api/bereich/${b}`, host, { cookie: keks });
|
|
let titel = [];
|
|
try { titel = (JSON.parse(a.text).eintraege || []).map((e) => e.titel); } catch { /* kein JSON */ }
|
|
return { code: a.code, titel };
|
|
}
|
|
{
|
|
let gefunden = 0;
|
|
for (const b of BRETTER) {
|
|
const r = await brett(b, CREW, gast.keks);
|
|
if (r.code === 200 && r.titel.includes(`CREW-PROBE-${b.toUpperCase()}`)) gefunden++;
|
|
}
|
|
/* FUENF VON SIEBEN, UND DAS IST DAS ERGEBNIS -- kein Mangel.
|
|
|
|
Termine und Highlights sind fuer die Community erst da, wenn
|
|
jemand aus dem Team sie freigegeben hat (TREFF_FREIGABE_BRETTER).
|
|
Die Zahl steht deshalb nicht als 7 hier, sondern wird aus der
|
|
Tafel gerechnet: Wer ein drittes Freigabe-Brett anlegt, bekommt
|
|
hier keine rote Zeile fuer etwas Richtiges. */
|
|
const OFFENE = BRETTER.filter((b) => !treffModul.TREFF_FREIGABE_BRETTER.includes(b));
|
|
ok(gefunden === OFFENE.length,
|
|
`die Community sieht die ${OFFENE.length} freien Bretter mit Inhalt (${gefunden}/${OFFENE.length})`);
|
|
|
|
/* DIE GEGENPROBE IN BEIDE RICHTUNGEN. Ohne sie waere die Zeile oben
|
|
auch dann gruen, wenn die Freigabe gar nicht wirkt und die beiden
|
|
Bretter aus einem anderen Grund leer sind. */
|
|
let vorher = 0;
|
|
for (const b of treffModul.TREFF_FREIGABE_BRETTER) {
|
|
const r = await brett(b, CREW, gast.keks);
|
|
if (r.titel.includes(`CREW-PROBE-${b.toUpperCase()}`)) vorher++;
|
|
}
|
|
ok(vorher === 0, `ohne Freigabe sieht die Community Termine und Highlights nicht (${vorher})`);
|
|
|
|
/* Jetzt freigeben -- ueber den echten Weg, nicht in der Datenbank. */
|
|
let freigegeben = 0;
|
|
for (const b of treffModul.TREFF_FREIGABE_BRETTER) {
|
|
const beiDogi2 = await roh(`/workspace/api/bereich/${b}`, HAUS, { cookie: kDogi });
|
|
const eintrag = (JSON.parse(beiDogi2.text).eintraege || [])
|
|
.find((e) => e.titel === `CREW-PROBE-${b.toUpperCase()}`);
|
|
if (!eintrag) continue;
|
|
const a = await roh(`/workspace/api/treff/eintrag/${eintrag.id}/freigeben`, HAUS,
|
|
{ cookie: kDogi }, {}, "PUT");
|
|
if (a.code === 200) freigegeben++;
|
|
}
|
|
ok(freigegeben === treffModul.TREFF_FREIGABE_BRETTER.length,
|
|
`DogFather gibt beide frei (${freigegeben})`);
|
|
|
|
let nachher = 0;
|
|
for (const b of treffModul.TREFF_FREIGABE_BRETTER) {
|
|
const r = await brett(b, CREW, gast.keks);
|
|
if (r.titel.includes(`CREW-PROBE-${b.toUpperCase()}`)) nachher++;
|
|
}
|
|
ok(nachher === treffModul.TREFF_FREIGABE_BRETTER.length,
|
|
`nach der Freigabe sieht sie beide (${nachher})`);
|
|
|
|
/* Und ein Modi darf nicht freigeben, was er nicht sehen darf --
|
|
hier: dass die Route ueberhaupt Rechte prueft. Eine Creatorin
|
|
bekommt 404, nicht 403: den Treff gibt es fuer sie nicht. */
|
|
const fremd = await roh("/workspace/api/treff/eintrag/1/freigeben", HAUS,
|
|
{ cookie: kCrea }, {}, "PUT");
|
|
ok(fremd.code === 404, `eine Creatorin kann nichts freigeben (${fremd.code})`);
|
|
}
|
|
|
|
/* =======================================================================
|
|
WAS DAS TEAM SELBST EINSETZT, IST SOFORT DA (30.09.2026)
|
|
=======================================================================
|
|
|
|
Filipe: „wenn wir bei highlights videos rein setzen will ich nicht
|
|
mehr dass wir sie freigeben muessen, sobald die reingesetzt wurden
|
|
sollen die sofort zu sehen sein."
|
|
|
|
Die Freigabe auf dem Highlight-Brett ist ein FILTER gegen fremde
|
|
Inhalte -- sie fragt „hat das jemand angesehen?". Wer ihn bedienen
|
|
darf und den Eintrag selbst anlegt, hat ihn damit schon passiert.
|
|
|
|
DER ABSCHNITT DARUEBER BEWEIST DAS NICHT. Er legt seine Eintraege
|
|
unmittelbar in der Datenbank an und prueft damit den Mechanismus,
|
|
nicht den Weg. Er blieb nach der Aenderung gruen -- zu Recht, und
|
|
ohne etwas ueber sie zu sagen.
|
|
======================================================================= */
|
|
melde("\n=== Was das Team selbst einsetzt, ist sofort da ===");
|
|
{
|
|
const anlegenUeberDenWeg = async (bereich, art, titel) => {
|
|
const a = await roh(`/workspace/api/bereich/${bereich}`, HAUS,
|
|
{ cookie: kDogi },
|
|
{ art, titel, datum: tagLokal() });
|
|
let id = 0;
|
|
try { id = JSON.parse(a.text).id || 0; } catch { /* kein JSON */ }
|
|
return { code: a.code, id };
|
|
};
|
|
|
|
const hl = await anlegenUeberDenWeg("highlight", "clip", "SOFORT-SICHTBAR");
|
|
ok(hl.code === 201 && hl.id > 0,
|
|
`DogFather legt ein Highlight ueber den echten Weg an (${hl.code})`);
|
|
|
|
const beiGast = await brett("highlight", CREW, gast.keks);
|
|
ok(beiGast.titel.includes("SOFORT-SICHTBAR"),
|
|
"die Community sieht es SOFORT -- ohne zweiten Klick");
|
|
|
|
/* ---- DIE ERSTE GEGENPROBE: der Schalter bleibt ein Schalter ----
|
|
Bei `ansteht` entscheidet das Team JE TERMIN, ob die Community
|
|
ihn sieht. Waere die Aenderung dort auch gelandet, stuende jeder
|
|
interne Termin sofort im Treff -- und danach hat niemand
|
|
gefragt. */
|
|
const tm = await anlegenUeberDenWeg("ansteht", "stream", "NICHT-SOFORT-SICHTBAR");
|
|
if (tm.code !== 201) {
|
|
melde(` -- konnte nicht nachsehen: Termin anlegen ergab ${tm.code}`);
|
|
} else {
|
|
const terminBeiGast = await brett("ansteht", CREW, gast.keks);
|
|
ok(!terminBeiGast.titel.includes("NICHT-SOFORT-SICHTBAR"),
|
|
"ein TERMIN bleibt bis zum Schalter „Im Treff zeigen\" verborgen");
|
|
}
|
|
|
|
/* ---- DIE ZWEITE GEGENPROBE: der Filter bleibt ein Filter ----
|
|
Ein Community-Mitglied ab „Stamm" darf auf demselben Brett etwas
|
|
einstellen. Sein Eintrag wartet weiter auf jemanden aus dem
|
|
Team -- Urheberrecht und Anstand sind nichts, was man
|
|
nachtraeglich klaert.
|
|
|
|
Gefragt wird die Regel unmittelbar: Eine Person mit „Stamm"
|
|
anzulegen brauchte Alter UND Beitraege, und diese Pruefung
|
|
wuerde damit den Stufenaufbau messen statt die Freigabe. */
|
|
const frei = treffModul.sofortFreigeben("highlight", { rolle: "gast", id: idGast }, hl.id);
|
|
ok(frei === false,
|
|
"fuer eine Rolle von aussen gibt die Regel NICHT frei");
|
|
const auchNicht = treffModul.sofortFreigeben("ansteht", { rolle: "admin", id: idDogi }, 1);
|
|
ok(auchNicht === false,
|
|
"und fuer Termine gibt sie ueberhaupt nicht frei");
|
|
}
|
|
{
|
|
/* KEIN EINZIGES BRETT DES HAUSES. Ueber die Adresszeile probiert,
|
|
nicht ueber die Kacheln -- eine Regel, die nur im Formular gilt,
|
|
ist keine Regel. */
|
|
const fremde = ["ideen", "angebote", "rueckmeldung", "entwicklung", "talente", "agentur", "live", "content"];
|
|
const durch = [];
|
|
for (const b of fremde) {
|
|
const r = await brett(b, CREW, gast.keks);
|
|
if (r.code === 200) durch.push(b);
|
|
}
|
|
ok(durch.length === 0, `und kein einziges des Hauses${durch.length ? " -- DURCH: " + durch.join(", ") : ""}`);
|
|
|
|
/* DIE SUCHE SPIEGELT DIE FRAGE ZURUECK (Feld `frage`). Wer den
|
|
Rohtext nach dem Suchwort durchsucht, findet deshalb IMMER einen
|
|
Treffer -- und meldet ein Leck, das es nicht gibt. Genau darauf
|
|
bin ich am 10.09. schon einmal hereingefallen und haette fast ein
|
|
Loch repariert, das keines war. Gezaehlt wird, was die Suche
|
|
WIRKLICH gefunden hat. */
|
|
const suche = await roh("/workspace/api/suche?q=HAUS-GEHEIM-4711", CREW, { cookie: gast.keks });
|
|
let treffer = -1;
|
|
try { treffer = JSON.parse(suche.text).gesamt; } catch { treffer = -1; }
|
|
ok(treffer === 0, `auch die Suche findet nichts aus dem Haus (${treffer} Treffer)`);
|
|
}
|
|
{
|
|
/* UND DIE AGENTUR SIEHT DEN CREW NICHT. Beide Richtungen, beide
|
|
ueber die Adresszeile. */
|
|
const durch = [];
|
|
for (const b of BRETTER) {
|
|
const r = await brett(b, HAUS, kCrea);
|
|
if (r.code === 200 && r.titel.some((t) => t.startsWith("CREW-PROBE"))) durch.push(b);
|
|
}
|
|
ok(durch.length === 0, `eine Creatorin sieht kein Brett des Rudels${durch.length ? " -- DURCH: " + durch.join(", ") : ""}`);
|
|
|
|
const durchM = [];
|
|
for (const b of BRETTER) {
|
|
const r = await brett(b, HAUS, kMana);
|
|
if (r.code === 200 && r.titel.some((t) => t.startsWith("CREW-PROBE"))) durchM.push(b);
|
|
}
|
|
ok(durchM.length === 0, `eine Managerin auch nicht${durchM.length ? " -- DURCH: " + durchM.join(", ") : ""}`);
|
|
|
|
/* GEGENPROBE: Der Modi sieht sie sehr wohl. Ohne diese Zeile waeren
|
|
die beiden darueber auch gruen, wenn die Bretter fuer NIEMANDEN
|
|
lesbar waeren. */
|
|
let sieht = 0;
|
|
for (const b of BRETTER) {
|
|
const r = await brett(b, CREW, kModi);
|
|
if (r.code === 200 && r.titel.includes(`CREW-PROBE-${b.toUpperCase()}`)) sieht++;
|
|
}
|
|
ok(sieht === 7, `ein Modi sieht alle sieben (${sieht}/7)`);
|
|
}
|
|
|
|
/* =======================================================================
|
|
4. DIE SEITEN: die aus der Tafel, und keine einzige mehr
|
|
======================================================================= */
|
|
melde("");
|
|
melde("=== 4. Nur die Seiten aus der Tafel ===");
|
|
{
|
|
/* DIE LISTE WIRD NICHT GETIPPT, SIE WIRD GEHOLT (11.09.2026).
|
|
|
|
Hier standen zwei Seiten und daneben "und keine dritte". Beim
|
|
Anlegen der Regelseite wurde daraus eine dritte -- die Prüfung
|
|
war rot, obwohl nichts kaputt war, und die naheliegende Reaktion
|
|
wäre gewesen, die Zahl von zwei auf drei zu setzen. Beim vierten
|
|
Mal wieder.
|
|
|
|
Gemessen wird jetzt das, was die Zeile eigentlich behaupten will:
|
|
Die Tafel und die Wirklichkeit stimmen überein — jede Seite, die
|
|
in rechte.js für "gast" steht, geht auf; jede andere ausgelieferte
|
|
Seite geht NICHT auf. Das ist strenger als die alte Fassung (die
|
|
elf Namen waren von Hand gewählt) und altert nicht. */
|
|
const rechte = await import("./rechte.js");
|
|
const { readdirSync } = await import("node:fs");
|
|
const ordner = new URL("../workspace/", import.meta.url).pathname
|
|
.replace(/^\/([A-Za-z]:)/, "$1");
|
|
const alleDateien = readdirSync(ordner).filter((f) => f.endsWith(".html"))
|
|
.map((f) => "/workspace/" + f);
|
|
|
|
const erlaubt = rechte.seitenFuer("gast");
|
|
const verboten = alleDateien
|
|
.filter((p) => !erlaubt.includes(p) && !rechte.OHNE_ANMELDUNG.includes(p));
|
|
|
|
ok(erlaubt.length > 0 && verboten.length > 0,
|
|
`die Tafel gibt der Community ${erlaubt.length} Seiten und verbietet ${verboten.length}`);
|
|
|
|
const zu = [];
|
|
for (const p of erlaubt) {
|
|
const a = await roh(p, CREW, { cookie: gast.keks });
|
|
if (a.code !== 200) zu.push(`${p.split("/").pop()} (${a.code})`);
|
|
}
|
|
ok(zu.length === 0,
|
|
`alle ${erlaubt.length} erlaubten öffnen sich${zu.length ? " -- ZU: " + zu.join(", ") : ""}`);
|
|
|
|
const durch = [];
|
|
for (const p of verboten) {
|
|
const a = await roh(p, CREW, { cookie: gast.keks });
|
|
if (a.code === 200) durch.push(p.split("/").pop());
|
|
}
|
|
ok(durch.length === 0,
|
|
`und keine der ${verboten.length} anderen${durch.length ? " -- DURCH: " + durch.join(", ") : ""}`);
|
|
|
|
/* UND DIE MODERATIONSSEITE AUSDRÜCKLICH NICHT. Sie steht zwar in der
|
|
Liste oben mit drin -- aber sie ist die eine, bei der ein Fehler
|
|
wirklich weh täte: Dort stehen die Namen derer, die gemeldet
|
|
haben. Eine eigene Zeile dafür, damit der Befund einen Namen hat
|
|
und nicht in "eine von 20" untergeht. */
|
|
const mod = await roh("/workspace/treff-moderation.html", CREW, { cookie: gast.keks });
|
|
ok(mod.code !== 200, `die Moderationsseite bleibt zu (${mod.code})`);
|
|
const modApi = await roh("/workspace/api/treff/meldungen", CREW, { cookie: gast.keks });
|
|
ok(modApi.code === 404, `und die Meldungen dahinter auch (${modApi.code})`);
|
|
}
|
|
|
|
/* =======================================================================
|
|
5. DAS HAUS BLEIBT ZU: Aufgaben, Termine, Dateien, Personen
|
|
======================================================================= */
|
|
melde("");
|
|
melde("=== 5. Vom Haus sieht sie nichts ===");
|
|
for (const [name, pfad] of [
|
|
["Aufgaben", "/workspace/api/aufgaben"],
|
|
["Termine", "/workspace/api/termine"],
|
|
["Dateien", "/workspace/api/dateien"],
|
|
["Personen", "/workspace/api/personen"],
|
|
/* "Gespräche" IST HIER HERAUSGENOMMEN (19.09.2026).
|
|
|
|
Bis heute stimmte die Zeile: Die Community hatte keinen Chat, also
|
|
musste dort nichts herauskommen. Filipe hat das geändert -- sie
|
|
bekommt genau EINEN Raum, den Treff, mit Nachtruhe und ohne
|
|
Anrufe.
|
|
|
|
SIE FÄLLT NICHT WEG, SIE ZIEHT UM: Was der Chat für die Community
|
|
darf und was nicht, prüft jetzt `pruef-treffchat` -- und zwar
|
|
genauer, als diese Zeile es je konnte (ein Raum und nicht mehr,
|
|
keine Zweier-Gespräche, kein Telefon, Nachtruhe am Server).
|
|
Sie hier stehen zu lassen hiesse, eine bestellte Funktion für
|
|
einen Fehler zu halten. */
|
|
]) {
|
|
const a = await roh(pfad, CREW, { cookie: gast.keks });
|
|
let leer = a.code !== 200;
|
|
if (a.code === 200) {
|
|
try {
|
|
const j = JSON.parse(a.text);
|
|
/* EINE WORTLISTE IST KEIN LECK (15.09.2026).
|
|
|
|
Diese Pruefung fragte "enthaelt die Antwort IRGENDEIN gefuelltes
|
|
Feld" -- und wurde rot, als das Aufgabenbrett am 15.09. die drei
|
|
Aufwandsstufen mitzuliefern begann (klein/mittel/gross). Die sind
|
|
fuer jeden Menschen dieselben, sie sagen ueber niemanden etwas,
|
|
und sie kommen mit, damit im Browser keine zweite Liste gepflegt
|
|
werden muss.
|
|
|
|
Ab da meldete die Pruefung bei JEDEM Lauf einen Befund, der keiner
|
|
war -- und eine Warnung, die immer kommt, wird ueberlesen. Genau
|
|
deshalb steht sie hier jetzt praeziser, statt lauter.
|
|
|
|
WAS EINE WORTLISTE VON DATEN UNTERSCHEIDET: Ein Datensatz in
|
|
diesem Haus hat eine `id`. Ein Vokabular hat einen `schluessel`
|
|
und keine. Das ist keine Auslegung, sondern die Bauweise -- jede
|
|
Tabelle vergibt id, jede Wortliste in workspace-*.js nicht. Ein
|
|
echter Eintrag kann dadurch nicht versehentlich als Vokabular
|
|
durchgehen. */
|
|
const istWortliste = (f) => f.length > 0 && f.every((x) => x
|
|
&& typeof x === "object" && typeof x.schluessel === "string" && x.id === undefined);
|
|
const felder = Object.values(j).filter(Array.isArray).filter((f) => !istWortliste(f));
|
|
leer = felder.every((f) => f.length === 0);
|
|
/* Die eigene Person in einer Personenliste ist kein Leck. */
|
|
if (!leer && pfad.endsWith("personen")) {
|
|
leer = felder.every((f) => f.every((p) => p.id === idGast));
|
|
}
|
|
} catch { leer = true; }
|
|
}
|
|
ok(leer, `${name}: nichts (${a.code})`);
|
|
}
|
|
|
|
/* =======================================================================
|
|
6. SCHREIBEN — und wo es ankommt
|
|
======================================================================= */
|
|
melde("");
|
|
melde("=== 6. Schreiben ===");
|
|
{
|
|
/* DIE ERSTE WOCHE IST EINE SANDKISTE. Wer eben erst dazugekommen
|
|
ist, liest und stimmt ab -- mehr nicht. Das wird von BEIDEN
|
|
Seiten der Grenze gemessen, sonst beweist es nichts: Eine
|
|
Schreibsperre, die immer sperrt, ist keine Stufe. */
|
|
const neu = await roh("/workspace/api/bereich/treff", CREW, { cookie: gast.keks },
|
|
{ art: "hallo", titel: "GAST-ZU-FRUEH-0001", text: "Hallo zusammen.", datum: heuteLokal() });
|
|
ok(neu.code === 403, `wer neu ist, schreibt noch nicht (${neu.code})`);
|
|
|
|
const lage0 = JSON.parse((await roh("/workspace/api/treff/lage", CREW, { cookie: gast.keks })).text);
|
|
ok(lage0.stufe === "neu", `und die Lage sagt es ihm auch: ${lage0.stufe}`);
|
|
ok(lage0.schreiben?.treff === false, "der Knopf waere aus");
|
|
|
|
/* Acht Tage zurueckdatieren -- genau EIN Tag ueber der Grenze. Eine
|
|
Grenze, die nur von weit weg geprueft wird, koennte um Tage
|
|
danebenliegen, ohne dass es auffaellt. */
|
|
const dd = new DatabaseSync(process.env.WORKSPACE_DB);
|
|
dd.prepare("UPDATE personen SET erstellt = ? WHERE id = ?")
|
|
.run(new Date(Date.now() - 8 * 86400000).toISOString(), idGast);
|
|
dd.close();
|
|
|
|
const a = await roh("/workspace/api/bereich/treff", CREW, { cookie: gast.keks },
|
|
{ art: "hallo", titel: "GAST-SCHREIBT-8899", text: "Hallo zusammen.", datum: heuteLokal() });
|
|
ok(a.code === 200 || a.code === 201, `nach acht Tagen schon (${a.code})`);
|
|
|
|
/* UND SECHS TAGE REICHEN NICHT. Die andere Seite derselben Grenze --
|
|
ohne sie waere "acht Tage gehen" auch dann gruen, wenn die Stufe
|
|
gar nicht geprueft wird. */
|
|
const dd2 = new DatabaseSync(process.env.WORKSPACE_DB);
|
|
dd2.prepare("UPDATE personen SET erstellt = ? WHERE id = ?")
|
|
.run(new Date(Date.now() - 6 * 86400000).toISOString(), idGast);
|
|
dd2.close();
|
|
const knapp = await roh("/workspace/api/bereich/treff", CREW, { cookie: gast.keks },
|
|
{ art: "hallo", titel: "GAST-ZU-FRUEH-0002", text: "Und?", datum: heuteLokal() });
|
|
ok(knapp.code === 403, `sechs Tage reichen nicht (${knapp.code})`);
|
|
const dd3 = new DatabaseSync(process.env.WORKSPACE_DB);
|
|
dd3.prepare("UPDATE personen SET erstellt = ? WHERE id = ?")
|
|
.run(new Date(Date.now() - 8 * 86400000).toISOString(), idGast);
|
|
dd3.close();
|
|
|
|
/* Und auf ein Brett des Teams kommt er auch als "Dabei" nicht. */
|
|
const brettDesTeams = await roh("/workspace/api/bereich/anschlag", CREW, { cookie: gast.keks },
|
|
{ art: Object.keys(BEREICHE.anschlag.arten)[0], titel: "GAST-AM-ANSCHLAG-0001",
|
|
text: "Ansage", datum: heuteLokal() });
|
|
ok(brettDesTeams.code === 403, `aufs Anschlagbrett schreibt nur das Team (${brettDesTeams.code})`);
|
|
|
|
/* ==== AUF DER CREW-ADRESSE, NICHT AUF DER AGENTURADRESSE ========
|
|
(nachgezogen 03.10.2026)
|
|
|
|
Hier stand `brett("treff", HAUS, kDogi)` -- DogFather liest das
|
|
Rudel-Brett ueber die AGENTURADRESSE. Das war richtig, solange
|
|
"getrennt wird das AUSSEHEN, nicht der Bestand" galt. Filipe hat
|
|
das am 24.09.2026 umgedreht: "wenn ich bei der einen was mache
|
|
soll nichts bei der anderen passieren."
|
|
|
|
Seit Riegel 0 in sichtbarEintrag() gibt es auf der Agenturadresse
|
|
keine Eintraege von Team Dogi mehr -- auch nicht fuer DogFather.
|
|
`treff` ist ein Brett des Rudels; dass dort auf workspace. nichts
|
|
steht, ist das Ziel und kein Fehler.
|
|
|
|
DIE ZUSAGE BLEIBT, sie wird nur am richtigen Ort geprueft -- und
|
|
bekommt eine GEGENPROBE dazu. Damit sagt diese Stelle jetzt mehr
|
|
als vorher: nicht nur "er sieht es", sondern "er sieht es DORT
|
|
und nur dort". */
|
|
const beiDogi = await brett("treff", CREW, kDogi);
|
|
ok(beiDogi.titel.includes("GAST-SCHREIBT-8899"),
|
|
"DogFather sieht den Beitrag auf der Crew-Adresse");
|
|
const beiDogiAgentur = await brett("treff", HAUS, kDogi);
|
|
ok(!beiDogiAgentur.titel.includes("GAST-SCHREIBT-8899"),
|
|
`und auf der Agenturadresse nicht (${beiDogiAgentur.titel.join(" | ") || "leer"})`);
|
|
const beiModi = await brett("treff", CREW, kModi);
|
|
ok(beiModi.titel.includes("GAST-SCHREIBT-8899"), "der Modi auch");
|
|
const beiCrea = await brett("treff", HAUS, kCrea);
|
|
ok(!beiCrea.titel.includes("GAST-SCHREIBT-8899"), "die Creatorin nicht");
|
|
}
|
|
{
|
|
/* UND NICHT IN EIN BRETT DES HAUSES. */
|
|
const a = await roh("/workspace/api/bereich/ideen", CREW, { cookie: gast.keks },
|
|
{ art: "content", titel: "GAST-IM-HAUS-0000", text: "", datum: heuteLokal() });
|
|
ok(a.code !== 200 && a.code !== 201, `aber nicht in ein Brett des Hauses (${a.code})`);
|
|
}
|
|
|
|
/* =======================================================================
|
|
7. WEM EIN EINTRAG GILT, SAGT DER SERVER -- NICHT DIE OBERFLAECHE
|
|
|
|
Filipe, 11.09.2026: "auf dieser seite soll nichts stehen von agentur,
|
|
diese seite hier ist nur fuer mich meine modis und meine community."
|
|
|
|
Er hatte recht, und der Fehler war ein fest eingetragenes Wort: In
|
|
bereich.js stand woertlich "Agentur" -- richtig auf der
|
|
Agenturadresse, falsch auf jeder anderen.
|
|
|
|
GEPRUEFT WIRD MIT EINEM EINZIGEN MENSCHEN AUF ZWEI ADRESSEN. Dieselbe
|
|
Person, dasselbe Brett, zwei Waende: Wandert das Wort mit, kommt es
|
|
vom Haus. Waere es an der Rolle festgemacht, stuende hier zweimal
|
|
dasselbe -- und das ist genau der Fehler, den es zu finden gilt.
|
|
======================================================================= */
|
|
melde("");
|
|
melde("=== 7. Das Wort für „Gilt für“ kommt vom Server ===");
|
|
{
|
|
const kCrew = (await anmelden("admin", "CODE-DOGI-0001", CREW)).keks;
|
|
const kHaus = (await anmelden("admin", "CODE-DOGI-0001", HAUS)).keks;
|
|
ok(!!kCrew && !!kHaus, "DogFather ist an beiden Waenden angemeldet");
|
|
|
|
const lies = async (brett, host, keks) => {
|
|
const a = await roh(`/workspace/api/bereich/${brett}`, host, { cookie: keks });
|
|
let g = null;
|
|
try { g = JSON.parse(a.text).gehoert; } catch { /* bleibt null */ }
|
|
return { code: a.code, gehoert: g };
|
|
};
|
|
|
|
const treff = await lies("treff", CREW, kCrew);
|
|
ok(treff.code === 200 && treff.gehoert === "Das Rudel",
|
|
`ein Rudel-Brett gehoert dem Rudel (${treff.gehoert})`);
|
|
|
|
const crew = await lies("ideen", CREW, kCrew);
|
|
ok(crew.code === 200 && crew.gehoert === "Team Dogi",
|
|
`dasselbe Haus-Brett auf crew. gehoert Team Dogi (${crew.gehoert})`);
|
|
|
|
/* GEGENPROBE, und ohne sie beweist die Zeile darueber nichts: Auf der
|
|
Agenturadresse MUSS weiterhin "Agentur" stehen. Ein Wort, das
|
|
ueberall dasselbe sagt, sagt nichts. */
|
|
const haus = await lies("ideen", HAUS, kHaus);
|
|
ok(haus.code === 200 && haus.gehoert === "Agentur",
|
|
`und auf workspace. weiterhin Agentur (${haus.gehoert})`);
|
|
ok(crew.gehoert !== haus.gehoert,
|
|
"dieselbe Person, dasselbe Brett, zwei Woerter -- es haengt am Haus");
|
|
|
|
/* UND DIE OBERFLAECHE SCHREIBT ES NICHT MEHR SELBST HIN. */
|
|
const js = readFileSync(new URL("../workspace/assets/js/bereich.js", import.meta.url), "utf8");
|
|
const ohneKommentare = js.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
ok(!/['"`]Agentur/.test(ohneKommentare),
|
|
"in bereich.js steht das Wort „Agentur“ in keinem Text mehr");
|
|
ok(/gehoert/.test(ohneKommentare),
|
|
"stattdessen fragt sie den Server danach");
|
|
/* Gegenprobe zum Sucher selbst: Er MUSS ein hingeschriebenes
|
|
"Agentur" auch wirklich finden, sonst ist die Zeile darueber nur
|
|
gruen, weil sie blind ist. */
|
|
ok(/['"`]Agentur/.test("const x = 'Agentur-Eintraege';"),
|
|
"und der Sucher findet so ein Wort auch wirklich");
|
|
}
|
|
|
|
/* =====================================================================
|
|
KEIN BRETT DARF ZWISCHEN DIE REGELN FALLEN (21.09.2026)
|
|
=====================================================================
|
|
|
|
Dreimal derselbe Fehler, dreimal derselbe Hergang:
|
|
|
|
19.09. Die Kachel "Regeln & Hilfe" bekam ein neues Ziel.
|
|
`regeln` fiel aus TREFF_BRETTER -- und war fuer die
|
|
Community 404, obwohl treff-regeln.html darauf verweist.
|
|
20.09. Dasselbe noch einmal, bei MODI_BEREICHE_ERLAUBT.
|
|
21.09. Die Kachel "Das Rudel" wurde zur Willkommensseite.
|
|
`treff` fiel heraus -- und diesmal ging es in die andere
|
|
Richtung: Die Schranke laesst eine AGENTURROLLE unbesehen
|
|
durch, wenn ein Brett in keiner Liste steht. Eine
|
|
Creatorin konnte das Brett des Treffs lesen.
|
|
|
|
DER GEMEINSAME KERN: Die Schranke kennt drei Toere -- Treff-Bretter,
|
|
Bretter mit eigener Kachel, Bretter hinter einer Seite. Ein Brett,
|
|
das durch KEINES davon abgedeckt ist, faellt nicht etwa heraus,
|
|
sondern DURCH: Fuer eine Agenturrolle endet der Weg bei
|
|
`return next()`.
|
|
|
|
Genau das wird hier gezaehlt. Nicht "ist treff drin" -- das waere
|
|
die Reparatur von gestern -- sondern: faellt IRGENDEIN Brett durch
|
|
alle Regeln? Wer morgen eine Kachel umleitet, bekommt es damit
|
|
gesagt, statt es zu verschenken.
|
|
===================================================================== */
|
|
melde("\n--- Kein Brett faellt zwischen die Regeln ---");
|
|
{
|
|
const bModul = await import("./workspace-bereiche.js");
|
|
const alleBretter = Object.keys(bModul.BEREICHE || {});
|
|
ok(alleBretter.length > 10,
|
|
`${alleBretter.length} Bretter angesehen`);
|
|
|
|
/* Die Tore der Schranke, aus dem Code geholt statt abgeschrieben. */
|
|
const durchTreff = new Set(w.TREFF_BRETTER);
|
|
const durchKachel = w.MODI_BEREICHE_ERLAUBT;
|
|
|
|
/* DIE EHRLICHE REGEL, GEMESSEN STATT GERATEN.
|
|
----------------------------------------------------------------
|
|
Mein erster Entwurf fragte "haengt dieses Brett an irgendeiner
|
|
Regel" -- und meldete sofort `content` und `schutz`. Beides sind
|
|
AGENTURBRETTER ("Content-Ideen", "Schutz & Regeln"), die eine
|
|
Agenturrolle zu Recht liest. Die Zeile war also zu breit und
|
|
haette zwei Fehlalarme dauerhaft festgeschrieben. Eine Pruefung,
|
|
die zu viel meldet, wird abgeschaltet -- dann haelt sie gar
|
|
nichts mehr.
|
|
|
|
Nachgemessen ist die Trennung exakt und in BEIDE Richtungen
|
|
deckungsgleich: Die Bretter mit `fuerAlle` sind genau die des
|
|
Treffs, plus die Agenturablage. Das ist eine Eigenschaft des
|
|
BRETTS und wandert nicht, wenn jemand eine Kachel umleitet --
|
|
genau der Unterschied, an dem es dreimal gescheitert ist. */
|
|
const fuerAlle = Object.entries(bModul.BEREICHE)
|
|
.filter(([, v]) => v && v.fuerAlle).map(([k]) => k);
|
|
ok(fuerAlle.length >= 7, `${fuerAlle.length} Bretter stehen allen offen`);
|
|
|
|
/* Die Agenturablage ist der eine gewollte Fall: Sie steht allen
|
|
offen, aber im ANDEREN Haus. Sie namentlich auszunehmen ist etwas
|
|
anderes als eine Liste zu pflegen -- es ist eine Ausnahme, die
|
|
man sieht. */
|
|
const GEMEINSCHAFT = fuerAlle.filter((b) => b !== "agentur");
|
|
const ungeschuetzt = GEMEINSCHAFT.filter((b) => !durchTreff.has(b));
|
|
ok(ungeschuetzt.length === 0, ungeschuetzt.length === 0
|
|
? `alle ${GEMEINSCHAFT.length} Bretter des Rudels stehen in TREFF_BRETTER`
|
|
: `NICHT GESCHUETZT -- eine Agenturrolle kaeme hinein: ${ungeschuetzt.join(", ")}`);
|
|
|
|
/* UND DIE ANDERE RICHTUNG: Nichts darf in TREFF_BRETTER stehen, das
|
|
dort nicht hingehoert -- sonst spannte die Liste einen Schutz ueber
|
|
ein Brett, das der Agentur gehoert, und sperrte sie aus. */
|
|
const zuviel = [...durchTreff].filter((b) => !GEMEINSCHAFT.includes(b));
|
|
ok(zuviel.length === 0, zuviel.length === 0
|
|
? "und nichts Fremdes steht darin"
|
|
: `ZUVIEL in TREFF_BRETTER: ${zuviel.join(", ")}`);
|
|
|
|
/* UND EIN MODI MUSS SIE AUCH OEFFNEN KOENNEN. Dieselbe Falle, zweite
|
|
Liste: `MODI_BEREICHE_ERLAUBT` wird ebenfalls aus Kachelzielen
|
|
abgeleitet und verliert ein Brett, sobald eine Kachel umzieht. */
|
|
const fuerModiZu = GEMEINSCHAFT.filter((b) => !durchKachel.has(b));
|
|
ok(fuerModiZu.length === 0, fuerModiZu.length === 0
|
|
? "und ein Modi kommt in jedes davon hinein"
|
|
: `fuer einen Modi gesperrt: ${fuerModiZu.join(", ")}`);
|
|
|
|
/* GEGENPROBE: Wuerden diese Zeilen ein ungeschuetztes Brett
|
|
ueberhaupt sehen? Ein erfundener Name steht in keiner Menge und
|
|
muss deshalb auffallen. */
|
|
const erfunden = "brett-das-es-nicht-gibt";
|
|
ok(!durchTreff.has(erfunden) && !durchKachel.has(erfunden),
|
|
"und ein Brett ohne Schutz wuerde hier wirklich auffallen");
|
|
}
|
|
|
|
melde("");
|
|
console.log(`${geprueft} Pruefungen, ${fehler} Fehler`);
|
|
console.log(fehler === 0 ? "ALLES IN ORDNUNG" : "NICHT IN ORDNUNG");
|
|
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* Aufraeumrest, kein Befund */ }
|
|
process.exit(fehler === 0 ? 0 : 1);
|