Files
dogfather-universe/server/pruef-an-alle.mjs
T
DogFatherGitandClaude Opus 5 8c599d7961 „An alle" bei „An wen" – und kein Creator mehr auf der Team-Seite
=== screen6: „An alle" ===

Filipe: „bei an ween fehlt noch die option alle neben den namen
allen."

Der Knopf steht VORN, nicht hinten: Wer etwas an das ganze Team geben
will, soll nicht erst an sechs Namen vorbeilesen. Er traegt die
Anzahl der Leute und faerbt sich wie die Stufe „Alle" -- beide
bedeuten „keine Einschraenkung", und zwei Farben dafuer waeren zwei
Aussagen fuer einen Gedanken. Ab zwei Personen; bei einer waere
„alle" derselbe Handgriff wie ihr Name.

EINE ANFRAGE, NICHT SECHS. Der Browser koennte fuer jede Person
einzeln fragen -- und beim dritten von sechs Aufrufen die Verbindung
verlieren. Dann haette die Haelfte des Teams die Aufgabe und die
andere nicht, und niemand saehe, wo es abgebrochen ist.

DER DUPLIKAT-SCHUTZ WANDERTE IN DIE SCHLEIFE. Er fragt, was EINE
Person schon liegen hat; stuende er davor, bekaeme nur die erste ihre
Pruefung und alle anderen die Vorlage doppelt -- genau so entstanden
am 22.09. aus zwei Klicks 112 Aufgaben. Gemessen: erster Klick 36
Aufgaben (12 mal 3 Modis), zweiter Klick 0 angelegt und 36
uebersprungen.

Ein gesperrter Modi bekommt nichts, ein Creator auch nicht, und wer
gar nicht verteilen darf, kommt ueber „alle" ebenfalls nicht weiter.

=== screen4: kein anderer Creator ===

Filipe: „in dieser app gibt es keinen und wird es niemals einen
anderen creator geben wie mich."

Auf der Aufgabenseite war eine Filterreihe mit „Alle Creator"
beschriftet -- und darunter standen Modis. Auf der Team-Adresse gibt
es keine Creator und wird es nie geben. Sie heisst dort jetzt „Für
wen".

DAS HAUS KOMMT VOM SERVER. `/api/ich` schickt jetzt `haus`; es stand
schon an der Sitzung und wurde nie mitgeschickt. Eine Rollenliste im
Browser waere die naechste zweite Wahrheit -- und falsch fuer
DogFather, der in beiden Haeusern arbeitet.

NEUN WEITERE „BEFUNDE" WAREN MEINE MESSFEHLER. Meine erste Messung
lief ueber 127.0.0.1 und meldete unter anderem „CREATOR WORKSPACE" in
der Kopfleiste JEDER Seite. Auf der echten Adresse steht dort „Team
Dogi" -- `person.haus` wird aus dem Host abgeleitet, und auf
127.0.0.1 ist das Haus nun einmal die Agentur. Nachgemessen mit
gesetztem Host: crew. -> haus="crew", marke="Team Dogi". Die
Uebersichtsseite mit ihren Creator-Texten steht auf crew. gar nicht
erst in den Kacheln.

Gemessen: pruef-an-alle, 19 Pruefungen, 0 Fehler. Gruen:
pruef-aufgabenbrett, pruef-aufgaben-vorlagen, pruef-vorlagen,
pruef-entwicklung (48), pruef-css-klassen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-24 11:27:10 +02:00

232 lines
9.8 KiB
JavaScript

