Files
tickte-system/scripts/workers-harness/scripted-fetch.js
T
ClaudeandClaude Sonnet 5.5 aceb558564 Workers P2: Prüfstand um Ausfall-Szenario erweitert (503, 401-Folge, hängende Verbindung, versteckter Tab)
Endprüfung P2. Die Zustände des Boards bei Ausfall des Dienstes und der Wechsel zwischen sichtbarem und verstecktem Tab
waren nur über Quelltext-Muster geprüft. Der Prüfstand (?fault=none|down|session|hang) schaltet jetzt den echten
API-Client hinter eine Fetch-Weiche (scripted-fetch.js, mit Node-Tests). Szenario F in shot.cjs prüft im Browser:
Hinweis mit „Neu laden“ bei 503 beim Öffnen und Rückkehr, Warnzeile „Verbindung gestört“ im Betrieb, versteckter Tab
(keine Abrufe, keine Bilder, beim Zurückkehren sofort beides), dauerhaftes 401 (höchstens ein frisches JWT, Meldung
„Anmeldung abgelaufen“, Erholung ohne neues Anmelden) und eine Antwort, die nie kommt (Zeitgrenze). Das Handy-Szenario
prüft zusätzlich Auswahl aus der Liste (Karte im Fenster, Fokus) und Fokus nach „Karte schließen“.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 05:41:03 +00:00

43 lines
2.4 KiB
JavaScript

// Fetch-Weiche des Prüfstands: nimmt die Anfragen des echten API-Clients (createWorkersApi, URLs unter /api/workers) entgegen und
// beantwortet sie mit der Skript-API. Damit läuft im Prüfstand dieselbe Kette wie im Betrieb (API-Client, Feed, Board), und die
// Ausfälle lassen sich schalten: control.fault = null (gesund) | 'down' (503 ohne JSON wie eine Fehlerseite des Proxys) |
// 'session' (401) | 'hang' (die Antwort kommt nie, wie auf einer toten Verbindung). Kein Netz, kein Appwrite.
// control.calls sammelt jede Anfrage ({ method, path, jwt }), damit der Prüfstand zählen kann.
const reply = (status, body) => ({
ok: status >= 200 && status < 300,
status,
json: async () => {
if (body === undefined) throw new SyntaxError('kein JSON')
return body
},
})
export function createScriptedFetch(api, control) {
return async function scriptedFetch(url, init = {}) {
const method = init.method || 'GET'
const parsed = new URL(url, 'http://harness.invalid')
const path = parsed.pathname.replace(/^\/api\/workers/, '')
control.calls.push({ method, path, jwt: init.headers ? init.headers['X-Appwrite-JWT'] : undefined })
if (control.fault === 'hang') return new Promise(() => {})
if (control.fault === 'down') return reply(503, undefined)
if (control.fault === 'session') return reply(401, {})
try {
const body = init.body === undefined ? undefined : JSON.parse(init.body)
if (method === 'GET' && path === '/state') return reply(200, await api.state())
if (method === 'GET' && path === '/events') {
const limit = parsed.searchParams.get('limit')
return reply(200, await api.events(parsed.searchParams.get('after'), limit === null ? undefined : Number(limit)))
}
const trace = /^\/worker\/([^/]+)\/trace$/.exec(path)
if (method === 'GET' && trace) return reply(200, await api.trace(decodeURIComponent(trace[1])))
if (method === 'POST' && path === '/pause') return reply(200, await api.pause(body.paused))
if (method === 'GET' && path === '/settings') return reply(200, await api.settings())
if (method === 'PUT' && path === '/settings') return reply(200, await api.saveSettings(body))
return reply(404, { error: 'Unbekannter Pfad im Prüfstand.' })
} catch (err) {
return reply(err && err.status ? err.status : 500, { error: err && err.message ? err.message : 'Fehler im Prüfstand.' })
}
}
}