// test/auftraegeKonstanten.test.js import { test, mock } from 'node:test' import assert from 'node:assert/strict' import * as K from '../server/services/auftraege/konstanten.js' import { AuftragError, sendeFehler } from '../server/services/auftraege/fehler.js' import { InvoiceError } from '../server/services/finance/stripeFinance.js' test('Konstanten stehen wie in Übersicht V2', () => { assert.deepEqual(K.RECHNUNGS_ARTEN, ['frei', 'festbetrag', 'aufwand', 'anzahlung', 'schluss']) assert.deepEqual(K.AB_RECHNUNGS_ARTEN, ['anzahlung', 'schluss']) assert.deepEqual(K.AUFTRAG_ARTEN, ['festpreis', 'aufwand']) assert.deepEqual(K.AUFTRAG_STATUS, ['entwurf', 'versendet', 'bestaetigt', 'abgerechnet', 'storniert', 'ersetzt', 'widerrufen']) assert.deepEqual(K.VEREINBARTE_STATUS, ['versendet', 'bestaetigt', 'abgerechnet']) assert.deepEqual(K.AUFTRAG_WEGE, { muendlich: 'mündlich', telefon: 'telefonisch', email: 'per E-Mail', vor_ort: 'vor Ort', chat: 'im Chat' }) assert.deepEqual(K.ZAHLUNGSPLAN_ARTEN, ['nach_abnahme', 'anzahlung', 'monatlich', 'frei']) assert.deepEqual(K.KUNDENARTEN, ['unternehmen', 'privat']) assert.equal(K.STUNDENSATZ_STANDARD_CENTS, 10000) assert.equal(K.VIERTELSTUNDE, 15) assert.equal(K.TOKEN_GUELTIG_TAGE, 60) assert.match(K.AGB_FASSUNG, /^\d{4}-\d{2}-\d{2}$/) // den Wert legt L7 fest assert.deepEqual(K.LIMITS, { maxPositionen: 20, maxBeschreibung: 200, maxMenge: 999, maxEinzelpreisCents: 10_000_000, maxSummeCents: 99_999_999, maxTitel: 200, maxZahlungsplanText: 500, maxZahlungszielTage: 60, standardZahlungszielTage: 14, standardAnzahlungProzent: 50, maxZeitquellen: 10, maxSchaetzungMinuten: 99_960, maxLeistungszeitraum: 140, }) // withLocks sperrt jedes Listenelement einzeln: je Sperre genau ein zusammengesetzter Schlüssel (verschiedene Kunden und ABs blockieren sich nicht) assert.deepEqual(K.SPERRE_NUMMER, ['auftraege-nummer']) assert.deepEqual(K.sperreRechnung('c1'), ['auftraege-rechnung:c1']) assert.deepEqual(K.sperreAuftrag('ab1'), ['auftraege-ab:ab1']) assert.equal(K.SCHALTER_AUS_MELDUNG, 'Auftragsbestätigungen sind für diesen Kunden noch nicht freigeschaltet.') for (const status of K.VEREINBARTE_STATUS) assert.ok(K.AUFTRAG_STATUS.includes(status), status) for (const art of K.AB_RECHNUNGS_ARTEN) assert.ok(K.RECHNUNGS_ARTEN.includes(art), art) }) const fakeRes = () => ({ statusCode: 200, body: undefined, status(code) { this.statusCode = code return this }, json(body) { this.body = body return this }, }) test('AuftragError trägt Status und Meldung; ein Status außerhalb 400 bis 599 ist ein Programmfehler', () => { const err = new AuftragError(409, 'Arbeitsblatt bereits abgerechnet (WEBKLAR-0009).') assert.ok(err instanceof Error) assert.equal(err.name, 'AuftragError') assert.equal(err.status, 409) assert.equal(err.message, 'Arbeitsblatt bereits abgerechnet (WEBKLAR-0009).') for (const status of [200, 399, 600, 404.5, '404', undefined, null]) { assert.throws(() => new AuftragError(status, 'x'), TypeError, String(status)) } }) test('sendeFehler gibt AuftragError und InvoiceError mit Status und Meldung weiter', () => { const a = fakeRes() sendeFehler(a, 'AB senden', new AuftragError(400, 'Kundenart fehlt.')) assert.deepEqual([a.statusCode, a.body], [400, { error: 'Kundenart fehlt.' }]) const b = fakeRes() sendeFehler(b, 'Rechnung anlegen', new InvoiceError(502, 'Stripe ist gerade nicht erreichbar.')) assert.deepEqual([b.statusCode, b.body], [502, { error: 'Stripe ist gerade nicht erreichbar.' }]) }) test('sendeFehler: alles andere wird 500 „Interner Fehler.“, das Protokoll nennt nie die Meldung', () => { const log = mock.method(console, 'error', () => {}) try { const appwrite = Object.assign(new Error('Appwrite: Key sk_live_geheim abgelehnt'), { status: 400, type: 'user_unauthorized' }) for (const err of [appwrite, 'sk_live_geheim', undefined, null]) { const res = fakeRes() sendeFehler(res, 'Projektkonto laden', err) assert.deepEqual([res.statusCode, res.body], [500, { error: 'Interner Fehler.' }]) } assert.equal(log.mock.calls.length, 4) const zeilen = JSON.stringify(log.mock.calls.map((call) => call.arguments)) assert.ok(!zeilen.includes('geheim'), zeilen) assert.ok(zeilen.includes('Projektkonto laden') && zeilen.includes('user_unauthorized'), zeilen) } finally { log.mock.restore() } })