/* =====================================================================
„AN ALLE" BEI „AN WEN" (24.09.2026)
Filipe: „bei an ween fehlt noch die option alle neben den namen
allen."
DREI FRAGEN, und die dritte ist die, an der es schiefgehen kann:
1. Trifft „alle" wirklich JEDEN -- oder nur den ersten?
2. Bleibt der Duplikat-Schutz erhalten? Er fragt, was EINE Person
schon hat; bei „alle" muss er je Person neu gestellt werden.
Sonst bekaeme nur die erste ihre Pruefung und alle anderen
die Vorlage doppelt -- genau so entstanden am 22.09. aus zwei
Klicks 112 Aufgaben.
3. Kommt jemand darueber an Leute heran, die ihn nichts angehen?
„alle" darf nicht mehr treffen als die Namen daneben.
===================================================================== */
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
const ordner = mkdtempSync(join(tmpdir(), "ws-aa-"));
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
import { notbremse } from "./helfer-notbremse.mjs";
const { eigenerPort } = await import("./helfer-port.mjs");
const PORT = await eigenerPort(import.meta, "die An-alle-Pruefung");
process.env.PORT = `${PORT}`;
process.env.SITE_ACCESS_SECRET = "lokaler-test";
process.env.SITE_ACCESS_CODE_DOGI = "DOGI-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");
notbremse(240_000, "pruef-an-alle");
await new Promise((r) => setTimeout(r, 900));
let fehler = 0, geprueft = 0;
const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
const melde = (t) => console.log(t);
const { DatabaseSync } = await import("node:sqlite");
const { scryptSync, randomBytes, createHmac } = await import("node:crypto");
const d = new DatabaseSync(process.env.WORKSPACE_DB);
const jetzt = new Date().toISOString();
const ks = randomBytes(32).toString("hex");
d.prepare("INSERT INTO einstellungen (schluessel, wert, geaendert, von)"
+ " VALUES (?,?,?,NULL) ON CONFLICT(schluessel) DO NOTHING")
.run("code_kennung_schluessel", ks, jetzt);
function anlegen(name, rolle, code, aktiv = 1) {
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 (?,?,?,?,?,?,?,?)")
.run(name, rolle, hash, salt, 32768,
createHmac("sha256", ks).update(code).digest("hex"), aktiv, jetzt);
return d.prepare("SELECT last_insert_rowid() AS id").get().id;
}
anlegen("Filipe", "admin", "CODE-DOGI-0001");
const modis = [
anlegen("Miss", "modi", "CODE-MODI-0001"),
anlegen("Ghost", "modi", "CODE-MODI-0002"),
anlegen("Marina", "modi", "CODE-MODI-0003"),
];
/* EIN GESPERRTER MODI. Er darf NICHT mitbekommen -- ein Zugang, der
nicht hereinkommt, braucht keine Aufgabenliste. */
const gesperrt = anlegen("Diene", "modi", "CODE-MODI-0004", 0);
/* Und ein Creator: Er steht gar nicht in dieser Auswahl und darf
auch ueber „alle" nichts bekommen. */
const creator = anlegen("Luna", "creator", "CODE-CREA-0001");
d.close();
const CREW = "crew.dogfather-universe.com";
const { request } = await import("node:http");
function roh(pfad, keks, koerper, methode) {
return new Promise((fertig, schief) => {
const daten = koerper ? JSON.stringify(koerper) : null;
const a = request({
host: "127.0.0.1", port: PORT, path: pfad,
method: methode || (daten ? "POST" : "GET"),
headers: {
Host: CREW, Origin: `https://${CREW}`,
...(keks ? { Cookie: keks } : {}),
...(daten ? { "Content-Type": "application/json",
"Content-Length": Buffer.byteLength(daten) } : {}),
},
}, (antwort) => {
let text = "";
antwort.on("data", (t) => { text += t; });
antwort.on("end", () => fertig({ code: antwort.statusCode, text }));
});
a.on("error", schief);
if (daten) a.write(daten);
a.end();
});
}
const json = (r) => { try { return JSON.parse(r.text); } catch { return {}; } };
async function anmelden(rolle, code) {
const r = await new Promise((fertig, schief) => {
const daten = JSON.stringify({ rolle, code, alter_ok: true });
const a = request({
host: "127.0.0.1", port: PORT, path: "/workspace/api/anmelden",
method: "POST",
headers: { Host: CREW, Origin: `https://${CREW}`,
"Content-Type": "application/json", "Content-Length": Buffer.byteLength(daten) },
}, (antwort) => {
let text = "";
antwort.on("data", (t) => { text += t; });
antwort.on("end", () => fertig({ code: antwort.statusCode, kopf: antwort.headers }));
});
a.on("error", schief);
a.write(daten); a.end();
});
if (r.code !== 200) return null;
return [].concat(r.kopf["set-cookie"] || []).map((z) => z.split(";")[0]).join("; ");
}
const kDogi = await anmelden("admin", "CODE-DOGI-0001");
const kModi = await anmelden("modi", "CODE-MODI-0001");
ok(!!kDogi && !!kModi, "DogFather und ein Modi sind angemeldet");
/* Wie viele Aufgaben hat wer? Direkt aus der Datenbank -- die Antwort
der Route zu glauben hiesse, sie mit sich selbst zu pruefen. */
function zaehle(personId) {
const db2 = new DatabaseSync(process.env.WORKSPACE_DB, { readOnly: true });
const { n } = db2.prepare(
"SELECT COUNT(*) AS n FROM aufgaben WHERE verantwortlich_id = ? AND vorlage IS NOT NULL")
.get(personId);
db2.close();
return n;
}
/* Welche Kategorie gibt es ueberhaupt? Aus dem Katalog, den die Seite
auch bekommt -- eine erfundene Gruppe waere eine Pruefung, die
nichts trifft. */
const katalog = json(await roh("/workspace/api/vorlagen", kDogi));
const ersteGruppe = (katalog.katalog?.gruppen || [])[0]?.schluessel
|| (katalog.katalog?.aufgaben || [])[0]?.gruppe || "";
ok(!!ersteGruppe, `es gibt eine Kategorie zum Verteilen (${ersteGruppe})`);
melde("\n=== 1. alle trifft jeden ===");
{
const vorher = modis.map(zaehle);
ok(vorher.every((x) => x === 0), `vorher hat niemand etwas (${vorher.join(", ")})`);
const a = await roh("/workspace/api/vorlagen/uebernehmen", kDogi,
{ art: "katalog_alle", gruppe: ersteGruppe, stufe: "", person_id: "alle" });
ok(a.code === 200, `die Anfrage geht durch (${a.code})`);
const r = json(a);
ok((r.angelegt || 0) > 0, `es wurde etwas angelegt (${r.angelegt})`);
const nachher = modis.map(zaehle);
ok(nachher.every((x) => x > 0),
`JEDER der drei Modis hat jetzt Aufgaben (${nachher.join(", ")})`);
ok(new Set(nachher).size === 1,
"und alle gleich viele -- sonst hat die Schleife jemanden ausgelassen");
ok((r.angelegt || 0) === nachher[0] * modis.length,
`die Zahl in der Antwort passt zur Datenbank (${r.angelegt} = ${nachher[0]} x ${modis.length})`);
}
melde("\n=== 2. Der Duplikat-Schutz gilt je Person ===");
{
const vorher = modis.map(zaehle);
const a = await roh("/workspace/api/vorlagen/uebernehmen", kDogi,
{ art: "katalog_alle", gruppe: ersteGruppe, stufe: "", person_id: "alle" });
ok(a.code === 200, `ein zweiter Klick geht durch (${a.code})`);
const r = json(a);
ok((r.angelegt || 0) === 0,
`und legt NICHTS mehr an (${r.angelegt}) -- genau das war der Fehler vom 22.09.`);
/* `uebersprungen` ist eine ZAHL, kein Feld -- nachgesehen in
workspace-vorlagen.js (`uebersprungen: uebersprungen.length`).
Mein erster Versuch rief `.length` darauf auf und bekam
`undefined`; ein Messfehler, der wie eine fehlende Antwort
aussah. */
ok(Number(r.uebersprungen) > 0,
`stattdessen sagt die Antwort, wie viele schon lagen (${r.uebersprungen})`);
ok(Number(r.uebersprungen) === 36,
`und zwar alle 36 -- 12 Vorlagen mal 3 Modis (${r.uebersprungen})`);
const nachher = modis.map(zaehle);
ok(nachher.join() === vorher.join(),
`niemand hat etwas doppelt (${nachher.join(", ")})`);
}
melde("\n=== 3. alle trifft nicht mehr als die Namen ===");
{
ok(zaehle(gesperrt) === 0,
`ein GESPERRTER Modi bekommt nichts (${zaehle(gesperrt)})`);
ok(zaehle(creator) === 0,
`ein Creator erst recht nicht (${zaehle(creator)})`);
/* Und wer gar nicht verteilen darf, kommt auch ueber „alle" nicht
weiter -- sonst waere der neue Wert ein Weg an der Rechtefrage
vorbei. */
const alsModi = await roh("/workspace/api/vorlagen/uebernehmen", kModi,
{ art: "katalog_alle", gruppe: ersteGruppe, stufe: "", person_id: "alle" });
ok(alsModi.code >= 400,
`ein Modi kann nicht an alle verteilen (${alsModi.code})`);
}
melde("\n=== 4. Gegenproben ===");
{
/* EINE EINZELNE PERSON GEHT WEITERHIN -- sonst hiesse „alle
funktioniert" nur, dass der alte Weg kaputt ist. */
const einzeln = await roh("/workspace/api/vorlagen/uebernehmen", kDogi,
{ art: "katalog", nr: 0, gruppe: ersteGruppe, person_id: modis[0] });
ok(einzeln.code === 200 || einzeln.code === 400,
`der Weg an EINE Person antwortet noch (${einzeln.code})`);
/* Ein erfundener Wert ist kein "alle". */
const quatsch = await roh("/workspace/api/vorlagen/uebernehmen", kDogi,
{ art: "katalog_alle", gruppe: ersteGruppe, stufe: "", person_id: "alle-zusammen" });
ok(quatsch.code >= 400,
`alle-zusammen ist nicht alle (${quatsch.code})`);
/* Und ohne Person weiterhin die alte Absage. */
const ohne = await roh("/workspace/api/vorlagen/uebernehmen", kDogi,
{ art: "katalog_alle", gruppe: ersteGruppe, stufe: "" });
ok(ohne.code === 400, `ohne Person immer noch 400 (${ohne.code})`);
}
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ }
console.log("");
console.log(`${geprueft} Pruefungen, ${fehler} Fehler`);
console.log(fehler ? "NICHT IN ORDNUNG" : "ALLES IN ORDNUNG");
process.exit(fehler ? 1 : 0);