From 13127451950834c9ee48f291e56764cb0af99026 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 29 Oct 2026 12:00:44 +0000 Subject: [PATCH] Kundenansicht neu P10: Korrekturen aus der Pruefung (Task 6) Co-Authored-By: Claude Opus 5.5 --- test/knUmschaltCheckliste.test.js | 73 +++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/test/knUmschaltCheckliste.test.js b/test/knUmschaltCheckliste.test.js index dbc0ba4..9694a71 100644 --- a/test/knUmschaltCheckliste.test.js +++ b/test/knUmschaltCheckliste.test.js @@ -12,3 +12,76 @@ test('Überschriften in der Reihenfolge der Spec 5.10', () => { test('nennt die Befehle und die Stopps', () => { for (const s of ['kundenart-bestand.mjs --pruefen', '--ausfuehren', 'KUNDENANSICHT_NEU=on', 'KUNDENANSICHT_NEU=off', 'docker compose up -d --no-deps kundenbereich-server', '--erwartet=neu', 'docker-compose.yml', 'Stop: Inhaber-OK nötig', 'kein Rückweg per Schalter']) assert.ok(doc.includes(s), s) }) + +// Regeln der Checkliste (Prüfung): Die zwei Tests oben suchen nur Wörter irgendwo im Text. Hier hängen die Zusagen an ihrem Abschnitt. +const STOPP = 'Stop: Inhaber-OK nötig' +const abschnitt = (d, n) => { const von = d.indexOf(KOEPFE[n - 1]); const bis = n < KOEPFE.length ? d.indexOf(KOEPFE[n]) : d.length; return von < 0 || bis < 0 ? '' : d.slice(von, bis) } +const anzahl = (s, t) => s.split(t).length - 1 +const vor = (s, a, b) => s.indexOf(a) > -1 && s.indexOf(b) > s.indexOf(a) +function regelverstoesse(d) { + const v = [] + const a = (n) => abschnitt(d, n) + const soll = (ok, was) => { if (!ok) v.push(was) } + // Stopps: insgesamt mindestens acht, in den Abschnitten mit Live-Schritten je mindestens einer, in 8 zwei (Offline-Seite, Festschreiben) + soll(anzahl(d, STOPP) >= 8, 'weniger als 8 Stopps') + for (const [n, min] of [[1, 1], [2, 1], [3, 1], [5, 1], [6, 1], [8, 2]]) soll(anzahl(a(n), STOPP) >= min, `Abschnitt ${n}: Stopp fehlt`) + soll(vor(a(5), STOPP, 'sed -i') && vor(a(5), 'cp -n', 'sed -i') && vor(a(5), STOPP, 'docker compose up'), 'Abschnitt 5: Sicherung und Stopp stehen nicht vor dem Schreiben der .env') + soll(vor(a(2), STOPP, '--ausfuehren'), 'Abschnitt 2: Stopp steht nicht vor --ausfuehren') + // Push nur mit Stopp, auf main, nie erzwungen + for (const l of d.split('\n').filter((z) => z.includes('git push'))) soll(l.includes(STOPP) && l.includes('refs/heads/main') && !/--force|\s-f\s/.test(l), 'Push ohne Stopp oder erzwungen') + // Kundenart-Skript: Protokoll mit tee, Ausführen mit Bestätigung und pipefail (sonst verschluckt tee den Exit-Code) + const art = a(2).split('\n').filter((l) => l.includes('kundenart-bestand.mjs')) + soll(art.length === 2 && art.every((l) => l.includes('| tee /root/reports/kundenansicht-neu-2026-10-15/umschalten/')), 'Abschnitt 2: Protokoll mit tee fehlt') + soll(art.some((l) => l.includes('--ausfuehren') && l.includes('--bestaetigung="Kundenart unternehmen setzen: "') && l.includes('set -o pipefail')), 'Abschnitt 2: --ausfuehren ohne Bestätigung oder pipefail') + // Neustart nur für den Server und nur mit --no-deps; in Abschnitt 5 und im Rückweg + const neustarts = d.match(/docker compose up[^`\n]*/g) || [] + soll(neustarts.length >= 3 && neustarts.every((c) => /^docker compose up -d --no-deps kundenbereich-server(?: |$)/.test(c)), 'Neustart ohne --no-deps oder nicht nur kundenbereich-server') + for (const n of [5, 7]) soll(/docker compose up -d --no-deps kundenbereich-server/.test(a(n)), `Abschnitt ${n}: Neustart fehlt`) + // Ruhezustand (1) und Rückweg (7) prüfen auf alt, Nachprüfung (6) und Festschreiben (8) auf neu + soll(anzahl(d, '--erwartet=alt') >= 2, 'weniger als 2 Prüfungen auf alt') + for (const [n, e] of [[1, 'alt'], [7, 'alt'], [6, 'neu'], [8, 'neu']]) soll(a(n).includes(`--erwartet=${e}`), `Abschnitt ${n}: --erwartet=${e} fehlt`) + // Rückweg und Schalter + for (const s of ['KUNDENANSICHT_NEU=off', 'bleiben gültig', 'Fassung 6']) soll(a(7).includes(s), `Abschnitt 7: ${s} fehlt`) + soll(a(5).includes('KUNDENANSICHT_NEU=on') && a(5).includes('docker-compose.yml'), 'Abschnitt 5: Schalter oder Compose fehlt') + soll(a(6).includes('Version 6'), 'Abschnitt 6: Version 6 fehlt') + soll(a(8).includes('kein Rückweg per Schalter'), 'Abschnitt 8: kein Rückweg per Schalter fehlt') + // Beobachtungszeit: Datei in 6, Zeitraum in 6 und 8 + soll(a(6).includes('/umschalten/beobachtung.txt') && a(6).includes('Beobachtungszeit') && a(8).includes('Beobachtungszeit'), 'Beobachtungszeit fehlt in Abschnitt 6 oder 8') + // je Abschnitt höchstens zehn Zeilen, in der du-Form + for (let n = 1; n <= KOEPFE.length; n++) soll(a(n).split('\n').slice(1).filter((l) => l.trim()).length <= 10, `Abschnitt ${n}: mehr als 10 Zeilen`) + soll(!/\bSie\b|\bIhr/.test(d) && /\bDu hast\b/.test(d), 'nicht in der du-Form') + return v +} +// Jede Änderung unten bricht eine Zusage der Checkliste; die Regeln müssen sie bemerken +const imAbschnitt = (n, f) => (d) => { const s = abschnitt(d, n); return d.replace(s, f(s)) } +const ERSTE = (t, neu) => (d) => d.replace(t, neu) +const ALLE = (t, neu) => (d) => d.replaceAll(t, neu) +const BRUCH = { + 'alle Stopps bis auf einen entfernt': (d) => { let i = 0; return d.replaceAll(STOPP, () => (i++ === 0 ? STOPP : 'Hinweis')) }, + 'Stopp vor dem Schreiben der .env entfernt': imAbschnitt(5, (s) => s.replace(STOPP, 'Hinweis')), + 'Stopp vor --ausfuehren entfernt': imAbschnitt(2, (s) => s.replace(STOPP, 'Hinweis')), + 'Stopp vor dem Festschreiben-Push entfernt': imAbschnitt(8, (s) => s.replace(`6. **${STOPP}**`, '6. **Hinweis**')), + 'Push erzwungen': ERSTE("git push origin 'kn-p10-a", "git push --force origin 'kn-p10-a"), + 'Sicherung der .env entfernt': ERSTE('cp -n', 'ls'), + 'Rückweg ohne --erwartet=alt': imAbschnitt(7, (s) => s.replace('--erwartet=alt', '--erwartet=neu')), + 'keine Prüfung auf alt': ALLE('--erwartet=alt', '--erwartet=neu'), + 'Rückweg ohne --no-deps': imAbschnitt(7, (s) => s.replace(' --no-deps', '')), + 'Neustart von preview-portal': ERSTE('up -d --no-deps kundenbereich-server', 'up -d --no-deps preview-portal'), + 'Schalter nie auf off': ALLE('KUNDENANSICHT_NEU=off', 'KUNDENANSICHT_NEU=aus'), + 'Verträge unter Fassung 6 nicht mehr gültig': ALLE('bleiben gültig', 'sind hinfällig'), + 'Beobachtungsdatei entfernt': ALLE('beobachtung.txt', 'notiz.txt'), + 'Beobachtungszeit entfernt': ALLE('Beobachtungszeit', 'Wartezeit'), + 'Sie statt du': ALLE('Du hast', 'Sie haben'), + '--ausfuehren ohne Bestätigung': ALLE('--bestaetigung', '--ja'), + '--ausfuehren ohne pipefail': ALLE('set -o pipefail; ', ''), + 'Protokolle entfernt': ALLE(' | tee ', ' > '), + 'Abschnitt 5 zu lang': imAbschnitt(5, (s) => `${s}9. a\n10. b\n11. c\n`), +} +test('die echte Checkliste hält alle Regeln', () => { assert.deepEqual(regelverstoesse(doc), []) }) +for (const [name, aendern] of Object.entries(BRUCH)) { + test(`Regeln bemerken: ${name}`, () => { + const kaputt = aendern(doc) + assert.notEqual(kaputt, doc, 'die Änderung greift nicht') + assert.ok(regelverstoesse(kaputt).length > 0, 'unbemerkt') + }) +}