Terminplaner: Loeschung abgeschalteter Funktionen seitenweise, Rest an Folgetagen

loescheAbgeschaltete liest je abgeschalteter Funktion (disabledAt vor 30 Tagen) Seiten zu 100
(neu buchungen.ersteBuchungen: nur $id, limit, nie Gastfelder) und loescht sie ueber arbeiteSeiten
samt Zellen, bis die Seite leer ist. Die Funktion wird nach jedem Lesen einer Seite und alle 50
Buchungen frisch geprueft (nochAbgeschaltet); wird sie wieder eingeschaltet, endet die Loeschung
fuer sie sofort. Deckel config.termine.loeschDeckel je Funktion: am Deckel bleibt die Funktion
disabled mit altem disabledAt, eine Warnung ohne Kennungen steht im Log, der naechste Tag macht
weiter, die uebrigen Funktionen kommen im selben Lauf dran. Die Einstellung (termineConfig) wird
nur nach leerer Seite ohne Fehler und mit letzter Pruefung unter termine-cfg:<id> geloescht.
loescheAbgeschalteteFunktionen liefert { funktionen, buchungen, gedeckelt }; taeglicherJob traegt
gedeckelt.abgeschaltet getrennt vom Loeschzaehler ein (Tagesbericht: Loeschlaeufe am Deckel).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-04 09:24:33 +00:00
1 parent 9b4305e5de
commit a1732613c9
4 files changed
+366 -58

No files matched your search

+41
View File
@@ -1011,6 +1011,47 @@ test('loeschKandidaten: hoechstens limit (Standard 100), aelteste Bezugszeit zue
for (const limit of [0, -1, 1.5, NaN, '10']) await assert.rejects(B.loeschKandidaten('erledigt', neunzig, { limit }), TypeError, `limit ${limit}`)
})
test('ersteBuchungen: hoechstens limit (Standard 100) Buchungen einer Funktion, jeder Status, nur die Kennung (nie Gastfelder)', async () => {
const nr = (i) => String(i).padStart(3, '0')
for (let i = 0; i < 130; i += 1) seedBuchung(`b_a${nr(i)}`, { status: ['bestaetigt', 'erledigt', 'abgesagt', 'gehalten'][i % 4] })
for (let i = 0; i < 7; i += 1) seedBuchung(`b_fremd${i}`, { featureId: 'f_feature02' })
const alleA = Array.from({ length: 130 }, (_, i) => `b_a${nr(i)}`)
aw.resetListCalls()
const standard = await B.ersteBuchungen(F)
assert.equal(standard.length, 100, 'Standard 100')
assert.ok(standard.every((doc) => alleA.includes(doc.$id)), 'nur Buchungen dieser Funktion')
assert.equal(new Set(standard.map((doc) => doc.$id)).size, 100)
assert.equal((await B.ersteBuchungen(F, 10)).length, 10)
assert.equal((await B.ersteBuchungen(F, 1)).length, 1)
assert.deepEqual((await B.ersteBuchungen('f_feature02')).map((doc) => doc.$id).sort(), Array.from({ length: 7 }, (_, i) => `b_fremd${i}`))
assert.deepEqual(await B.ersteBuchungen('f_gibt_es_nicht'), [])
// nur die Kennung: keine Gastfelder, und die Abfrage sagt es selbst (select, limit, Funktionsfilter)
for (const doc of standard) {
assert.ok(doc.$id)
assert.deepEqual(Object.keys(doc).filter((key) => !key.startsWith('$')), [], `${doc.$id}: keine Felder ausser den $-Feldern`)
}
const aufrufe = aw.listCalls('termineBookings')
const abfrage = aufrufe[0].queries
assert.deepEqual(abfrage.find((q) => q.method === 'select').values, ['$id'])
assert.deepEqual(abfrage.find((q) => q.method === 'limit').values, [100])
assert.deepEqual(abfrage.find((q) => q.method === 'equal'), { method: 'equal', attribute: 'featureId', values: [F] })
for (const { queries } of aufrufe) assert.ok(queries.find((q) => q.method === 'limit').values[0] <= 100)
// wer loescht, liest neu: Geloeschtes fehlt in der naechsten Seite
for (const doc of standard) await B.loesche(doc.$id)
const rest = await B.ersteBuchungen(F)
assert.deepEqual(rest.map((doc) => doc.$id).sort(), alleA.filter((id) => !standard.some((doc) => doc.$id === id)))
assert.equal(rest.length, 30)
// ungueltige Eingaben: nie eine Abfrage ohne Funktionsfilter oder ohne Grenze
aw.resetListCalls()
for (const featureId of [undefined, null, '', 5]) await assert.rejects(B.ersteBuchungen(featureId), TypeError, `featureId ${featureId}`)
for (const limit of [0, -1, 1.5, NaN, '10', null]) await assert.rejects(B.ersteBuchungen(F, limit), TypeError, `limit ${limit}`)
assert.equal(aw.listCalls('termineBookings').length, 0, 'bei ungueltigen Eingaben geht keine Abfrage raus')
})
test('zaehleAnfragen: offene Anfragen einer Funktion (auch ueber 25)', async () => {
for (let i = 0; i < 27; i += 1) seedBuchung(`b_anf${String(i).padStart(2, '0')}`, { status: 'angefragt' })
seedBuchung('b_best', { status: 'bestaetigt' })