import { test, beforeEach, afterEach } from 'node:test' import assert from 'node:assert/strict' import { config } from '../server/config.js' import * as schalter from '../server/services/termine/schalter.js' const { termineAus, termineFuerKunde, termineModus } = schalter // Namensraum für termineTextformNoetig: fehlt der Export, scheitert nur sein Test const termineTextformNoetig = (email) => schalter.termineTextformNoetig(email) const TERMINE_ENV = [ 'TERMINE_ENABLED', 'TERMINE_PILOT_EMAILS', 'TERMINE_FORM_MIN_SECONDS', 'TERMINE_MIN_ANTWORT_MS', 'APPWRITE_COLLECTION_TERMINE_CONFIG', 'APPWRITE_COLLECTION_TERMINE_BOOKINGS', 'APPWRITE_COLLECTION_TERMINE_CLAIMS', 'APPWRITE_COLLECTION_TERMINE_OUTBOX', ] let original beforeEach(() => { original = { ...config.termine } }) afterEach(() => { Object.assign(config.termine, original) }) /** Frische config.js-Instanz mit genau diesen Termine-Umgebungsvariablen (alle anderen entfernt). */ let freshCounter = 0 async function freshConfig(env = {}) { const saved = Object.fromEntries(TERMINE_ENV.map((key) => [key, process.env[key]])) for (const key of TERMINE_ENV) delete process.env[key] Object.assign(process.env, env) try { freshCounter += 1 return (await import(`../server/config.js?termine-test=${freshCounter}`)).config } finally { for (const key of TERMINE_ENV) { if (saved[key] === undefined) delete process.env[key] else process.env[key] = saved[key] } } } test('Konfiguration: Vorgaben ohne Umgebungsvariablen', async () => { const fresh = await freshConfig() // avvVersion: Fassung der Bestellunterlagen (Berliner Tag des Fassungs-Commits), hier nur das Datumsmuster assert.match(fresh.termine.avvVersion, /^\d{4}-\d{2}-\d{2}$/) assert.deepEqual(fresh.termine, { mode: 'off', pilotEmails: [], avvVersion: fresh.termine.avvVersion, holdMinutes: 30, zelleMin: 5, mailsProStunde: 200, mailsProFunktion: 40, mailsReserve: 0.25, mailListe: 100, loeschDeckel: 2000, loeschDeckelGesamt: 20000, zellenDeckel: 5000, formMinSekunden: 3, minAntwortMs: 300, }) assert.equal(fresh.collections.termineConfig, 'termineConfig') assert.equal(fresh.collections.termineBookings, 'termineBookings') assert.equal(fresh.collections.termineClaims, 'termineClaims') assert.equal(fresh.collections.termineOutbox, 'termineOutbox') }) test('Konfiguration: Umgebungsvariablen werden gelesen und bereinigt', async () => { const fresh = await freshConfig({ TERMINE_ENABLED: ' Pilot ', TERMINE_PILOT_EMAILS: ' Kenso.Gri@Gmail.com, ,justin@webklar.com ', TERMINE_FORM_MIN_SECONDS: '0', TERMINE_MIN_ANTWORT_MS: '0', APPWRITE_COLLECTION_TERMINE_CONFIG: 'tConfig', APPWRITE_COLLECTION_TERMINE_BOOKINGS: 'tBookings', APPWRITE_COLLECTION_TERMINE_CLAIMS: 'tClaims', APPWRITE_COLLECTION_TERMINE_OUTBOX: 'tOutbox', }) assert.equal(fresh.termine.mode, 'pilot') assert.deepEqual(fresh.termine.pilotEmails, ['kenso.gri@gmail.com', 'justin@webklar.com']) assert.equal(fresh.termine.formMinSekunden, 0) assert.equal(fresh.termine.minAntwortMs, 0) assert.deepEqual( [fresh.collections.termineConfig, fresh.collections.termineBookings, fresh.collections.termineClaims, fresh.collections.termineOutbox], ['tConfig', 'tBookings', 'tClaims', 'tOutbox'], ) }) test('Konfiguration: leerer oder unbekannter Schalter ist off, ungueltige Sekunden fallen auf 3', async () => { for (const value of ['', ' ', 'banane', 'true', '1', 'ON ']) { const fresh = await freshConfig({ TERMINE_ENABLED: value }) assert.equal(fresh.termine.mode, value.trim().toLowerCase() === 'on' ? 'on' : 'off', `TERMINE_ENABLED=${JSON.stringify(value)}`) } for (const value of ['', 'abc', '-1', 'NaN']) { const fresh = await freshConfig({ TERMINE_FORM_MIN_SECONDS: value }) assert.equal(fresh.termine.formMinSekunden, 3, `TERMINE_FORM_MIN_SECONDS=${JSON.stringify(value)}`) } assert.equal((await freshConfig({ TERMINE_FORM_MIN_SECONDS: '7' })).termine.formMinSekunden, 7) for (const value of ['', 'abc', '-1', 'NaN']) { const fresh = await freshConfig({ TERMINE_MIN_ANTWORT_MS: value }) assert.equal(fresh.termine.minAntwortMs, 300, `TERMINE_MIN_ANTWORT_MS=${JSON.stringify(value)}`) } assert.equal((await freshConfig({ TERMINE_MIN_ANTWORT_MS: '750' })).termine.minAntwortMs, 750) }) test('termineModus: leer und unbekannt sind off, pilot und on bleiben', () => { for (const value of [undefined, null, '', 'banane', 'PILOT', 'true']) { config.termine.mode = value assert.equal(termineModus(), 'off', `mode=${JSON.stringify(value)}`) assert.equal(termineAus(), true) } config.termine.mode = 'pilot' assert.equal(termineModus(), 'pilot') assert.equal(termineAus(), false) config.termine.mode = 'on' assert.equal(termineModus(), 'on') assert.equal(termineAus(), false) }) test('termineModus: Aenderung von config.termine.mode wirkt sofort', () => { config.termine.mode = 'off' assert.equal(termineModus(), 'off') config.termine.mode = 'on' assert.equal(termineModus(), 'on') config.termine.mode = 'off' assert.equal(termineAus(), true) }) test('termineFuerKunde: off sperrt alle, auch mit Stern in der Liste', () => { config.termine.mode = 'off' config.termine.pilotEmails = ['*', 'kenso.gri@gmail.com'] assert.equal(termineFuerKunde('kenso.gri@gmail.com'), false) assert.equal(termineFuerKunde('jemand@example.com'), false) }) test('termineFuerKunde: pilot erlaubt nur Adressen der Liste, Schreibweise egal', () => { config.termine.mode = 'pilot' config.termine.pilotEmails = ['kenso.gri@gmail.com'] assert.equal(termineFuerKunde('KENSO.gri@gmail.com '), true) assert.equal(termineFuerKunde('kenso.gri@gmail.com'), true) assert.equal(termineFuerKunde('jemand@example.com'), false) assert.equal(termineFuerKunde('kenso.gri@gmail.com.evil.de'), false) }) test('termineFuerKunde: pilot mit Stern erlaubt alle, mit leerer Liste niemanden', () => { config.termine.mode = 'pilot' config.termine.pilotEmails = ['*'] assert.equal(termineFuerKunde('jemand@example.com'), true) config.termine.pilotEmails = [] assert.equal(termineFuerKunde('kenso.gri@gmail.com'), false) }) test('termineFuerKunde: pilot ohne E-Mail-Adresse ist false', () => { config.termine.mode = 'pilot' config.termine.pilotEmails = ['kenso.gri@gmail.com'] for (const email of [undefined, null, '', ' ']) assert.equal(termineFuerKunde(email), false) }) test('termineFuerKunde: on gilt fuer alle ohne E-Mail-Filter', () => { config.termine.mode = 'on' config.termine.pilotEmails = ['kenso.gri@gmail.com'] assert.equal(termineFuerKunde('jemand@example.com'), true) assert.equal(termineFuerKunde(undefined), true) }) test('termineTextformNoetig: nur im Modus on und nur außerhalb der Pilotliste; „*“ und fehlende Adresse befreien nicht', () => { for (const mode of ['off', 'pilot', undefined, '', 'banane']) { config.termine.mode = mode config.termine.pilotEmails = [] assert.equal(termineTextformNoetig('jemand@example.com'), false, `mode=${JSON.stringify(mode)}`) config.termine.pilotEmails = ['*'] assert.equal(termineTextformNoetig('jemand@example.com'), false, `mode=${JSON.stringify(mode)} mit Stern`) } config.termine.mode = 'on' config.termine.pilotEmails = [] assert.equal(termineTextformNoetig('jemand@example.com'), true) config.termine.pilotEmails = ['kenso.gri@gmail.com'] assert.equal(termineTextformNoetig('jemand@example.com'), true) assert.equal(termineTextformNoetig('KENSO.gri@gmail.com '), false, 'Pilotadresse, Schreibweise egal') assert.equal(termineTextformNoetig('kenso.gri@gmail.com.evil.de'), true) config.termine.pilotEmails = ['*'] assert.equal(termineTextformNoetig('jemand@example.com'), true, 'der Stern zählt in on nicht als Pilotadresse') assert.equal(termineTextformNoetig('*'), true) config.termine.pilotEmails = ['*', 'kenso.gri@gmail.com'] assert.equal(termineTextformNoetig('kenso.gri@gmail.com'), false) for (const email of [undefined, null, '', ' ']) assert.equal(termineTextformNoetig(email), true, `ohne Adresse: ${JSON.stringify(email)}`) // wirkt sofort wie die übrigen Schalter config.termine.mode = 'pilot' assert.equal(termineTextformNoetig('jemand@example.com'), false) })