import test from 'node:test' import assert from 'node:assert/strict' import { loadModule } from './load.mjs' // API-Client der Hustler-Seite: URLs, Methoden, Header, deutsche Fehlertexte, JWT-Wiederholung, Pult-Prüfungen const { createWorkersApi } = await loadModule('src/lib/workersApi.js') const jwts = [] const getJwt = async (o) => { jwts.push(o?.fresh === true) return o?.fresh ? 'jwt-neu' : 'jwt-alt' } function fakeFetch(replies) { const calls = [] const fn = async (url, init) => { calls.push({ url, init }) const r = replies.shift() if (r instanceof Error) throw r return { ok: r.status >= 200 && r.status < 300, status: r.status, json: async () => { if (r.body === undefined) throw new SyntaxError('kein JSON') return r.body }, } } fn.calls = calls return fn } const okReplies = (n) => Array.from({ length: n }, () => ({ status: 200, body: {} })) test('URLs, Methoden, Header, Body', async () => { const f = fakeFetch(okReplies(12)) const api = createWorkersApi({ getJwt, fetchImpl: f }) await api.state() await api.events('2026-10-17T20:00:00.000+00:00') await api.events(null, 500) await api.trace('kim') await api.pause(true) await api.settings() await api.saveSettings({ warnPct: 70 }) await api.pult() await api.pultAction('p1', 'claim') await api.botleads({ room: 'netz', q: 'Bäcker', page: 2 }) await api.botleads({ id: 'p1' }) await api.prospect('x/y') assert.deepEqual(f.calls.map((c) => [c.init.method || 'GET', c.url]), [ ['GET', '/api/workers/state'], ['GET', '/api/workers/events?after=2026-10-17T20%3A00%3A00.000%2B00%3A00&limit=200'], ['GET', '/api/workers/events?limit=200'], ['GET', '/api/workers/worker/kim/trace?limit=50'], ['POST', '/api/workers/pause'], ['GET', '/api/workers/settings'], ['PUT', '/api/workers/settings'], ['GET', '/api/workers/pult?status=offen'], ['POST', '/api/workers/pult/p1/claim'], ['GET', '/api/workers/botleads?room=netz&q=B%C3%A4cker&page=2'], ['GET', '/api/workers/botleads?id=p1'], ['GET', '/api/workers/prospect/x%2Fy'], ]) assert.equal(f.calls[0].init.headers['X-Appwrite-JWT'], 'jwt-alt') assert.equal(f.calls[4].init.body, JSON.stringify({ paused: true })) assert.equal(f.calls[4].init.headers['Content-Type'], 'application/json') assert.equal(f.calls[6].init.body, JSON.stringify({ warnPct: 70 })) }) test('Fehlertexte (Review Focus 1)', async () => { const run = (reply) => createWorkersApi({ getJwt, fetchImpl: fakeFetch([reply]) }).state().then(() => null, (e) => [e.message, e.status]) assert.deepEqual(await run({ status: 200, body: undefined }), ['Der Hustler-Dienst antwortet nicht richtig.', 200]) // nginx liefert index.html assert.deepEqual(await run({ status: 404, body: undefined }), ['Der Hustler-Dienst antwortet nicht richtig.', 404]) assert.deepEqual(await run({ status: 502, body: undefined }), ['Der Hustler-Dienst ist gerade nicht erreichbar.', 502]) assert.deepEqual(await run(new TypeError('fetch failed')), ['Der Hustler-Dienst ist gerade nicht erreichbar.', 0]) assert.deepEqual(await run({ status: 403, body: {} }), ['Dafür fehlt dir die Berechtigung.', 403]) assert.deepEqual(await run({ status: 400, body: { error: 'Tagesgrenze ungültig.' } }), ['Tagesgrenze ungültig.', 400]) assert.deepEqual(await run({ status: 500, body: {} }), ['Hustler-Fehler 500', 500]) await assert.rejects( createWorkersApi({ getJwt: async () => { throw new Error('x') }, fetchImpl: fakeFetch([]) }).state(), { message: 'Anmeldung abgelaufen. Bitte neu anmelden.', status: 401 }, ) }) test('401 ⇒ einmal mit frischem JWT wiederholen, danach Meldung', async () => { jwts.length = 0 const f = fakeFetch([{ status: 401, body: {} }, { status: 200, body: { ok: 1 } }]) assert.deepEqual(await createWorkersApi({ getJwt, fetchImpl: f }).state(), { ok: 1 }) assert.deepEqual(jwts, [false, true]) assert.equal(f.calls[1].init.headers['X-Appwrite-JWT'], 'jwt-neu') await assert.rejects( createWorkersApi({ getJwt, fetchImpl: fakeFetch([{ status: 401, body: {} }, { status: 401, body: {} }]) }).state(), { message: 'Anmeldung abgelaufen. Bitte neu anmelden.', status: 401 }, ) }) test('pult: ein Status, Komma-Liste oder Array (Übersicht §7); unbekannte Status werden abgewiesen, ohne Anfrage', async () => { const f = fakeFetch(okReplies(5)) const api = createWorkersApi({ getJwt, fetchImpl: f }) await api.pult('offen,in_arbeit') await api.pult(['gesendet', 'verworfen', 'erledigt']) await api.pult(' veraltet ') await api.pult('') await api.pult('gesendet, verworfen , erledigt') // Kommas bleiben unkodiert, Einträge getrimmt, leer = offen assert.deepEqual(f.calls.map((c) => c.url), [ '/api/workers/pult?status=offen,in_arbeit', '/api/workers/pult?status=gesendet,verworfen,erledigt', '/api/workers/pult?status=veraltet', '/api/workers/pult?status=offen', '/api/workers/pult?status=gesendet,verworfen,erledigt', ]) for (const bad of ['offen,../x', 'quatsch', ['offen', 'x y'], 'offen;in_arbeit', 'offen,,in_arbeit']) { await assert.rejects(api.pult(bad), /Unbekannter Pult-Status/) } assert.equal(f.calls.length, 5) // die fünf abgewiesenen Aufrufe haben nichts gesendet }) test('pultAction: nur bekannte Aktionen, Pfad-Tricks abgewiesen', async () => { const f = fakeFetch(okReplies(7)) const api = createWorkersApi({ getJwt, fetchImpl: f }) for (const bad of ['../x', 'delete', '']) { await assert.rejects(api.pultAction('p1', bad), /Unbekannte Aktion/) } assert.equal(f.calls.length, 0) for (const a of ['claim', 'sent', 'discard', 'done', 'reply', 'approve-preview', 'paste']) { await api.pultAction('p1', a, a === 'reply' || a === 'paste' ? { text: 'x' } : undefined) } assert.deepEqual(f.calls.map((c) => c.url.split('/').pop()), ['claim', 'sent', 'discard', 'done', 'reply', 'approve-preview', 'paste']) assert.equal(f.calls[4].init.body, JSON.stringify({ text: 'x' })) // Plattform-Folge-Antwort einfügen (P4-T34) assert.deepEqual([f.calls[6].url, f.calls[6].init.method, f.calls[6].init.body], ['/api/workers/pult/p1/paste', 'POST', JSON.stringify({ text: 'x' })]) }) test('limit wird geklemmt (Ereignisse 1..200, Spur 1..50)', async () => { const f = fakeFetch(okReplies(3)) const api = createWorkersApi({ getJwt, fetchImpl: f }) await api.trace('kim', 999) await api.events(null, 0) await api.events(null, 150) assert.deepEqual(f.calls.map((c) => c.url), [ '/api/workers/worker/kim/trace?limit=50', '/api/workers/events?limit=1', '/api/workers/events?limit=150', ]) })