Terminplaner: Hintergrund-Jobs

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-03 08:17:43 +00:00
1 parent efe17551c2
commit 485ff3bc15
6 files changed
+1428 -3

No files matched your search

+49 -1
View File
@@ -23,7 +23,7 @@ const { setMailTransportForTests } = await import('../server/services/mailer.js'
const { loadTemplate } = await import('../server/services/mailTemplates.js')
const { icsTermin } = await import('../server/services/termine/ics.js')
const O = await import('../server/services/termine/outbox.js')
const { einreihen, sendeEintrag, verarbeite, warteAufVersand, zustellFehler, raeumeAuf, pausiertBis, resetOutboxForTests } = O
const { einreihen, sendeEintrag, verarbeite, warteAufVersand, zustellFehler, raeumeAuf, beendeFestgefahrene, pausiertBis, resetOutboxForTests } = O
after(() => setMailTransportForTests(null))
const MIN = 60000
@@ -550,6 +550,54 @@ test('raeumeAuf: gesendet 7 Tage nach dem Versand, fehler 48 Stunden nach dem Fe
await assert.rejects(() => raeumeAuf(), TypeError)
})
test('beendeFestgefahrene: wartet länger als 48 Stunden (createdAt) ⇒ fehler mit Löschtermin in 48 Stunden; Jüngere und andere Status bleiben', async () => {
const alt = await legeAn({ bookingId: 'b_alt', createdAt: plus(-48 * STUNDE - 1).toISOString() })
const altVertagt = await legeAn({ bookingId: 'b_alt2', createdAt: plus(-5 * TAG).toISOString(), naechsterVersuch: plus(20 * MIN).toISOString() })
const grenze = await legeAn({ bookingId: 'b_grenze', createdAt: plus(-48 * STUNDE).toISOString() })
const jung = await legeAn({ bookingId: 'b_jung', createdAt: plus(-2 * STUNDE).toISOString() })
const fertig = await legeAn({ bookingId: 'b_fertig', status: 'gesendet', createdAt: plus(-5 * TAG).toISOString(), naechsterVersuch: plus(3 * TAG).toISOString() })
assert.equal(await beendeFestgefahrene(JETZT), 2)
for (const eintragDoc of [alt, altVertagt]) {
const e = await lies(eintragDoc.$id)
assert.equal(e.status, 'fehler')
assert.equal(ms(e.naechsterVersuch), JETZT.getTime() + 48 * STUNDE, 'Löschtermin = jetzt + 48 Stunden')
assert.ok(e.fehler.length > 0 && e.fehler.length <= 300)
assert.equal(e.versuche, 0)
}
for (const eintragDoc of [grenze, jung]) assert.equal((await lies(eintragDoc.$id)).status, 'wartet')
assert.equal((await lies(fertig.$id)).status, 'gesendet')
assert.deepEqual([...(await zustellFehler(['b_alt', 'b_alt2', 'b_jung']))].sort(), ['b_alt', 'b_alt2'], 'das Portal zeigt den Zustellfehler')
assert.equal(await beendeFestgefahrene(JETZT), 0, 'zweiter Lauf ändert nichts')
assert.equal(await raeumeAuf(JETZT), 0, 'nicht sofort gelöscht')
assert.equal(await raeumeAuf(plus(48 * STUNDE - 1)), 0)
assert.equal(await raeumeAuf(plus(48 * STUNDE)), 2, 'erst 48 Stunden später')
await assert.rejects(() => beendeFestgefahrene(), TypeError)
})
test('beendeFestgefahrene: ein Eintrag, der inzwischen gesendet wurde, bleibt gesendet', async () => {
const doc = await legeAn({ bookingId: 'b_race', createdAt: plus(-3 * TAG).toISOString() })
const echt = globalThis.fetch
let einmal = true
// zwischen Auflisten und Sperre sendet ein anderer Lauf den Eintrag
globalThis.fetch = async (url, optionen) => {
const antwort = await echt(url, optionen)
if (einmal && (optionen?.method ?? 'GET') === 'GET' && String(url).includes('/documents?')) {
einmal = false
await sendeEintrag(doc.$id, JETZT)
}
return antwort
}
try {
assert.equal(await beendeFestgefahrene(JETZT), 0)
} finally {
globalThis.fetch = echt
}
assert.equal(einmal, false, 'der Versand lief zwischen Auflisten und Sperre')
assert.equal((await lies(doc.$id)).status, 'gesendet')
})
test('raeumeAuf löscht mehr als eine Seite (über 25 Einträge)', async () => {
for (let i = 0; i < 40; i++) await legeAn({ bookingId: `b_${i}`, status: 'gesendet', an: '', html: '', text: '', naechsterVersuch: JETZT.toISOString() })
assert.equal(await raeumeAuf(JETZT), 40)