Files
Webklar-Kundenbereich/test/auftraegeRoutesAb.test.js
T

210 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test, after, beforeEach, mock } from 'node:test'
import assert from 'node:assert/strict'
import { starteUmgebung, KUNDE_ID, PROJEKT_ID, TICKET_ID, FREMD, VON, MELDUNG, festpreisEingabe, aufwandEingabe, kundeDoc, baueDeps } from './helpers/auftraegeAb.js'
const umg = await starteUmgebung()
const { aw, config } = umg
const { startTestApp, send } = await import('./support/testApp.js')
const { default: router } = await import('../server/routes/admin/auftraege.js')
const { getAuftrag } = await import('../server/services/auftraege/auftragStore.js')
const { SCHALTER_AUS_MELDUNG } = await import('../server/services/auftraege/konstanten.js')
const { legeEntwurfAn, sendeAuftrag, setzeDepsFuerTests } = await import('../server/services/auftraege/auftraege.js')
const app = await startTestApp({ '/api/admin/finanzen/auftraege': router })
after(async () => {
await app.close()
await umg.schliesse()
})
let kontext // Attrappen für PDF, Token und Mail des aktuellen Tests
beforeEach(async () => {
await umg.zuruecksetzen()
kontext = baueDeps()
setzeDepsFuerTests(kontext.deps)
})
const BASIS = '/api/admin/finanzen/auftraege'
const api = (methode, pfad, body, jwt = 'jwt-admin') => send(methode, BASIS + pfad, { headers: { Authorization: `Bearer ${jwt}` }, body })
const entwurfAnlegen = (patch) => legeEntwurfAn(festpreisEingabe(patch), { von: VON })
const ZUGRIFF = [
['GET', `/?kunde=${KUNDE_ID}`], ['POST', '/'], ['GET', '/a_1'], ['PATCH', '/a_1'], ['DELETE', '/a_1'], ['GET', '/a_1/pdf'],
['POST', '/a_1/senden'], ['POST', '/a_1/erinnern'], ['POST', '/a_1/neue-fassung'], ['POST', '/a_1/stornieren'], ['POST', '/a_1/projekt'],
]
test('Zugriff: ohne JWT 401, ohne Admin-Label 403 – bei jeder Route', async () => {
for (const [methode, pfad] of ZUGRIFF) {
const body = ['GET', 'DELETE'].includes(methode) ? undefined : {}
assert.equal((await send(methode, BASIS + pfad, { body })).status, 401, `${methode} ${pfad} ohne JWT`)
for (const jwt of ['jwt-staff', 'jwt-kunde']) assert.equal((await api(methode, pfad, body, jwt)).status, 403, `${methode} ${pfad} mit ${jwt}`)
}
})
test('Schalter: ist die Funktion für den Kunden aus, antworten Liste, Anlegen, Lesen, Ändern, Löschen und PDF mit 404 und der festen Meldung', async () => {
const a = await entwurfAnlegen() // angelegt, solange der Schalter an ist
config.auftraege.mode = 'off'
const routen = [['GET', `/?kunde=${KUNDE_ID}`], ['POST', '/', festpreisEingabe()], ['GET', `/${a.id}`], ['PATCH', `/${a.id}`, { titel: 'x' }], ['DELETE', `/${a.id}`], ['GET', `/${a.id}/pdf`]]
for (const [methode, pfad, body] of routen) {
const r = await api(methode, pfad, body)
assert.deepEqual([r.status, r.body], [404, { error: SCHALTER_AUS_MELDUNG }], `${methode} ${pfad}`)
}
assert.equal((await getAuftrag(a.id)).titel, 'Website Beispiel') // nichts wurde geändert oder gelöscht
config.auftraege.mode = 'pilot'
config.auftraege.pilotEmails = ['anna@example.invalid'] // E-Mail des Kunden
assert.equal((await api('GET', `/${a.id}`)).status, 200)
config.auftraege.pilotEmails = ['jemand@example.invalid']
assert.equal((await api('GET', `/${a.id}`)).status, 404)
})
test('GET Liste: Aufträge des Kunden, neueste zuerst, ohne interne Felder; 400 ohne Kunde, 404 bei unbekanntem Kunden', async () => {
const a = await entwurfAnlegen()
const b = await legeEntwurfAn(aufwandEingabe(), { von: VON })
const r = await api('GET', `/?kunde=${KUNDE_ID}`)
assert.equal(r.status, 200)
assert.deepEqual(r.body.auftraege.map((x) => x.id), [b.id, a.id])
for (const x of r.body.auftraege) {
for (const feld of ['tokenHash', 'pdfDatei', 'bestaetigtPdfSha256']) assert.equal(feld in x, false, feld)
assert.equal(typeof x.pdfVorhanden, 'boolean')
}
const ohneKunde = await api('GET', '/')
assert.deepEqual([ohneKunde.status, ohneKunde.body], [400, { error: 'Kunde fehlt.' }])
assert.equal((await api('GET', '/?kunde=c_gibtsnicht')).status, 404)
assert.equal((await api('GET', '/?kunde=../x')).status, 404)
})
test('POST: legt einen Entwurf an (200, AuftragTeam mit Texten); Fehler als { error } mit festen Meldungen', async () => {
const r = await api('POST', '/', festpreisEingabe())
assert.equal(r.status, 200)
const a = r.body.auftrag
assert.deepEqual([a.status, a.titel, a.summeCents, a.erstelltVon], ['entwurf', 'Website Beispiel', 80000, 'kenso@webklar.com'])
assert.deepEqual([typeof a.zahlungsplanText, a.pdfVorhanden, 'tokenHash' in a], ['string', false, false])
const ohnePositionen = await api('POST', '/', festpreisEingabe({ positionen: [] }))
assert.deepEqual([ohnePositionen.status, ohnePositionen.body], [400, { error: MELDUNG.positionen }])
const ohneKunde = await api('POST', '/', festpreisEingabe({ kundenId: undefined }))
assert.deepEqual([ohneKunde.status, ohneKunde.body], [400, { error: 'Kunde: Bitte einen Kunden angeben.' }])
assert.equal((await api('POST', '/', festpreisEingabe({ kundenId: 'c_gibtsnicht' }))).status, 404)
})
test('GET, PATCH, DELETE: Entwurf lesen, ändern, löschen; gesendete AB ist unveränderlich (409); ungültige Kennungen 404', async () => {
const a = await entwurfAnlegen()
const gelesen = await api('GET', `/${a.id}`)
assert.deepEqual([gelesen.status, gelesen.body.auftrag.id], [200, a.id])
const geaendert = await api('PATCH', `/${a.id}`, { titel: 'Neu' })
assert.deepEqual([geaendert.status, geaendert.body.auftrag.titel], [200, 'Neu'])
const falsch = await api('PATCH', `/${a.id}`, { zahlungszielTage: 99 })
assert.deepEqual([falsch.status, falsch.body], [400, { error: 'Zahlungsziel: Das Zahlungsziel muss eine ganze Zahl von 0 bis 60 Tagen sein.' }])
const geloescht = await api('DELETE', `/${a.id}`)
assert.deepEqual([geloescht.status, geloescht.body], [200, { ok: true }])
assert.deepEqual((await api('GET', `/${a.id}`)).body, { error: MELDUNG.auftragFehlt })
for (const kennung of ['a%20b', 'x'.repeat(40), '..%2F..']) assert.equal((await api('GET', `/${kennung}`)).status, 404, kennung)
const b = await entwurfAnlegen()
await sendeAuftrag(b.id, { von: VON })
const patch = await api('PATCH', `/${b.id}`, { titel: 'Nachträglich' })
assert.deepEqual([patch.status, patch.body], [409, { error: MELDUNG.nurEntwurfAendern }])
const loeschen = await api('DELETE', `/${b.id}`)
assert.deepEqual([loeschen.status, loeschen.body], [409, { error: MELDUNG.nurEntwurfLoeschen }])
})
test('GET pdf: Entwurf als Vorschau, gesendete AB aus der gespeicherten Datei, mit Kopfzeilen', async () => {
const entwurf = await entwurfAnlegen()
const vorschau = await api('GET', `/${entwurf.id}/pdf`)
assert.equal(vorschau.status, 200)
assert.equal(vorschau.body, '%PDF-1.4 Entwurf')
assert.match(vorschau.headers.get('content-type'), /^application\/pdf/)
assert.equal(vorschau.headers.get('content-disposition'), 'inline; filename="Auftragsbestaetigung-Entwurf.pdf"')
assert.equal(vorschau.headers.get('cache-control'), 'no-store')
const { auftrag } = await sendeAuftrag(entwurf.id, { von: VON }) // Attrappen aus setzeDepsFuerTests
const gespeichert = await api('GET', `/${entwurf.id}/pdf`)
assert.equal(gespeichert.body, `%PDF-1.4 ${auftrag.nummer}`)
assert.equal(gespeichert.headers.get('content-disposition'), `inline; filename="${auftrag.nummer}.pdf"`)
assert.equal((await api('GET', '/a_gibtsnicht/pdf')).status, 404)
})
test('Unerwartete Fehler: 500 mit fester Meldung, nichts von Appwrite gelangt an die Seite', async () => {
mock.method(console, 'error', () => {})
const a = await entwurfAnlegen()
aw.failNext({ collection: config.collections.auftraege, method: 'GET', status: 500, message: 'GEHEIM-Appwrite-Detail' })
const r = await api('GET', `/${a.id}`)
assert.deepEqual([r.status, r.body], [500, { error: 'Interner Fehler.' }])
})
test('POST senden: 200 ohne Token und Dateipfad; Kundendaten-, PDF- und Statusfehler mit festen Meldungen; Mail-Fehler als mailFehler: true', async () => {
mock.method(console, 'error', () => {})
const a = await entwurfAnlegen()
aw.seed(config.collections.customers, kundeDoc({ email: '' }))
let r = await api('POST', `/${a.id}/senden`)
assert.deepEqual([r.status, r.body], [400, { error: MELDUNG.keineMail }])
aw.seed(config.collections.customers, kundeDoc({ street: '' }))
r = await api('POST', `/${a.id}/senden`)
assert.deepEqual([r.status, r.body], [409, { error: MELDUNG.adresse }])
aw.seed(config.collections.customers, kundeDoc())
kontext = baueDeps({ pdfFehler: new Error('pdfkit') })
setzeDepsFuerTests(kontext.deps)
r = await api('POST', `/${a.id}/senden`)
assert.deepEqual([r.status, r.body], [500, { error: MELDUNG.pdf }])
kontext = baueDeps({ mailFehler: new Error('smtp') })
setzeDepsFuerTests(kontext.deps)
r = await api('POST', `/${a.id}/senden`)
assert.equal(r.status, 200)
assert.equal(r.body.mailFehler, true)
assert.deepEqual([r.body.auftrag.status, r.body.auftrag.mailStatus, r.body.auftrag.pdfVorhanden], ['versendet', 'fehler', true])
assert.match(r.body.auftrag.nummer, /^AB-\d{4}-0001$/)
for (const feld of ['tokenHash', 'pdfDatei', 'bestaetigtPdfSha256']) assert.equal(feld in r.body.auftrag, false, feld)
r = await api('POST', `/${a.id}/senden`)
assert.deepEqual([r.status, r.body], [409, { error: MELDUNG.nurEntwurfSenden }])
})
test('POST erinnern, neue-fassung, stornieren: Antworten, Folgezustände und 409 bei falschem Status', async () => {
const a = await entwurfAnlegen()
let r = await api('POST', `/${a.id}/erinnern`)
assert.deepEqual([r.status, r.body], [409, { error: MELDUNG.keineErinnerung }])
r = await api('POST', `/${a.id}/senden`)
assert.deepEqual([r.status, r.body.mailFehler, r.body.auftrag.status], [200, false, 'versendet'])
r = await api('POST', `/${a.id}/erinnern`)
assert.deepEqual([r.status, r.body.mailFehler, r.body.auftrag.status], [200, false, 'versendet'])
assert.equal(kontext.calls.mail.at(-1).erinnerung, true)
r = await api('POST', `/${a.id}/neue-fassung`)
assert.deepEqual([r.status, r.body.auftrag.status, r.body.auftrag.ersetztId], [200, 'entwurf', a.id])
r = await api('POST', `/${a.id}/neue-fassung`)
assert.deepEqual([r.status, r.body], [409, { error: MELDUNG.schonEntwurf }])
r = await api('POST', `/${a.id}/stornieren`)
assert.deepEqual([r.status, r.body.auftrag.status], [200, 'storniert'])
r = await api('POST', `/${a.id}/stornieren`)
assert.deepEqual([r.status, r.body], [409, { error: MELDUNG.keinStorno }])
})
test('POST projekt: ordnet einer AB ohne Projekt das Projekt zu (200, AuftragTeam); 400 ohne oder mit fremdem Projekt, 404 bei unbekannter AB, 409 bei vorhandenem Projekt oder falschem Status', async () => {
const a = await entwurfAnlegen({ projektId: '', titel: 'Beratung ohne Projekt' })
await api('POST', `/${a.id}/senden`)
let r = await api('POST', `/${a.id}/projekt`, {})
assert.deepEqual([r.status, r.body], [400, { error: MELDUNG.projektFehlt }])
r = await api('POST', `/${a.id}/projekt`)
assert.deepEqual([r.status, r.body], [400, { error: MELDUNG.projektFehlt }])
r = await api('POST', `/${a.id}/projekt`, { projektId: FREMD.projekt })
assert.deepEqual([r.status, r.body], [400, { error: MELDUNG.projektFremd }])
r = await api('POST', '/a_gibtsnicht/projekt', { projektId: PROJEKT_ID })
assert.deepEqual([r.status, r.body], [404, { error: MELDUNG.auftragFehlt }])
assert.equal((await getAuftrag(a.id)).projektId, '') // die abgelehnten Aufrufe haben nichts geändert
r = await api('POST', `/${a.id}/projekt`, { projektId: PROJEKT_ID })
assert.equal(r.status, 200)
assert.deepEqual([r.body.auftrag.projektId, r.body.auftrag.zeitquelle, r.body.auftrag.status], [PROJEKT_ID, [TICKET_ID], 'versendet'])
for (const feld of ['tokenHash', 'pdfDatei', 'bestaetigtPdfSha256']) assert.equal(feld in r.body.auftrag, false, feld)
r = await api('POST', `/${a.id}/projekt`, { projektId: PROJEKT_ID })
assert.deepEqual([r.status, r.body], [409, { error: MELDUNG.schonProjekt }])
const b = await entwurfAnlegen({ projektId: '', titel: 'Zweite Beratung ohne Projekt' })
await api('POST', `/${b.id}/senden`)
await api('POST', `/${b.id}/stornieren`)
r = await api('POST', `/${b.id}/projekt`, { projektId: PROJEKT_ID })
assert.deepEqual([r.status, r.body], [409, { error: MELDUNG.keinProjekt }])
})
test('Schalter aus: auch senden, erinnern, neue-fassung, stornieren und projekt antworten 404 mit der festen Meldung', async () => {
const a = await entwurfAnlegen({ projektId: '', titel: 'Beratung ohne Projekt' })
config.auftraege.mode = 'off'
for (const aktion of ['senden', 'erinnern', 'neue-fassung', 'stornieren', 'projekt']) {
const r = await api('POST', `/${a.id}/${aktion}`, aktion === 'projekt' ? { projektId: PROJEKT_ID } : undefined)
assert.deepEqual([r.status, r.body], [404, { error: SCHALTER_AUS_MELDUNG }], aktion)
}
const danach = await getAuftrag(a.id)
assert.deepEqual([danach.status, danach.projektId], ['entwurf', ''])
assert.equal(kontext.calls.pdf.length + kontext.calls.mail.length, 0)
})