Terminplaner: Abschluss – Server (Widerruf auch bei Anfragen, Gastdaten löschen vollständig, formMinSekunden)

- R55: Widerrufsbelehrung, Anschrift-Pflicht und Knopf "Vertrag widerrufen" gelten für jede
  Verbraucher-Buchung mit sichtbarer Leistung, auch Anfragen (zeigtWiderruf); der Nachweis
  hält die Belehrung auch bei Anfragen fest. Damit bleibt die Annahme einer Anfrage nie mehr
  still ohne G02.
- "Gastdaten löschen" leert auch absageGrund und löscht noch nicht versandte Mails der Buchung
  (neu: verwerfeWartende in der Warteschlange).
- R53: GET /:siteKey/config nennt formMinSekunden.

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:44 +00:00
1 parent 4be886a4de
commit bd65c9dc58
11 files changed
+216 -18

No files matched your search

+38 -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, beendeFestgefahrene, pausiertBis, resetOutboxForTests } = O
const { einreihen, sendeEintrag, verarbeite, warteAufVersand, zustellFehler, raeumeAuf, beendeFestgefahrene, verwerfeWartende, pausiertBis, resetOutboxForTests } = O
after(() => setMailTransportForTests(null))
const MIN = 60000
@@ -598,6 +598,43 @@ test('beendeFestgefahrene: ein Eintrag, der inzwischen gesendet wurde, bleibt ge
assert.equal((await lies(doc.$id)).status, 'gesendet')
})
test('verwerfeWartende: löscht nur die wartenden Einträge dieser Buchung (auch über mehrere Seiten); gesendete, fehlgeschlagene und fremde bleiben', async () => {
for (let i = 0; i < 30; i++) await legeAn({ bookingId: 'b_weg', art: 'G05' })
const gesendet = await legeAn({ bookingId: 'b_weg', status: 'gesendet', an: '', html: '', text: '' })
const fehler = await legeAn({ bookingId: 'b_weg', status: 'fehler' })
const fremd = await legeAn({ bookingId: 'b_fremd' })
assert.equal(await verwerfeWartende('b_weg'), 30)
assert.deepEqual(alle().map((d) => d.$id).sort(), [gesendet.$id, fehler.$id, fremd.$id].sort())
assert.equal(await verwerfeWartende('b_weg'), 0, 'zweiter Aufruf findet nichts')
assert.equal(await verwerfeWartende('b_gibtsnicht'), 0)
for (const ungueltig of [undefined, null, '', 5]) await assert.rejects(() => verwerfeWartende(ungueltig), TypeError)
})
test('verwerfeWartende: ein Eintrag, der inzwischen gesendet wurde, bleibt (Sperre, neu lesen); ein Eintrag, der inzwischen weg ist, stört nicht', async () => {
const doc = await legeAn({ bookingId: 'b_race' })
const weg = await legeAn({ bookingId: 'b_race' })
const echt = globalThis.fetch
let einmal = true
// zwischen Auflisten und Sperre sendet ein anderer Lauf den ersten Eintrag und ein anderer Lauf loescht den zweiten
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)
await deleteDocument(col(), weg.$id)
}
return antwort
}
try {
assert.equal(await verwerfeWartende('b_race'), 0)
} finally {
globalThis.fetch = echt
}
assert.equal(einmal, false, 'der Versand lief zwischen Auflisten und Sperre')
assert.equal((await lies(doc.$id)).status, 'gesendet')
assert.equal(await lies(weg.$id), undefined)
})
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)