verarbeite liest nicht mehr alle wartenden Eintraege, sondern je Klasse (vorrang, normal, nachrangig) eine begrenzte Liste (config.termine.mailListe, Standard 100) mit select und orderAsc(naechsterVersuch). Bei Pause oder leerem Gesamtbudget endet der Lauf ohne Abfrage, bei erreichter Reserve entfaellt die nachrangige Klasse. Funktionen mit ausgeschoepftem Kontingent kommen in eine Ausschlussliste (hoechstens 90 Werte); hoechstens vier weitere Runden je Klasse, damit keine Funktion hinter einer vollen verhungert. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
162 lines
6.2 KiB
JavaScript
162 lines
6.2 KiB
JavaScript
import { test, beforeEach, afterEach } from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { config } from '../server/config.js'
|
|
import { termineAus, termineFuerKunde, termineModus } from '../server/services/termine/schalter.js'
|
|
|
|
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()
|
|
assert.deepEqual(fresh.termine, {
|
|
mode: 'off',
|
|
pilotEmails: [],
|
|
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)
|
|
})
|