import { test, after } from 'node:test' import assert from 'node:assert/strict' import { startAppwriteMock } from './support/appwriteMock.js' import { startFakeStripe } from './support/fakeStripe.js' import { useTestEnv } from './support/testEnv.js' import { schemaKeys } from './support/schema.js' // Fassungsweiche (V12) und Vertragsnachweis: eine Zeile in featureConsents ohne Projekt, mit Kundenart, Hash und Konto const aw = await startAppwriteMock({ schema: { featureConsents: schemaKeys('featureConsents') } }) const st = await startFakeStripe() after(() => Promise.all([aw.close(), st.close()])) useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url }) Object.assign(process.env, { KUNDENANSICHT_NEU: 'pilot', KUNDENANSICHT_NEU_PILOT_EMAILS: 'k3@example.invalid' }) const { config } = await import('../server/config.js') const { fassungDerAnfrage, istNeueFassung, speichereVertragsNachweis, speichereNachweisFuerRoute } = await import('../server/services/vertragsNachweis.js') const { nachweisTexte, FEHLER_NACHWEIS } = await import('../server/services/bestelltexte.js') const { texteHash } = await import('../server/services/featureConsents.js') const req = (email = 'k3@example.invalid') => ({ ip: '203.0.113.9', get: (h) => (h === 'user-agent' ? 'UA/1' : ''), session: { customerId: 'c3', appwriteUserId: 'u3', name: 'Kunde Drei', email }, }) test('fassungDerAnfrage: alt, neu, null; fehlende Neu-Fassung ist nie neu', () => { const NEU = config.abo.termsVersionNeu const modus = config.kundenansichtNeu.mode // P8 setzt die Neu-Fassung, der Test liest sie nur try { assert.equal(fassungDerAnfrage(req('fremd@example.invalid'), config.abo.termsVersion), 'alt') assert.equal(fassungDerAnfrage(req('k3@example.invalid'), NEU), 'neu') assert.equal(fassungDerAnfrage(req('fremd@example.invalid'), NEU), null) // nicht Pilot assert.equal(fassungDerAnfrage(req('k3@example.invalid'), '2026-09-25-v3'), null) for (const v of [undefined, null, '', 6, {}]) assert.equal(fassungDerAnfrage(req('k3@example.invalid'), v), null) config.abo.termsVersionNeu = undefined assert.equal(fassungDerAnfrage(req('k3@example.invalid'), undefined), null) config.abo.termsVersionNeu = NEU config.kundenansichtNeu.mode = 'off' assert.equal(fassungDerAnfrage(req('k3@example.invalid'), NEU), null) // off schlägt die Liste assert.deepEqual([istNeueFassung(NEU), istNeueFassung(''), istNeueFassung(undefined), istNeueFassung(config.abo.termsVersion)], [true, false, false, false]) // gleiche Werte (nach P10): nur noch mit neuer Ansicht 'neu', sonst null config.kundenansichtNeu.mode = 'pilot' config.abo.termsVersionNeu = config.abo.termsVersion assert.equal(fassungDerAnfrage(req('k3@example.invalid'), config.abo.termsVersion), 'neu') assert.equal(fassungDerAnfrage(req('fremd@example.invalid'), config.abo.termsVersion), null) // eine Sitzung ohne Konto-Kennung ist keine Kunden-Sitzung (P0) config.abo.termsVersionNeu = NEU const ohneKonto = req(); delete ohneKonto.session.appwriteUserId assert.equal(fassungDerAnfrage(ohneKonto, NEU), null) } finally { config.abo.termsVersionNeu = NEU config.kundenansichtNeu.mode = modus } }) test('speichereVertragsNachweis schreibt eine Zeile ohne Projekt, mit Kundenart, Hash und Konto', async () => { const FASSUNG = config.abo.termsVersionNeu const texte = nachweisTexte('abo', { kundenart: 'privat', fassung: FASSUNG, bestellung: 'Zeile', beginnGehakt: true }) const d = await speichereVertragsNachweis({ customerId: 'c3', art: 'abo', bezug: 'wartung', texte, fassungen: { zustimmung: FASSUNG }, kundenart: 'privat', req: req('k3@example.invalid'), at: '2026-10-17T08:00:00.000Z' }) assert.deepEqual([d.featureKey, d.projectId, d.stripeSessionId, d.kundenart, d.konto, d.ip, d.userAgent], ['abo', '', '', 'privat', 'Kunde Drei ', '203.0.113.9', 'UA/1']) assert.deepEqual(JSON.parse(d.fassungen), { zustimmung: FASSUNG, bezug: 'wartung', wortlaut: 'entwurf' }) assert.deepEqual(JSON.parse(d.texte), texte) assert.equal(d.textHash, texteHash(texte)) assert.equal(d.acceptedAt, '2026-10-17T08:00:00.000Z') assert.equal(aw.docs('featureConsents').get(d.$id).customerId, 'c3') }) test('Mitarbeiter-Kundenansicht: das Konto ist die handelnde Person, nicht der Kunde', async () => { const r = req() r.session.adminName = 'Team Mitglied' r.session.adminEmail = 'team@example.invalid' const d = await speichereVertragsNachweis({ customerId: 'c3', art: 'wechsel', texte: nachweisTexte('gutschrift', {}), fassungen: {}, kundenart: 'unternehmen', req: r, at: '2026-10-17T08:00:00.000Z' }) assert.equal(d.konto, 'Team Mitglied ') assert.deepEqual([d.kundenart, JSON.parse(d.fassungen).bezug], ['unternehmen', '']) }) test('Ungültiges wird vor dem Schreiben mit 400 abgelehnt, nie als privat ergänzt', async () => { const vorher = aw.docs('featureConsents').size const gut = { customerId: 'c3', art: 'abo', texte: [{ id: 'knopf', text: 'Zahlungspflichtig bestellen', haken: false }], fassungen: {}, kundenart: 'privat', req: req(), at: '2026-10-17T08:00:00.000Z' } for (const schlecht of [ { art: 'feature' }, { art: undefined }, { kundenart: '' }, { kundenart: 'firma' }, { kundenart: undefined }, { texte: [] }, { texte: undefined }, { texte: [{ id: 'x', text: '' }] }, { texte: [{ id: '', text: 'a' }] }, { texte: [{ id: 'x', text: 'a', haken: 'ja' }] }, { texte: [{ id: 'x', text: 'a'.repeat(20001) }] }, { fassungen: { zustimmung: 'a'.repeat(1001) } }, { fassungen: null }, { customerId: '' }, { customerId: undefined }, { at: 'gestern' }, { at: undefined }, ]) { await assert.rejects(speichereVertragsNachweis({ ...gut, ...schlecht }), (e) => e.status === 400, JSON.stringify(Object.keys(schlecht))) } assert.equal(aw.docs('featureConsents').size, vorher) for (const art of ['abo', 'wechsel', 'speicher', 'gutschrift']) await speichereVertragsNachweis({ ...gut, art }) assert.equal(aw.docs('featureConsents').size, vorher + 4) }) test('Appwrite-Fehler: 500 mit dem festen Text, keine Ursache nach außen', async () => { const vorher = aw.docs('featureConsents').size aw.failNext({ collection: config.collections.featureConsents, method: 'POST', status: 500, message: 'geheimer Appwrite-Text' }) await assert.rejects( speichereVertragsNachweis({ customerId: 'c3', art: 'gutschrift', texte: nachweisTexte('gutschrift', {}), fassungen: {}, kundenart: 'unternehmen', req: req(), at: '2026-10-17T08:00:00.000Z' }), (e) => e.status === 500 && e.message === FEHLER_NACHWEIS && !JSON.stringify(e).includes('geheimer'), ) assert.equal(aw.docs('featureConsents').size, vorher) }) test('speichereNachweisFuerRoute: auch eine abgelehnte Eingabe ist ein 500 mit dem festen Text, nichts geschrieben', async () => { const vorher = aw.docs('featureConsents').size const gut = { customerId: 'c3', art: 'abo', texte: nachweisTexte('gutschrift', {}), fassungen: {}, kundenart: 'privat', req: req(), at: '2026-10-17T08:00:00.000Z' } await assert.rejects(speichereNachweisFuerRoute({ ...gut, kundenart: '' }), (e) => e.status === 500 && e.message === FEHLER_NACHWEIS) assert.equal(aw.docs('featureConsents').size, vorher) assert.equal((await speichereNachweisFuerRoute(gut)).featureKey, 'abo') assert.equal(aw.docs('featureConsents').size, vorher + 1) })