Files
Webklar-Kundenbereich/test/knUmschaltSeiten.test.js
T
ClaudeandClaude Opus 5.5 db5e6ece2c Kundenansicht neu P10: Korrekturen aus der Pruefung (Task 3)
Archiv v5 bei on für Fassung-5-Zustimmungen (termsDocFor archiv), /auftrag.html in der Weiche (Übersicht N40), Test mit dem echten Schalter, Hinweise zu Task 14

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 12:07:57 +00:00

169 lines
8.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test, after } from 'node:test'
import assert from 'node:assert/strict'
import express from 'express'
import { mkdtempSync, readFileSync, rmdirSync, unlinkSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { startAppwriteMock } from './support/appwriteMock.js'
import { startFakeStripe } from './support/fakeStripe.js'
import { useTestEnv } from './support/testEnv.js'
import { schemaKeys } from './support/schema.js'
// P10 Task 3: Umschalt-Weiche – erst bei KUNDENANSICHT_NEU=on liefert sie die neuen öffentlichen Seiten, das Manifest und die Fassung-6-Adresse
const aw = await startAppwriteMock({ schema: { customerViewLog: schemaKeys('customerViewLog') } })
const st = await startFakeStripe()
useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url })
const { startTestApp } = await import('./support/testApp.js')
const { umschaltSeitenRouter, UMSCHALT_SEITEN, fassung6Aus } = await import('../server/routes/umschaltSeiten.js')
const { config } = await import('../server/config.js')
const { termsDocFor } = await import('../server/services/products.js')
const publicDir = fileURLToPath(new URL('../public/', import.meta.url))
let modus = 'off'
let fassung6 = null
const router = express.Router()
router.use(umschaltSeitenRouter({ publicDir, modus: () => modus, fassung6: () => fassung6 }))
router.use(express.static(publicDir))
const app = await startTestApp({ '/': router })
after(() => Promise.all([app.close(), aw.close(), st.close()]))
const holen = async (pfad, basis = app.url, methode = 'GET') => {
const r = await fetch(basis + pfad, { method: methode, redirect: 'manual' })
return { status: r.status, bytes: Buffer.from(await r.arrayBuffer()), headers: r.headers }
}
const datei = (rel) => readFileSync(path.join(publicDir, rel))
test('Übersicht N40: auch die öffentliche Auftragsseite (/auftrag.html#t=<Token> aus der Mail K31) ist eine Zeile der Weiche', () => {
assert.equal(UMSCHALT_SEITEN['/auftrag.html'], 'neu/oeffentlich/auftrag.html')
})
for (const [pfad, neu] of Object.entries(UMSCHALT_SEITEN)) {
test(`${pfad}: off und pilot liefern die Datei von heute, on die neue (no-cache)`, async () => {
for (const m of ['off', 'pilot']) {
modus = m
const r = await holen(pfad)
assert.ok(r.status === 200 && r.bytes.equals(datei(pfad.slice(1))), `${m} ${pfad}`)
}
modus = 'on'
const r = await holen(pfad)
assert.ok(r.status === 200 && r.bytes.equals(datei(neu)), pfad)
assert.equal(r.headers.get('cache-control'), 'no-cache')
})
}
test('Manifest bei on ist JSON', async () => {
modus = 'on'
assert.match((await holen('/manifest.webmanifest')).headers.get('content-type'), /json/)
})
// P10 Task 14: entfällt (Parameter fassung6 verschwindet, Fassung 6 liegt dann unter /abo-bedingungen.html)
test('Fassung 6: Seite und PDF kommen bei on aus der Neu-Fassung, sonst bleibt die Datei von heute', async () => {
fassung6 = { url: '/neu/oeffentlich/offline.html', pdfFile: 'WEBklar-Wartungs-Abo-Vertragsunterlagen.pdf' } // Stellvertreter-Dateien
modus = 'pilot'
assert.ok((await holen('/abo-bedingungen.html')).bytes.equals(datei('abo-bedingungen.html')))
modus = 'on'
assert.ok((await holen('/abo-bedingungen.html')).bytes.equals(datei('neu/oeffentlich/offline.html')))
assert.ok((await holen('/WEBklar-Abo-Vertragsunterlagen.pdf')).bytes.equals(datei('WEBklar-Wartungs-Abo-Vertragsunterlagen.pdf')))
fassung6 = null
assert.ok((await holen('/abo-bedingungen.html')).bytes.equals(datei('abo-bedingungen.html')))
})
// P10 Task 14: entfällt (fassung6Aus verschwindet, public/neu/rechtliches/ wird zu Weiterleitungen)
test('Standard-Fassung 6 (fassung6Aus): bei on kommen Seite und PDF der Fassung 6 aus public/neu/rechtliches/', async () => {
const f6 = fassung6Aus()
assert.deepEqual([f6.url, f6.pdfFile], ['/neu/rechtliches/abo-bedingungen.html', 'neu/rechtliches/WEBklar-Abo-Vertragsunterlagen.pdf'])
const echt = express.Router()
echt.use(umschaltSeitenRouter({ publicDir, modus: () => modus }))
echt.use(express.static(publicDir))
const a3 = await startTestApp({ '/': echt })
try {
modus = 'pilot'
assert.ok((await holen('/abo-bedingungen.html', a3.url)).bytes.equals(datei('abo-bedingungen.html')))
modus = 'on'
assert.ok((await holen('/abo-bedingungen.html', a3.url)).bytes.equals(datei('neu/rechtliches/abo-bedingungen.html')))
assert.ok((await holen('/WEBklar-Abo-Vertragsunterlagen.pdf', a3.url)).bytes.equals(datei('neu/rechtliches/WEBklar-Abo-Vertragsunterlagen.pdf')))
} finally {
await a3.close()
}
})
// P10 Task 14: fällt mit dem Parameter fassung6 weg oder prüft dann die Ziele der Weiterleitungen
test('Pfadwächter: eine Adresse außerhalb von public/ wird nie geliefert', async () => {
modus = 'on'
fassung6 = { url: '/../../package.json', pdfFile: '../../server/config.js' }
for (const p of ['/abo-bedingungen.html', '/WEBklar-Abo-Vertragsunterlagen.pdf']) assert.ok((await holen(p)).bytes.equals(datei(p.slice(1))), p)
})
test('fehlende neue Datei: Rückfall auf die alte statt 500', async () => {
const tmp = mkdtempSync(path.join(tmpdir(), 'kn-weiche-'))
writeFileSync(path.join(tmp, 'login.html'), 'ALT')
const a2 = express()
a2.use(umschaltSeitenRouter({ publicDir: tmp, modus: () => 'on', fassung6: () => null }))
a2.use(express.static(tmp))
const srv = await new Promise((res) => {
const s = a2.listen(0, '127.0.0.1', () => res(s))
})
try {
const r = await holen('/login.html', `http://127.0.0.1:${srv.address().port}`)
assert.deepEqual([r.status, r.bytes.toString()], [200, 'ALT'])
// der Rückfall erbt kein no-cache der Weiche (die Datei von heute behält ihren eigenen Cache-Kopf)
assert.notEqual(r.headers.get('cache-control'), 'no-cache')
} finally {
srv.close()
unlinkSync(path.join(tmp, 'login.html'))
rmdirSync(tmp)
}
})
test('nur GET und HEAD: HEAD liefert die neue Seite ohne Inhalt, POST bleibt unberührt', async () => {
modus = 'on'
fassung6 = null
const kopf = await holen('/login.html', app.url, 'HEAD')
assert.deepEqual([kopf.status, kopf.bytes.length, kopf.headers.get('cache-control')], [200, 0, 'no-cache'])
assert.equal(Number(kopf.headers.get('content-length')), datei('neu/oeffentlich/login.html').length)
const post = await holen('/login.html', app.url, 'POST')
assert.equal(post.status, 404)
})
test('Groß- und Kleinschreibung: /Login.html wird nicht umgeleitet (wie express.static)', async () => {
modus = 'on'
assert.equal((await holen('/Login.html')).status, 404)
})
test('echter Schalter: der Router mit den Vorgaben (wie in server/index.js) folgt config.kundenansichtNeu, nur on ändert Seiten', async () => {
const standard = express.Router()
standard.use(umschaltSeitenRouter({ publicDir })) // ohne modus und fassung6: genau der Aufruf in server/index.js
standard.use(express.static(publicDir))
const echt = await startTestApp({ '/': standard })
const gemerkt = { ...config.kundenansichtNeu }
const aboNeu = datei(termsDocFor(config.abo.termsVersionNeu).url.slice(1))
const faelle = [ // [mode, pilotEmails, liefert die Weiche die neuen Seiten?]
['off', [], false], ['off', ['*'], false],
['pilot', [], false], ['pilot', ['*'], false], ['pilot', ['kunde@example.invalid'], false],
['an', ['*'], false], ['', ['*'], false], [undefined, ['*'], false],
['on', [], true], ['on', ['*'], true],
]
try {
for (const [mode, pilotEmails, neu] of faelle) {
Object.assign(config.kundenansichtNeu, { mode, pilotEmails })
const fall = `${mode} [${pilotEmails}]`
for (const [pfad, rel] of Object.entries(UMSCHALT_SEITEN)) {
const r = await holen(pfad, echt.url)
assert.ok(r.status === 200 && r.bytes.equals(datei(neu ? rel : pfad.slice(1))), `${fall} ${pfad}`)
}
const abo = await holen('/abo-bedingungen.html', echt.url)
assert.ok(abo.status === 200 && abo.bytes.equals(neu ? aboNeu : datei('abo-bedingungen.html')), `${fall} /abo-bedingungen.html`)
}
} finally {
Object.assign(config.kundenansichtNeu, gemerkt)
await echt.close()
}
})
test('server/index.js hängt die Weiche hinter P0s Weichen und vor express.static ein', () => {
const index = readFileSync(new URL('../server/index.js', import.meta.url), 'utf8')
const reihe = ["app.get('/dashboard.html', dashboardWeiche(", 'app.use(umschaltSeitenRouter(', 'app.use(express.static(publicDir))'].map((s) => index.indexOf(s))
assert.ok(reihe[0] > -1 && reihe.every((n, i) => i === 0 || n > reihe[i - 1]), reihe.join(','))
})