87 lines
4.5 KiB
JavaScript
87 lines
4.5 KiB
JavaScript
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { loadModule } from './load.mjs'
|
|
|
|
const { createAuftraegeEndpoints, abgerechnetPfad } = await loadModule('src/lib/auftraegeEndpoints.js')
|
|
const { createFinanceRequest } = await loadModule('src/lib/financeRequest.js')
|
|
|
|
const BASE = 'https://portal.example.invalid/api/admin/finanzen'
|
|
|
|
// Echte Anfrage-Schicht mit Fetch-Attrappe: geprüft wird, was das Portal erreicht (URL, Methode, Kopf, JSON-Körper)
|
|
function setup(answer = () => ({ ok: true, status: 200, json: async () => ({ ok: true }) })) {
|
|
const calls = []
|
|
const request = createFinanceRequest({
|
|
baseUrl: BASE,
|
|
createJWT: async () => ({ jwt: 'jwt-1' }),
|
|
fetchImpl: async (url, init) => {
|
|
calls.push({ url, method: init.method, body: init.body, auth: init.headers.Authorization })
|
|
return answer()
|
|
},
|
|
})
|
|
return { api: createAuftraegeEndpoints(request), calls }
|
|
}
|
|
|
|
// [Funktion, Argumente, Methode, Pfad ab BASE, Körper]
|
|
const FAELLE = [
|
|
['getProjektkonten', ['k1'], 'GET', '/projektkonten?kunde=k1', undefined],
|
|
['getProjektkonto', ['p1'], 'GET', '/projektkonten/p1', undefined],
|
|
['previewInvoice', [{ customerId: 'k1', art: 'aufwand', projektId: 'p1' }], 'POST', '/rechnungen/vorschau', { customerId: 'k1', art: 'aufwand', projektId: 'p1' }],
|
|
['listAuftraege', ['k1'], 'GET', '/auftraege?kunde=k1', undefined],
|
|
['getAuftrag', ['a1'], 'GET', '/auftraege/a1', undefined],
|
|
['createAuftrag', [{ kundenId: 'k1', titel: 'Website' }], 'POST', '/auftraege', { kundenId: 'k1', titel: 'Website' }],
|
|
['patchAuftrag', ['a1', { titel: 'Neu' }], 'PATCH', '/auftraege/a1', { titel: 'Neu' }],
|
|
['deleteAuftrag', ['a1'], 'DELETE', '/auftraege/a1', undefined],
|
|
['sendAuftrag', ['a1'], 'POST', '/auftraege/a1/senden', undefined],
|
|
['erinnereAuftrag', ['a1'], 'POST', '/auftraege/a1/erinnern', undefined],
|
|
['neueFassungAuftrag', ['a1'], 'POST', '/auftraege/a1/neue-fassung', undefined],
|
|
['storniereAuftrag', ['a1'], 'POST', '/auftraege/a1/stornieren', undefined],
|
|
['ordneAuftragProjektZu', ['a1', 'p1'], 'POST', '/auftraege/a1/projekt', { projektId: 'p1' }],
|
|
]
|
|
|
|
test('alle 14 Funktionen sind da (V8 samt „Projekt zuordnen“ aus V5)', () => {
|
|
const { api } = setup()
|
|
assert.deepEqual(Object.keys(api).sort(), [
|
|
'createAuftrag', 'deleteAuftrag', 'erinnereAuftrag', 'getAuftrag', 'getAuftragPdf', 'getProjektkonten', 'getProjektkonto',
|
|
'listAuftraege', 'neueFassungAuftrag', 'ordneAuftragProjektZu', 'patchAuftrag', 'previewInvoice', 'sendAuftrag', 'storniereAuftrag',
|
|
])
|
|
})
|
|
|
|
test('Pfad, Methode, Kopf und Körper jeder Funktion (die Vorschau trägt keinen Schlüssel dazu)', async () => {
|
|
for (const [name, args, method, path, body] of FAELLE) {
|
|
const { api, calls } = setup()
|
|
await api[name](...args)
|
|
assert.equal(calls.length, 1, name)
|
|
assert.equal(calls[0].url, `${BASE}${path}`, name)
|
|
assert.equal(calls[0].method, method, name)
|
|
assert.equal(calls[0].auth, 'Bearer jwt-1', name)
|
|
assert.equal(calls[0].body, body === undefined ? undefined : JSON.stringify(body), name)
|
|
}
|
|
})
|
|
|
|
test('Kennungen werden URL-kodiert, auch im Pfad der abgerechneten Arbeitsblätter', async () => {
|
|
const { api, calls } = setup()
|
|
await api.getProjektkonten('k 1&x=2')
|
|
await api.getAuftrag('a/b?c')
|
|
await api.storniereAuftrag('a#1')
|
|
await api.ordneAuftragProjektZu('a b', 'p/1')
|
|
assert.deepEqual(calls.map((c) => c.url.slice(BASE.length)), [
|
|
'/projektkonten?kunde=k%201%26x%3D2',
|
|
'/auftraege/a%2Fb%3Fc',
|
|
'/auftraege/a%231/stornieren',
|
|
'/auftraege/a%20b/projekt',
|
|
])
|
|
assert.equal(calls[3].body, JSON.stringify({ projektId: 'p/1' })) // die Projektkennung steht im Körper, nicht im Pfad
|
|
assert.equal(abgerechnetPfad('100131'), '/arbeitsblaetter/abgerechnet?ticketId=100131')
|
|
assert.equal(abgerechnetPfad('t 1/2'), '/arbeitsblaetter/abgerechnet?ticketId=t%201%2F2')
|
|
})
|
|
|
|
test('getAuftragPdf liefert das PDF als Blob; eine Fehlerantwort kommt als Meldung des Portals', async () => {
|
|
const datei = new Blob(['%PDF-1.7'], { type: 'application/pdf' })
|
|
const ok = setup(() => ({ ok: true, status: 200, json: async () => assert.fail('kein JSON lesen'), blob: async () => datei }))
|
|
assert.equal(await ok.api.getAuftragPdf('a1'), datei)
|
|
assert.equal(ok.calls[0].url, `${BASE}/auftraege/a1/pdf`)
|
|
assert.equal(ok.calls[0].method, 'GET')
|
|
const fehler = setup(() => ({ ok: false, status: 404, json: async () => ({ error: 'PDF nicht gefunden.' }), blob: async () => assert.fail('kein Blob lesen') }))
|
|
await assert.rejects(fehler.api.getAuftragPdf('a1'), (e) => e.message === 'PDF nicht gefunden.' && e.status === 404)
|
|
})
|