Der komplette Pruefstand vor der Vorstellung (38 Laeufe im Workspace) lief bis auf zwei durch. Beide Fehlschlaege waren KEINE Fehler in der Anwendung, sondern Pruefungen mit veraltetem Weltbild -- Folgen von Entscheidungen, die Filipe selbst getroffen hat: 1. pruef-workspace-seiten (32 von 32 rot) pruefte, dass JEDE Seite ihre EIGENE Buehne hat (Aufgaben = Werkstatt, Kalender = Nachtstadt, ...). Am 03.09.2026 hat Filipe die neun Szenen durch EIN Bild ersetzt. Jetzt wird geprueft, was weiterhin wichtig ist: dass ueberhaupt ein Hintergrundbild ANKOMMT (ein data-buehne ohne CSS-Regel waere still wirkungslos) -- und dass es auf JEDER Seite dasselbe ist. Ohne die zweite Bedingung waere sie auch gruen, wenn irgendwo eine alte Szene zurueckkaeme. 2. pruef-zustand-ansicht erwartete, dass ein Manager den Systemzustand sieht. Seit dem 02.09.2026 gilt "die manager sollen diese kategorien garnicht sehen"; der Zustand gehoert zur Automationen-Seite. Beide UMGEDREHT statt geloescht. Eine geloeschte Pruefung hinterlaesst keine Spur davon, dass hier einmal etwas anderes galt -- und niemand merkt, wenn eine Sperre spaeter versehentlich wieder faellt. Co-Authored-By: Claude Opus 5 <[email protected]>
170 lines
8.2 KiB
JavaScript
170 lines
8.2 KiB
JavaScript
/* Browserpruefung des Sicherungs-Kastens auf der Automationen-Seite.
|
|
Geprueft wird vor allem: sieht man ueberhaupt, WANN zuletzt gesichert
|
|
wurde, funktioniert der Knopf, und verschwindet der Kasten fuer Rollen,
|
|
die davon nichts wissen sollen. */
|
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
|
|
const ordner = mkdtempSync(join(tmpdir(), "ws-zustand-"));
|
|
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
|
process.env.PORT = "4182";
|
|
process.env.SITE_ACCESS_SECRET = "lokaler-test";
|
|
process.env.SITE_ACCESS_CODE_DOGI = "DOGI-TEST-1";
|
|
process.env.SITE_ACCESS_CODE_VANVAN = "VANVAN-TEST-1";
|
|
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 }); };
|
|
await import("./index.js");
|
|
await new Promise((r) => setTimeout(r, 700));
|
|
const BASIS = "http://127.0.0.1:4182";
|
|
setTimeout(() => { console.log("ABBRUCH"); process.exit(1); }, 180_000).unref?.();
|
|
|
|
let fehler = 0;
|
|
const ok = (b, t) => { console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
|
|
|
await fetch(BASIS + "/workspace/api/anmelden", {
|
|
method: "POST", headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ rolle: "admin", code: "x" }) }).catch(() => {});
|
|
|
|
const { DatabaseSync } = await import("node:sqlite");
|
|
const { scryptSync, randomBytes } = await import("node:crypto");
|
|
const d = new DatabaseSync(process.env.WORKSPACE_DB);
|
|
const jetzt = new Date().toISOString();
|
|
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, aktiv, erstellt) VALUES (?,?,?,?,?,1,?)")
|
|
.run(name, rolle, hash, salt, 32768, jetzt);
|
|
}
|
|
anlegen("Dogfather", "admin", "CODE-DOGI-0001");
|
|
anlegen("Ein Manager", "manager", "CODE-MANA-0001");
|
|
d.close();
|
|
|
|
const { chromium } = await import(
|
|
"file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
|
|
const browser = await chromium.launch();
|
|
|
|
async function seiteFuer(kontext, rolle, code) {
|
|
const seite = await kontext.newPage();
|
|
await seite.goto(BASIS + "/workspace/", { waitUntil: "domcontentloaded" });
|
|
await seite.click(`.rolle[data-rolle="${rolle}"]`);
|
|
await seite.fill("#code", code);
|
|
await seite.click("button[type=submit]");
|
|
await seite.waitForURL("**/start.html", { timeout: 15000 });
|
|
await seite.goto(BASIS + "/workspace/automation.html", { waitUntil: "networkidle" });
|
|
await seite.waitForTimeout(600);
|
|
return seite;
|
|
}
|
|
|
|
const lesen = (seite) => seite.evaluate(() => ({
|
|
da: !document.getElementById("zustand-kasten")?.hidden,
|
|
zustand: document.getElementById("zustand-kasten")?.dataset.zustand,
|
|
titel: document.getElementById("zustand-titel")?.textContent,
|
|
text: document.getElementById("zustand-text")?.textContent,
|
|
zeilen: [...document.querySelectorAll("#zustand-daten li")].map((l) => l.textContent),
|
|
sicherungen: document.querySelectorAll("#sicherungen .verlauf__zeile").length,
|
|
ueber: document.documentElement.scrollWidth - document.documentElement.clientWidth,
|
|
}));
|
|
|
|
/* Vor jedem Durchlauf denselben Ausgangszustand herstellen. Ohne das
|
|
traegt der zweite Durchlauf die Sicherung UND die gemeldete Kopie aus
|
|
dem ersten mit sich herum und prueft etwas ganz anderes -- ein Test,
|
|
der beim zweiten Mal grundlos rot wird, sagt nichts mehr aus. */
|
|
function zuruecksetzen() {
|
|
const z = new DatabaseSync(process.env.WORKSPACE_DB);
|
|
try {
|
|
z.prepare("DELETE FROM einstellungen WHERE schluessel IN ('kopie_letzte','sicherung_letzte','sicherung_fehler')").run();
|
|
} catch { /* egal */ } finally { z.close(); }
|
|
try {
|
|
const { rmSync } = require("node:fs");
|
|
rmSync(join(ordner, "sicherungen"), { recursive: true, force: true });
|
|
} catch { /* egal */ }
|
|
}
|
|
|
|
const { createRequire } = await import("node:module");
|
|
const { pathToFileURL } = await import("node:url");
|
|
const require = createRequire(pathToFileURL(process.cwd() + "/"));
|
|
|
|
for (const [breite, wie] of [[1440, "Computer"], [390, "Handy"]]) {
|
|
console.log(`\n=== DogFather · ${wie}`);
|
|
zuruecksetzen();
|
|
const kontext = await browser.newContext({ viewport: { width: breite, height: 950 } });
|
|
const konsole = [];
|
|
const seite = await seiteFuer(kontext, "admin", "CODE-DOGI-0001");
|
|
seite.on("console", (m) => { if (m.type() === "error") konsole.push(m.text()); });
|
|
seite.on("pageerror", (e) => konsole.push("pageerror: " + e.message));
|
|
|
|
const vorher = await lesen(seite);
|
|
ok(vorher.da, "der Kasten ist da");
|
|
ok(/Noch keine Sicherung/.test(vorher.titel || ""),
|
|
`sagt ehrlich, dass noch nichts lief ("${vorher.titel}")`);
|
|
ok(vorher.ueber <= 0, `nichts steht über (${vorher.ueber}px)`);
|
|
ok(vorher.zeilen.some((z) => /WAL/.test(z)), "die wartenden Schreibvorgänge stehen da");
|
|
|
|
/* Der Knopf ist der eigentliche Test: Er muss wirklich sichern. */
|
|
await seite.click("#jetzt-sichern");
|
|
await seite.waitForTimeout(1500);
|
|
const nachher = await lesen(seite);
|
|
console.log(" ->", nachher.titel, "|", nachher.text);
|
|
/* Ohne Kopie ausserhalb des Servers MUSS der Kasten gelb bleiben --
|
|
und die Ueberschrift muss genau das als Grund nennen, statt nur
|
|
"gerade gesichert" zu melden und die Farbe unerklaert zu lassen. */
|
|
ok(nachher.zustand === "alt", `ohne Kopie ausser Haus bleibt es gelb (${nachher.zustand})`);
|
|
ok(/Kopie au.erhalb/.test(nachher.titel || ""),
|
|
`die Ueberschrift nennt den Grund ("${nachher.titel}")`);
|
|
ok(/Personen/.test(nachher.text || ""), "und der Text sagt, dass der Server frisch ist");
|
|
|
|
/* Jetzt die Kopie melden -- danach muss es gruen werden. */
|
|
const schluessel = await seite.evaluate(async () =>
|
|
(await (await fetch("/workspace/api/zustand/kopie-schluessel",
|
|
{ credentials: "same-origin" })).json()).schluessel);
|
|
await seite.evaluate(async (s) => {
|
|
await fetch("/workspace/api/zustand/kopie", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json", "X-Kopie-Schluessel": s },
|
|
body: JSON.stringify({ dateien: 4, sicherungen: 1, bytes: 180000, rechner: "Filipes PC" }),
|
|
});
|
|
}, schluessel);
|
|
await seite.reload({ waitUntil: "networkidle" });
|
|
await seite.waitForTimeout(600);
|
|
const gruen = await lesen(seite);
|
|
ok(gruen.zustand === "gut", `mit Kopie ausser Haus wird es gruen (${gruen.zustand})`);
|
|
ok(/Zuletzt gesichert/.test(gruen.titel || ""), `und die Ueberschrift wieder ruhig ("${gruen.titel}")`);
|
|
ok(gruen.zeilen.some((z) => /Filipes PC/.test(z)), "der Rechner steht in der Uebersicht");
|
|
ok(nachher.sicherungen >= 1, `die Sicherung taucht in der Liste auf (${nachher.sicherungen})`);
|
|
ok(nachher.ueber <= 0, `auch danach steht nichts über (${nachher.ueber}px)`);
|
|
ok(konsole.length === 0, `keine Fehler in der Konsole${konsole.length ? " -> " + konsole.join(" | ") : ""}`);
|
|
|
|
if (breite === 1440) await seite.screenshot({ path: "pruef-zustand.png", fullPage: true });
|
|
await kontext.close();
|
|
}
|
|
|
|
/* AM 02.09.2026 UMGEDREHT.
|
|
|
|
Hier stand "Manager darf es auch sehen". Das galt -- bis Filipe
|
|
entschied: "die manager sollen diese kategorien garnicht sehen"
|
|
(Personen & Zugänge, Automationen). Der Systemzustand ist Teil der
|
|
Automationen-Seite und faellt damit unter dieselbe Regel.
|
|
|
|
Die Pruefung wird deshalb nicht geloescht, sondern gedreht: Eine
|
|
geloeschte Pruefung hinterlaesst keine Spur davon, dass hier einmal
|
|
etwas anderes galt -- und niemand merkt, wenn die Sperre spaeter
|
|
versehentlich wieder faellt. */
|
|
console.log("\n=== Manager darf es NICHT mehr sehen");
|
|
{
|
|
const kontext = await browser.newContext({ viewport: { width: 1440, height: 950 } });
|
|
const seite = await seiteFuer(kontext, "manager", "CODE-MANA-0001");
|
|
const r = await lesen(seite);
|
|
ok(!r.da || !/Zuletzt gesichert/.test(r.titel || ""),
|
|
`Manager sieht den Zustand NICHT mehr ("${r.titel}")`);
|
|
await kontext.close();
|
|
}
|
|
|
|
await browser.close();
|
|
console.log(`\n${fehler === 0 ? "Alles in Ordnung." : fehler + " Prüfung(en) fehlgeschlagen."}`);
|
|
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ }
|
|
process.exit(fehler === 0 ? 0 : 1);
|