Kundenansicht neu P7: Einschalten zeigt Linkzeile, Häkchen und Unterlagen der Fassung 6 aus P8
Verdrahtet P5 Task 15 und P7 Task 0 (N33): consentAbo liest Linkzeile und Hinweis aus WORTLAUT_V6, unterlagenFuer die fünf Funktionen aus FUNKTIONEN_UNTERLAGEN_V6 (Analyse bleibt bei den Alt-Unterlagen bis zur Freigabe), config.aboFunktionen.texte.hinweis entfällt, pdfAnhaenge liest PDFs der Fassung 6, sichereAdresse lässt /neu/rechtliches/ zu. Der Analyse-Kundenweg setzt wie der Team-Weg den Abo-Marker (unlockedBy und einrichtungAbrechnung abo). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
447129d4e2
commit
bfe3895a50
11 files changed
+177
-41
No files matched your search
@@ -36,6 +36,7 @@ const { setMailTransportForTests } = await import('../server/services/mailer.js'
|
||||
const { default: funktionenRoutes } = await import('../server/routes/funktionen.js')
|
||||
const { resetZeitenCacheForTests } = await import('../server/services/zeiten/cache.js')
|
||||
const { einschaltenDeps } = await import('../server/services/zeiten/einschalten.js')
|
||||
const { FUNKTIONEN_FASSUNG_V6, FUNKTIONEN_UNTERLAGEN_V6 } = await import('../server/services/products.js')
|
||||
|
||||
const app = await startTestApp({ '/api/funktionen': funktionenRoutes })
|
||||
after(() => Promise.all([app.close(), aw.close(), st.close()]))
|
||||
@@ -128,7 +129,7 @@ test('mit neuer Ansicht: Modell, Kundenart, Abo, Katalog in fester Reihenfolge',
|
||||
assert.equal(mit.katalog.at(-1).consent, null)
|
||||
assert.equal(b.katalog.find((e) => e.key === 'shop').nurBetrieb, true)
|
||||
assert.doesNotMatch(JSON.stringify(b.katalog), /€|priceLabel/)
|
||||
assert.deepEqual(t.unterlagen.leistungsbeschreibung, { version: config.termine.avvVersion, url: config.features.termine.leistungsbeschreibung.url })
|
||||
assert.deepEqual(t.unterlagen.leistungsbeschreibung, { version: FUNKTIONEN_FASSUNG_V6, url: FUNKTIONEN_UNTERLAGEN_V6.termine.lb.url })
|
||||
assert.equal(mit.katalog.at(-1).unterlagen.avv.url, `/rechtliches/avv-website-analyse-${config.analyse.avvVersion}.html`)
|
||||
config.analyse.enabled = false
|
||||
})
|
||||
|
||||
@@ -15,11 +15,12 @@ const { renderMail } = await import('../server/services/mailTemplates.js')
|
||||
const { anleitungsLink } = await import('../server/services/featureRegistry.js')
|
||||
const { anfrageEntschiedenMail, k38Mail, k39Mail, t24Mail, loeschFristTage, kontostandText } = await import('../server/services/aboFunktionenMails.js')
|
||||
const { k29Mail, k30Mail } = await import('../server/services/zeiten/mails.js')
|
||||
const { FUNKTIONEN_FASSUNG_V6 } = await import('../server/services/products.js')
|
||||
|
||||
test('k38Mail: Werte und Blöcke je Abrechnung', () => {
|
||||
const m = k38Mail({ kunde: { name: 'Silke Müller' }, projekt: 'Bäckerei', key: 'termine', abrechnung: 'abo', ticketNr: '#100150', anhang: true })
|
||||
assert.equal(m.template, 'K38'); assert.deepEqual(m.blocks, { stundenkonto: true, projekt_kosten: false, anhang: true })
|
||||
assert.deepEqual(m.values, { kunde_name: 'Silke Müller', projekt: 'Bäckerei', funktion: 'Terminplaner', funktion_akk: 'den Terminplaner', einrichtung: 'meist in 1–2 Wochen', ticket: ' (Ticket #100150)', fassung: config.features.termine.leistungsbeschreibung.version, portal_url: `${config.publicBaseUrl}/dashboard.html#termine` })
|
||||
assert.deepEqual(m.values, { kunde_name: 'Silke Müller', projekt: 'Bäckerei', funktion: 'Terminplaner', funktion_akk: 'den Terminplaner', einrichtung: 'meist in 1–2 Wochen', ticket: ' (Ticket #100150)', fassung: FUNKTIONEN_FASSUNG_V6, portal_url: `${config.publicBaseUrl}/dashboard.html#termine` })
|
||||
assert.deepEqual(k38Mail({ kunde: 'K', projekt: 'P', key: 'shop', abrechnung: 'projekt' }).blocks, { stundenkonto: false, projekt_kosten: true, anhang: false })
|
||||
assert.equal(k38Mail({ kunde: 'K', projekt: 'P', key: 'news', abrechnung: 'abo' }).values.ticket, '')
|
||||
})
|
||||
|
||||
@@ -12,6 +12,7 @@ useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url })
|
||||
const { config } = await import('../server/config.js')
|
||||
const reg = await import('../server/services/featureRegistry.js')
|
||||
const { unterlagenFuer, analyseUnterlagen } = await import('../server/services/aboUnterlagen.js')
|
||||
const { WORTLAUT_V6, FUNKTIONEN_FASSUNG_V6, FUNKTIONEN_UNTERLAGEN_V6, mitFassung } = await import('../server/services/products.js')
|
||||
|
||||
const KEYS = ['news', 'termine', 'shop', 'unterkunft', 'zeiten']
|
||||
test('Reihenfolge und Felder je Funktion', () => {
|
||||
@@ -33,7 +34,7 @@ test('Reihenfolge und Felder je Funktion', () => {
|
||||
assert.equal(reg.katalogFelder('analyse').beschreibung, 'Sieh, wie viele Besucher auf deine Website kommen und welche Seiten sie ansehen. Die Auswertung findest du direkt im Portal.')
|
||||
})
|
||||
|
||||
const HINWEIS = { // Pflichten der alten Häkchen verantwortung, stripe, gesundheit; wörtlich wie WORTLAUT_V6.hinweis (P8)
|
||||
const HINWEIS = { // Pflichten der alten Häkchen verantwortung, stripe, gesundheit; wörtlich wie WORTLAUT_V6.hinweis (P8), gleich gepinnt unten
|
||||
termine: 'Der Terminplaner ist nicht für den Gesundheitsbereich gedacht. Das fragen wir im Fragebogen ab.',
|
||||
shop: 'Du verkaufst in eigenem Namen und bist für deine Produkte, Preise und Rechtstexte verantwortlich. Die Zahlungen deiner Käufer laufen über dein eigenes Stripe-Konto.',
|
||||
unterkunft: 'Du vermietest in eigenem Namen und bist für Preise, Stornobedingungen, Rechnungen und Meldepflichten verantwortlich. Online-Zahlungen deiner Gäste laufen über dein eigenes Stripe-Konto.',
|
||||
@@ -50,9 +51,11 @@ test('consentAbo: Linkzeile mit Fassung, Hinweis (termine, shop, unterkunft), be
|
||||
}
|
||||
assert.equal(config.aboFunktionen.texte.beginn, 'Bitte beginnt sofort mit der Einrichtung. Mir ist klar: Mit der vollständigen Einrichtung erlischt mein Widerrufsrecht, und wenn ich vorher widerrufe, zahle ich den bis dahin erbrachten Teil.')
|
||||
assert.equal(V, '2026-10-16')
|
||||
assert.equal(reg.consentAbo('termine').items[0].text, `Es gelten Leistungsbeschreibung und Auftragsverarbeitung (Fassung ${config.termine.avvVersion}).`)
|
||||
assert.equal(reg.consentAbo('zeiten').items[0].text, 'Es gilt die Leistungsbeschreibung (Fassung 2026-10-04).')
|
||||
assert.equal(reg.consentAbo('news').items[0].text, `Es gilt die Leistungsbeschreibung (Fassung ${config.termine.avvVersion}).`)
|
||||
assert.deepEqual(HINWEIS, { ...WORTLAUT_V6.hinweis }, 'die Hinweise sind die Sätze aus WORTLAUT_V6')
|
||||
assert.equal(V, FUNKTIONEN_FASSUNG_V6)
|
||||
assert.equal(reg.consentAbo('termine').items[0].text, mitFassung(WORTLAUT_V6.linkzeile.einschaltenMitAvv, FUNKTIONEN_FASSUNG_V6))
|
||||
assert.equal(reg.consentAbo('zeiten').items[0].text, mitFassung(WORTLAUT_V6.linkzeile.einschaltenOhneAvv, FUNKTIONEN_FASSUNG_V6))
|
||||
assert.equal(reg.consentAbo('news').items[0].text, mitFassung(WORTLAUT_V6.linkzeile.einschaltenOhneAvv, FUNKTIONEN_FASSUNG_V6))
|
||||
assert.ok(!reg.consentAbo('termine').items.some((i) => i.id === 'gesundheit'), 'das Häkchen gesundheit entfällt, der Satz steht als Hinweis')
|
||||
assert.doesNotMatch(JSON.stringify(Object.values(HINWEIS)), /€|\bSie\b|\bIhre?[nmrs]?\b/)
|
||||
assert.equal(reg.consentAbo('analyse'), null)
|
||||
@@ -64,7 +67,7 @@ test('consentAboAktiv und aboweg zeigen nur, was verlangt ist; das alte Register
|
||||
assert.deepEqual(reg.consentAboAktiv('news').items.map((i) => i.id), ['einschalten-zeile'])
|
||||
assert.deepEqual(reg.consentAboAktiv('shop', { beginn: true }).items.map((i) => [i.id, i.haken]), [['einschalten-zeile', false], ['hinweis', false], ['beginn', true]])
|
||||
const w = reg.aboweg(reg.featureConfig('termine'), { beginn: true })
|
||||
assert.deepEqual([w.consent.version, w.widerruf, w.avv.version, w.leistungsbeschreibung.version, w.key], [config.aboFunktionen.fassung, null, config.termine.avvVersion, config.termine.avvVersion, 'termine'])
|
||||
assert.deepEqual([w.consent.version, w.widerruf, w.avv.version, w.leistungsbeschreibung.version, w.key], [config.aboFunktionen.fassung, null, FUNKTIONEN_FASSUNG_V6, FUNKTIONEN_FASSUNG_V6, 'termine'])
|
||||
assert.deepEqual(reg.featureConfig('termine').consent.items.map((i) => i.id), ['unterlagen', 'gesundheit', 'beginn', 'information'])
|
||||
assert.notEqual(reg.featureConfig('termine').widerruf, null, 'aboweg ist eine Kopie, das Register bleibt')
|
||||
assert.equal(Object.keys(reg.oeffentlicheAnsicht('termine')).length, 11, 'oeffentlicheAnsicht ohne neue Felder')
|
||||
@@ -72,7 +75,7 @@ test('consentAboAktiv und aboweg zeigen nur, was verlangt ist; das alte Register
|
||||
|
||||
test('unterlagenFuer und analyseUnterlagen: eine Quelle für Katalog, Nachweis und Analyse', () => {
|
||||
const t = unterlagenFuer('termine')
|
||||
assert.deepEqual([t.leistungsbeschreibung, t.avv], [{ version: config.termine.avvVersion, url: config.features.termine.leistungsbeschreibung.url }, { version: config.termine.avvVersion, url: config.features.termine.avv.url }])
|
||||
assert.deepEqual([t.leistungsbeschreibung, t.avv], [{ version: FUNKTIONEN_FASSUNG_V6, url: FUNKTIONEN_UNTERLAGEN_V6.termine.lb.url }, { version: FUNKTIONEN_FASSUNG_V6, url: FUNKTIONEN_UNTERLAGEN_V6.termine.avv.url }])
|
||||
assert.deepEqual([unterlagenFuer('news').avv, unterlagenFuer('zeiten').avv], [null, null])
|
||||
assert.ok(unterlagenFuer('news').leistungsbeschreibung.version)
|
||||
const a = analyseUnterlagen()
|
||||
|
||||
@@ -29,6 +29,7 @@ const { default: funktionenRoutes } = await import('../server/routes/funktionen.
|
||||
const an = await import('../server/services/analyse.js')
|
||||
const { analyseUnterlagen } = await import('../server/services/aboUnterlagen.js')
|
||||
const { aboModellFuerKunde } = await import('../server/services/aboFunktionen.js')
|
||||
const { istAboModell } = await import('../server/services/aboModell.js')
|
||||
const { setMailTransportForTests } = await import('../server/services/mailer.js')
|
||||
|
||||
const app = await startTestApp({ '/api/funktionen': funktionenRoutes })
|
||||
@@ -152,11 +153,26 @@ test('Nachweis scheitert: alles zurückgenommen (kein on ohne Nachweis), der nä
|
||||
assert.equal(aw.docs('featureConsents').size, 1)
|
||||
})
|
||||
|
||||
test('alte Ansicht unverändert: ohne audienceConfirmed 200, kein Eintrag in featureConsents', async () => {
|
||||
test('Abo-Marker (Ruling Nachzüge): der Kundenweg setzt unlockedBy und einrichtungAbrechnung "abo" wie der Team-Weg, nie aus dem Body; Rücknahme stellt ihn her', async () => {
|
||||
abo(hostingDoc)
|
||||
aw.failNext({ collection: 'featureConsents', method: 'POST', status: 500 })
|
||||
assert.equal((await aktivieren({ abrechnung: 'projekt', unlockedBy: 'projekt' })).status, 500)
|
||||
const roll = aw.docs('portalFeatures').get('f_p1_analyse')
|
||||
assert.deepEqual([roll.state, roll.unlockedBy ?? null, roll.einrichtungAbrechnung ?? null, istAboModell(roll)], ['off', null, null, false], 'Nachweis scheitert: kein Marker bleibt')
|
||||
assert.equal((await aktivieren({ abrechnung: 'projekt', unlockedBy: 'projekt' })).status, 200) // Body-Werte zählen nicht
|
||||
const d = aw.docs('portalFeatures').get('f_p1_analyse')
|
||||
assert.deepEqual([d.state, d.unlockedBy, d.einrichtungAbrechnung, istAboModell(d)], ['active', 'abo', 'abo', true])
|
||||
assert.equal((await aktivieren({ accentColor: '#222222' })).status, 200) // zweiter Aufruf einer aktiven Analyse lässt den Marker stehen
|
||||
assert.deepEqual([aw.docs('portalFeatures').get('f_p1_analyse').unlockedBy, aw.docs('portalFeatures').get('f_p1_analyse').einrichtungAbrechnung], ['abo', 'abo'])
|
||||
})
|
||||
|
||||
test('alte Ansicht unverändert: ohne audienceConfirmed 200, kein Eintrag in featureConsents, kein Abo-Marker', async () => {
|
||||
config.kundenansichtNeu.mode = 'off'
|
||||
abo({ ...wartungDoc })
|
||||
assert.equal((await aktivieren({ audienceConfirmed: undefined })).status, 200)
|
||||
assert.equal(aw.docs('featureConsents').size, 0)
|
||||
const d = aw.docs('portalFeatures').get('f_p1_analyse')
|
||||
assert.deepEqual([d.state, d.unlockedBy ?? null, d.einrichtungAbrechnung ?? null, istAboModell(d)], ['active', null, null, false])
|
||||
})
|
||||
|
||||
test('Tagesprüfung: Hosting-Analyse bleibt für Kunden der neuen Ansicht, sonst endet sie wie bisher (K19)', async () => {
|
||||
|
||||
@@ -71,9 +71,9 @@ test('einschaltKoerper: Fassung aus dem Register, Haken nur "beginn", nie die Ku
|
||||
assert.equal('kundenart' in mit, false)
|
||||
})
|
||||
|
||||
test('sichereAdresse: nur https und Pfade unter /rechtliches/ oder /vertragsunterlagen/', () => {
|
||||
for (const gut of ['/rechtliches/avv-2026-10-05.html', '/vertragsunterlagen/funktionen-leistungsbeschreibung.html', 'https://example.org/a.pdf']) assert.equal(sichereAdresse(gut), gut)
|
||||
for (const schlecht of ['javascript:alert(1)', 'http://example.org/', '//example.org/x', '/rechtliches/../geheim', '/rechtliches/%2e%2e/x', 'https://user:pw@example.org/', '/api/abo', '/rechtliches/a b', '', null, 42]) assert.equal(sichereAdresse(schlecht), '', String(schlecht))
|
||||
test('sichereAdresse: nur https und Pfade unter /rechtliches/, /vertragsunterlagen/ oder /neu/rechtliches/', () => {
|
||||
for (const gut of ['/rechtliches/avv-2026-10-05.html', '/vertragsunterlagen/funktionen-leistungsbeschreibung.html', '/neu/rechtliches/leistungsbeschreibung-terminplaner-2026-10-16.html', 'https://example.org/a.pdf']) assert.equal(sichereAdresse(gut), gut)
|
||||
for (const schlecht of ['javascript:alert(1)', 'http://example.org/', '//example.org/x', '/rechtliches/../geheim', '/rechtliches/%2e%2e/x', 'https://user:pw@example.org/', '/api/abo', '/neu/rechtliches/../geheim', '/neu/api/abo', '/rechtliches/a b', '', null, 42]) assert.equal(sichereAdresse(schlecht), '', String(schlecht))
|
||||
})
|
||||
|
||||
test('linkzeile: die zwei Wörter werden Links; unsichere oder fehlende Adressen nicht', () => {
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
import { test, after } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { mkdtemp, mkdir, writeFile } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { startAppwriteMock } from './support/appwriteMock.js'
|
||||
import { startFakeStripe } from './support/fakeStripe.js'
|
||||
import { useTestEnv } from './support/testEnv.js'
|
||||
import { sichereAdresse } from '../public/neu/funktion-einschalten.js'
|
||||
|
||||
// Verdrahtung P5 <-> P8 <-> P7 (N33): Einschalten, Katalog und Nachweis zeigen Linkzeile, Haekchen, Hinweis und Unterlagen der
|
||||
// Fassung 6 aus products.js. Die Gleichheit mit WORTLAUT_V6 und FUNKTIONEN_UNTERLAGEN_V6 ist gepinnt, nicht ein Literal.
|
||||
const aw = await startAppwriteMock()
|
||||
const st = await startFakeStripe()
|
||||
after(() => Promise.all([aw.close(), st.close()]))
|
||||
useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url })
|
||||
const { config } = await import('../server/config.js')
|
||||
const reg = await import('../server/services/featureRegistry.js')
|
||||
const { katalogFuerKunde } = await import('../server/services/aboKatalog.js')
|
||||
const { unterlagenFuer, analyseUnterlagen, pdfAnhaenge } = await import('../server/services/aboUnterlagen.js')
|
||||
const { WORTLAUT_V6, FUNKTIONEN_FASSUNG_V6, FUNKTIONEN_UNTERLAGEN_V6, mitFassung } = await import('../server/services/products.js')
|
||||
|
||||
const KEYS = ['news', 'termine', 'shop', 'unterkunft', 'zeiten']
|
||||
const unterlage = (d) => (d ? { version: FUNKTIONEN_FASSUNG_V6, url: d.url } : null)
|
||||
|
||||
test('config.aboFunktionen trägt die Literale aus P8, config.js importiert keine Dienste', () => {
|
||||
assert.deepEqual([config.aboFunktionen.fassung, config.aboFunktionen.texte.beginn], [FUNKTIONEN_FASSUNG_V6, WORTLAUT_V6.beginn.einrichtung])
|
||||
assert.equal(Object.hasOwn(config.aboFunktionen.texte, 'hinweis'), false, 'der Hinweis kommt aus WORTLAUT_V6, nicht aus config')
|
||||
assert.doesNotMatch(readFileSync(new URL('../server/config.js', import.meta.url), 'utf8'), /from '\.\/services\//)
|
||||
})
|
||||
|
||||
test('consentAbo: Linkzeile mit und ohne AVV aus WORTLAUT_V6, Hinweis und Häkchen aus WORTLAUT_V6, Nachweis gleich', () => {
|
||||
for (const key of KEYS) {
|
||||
const mitAvv = FUNKTIONEN_UNTERLAGEN_V6[key].avv !== null
|
||||
const zeile = mitFassung(WORTLAUT_V6.linkzeile[mitAvv ? 'einschaltenMitAvv' : 'einschaltenOhneAvv'], FUNKTIONEN_FASSUNG_V6)
|
||||
const c = reg.consentAbo(key)
|
||||
assert.equal(c.version, FUNKTIONEN_FASSUNG_V6, key)
|
||||
assert.equal(c.items[0].text, zeile, key)
|
||||
assert.equal(c.items.at(-1).text, WORTLAUT_V6.beginn.einrichtung, key)
|
||||
assert.equal(c.items.find((i) => i.id === 'hinweis')?.text, WORTLAUT_V6.hinweis[key], key)
|
||||
assert.deepEqual(reg.consentAboAktiv(key, { beginn: true }).items.map((i) => i.text), [zeile, ...(WORTLAUT_V6.hinweis[key] ? [WORTLAUT_V6.hinweis[key]] : []), WORTLAUT_V6.beginn.einrichtung], key)
|
||||
}
|
||||
assert.equal(reg.consentAbo('analyse'), null)
|
||||
assert.equal(reg.consentAbo('gibtEsNicht'), null)
|
||||
})
|
||||
|
||||
test('unterlagenFuer und aboweg: die fünf Funktionen aus FUNKTIONEN_UNTERLAGEN_V6, die Analyse bleibt bei den Alt-Unterlagen (N33)', () => {
|
||||
for (const key of KEYS) {
|
||||
assert.deepEqual(unterlagenFuer(key), { leistungsbeschreibung: unterlage(FUNKTIONEN_UNTERLAGEN_V6[key].lb), avv: unterlage(FUNKTIONEN_UNTERLAGEN_V6[key].avv) }, key)
|
||||
const w = reg.aboweg(reg.featureConfig(key))
|
||||
assert.deepEqual([w.leistungsbeschreibung, w.avv], [unterlage(FUNKTIONEN_UNTERLAGEN_V6[key].lb), unterlage(FUNKTIONEN_UNTERLAGEN_V6[key].avv)], key)
|
||||
}
|
||||
const a = analyseUnterlagen()
|
||||
assert.deepEqual(a, { avvVersion: config.analyse.avvVersion, legal: { lb: `/rechtliches/leistungsbeschreibung-website-analyse-${config.analyse.avvVersion}.html`, avv: `/rechtliches/avv-website-analyse-${config.analyse.avvVersion}.html` } })
|
||||
assert.equal(reg.consentAboAnalyse().items[0].text, `Es gelten Leistungsbeschreibung und Auftragsverarbeitung (Fassung ${config.analyse.avvVersion}).`)
|
||||
})
|
||||
|
||||
test('Katalog: unterlagen aus FUNKTIONEN_UNTERLAGEN_V6, hinweis aus WORTLAUT_V6, Analyse bleibt bei den Alt-Unterlagen', async () => {
|
||||
const vorher = config.analyse.enabled
|
||||
config.analyse.enabled = true
|
||||
try {
|
||||
const katalog = await katalogFuerKunde({ email: 'k@example.invalid' })
|
||||
for (const key of KEYS) {
|
||||
const e = katalog.find((x) => x.key === key)
|
||||
assert.deepEqual(e.unterlagen, { leistungsbeschreibung: unterlage(FUNKTIONEN_UNTERLAGEN_V6[key].lb), avv: unterlage(FUNKTIONEN_UNTERLAGEN_V6[key].avv) }, key)
|
||||
assert.equal(e.hinweis, WORTLAUT_V6.hinweis[key] ?? null, key)
|
||||
}
|
||||
const a = katalog.find((x) => x.key === 'analyse')
|
||||
assert.deepEqual([a.unterlagen.avv.version, a.unterlagen.avv.url, a.hinweis], [config.analyse.avvVersion, `/rechtliches/avv-website-analyse-${config.analyse.avvVersion}.html`, null])
|
||||
assert.doesNotMatch(JSON.stringify(katalog.filter((e) => e.key !== 'analyse')), /"\/(rechtliches|vertragsunterlagen)\//, 'kein Alt-Dokument mit Preisen')
|
||||
} finally {
|
||||
config.analyse.enabled = vorher
|
||||
}
|
||||
})
|
||||
|
||||
test('sichereAdresse (P3) lässt jede Adresse der Funktionsunterlagen durch, auch unter /neu/rechtliches/', () => {
|
||||
for (const u of Object.values(FUNKTIONEN_UNTERLAGEN_V6).flatMap((x) => [x.lb, x.avv]).filter(Boolean)) assert.equal(sichereAdresse(u.url), u.url, u.url)
|
||||
for (const schlecht of ['/neu/rechtliches/../geheim', '/neu/rechtliches/%2e%2e/x', '/neu/rechtliches/a b', '/neu/rechtliches\\x', '/neu/andere/seite.html', '/neu/rechtliches', '//example.org/neu/rechtliches/x']) {
|
||||
assert.equal(sichereAdresse(schlecht), '', schlecht)
|
||||
}
|
||||
})
|
||||
|
||||
test('pdfAnhaenge: liest die PDFs; fehlende, leere und Nicht-PDF-Dateien werfen', async () => {
|
||||
const dir = await mkdtemp(join(process.env.TMPDIR ?? tmpdir(), 'kn-pdf-')) // im eigenen Scratch-Ordner, wird nicht gelöscht
|
||||
await mkdir(join(dir, 'neu/rechtliches'), { recursive: true })
|
||||
const dok = (name) => ({ pdfFile: `neu/rechtliches/${name}.pdf`, anhangName: `${name}-2026-10-16.pdf` })
|
||||
await writeFile(join(dir, dok('ok').pdfFile), '%PDF-1.4 x')
|
||||
await writeFile(join(dir, dok('leer').pdfFile), '')
|
||||
await writeFile(join(dir, dok('text').pdfFile), 'hallo')
|
||||
const ok = await pdfAnhaenge([dok('ok')], { basis: dir })
|
||||
assert.deepEqual(ok.map((x) => [x.filename, x.contentType]), [['ok-2026-10-16.pdf', 'application/pdf']])
|
||||
assert.ok(Buffer.isBuffer(ok[0].content) && ok[0].content.length > 0)
|
||||
for (const name of ['fehlt', 'leer', 'text']) await assert.rejects(() => pdfAnhaenge([dok(name)], { basis: dir }), name)
|
||||
})
|
||||
Reference in new issue
Block a user