test: Zusagen pruefbar machen, die nur von Hand hielten (#038, #037)
Prüfung / pruefung (push) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s

#038: Die E2E-Ablaeufe laufen ab jetzt vor jedem Merge nach main, nicht vor
jedem Commit. Gemessen: Bau 14 s, Start 7 s, Ablaeufe 256 s — zu den vier
Minuten des bestehenden Tores. Neun Minuten vor jedem Commit ist ein Tor, das
umgangen wird. Vor jedem Merge waeren es in Phase 2 acht Laeufe gewesen, und
jeder der vier Befunde aus #027 waere am Tag seiner Entstehung aufgefallen.

Geprueft wurde jeder der 16 Ablaeufe gegen die Frage, ob es ein
Vitest-Gegenstueck gibt. Neun Zusagen haengen ausschliesslich dort, und keine
laesst sich holen: alle brauchen einen echten Browser. Die Liste steht in
CLAUDE.md.

Der eigentliche Fund dabei war keine Testluecke, sondern eine Konvention ohne
Pruefer: "jede Seite prueft selbst" steht seit #007 in CLAUDE.md, und geprueft
hat es bis hierher nur ein Mensch, der nachsieht. Der Review von Phase 2 hat
alle 27 Seiten durchgesehen — ein Ergebnis, das fuer genau einen Tag gilt.
tests/zugang.test.ts liest sie jetzt, samt jedem Route Handler; die Ausnahmen
(Auth, Healthcheck) stehen namentlich mit Begruendung, und ein Test prueft auch
die Gegenrichtung.

#037: Das Lesemodell der Anhaenge liegt in queries/ und engt selbst ein — vorher
hing das daran, dass beide Aufrufer den Lauf vorher pruefen. /meine-geraete wird
bei 390 px gemessen, mit einem echten MITARBEITER-Konto aus dem E2E-Vorlauf,
dazu ein Test, dass dort kein Link in die Verwaltung angeboten wird.

Dabei selbst einen Fehler gemacht, der die neue Regel begruendet: fuer das
zweite Konto brauchte `anmelden` eine Variante, die nicht auf / wartet, und ich
habe das Warten fuer alle gelockert. Das machte zwei unverwandte Ablaeufe
unzuverlaessig — sichtbar nur im vollen Durchgang, einzeln waren beide gruen.
Ein gemeinsamer Helfer in einer Testsuite ist geteilter Zustand. Ohne vollen
Durchgang haette ich zwei rote Ablaeufe gemergt.

1457 Vitest-Tests (vorher 1454), 115 E2E-Ablaeufe (vorher 113). Offen bleibt nur
noch #024, und das verlangt einen echten Proxmox-Host.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Florian Wilde
2026-10-11 06:05:29 +02:00
co-authored by Claude Opus 5
parent 80d97467da
commit 75c335df23
16 changed files with 639 additions and 80 deletions
+50 -2
View File
@@ -12,12 +12,12 @@ import {
} from "@/lib/anhaenge";
import {
AnhangFehler,
anhaengeDesLaufs,
anhangEntfernen,
anhangLesen,
anhangPfad,
anhangSpeichern,
} from "@/server/services/anhaenge";
import { anhaengeDesLaufs } from "@/server/queries/anhaenge";
import { MandantFehler } from "@/server/mandant";
import { LaufFehler } from "@/server/services/laeufe";
import { datenbankLeeren, datenbankVerfuegbar, testPrisma } from "./helpers/db";
@@ -393,7 +393,7 @@ describe.runIf(datenbankVerfuegbar)("Anhänge", () => {
admin,
);
const liste = await anhaengeDesLaufs(laufId);
const liste = await anhaengeDesLaufs(laufId, admin);
expect(liste).toHaveLength(1);
expect(liste[0]).toMatchObject({
schrittergebnisId: ergebnisId,
@@ -402,6 +402,54 @@ describe.runIf(datenbankVerfuegbar)("Anhänge", () => {
});
});
it("engt die Liste selbst auf die sichtbaren Kunden ein – ohne Vorprüfung des Aufrufers", async () => {
/*
* **Die Zusage von #037.** Vorher nahm `anhaengeDesLaufs` nur eine
* Lauf-Kennung und wendete keine Sicht an; dass niemand fremde Anhänge sah,
* hing daran, dass beide Aufrufer den Lauf vorher prüften. Das war kein
* Schaden und trotzdem die falsche Form: ein Lesemodell wendet seine Sicht
* selbst an, **damit kein Aufrufer sie vergessen kann** (CLAUDE.md).
*
* Dieser Test ruft die Funktion deshalb **direkt** und ohne jede
* Vorprüfung — so wie es der dritte Aufrufer täte, der die Prüfung vergisst.
*/
const vorher = process.env["OIDC_CUSTOMER_GROUP_PREFIX"];
process.env["OIDC_CUSTOMER_GROUP_PREFIX"] = "kunde-";
try {
const { admin, laufId, schrittId } = await aufbau();
await testPrisma.einstellung.update({
where: { id: "global" },
data: { modus: Betriebsmodus.DIENSTLEISTUNG },
});
await anhangSpeichern(
{
laufId,
vorlagenSchrittId: schrittId,
dateiname: "fremd.png",
medientyp: "image/png",
inhalt: new Uint8Array(PNG),
},
admin,
);
const fremd = await testPrisma.kunde.create({ data: { name: "Fremd GmbH", kuerzel: "FRD" } });
const techniker = await testPrisma.benutzer.create({
data: { email: "tina@firma.example", name: "Tina", rolle: "TECHNIKER" },
});
await testPrisma.benutzerKunde.create({
data: { benutzerId: techniker.id, kundeId: fremd.id },
});
// Der Administrator sieht den Anhang.
expect(await anhaengeDesLaufs(laufId, admin)).toHaveLength(1);
// Der auf einen anderen Kunden beschränkte Techniker nicht.
expect(await anhaengeDesLaufs(laufId, { id: techniker.id, rolle: techniker.rolle })).toHaveLength(0);
} finally {
if (vorher === undefined) delete process.env["OIDC_CUSTOMER_GROUP_PREFIX"];
else process.env["OIDC_CUSTOMER_GROUP_PREFIX"] = vorher;
}
});
it("hält die Mandantengrenze in der Datenbank, nicht nur im Dienst", async () => {
const { laufId, schrittId, ergebnisId } = await aufbau();
const fremd = await testPrisma.kunde.create({ data: { name: "Fremd GmbH", kuerzel: "FREMD" } });