Aufträge L5: öffentliche Bestätigung per Link (idempotent, Frühbeginn nur bei Privatpersonen)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
aca393d564
commit
6cb5bfa6c7
2 files changed
+114
-1
No files matched your search
@@ -112,3 +112,102 @@ test('Ratenbegrenzung: 120 Aufrufe (GET) je Stunde und IP, danach 429; eine ande
|
||||
assert.deepEqual([zuViele.status, zuViele.body], [429, ZU_VIELE])
|
||||
assert.equal((await get(TOKEN, '', ip('203.0.113.10'))).status, 200)
|
||||
})
|
||||
|
||||
const HAEKCHEN = { error: 'Bitte bestätigen Sie den Auftrag mit dem Häkchen.' }
|
||||
|
||||
test('POST …/bestaetigen ohne bestaetigt: true → 400, nichts geändert, keine Mail', async () => {
|
||||
for (const body of [{}, { bestaetigt: false }, { bestaetigt: 'true' }, { bestaetigt: 1 }, { fruehbeginn: true }]) {
|
||||
const res = await post(body)
|
||||
assert.deepEqual([res.status, res.body], [400, HAEKCHEN], JSON.stringify(body))
|
||||
}
|
||||
const doc = await getAuftrag('ab1')
|
||||
assert.deepEqual([doc.status, doc.bestaetigtAm], ['versendet', null])
|
||||
assert.equal(sink.sent.length, 0)
|
||||
// die Prüfung des Links geht dem Häkchen vor: bei einem unbekannten Link 404, nie 400
|
||||
const unbekannt = await post({}, TOKEN_NEU)
|
||||
assert.deepEqual([unbekannt.status, unbekannt.body], [404, { error: UNGUELTIG }])
|
||||
})
|
||||
|
||||
test('POST mit unbekanntem, falschem, altem oder abgelaufenem Link, bei ersetzter AB oder abgeschaltetem Schalter: 404 bzw. 410, nichts ändert sich', async () => {
|
||||
const pruefe = async (erwartet, token = TOKEN) => {
|
||||
const res = await post({ bestaetigt: true }, token)
|
||||
assert.deepEqual([res.status, res.body], erwartet)
|
||||
const doc = await getAuftrag('ab1')
|
||||
assert.deepEqual([doc.status, doc.bestaetigtAm, sink.sent.length], ['versendet', null, 0])
|
||||
}
|
||||
await pruefe([404, { error: UNGUELTIG }], TOKEN_NEU)
|
||||
await pruefe([404, { error: UNGUELTIG }], 'kurz')
|
||||
seed({ tokenHash: hashToken(TOKEN_NEU) }) // wie nach „Erinnerung senden“: nur der neue Link gilt
|
||||
await pruefe([404, { error: UNGUELTIG }], TOKEN)
|
||||
seed({ tokenGueltigBis: new Date(JETZT.getTime() - 1).toISOString() })
|
||||
await pruefe([404, { error: UNGUELTIG }])
|
||||
seed({ status: 'ersetzt' })
|
||||
const ersetzt = await post({ bestaetigt: true })
|
||||
assert.deepEqual([ersetzt.status, ersetzt.body], [410, { error: GRUND.ersetzt }])
|
||||
assert.deepEqual([(await getAuftrag('ab1')).bestaetigtAm, sink.sent.length], [null, 0])
|
||||
seed()
|
||||
config.auftraege.mode = 'off'
|
||||
await pruefe([404, { error: UNGUELTIG }])
|
||||
})
|
||||
|
||||
test('POST bestätigt über den Link: Protokoll am Dokument, öffentliche Antwort, K32 und T22; keine IP-Adresse gespeichert', async () => {
|
||||
const res = await post({ bestaetigt: true, fruehbeginn: true }, TOKEN, ip('203.0.113.77'))
|
||||
assert.equal(res.status, 200)
|
||||
assert.deepEqual([res.body.auftrag.status, res.body.auftrag.fruehbeginn, new Date(res.body.auftrag.bestaetigtAm).getTime()], ['bestaetigt', true, JETZT.getTime()])
|
||||
assert.ok(!('tokenHash' in res.body.auftrag) && !('pdfSha256' in res.body.auftrag))
|
||||
const doc = await getAuftrag('ab1')
|
||||
assert.deepEqual([doc.bestaetigtUeber, doc.bestaetigtPdfSha256, doc.fruehbeginn], ['link', SHA, true])
|
||||
assert.deepEqual(sink.sent.map((m) => m.subject), ['Ihre Bestätigung zu AB-2026-0001 ist eingegangen', '[Aufträge] Auftrag AB-2026-0001 bestätigt: Erika Muster'])
|
||||
assert.ok(!JSON.stringify([...aw.docs('auftraege').values()]).includes('203.0.113.77'), 'keine IP-Adresse im Dokument')
|
||||
})
|
||||
|
||||
test('fruehbeginn zählt nur bei Privatpersonen und nur als true', async () => {
|
||||
for (const [kundenart, eingabe, erwartet] of [['privat', 'ja', false], ['privat', 1, false], ['privat', true, true], ['unternehmen', true, false]]) {
|
||||
seed({ kunde: kunde(kundenart) })
|
||||
const res = await post({ bestaetigt: true, fruehbeginn: eingabe })
|
||||
assert.equal(res.status, 200, `${kundenart} ${eingabe}`)
|
||||
assert.equal((await getAuftrag('ab1')).fruehbeginn, erwartet, `${kundenart} ${eingabe}`)
|
||||
}
|
||||
})
|
||||
|
||||
test('zweite und gleichzeitige Bestätigungen: 200, die erste zählt, genau zwei Mails', async () => {
|
||||
const erste = await post({ bestaetigt: true, fruehbeginn: true })
|
||||
setzeUhrFuerTests(() => new Date(JETZT.getTime() + 3_600_000))
|
||||
const zweite = await post({ bestaetigt: true, fruehbeginn: false })
|
||||
assert.equal(zweite.status, 200)
|
||||
assert.deepEqual(zweite.body, erste.body)
|
||||
assert.equal(sink.sent.length, 2)
|
||||
seed()
|
||||
sink.sent.length = 0
|
||||
const [a, b] = await Promise.all([post({ bestaetigt: true }), post({ bestaetigt: true })])
|
||||
assert.deepEqual([a.status, b.status], [200, 200])
|
||||
assert.equal(a.body.auftrag.bestaetigtAm, b.body.auftrag.bestaetigtAm)
|
||||
assert.equal(sink.sent.length, 2)
|
||||
})
|
||||
|
||||
test('abgerechnete AB ohne frühere Bestätigung: 200, der Status bleibt abgerechnet', async () => {
|
||||
seed({ status: 'abgerechnet' })
|
||||
const res = await post({ bestaetigt: true })
|
||||
assert.deepEqual([res.status, res.body.auftrag.status], [200, 'abgerechnet'])
|
||||
assert.notEqual((await getAuftrag('ab1')).bestaetigtAm, null)
|
||||
})
|
||||
|
||||
test('Mailfehler: die Bestätigung gilt trotzdem (200), der Fehler steht nur im Protokoll', async () => {
|
||||
setMailTransportForTests({ async sendMail() { throw new Error('SMTP 451') } })
|
||||
const log = mock.method(console, 'error', () => {})
|
||||
const res = await post({ bestaetigt: true })
|
||||
assert.equal(res.status, 200)
|
||||
assert.equal((await getAuftrag('ab1')).status, 'bestaetigt')
|
||||
assert.ok(log.mock.callCount() > 0)
|
||||
mock.restoreAll()
|
||||
})
|
||||
|
||||
test('Ratenbegrenzung: höchstens 10 POST je Stunde und IP, danach 429; GET und andere IP sind nicht betroffen', async () => {
|
||||
const kopf = ip('203.0.113.20')
|
||||
for (let i = 1; i <= 10; i += 1) assert.equal((await post({}, TOKEN, kopf)).status, 400, `Anfrage ${i}`)
|
||||
const zuViele = await post({ bestaetigt: true }, TOKEN, kopf)
|
||||
assert.deepEqual([zuViele.status, zuViele.body], [429, ZU_VIELE])
|
||||
assert.equal((await getAuftrag('ab1')).bestaetigtAm, null)
|
||||
assert.equal((await get(TOKEN, '', kopf)).status, 200)
|
||||
assert.equal((await post({ bestaetigt: true }, TOKEN, ip('203.0.113.21'))).status, 200)
|
||||
})
|
||||
Reference in new issue
Block a user