Files
Webklar-Kundenbereich/test/termineSchalter.test.js
T
ClaudeandClaude Sonnet 5.5 978722023f Terminplaner: Zellen-Sweep seitenweise ueber idx_bis mit Deckel
raeumeVerwaisteZellen(now, { deckel = config.termine.zellenDeckel }) liest
Zellen mit bis < jetzt - 1 Tag in Seiten zu 100 ueber idx_bis (aelteste
zuerst, ohne Cursor neu gelesen), gruppiert je Ressourcensperre mit Map und
loescht unter der Sperre nur, was dort noch dieselbe Buchung hat und noch alt
ist. Die Buchungen werden nicht gelesen. Der Lauf endet am Deckel (5000,
gedeckelt wird im Tagesjob gezaehlt und im Log gewarnt), bei einer leeren
Seite oder einer Seite ohne Erfolg; der erste Fehler wird am Ende geworfen.
ungueltigeZellen prueft zuerst das Zeitfenster (auch fuer belegeZellen).
verwaisteZellen, alleZellen und ALLE_ZELLEN_MAX entfallen.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 09:44:47 +00:00

159 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,
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)
})