Files
Webklar-Kundenbereich/test/rechtlichesHochladen.test.js

94 lines
9.0 KiB
JavaScript

import { test } from 'node:test'
import assert from 'node:assert/strict'
// Hochladen der Fassung 6 nach WOMS "Rechtliches" (scripts/rechtliches-hochladen.mjs): die Tests rufen main nur mit Nachbau-deps auf,
// nie mit dem echten Schlüssel oder dem Netz.
const { FUNKTIONEN_UNTERLAGEN_V6, FUNKTIONEN_FASSUNG_V6 } = await import('../server/services/products.js')
const m = await import('../scripts/rechtliches-hochladen.mjs') // der Import selbst darf weder Schlüssel lesen noch das Netz nutzen
test('parseArgs: ein Dokument je Aufruf, Hochladen nur mit --freigabe=Datum, --probe und --pruefen lesen nur, --karten-anlegen legt nur Karten an', () => {
assert.deepEqual(m.parseArgs(['--probe']), { modus: 'probe', documentKey: null, freigabe: null })
assert.deepEqual(m.parseArgs(['--karten-anlegen']), { modus: 'karten', documentKey: null, freigabe: null })
assert.deepEqual(m.parseArgs(['--pruefen']), { modus: 'pruefen', documentKey: null, freigabe: null })
assert.deepEqual(m.parseArgs(['avv-webshop', '--freigabe=2026-10-20']), { modus: 'hochladen', documentKey: 'avv-webshop', freigabe: '2026-10-20' })
for (const bad of [[], ['avv-webshop'], ['--freigabe=2026-10-20'], ['avv-webshop', 'avv-terminplaner', '--freigabe=2026-10-20'], ['avv-webshop', '--freigabe=20.10.2026'], ['avv-webshop', '--freigabe=2026-13-40'], ['datenschutz', '--freigabe=2026-10-20'], ['--probe', 'avv-webshop'], ['--probe', '--freigabe=2026-10-20'], ['--karten-anlegen', 'avv-webshop'], ['--karten-anlegen', '--freigabe=2026-10-20'], ['--pruefen', '--probe'], ['--pruefen', 'avv-webshop']]) {
assert.throws(() => m.parseArgs(bad), /Aufruf|Freigabe|Dokument/, JSON.stringify(bad))
}
assert.equal(typeof m.main, 'function')
})
test('Tabelle: elf Dokumente, Dateien im Vorschaupfad, passend zu FUNKTIONEN_UNTERLAGEN_V6', () => {
assert.deepEqual(m.DOKUMENTE_V6.map((d) => d.documentKey), ['abo-bedingungen', 'leistungsbeschreibung-news', 'leistungsbeschreibung-terminplaner', 'avv-terminplaner', 'leistungsbeschreibung-webshop', 'avv-webshop', 'leistungsbeschreibung-unterkunft', 'avv-unterkunft', 'leistungsbeschreibung-zeiten', 'leistungsbeschreibung-website-analyse', 'avv-website-analyse'])
assert.deepEqual(m.dokumentFuer('abo-bedingungen').dateien, ['public/neu/rechtliches/WEBklar-Abo-Vertragsunterlagen.pdf', 'public/neu/rechtliches/abo-bedingungen.html'])
const unterlagen = Object.values(FUNKTIONEN_UNTERLAGEN_V6).flatMap((u) => [u.lb, u.avv]).filter(Boolean)
assert.deepEqual(m.DOKUMENTE_V6.slice(1).map((d) => d.dateien), unterlagen.map((u) => [`public/${u.pdfFile}`, `public${u.url}`]))
assert.deepEqual(m.DOKUMENTE_V6.map((d) => d.quelle), m.DOKUMENTE_V6.map((d) => d.dateien[1]), 'quelle = HTML-Pfad, der in source steht')
assert.throws(() => m.dokumentFuer('datenschutz'), /Unbekanntes Dokument/)
})
test('planVersion: neue Fassung als vN, bisherige gültige wird abgelöst, doppelter Upload wird erkannt', () => {
const alle = [{ $id: 'a', version: 'v1', validFrom: '2026-09-30', status: 'current', source: 'x' }, { $id: 'b', version: 'v0', validFrom: '2026-09-25', status: 'superseded', source: 'y' }]
assert.deepEqual(m.planVersion({ alle, source: 's', validFrom: '2026-10-20' }), { schon: false, nummer: 'v3', aktuellId: 'a', validToAltes: '2026-10-19' })
assert.equal(m.planVersion({ alle, source: 'x', validFrom: '2026-10-20' }).schon, true)
assert.deepEqual(m.planVersion({ alle: [], source: 's', validFrom: '2026-10-20' }), { schon: false, nummer: 'v1', aktuellId: null, validToAltes: null })
assert.equal(m.planVersion({ alle, source: 's', validFrom: '2026-09-30' }).validToAltes, '2026-09-30', 'gleicher Tag: validTo = validFrom wie in der Vorlage')
assert.equal(m.tagVorher('2026-03-01'), '2026-02-28')
})
// Nachbau des WOMS-Backends für main(): Karten als Dokumente { $id, key, sort }, Versionen und Dateien leer; zählt alle Aufrufe
function backend(karten) {
const aufrufe = [], log = [], antwort = (obj) => ({ ok: true, status: 200, text: async () => JSON.stringify(obj) })
const fetch = async (url, opt = {}) => {
const methode = opt.method ?? 'GET', pfad = new URL(url).pathname.replace(/^\/v1/, ''); aufrufe.push(`${methode} ${pfad}`)
if (methode === 'GET' && pfad.endsWith('/collections/legalDocuments/documents')) return antwort({ total: karten.length, documents: karten })
if (methode === 'GET' && pfad.endsWith('/collections/legalVersions/documents')) return antwort({ total: 0, documents: [] })
if (methode === 'GET' && pfad.startsWith('/storage/buckets/legal-files/files')) return antwort({ total: 0, files: [] })
if (methode === 'POST' && pfad.endsWith('/collections/legalDocuments/documents')) { const b = JSON.parse(opt.body); karten.push({ $id: b.documentId, ...b.data }); return antwort({ $id: b.documentId }) }
throw new Error(`unerwartet: ${methode} ${pfad}`)
}
return { karten, aufrufe, log, deps: { fetch, schluessel: () => 'test-schluessel', log: (z) => log.push(z) } }
}
const NUR_LESEN = (b) => b.aufrufe.every((a) => a.startsWith('GET '))
const FEHLEN = ['leistungsbeschreibung-news', 'leistungsbeschreibung-website-analyse', 'avv-website-analyse'] // „leistungsbeschreibung-website-analyse“ hat 37 Zeichen, eine Dokument-ID darf höchstens 36 haben
const kartenDoc = (key, i) => ({ $id: key.slice(0, 36), key, sort: i + 1 })
const VORHANDEN = m.DOKUMENTE_V6.map((d) => d.documentKey).filter((k) => !FEHLEN.includes(k)) // die Karten laut WOMS scripts/setup-legal.mjs
test('kartenPlan: genau die drei fehlenden Karten in Tabellenreihenfolge, mit zulässiger Dokument-ID', () => {
const plan = m.kartenPlan({ vorhandeneKeys: VORHANDEN, hoechsteSort: 14 })
assert.deepEqual(plan.map((k) => [k.daten.key, k.daten.sort]), [[FEHLEN[0], 15], [FEHLEN[1], 16], [FEHLEN[2], 17]])
assert.deepEqual(plan.map((k) => k.daten.title), ['Leistungsbeschreibung News-Pinnwand', 'Leistungsbeschreibung Website-Analyse', 'AVV Website-Analyse'])
assert.ok(plan.every((k) => k.daten.liveUrl.startsWith('https://project.webklar.com/neu/rechtliches/') && k.daten.liveUrl.endsWith(`-${FUNKTIONEN_FASSUNG_V6}.html`)))
assert.ok(plan.every((k) => /^[A-Za-z0-9][A-Za-z0-9._-]{0,35}$/.test(k.id)), 'Dokument-ID höchstens 36 Zeichen')
assert.equal(plan[0].id, FEHLEN[0], 'kurze Schlüssel bleiben die ID, wie in setup-legal.mjs')
assert.notEqual(plan[1].id, FEHLEN[1]); assert.equal(m.kartenId(FEHLEN[1]), plan[1].id, 'lange Schlüssel: abgeleitete ID, immer dieselbe')
assert.deepEqual(m.kartenPlan({ vorhandeneKeys: m.DOKUMENTE_V6.map((d) => d.documentKey), hoechsteSort: 20 }), [])
})
test('--probe: eine fehlende Karte ist ein Stopp (Exit 1), mit allen Karten Exit 0; schreibt nie', async () => {
const b = backend(VORHANDEN.map(kartenDoc))
assert.equal(await m.main(['--probe'], b.deps), 1)
for (const k of FEHLEN) assert.ok(b.log.includes(`STOPP: Karte fehlt: ${k}`), k)
assert.ok(NUR_LESEN(b), b.aufrufe.join(', '))
assert.equal(await m.main(['--probe'], backend(m.DOKUMENTE_V6.map((d, i) => kartenDoc(d.documentKey, i))).deps), 0)
})
test('--karten-anlegen: legt nur die drei fehlenden Karten an, ein zweiter Lauf tut nichts, danach ist --probe grün', async () => {
const b = backend(VORHANDEN.map(kartenDoc))
assert.equal(await m.main(['--karten-anlegen'], b.deps), 0)
assert.deepEqual(b.aufrufe.filter((a) => !a.startsWith('GET ')), FEHLEN.map(() => 'POST /databases/woms-database/collections/legalDocuments/documents'))
assert.deepEqual(b.karten.slice(VORHANDEN.length).map((k) => k.key), FEHLEN)
b.aufrufe.length = 0
assert.equal(await m.main(['--karten-anlegen'], b.deps), 0); assert.ok(NUR_LESEN(b))
assert.equal(await m.main(['--probe'], b.deps), 0)
})
test('Hochladen ohne Karte bricht ab, bevor etwas geschrieben wird', async () => {
const b = backend(VORHANDEN.map(kartenDoc))
assert.equal(await m.main([FEHLEN[0], '--freigabe=2026-10-20'], b.deps), 1)
assert.ok(b.log.some((z) => z.includes(`Karte fehlt: ${FEHLEN[0]}`)) && NUR_LESEN(b))
})
test('pruefeHochgeladen und --pruefen: alle elf Dokumente brauchen eine aktuelle Version mit passender Quelle', async () => {
const ok = (d) => [{ $id: 'v', version: 'v2', status: 'current', source: `kundenbereich@abc1234:${d.quelle}` }]
const alle = Object.fromEntries(m.DOKUMENTE_V6.map((d) => [d.documentKey, ok(d)]))
assert.deepEqual(m.pruefeHochgeladen(alle), { fehlt: [] })
assert.deepEqual(m.pruefeHochgeladen({ ...alle, 'avv-website-analyse': [] }).fehlt, ['avv-website-analyse'])
assert.deepEqual(m.pruefeHochgeladen({ ...alle, 'abo-bedingungen': [{ ...alle['abo-bedingungen'][0], source: 'kundenbereich@7ae0ffd:public/abo-bedingungen.html' }] }).fehlt, ['abo-bedingungen'], 'Fassung 5 zählt nicht')
assert.deepEqual(m.pruefeHochgeladen({ ...alle, 'leistungsbeschreibung-news': [{ ...alle['leistungsbeschreibung-news'][0], status: 'superseded' }] }).fehlt, ['leistungsbeschreibung-news'])
assert.equal(m.pruefeHochgeladen({}).fehlt.length, 11)
const b = backend([]) // Versionen leer: alle elf fehlen
assert.equal(await m.main(['--pruefen'], b.deps), 1)
assert.equal(b.log.filter((z) => z.startsWith('FEHLT ')).length, 11); assert.ok(NUR_LESEN(b))
})