import { test, beforeEach } from 'node:test' import assert from 'node:assert/strict' import { zaehle, zaehleAlle, darfG08, darfK21NichtZugeordnet, resetTermineLimitsForTests } from '../server/services/termine/limits.js' // Grenzen im Speicher: festes Fenster ab dem ersten Ereignis, alle-oder-keine bei mehreren Regeln, Obergrenze der Schluessel. const T0 = new Date('2026-10-05T08:00:00.000Z') const STUNDE = 60 * 60 * 1000 const TAG = 24 * STUNDE const plus = (ms) => new Date(T0.getTime() + ms) beforeEach(() => resetTermineLimitsForTests()) test('zaehle: erlaubt bis max, danach false; das Fenster beginnt mit dem ersten Ereignis und endet nach fensterMs', () => { assert.deepEqual([1, 2, 3].map(() => zaehle('a', 3, STUNDE, T0)), [true, true, true]) assert.equal(zaehle('a', 3, STUNDE, plus(10 * 60000)), false) assert.equal(zaehle('a', 3, STUNDE, plus(STUNDE - 1)), false, 'bis zur letzten Millisekunde des Fensters') assert.equal(zaehle('b', 3, STUNDE, T0), true, 'andere Schluessel zaehlen getrennt') // die abgelehnten Anfragen haben nichts gezaehlt: das neue Fenster (ab plus(STUNDE)) erlaubt wieder drei assert.deepEqual([1, 2, 3, 4].map((i) => zaehle('a', 3, STUNDE, plus(STUNDE + i))), [true, true, true, false]) }) test('zaehle: max 0 erlaubt nie, ungueltige Angaben werfen TypeError', () => { assert.equal(zaehle('a', 0, STUNDE, T0), false) assert.throws(() => zaehle('a', 1.5, STUNDE, T0), TypeError) assert.throws(() => zaehle('a', -1, STUNDE, T0), TypeError) assert.throws(() => zaehle('a', 1, 0, T0), TypeError) assert.throws(() => zaehle('', 1, STUNDE, T0), TypeError) assert.throws(() => zaehle('a', 1, STUNDE, undefined), TypeError) assert.throws(() => zaehle('a', 1, STUNDE, Date.now()), TypeError) assert.throws(() => zaehleAlle([], T0), TypeError) }) test('zaehleAlle: alle oder keine - scheitert eine Regel, zaehlt keine', () => { const regeln = [ { schluessel: 'adresse', max: 3, fensterMs: TAG }, { schluessel: 'funktion', max: 1, fensterMs: STUNDE }, ] assert.equal(zaehleAlle(regeln, T0), true) assert.equal(zaehleAlle(regeln, plus(1000)), false, 'die Funktionsgrenze ist erreicht') // der Adress-Zaehler wurde dabei nicht verbraucht: nach der Stunde sind noch zwei Ereignisse an diese Adresse frei const nurAdresse = [{ schluessel: 'adresse', max: 3, fensterMs: TAG }] assert.equal(zaehleAlle(nurAdresse, plus(2 * STUNDE)), true) assert.equal(zaehleAlle(nurAdresse, plus(2 * STUNDE)), true) assert.equal(zaehleAlle(nurAdresse, plus(2 * STUNDE)), false) }) test('darfG08: 3 je Adresse und Tag (auch ueber Funktionen), 20 je Funktion und Stunde', () => { assert.deepEqual([1, 2, 3, 4].map(() => darfG08('f1', 'max@example.com', T0)), [true, true, true, false]) assert.equal(darfG08('f2', 'max@example.com', plus(TAG - 1)), false, 'die Adresse zaehlt fuer alle Funktionen zusammen') assert.equal(darfG08('f1', 'max@example.com', plus(TAG)), true, 'am naechsten Tag wieder') resetTermineLimitsForTests() const erlaubt = Array.from({ length: 22 }, (_, i) => darfG08('f1', `gast${i}@example.com`, T0)) assert.equal(erlaubt.filter(Boolean).length, 20) assert.equal(darfG08('f2', 'neu@example.com', T0), true, 'andere Funktion hat ihr eigenes Kontingent') assert.equal(darfG08('f1', 'spaet@example.com', plus(STUNDE)), true, 'nach einer Stunde wieder') }) test('darfK21NichtZugeordnet: 30 je Funktion und Stunde', () => { assert.equal(Array.from({ length: 32 }, () => darfK21NichtZugeordnet('f1', T0)).filter(Boolean).length, 30) assert.equal(darfK21NichtZugeordnet('f2', T0), true) assert.equal(darfK21NichtZugeordnet('f1', plus(STUNDE)), true) }) test('Obergrenze: ueber 10 000 Schluessel faellt der aelteste Zaehler weg, die neueren bleiben', () => { assert.equal(zaehle('aeltester', 1, TAG, T0), true) assert.equal(zaehle('aeltester', 1, TAG, T0), false) for (let i = 0; i < 9998; i += 1) zaehle(`k${i}`, 1, TAG, plus(1000)) assert.equal(zaehle('aeltester', 1, TAG, T0), false, 'bei 9 999 Schluesseln noch da') assert.equal(zaehle('k9997', 1, TAG, plus(2000)), false) zaehle('neu1', 1, TAG, plus(3000)) // 10 000 zaehle('neu2', 1, TAG, plus(3000)) // 10 001: der aelteste faellt weg assert.equal(zaehle('aeltester', 1, TAG, plus(4000)), true, 'der aelteste Zaehler wurde verdraengt') assert.equal(zaehle('neu2', 1, TAG, plus(4000)), false, 'der neueste bleibt') assert.equal(zaehle('k9997', 1, TAG, plus(4000)), false, 'die juengeren bleiben') }) test('resetTermineLimitsForTests leert alles', () => { zaehle('a', 1, TAG, T0) assert.equal(zaehle('a', 1, TAG, T0), false) resetTermineLimitsForTests() assert.equal(zaehle('a', 1, TAG, T0), true) })