Workers P3-Endprüfung W1: createJWT sperrt nur bei echter Ablehnung durch Appwrite
Nur ein HTTP-Code ab 400 von Appwrite startet die 2-Minuten-Sperre. Übertragungsfehler (TypeError, SDK-Fehler mit code 0, Fehler ohne Code), auch wenn sie erst nach der Zeitgrenze eintreffen, geben die Dienst-Meldung (status 0) und sperren nichts: nach dem Aufwachen aus dem Ruhezustand erholt sich das Hochhaus mit dem nächsten Takt. Eine späte echte Ablehnung (etwa 429) sperrt weiter. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e3c7a22ae2
commit
2b2b45a0c7
2 files changed
+144
-11
No files matched your search
@@ -78,7 +78,7 @@ test('Fehlertexte (Review Focus 1)', async () => {
|
||||
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(),
|
||||
createWorkersApi({ getJwt: async () => { throw Object.assign(new Error('x'), { code: 401 }) }, fetchImpl: fakeFetch([]) }).state(),
|
||||
{ message: 'Anmeldung abgelaufen. Bitte neu anmelden.', status: 401 },
|
||||
)
|
||||
})
|
||||
@@ -248,7 +248,7 @@ test('401-Folge: scheitert schon die Erzeugung des frischen JWT, beginnt dieselb
|
||||
const mints = []
|
||||
const brokenFresh = async (o) => {
|
||||
mints.push(o?.fresh === true)
|
||||
if (o?.fresh) throw new Error('429 Too Many Requests')
|
||||
if (o?.fresh) throw Object.assign(new Error('429 Too Many Requests'), { code: 429 })
|
||||
return 'jwt-alt'
|
||||
}
|
||||
const api = createWorkersApi({ getJwt: brokenFresh, fetchImpl: fakeFetch(Array.from({ length: 6 }, () => ({ status: 401, body: {} }))), now: () => t, authCooldownMs: 60000 })
|
||||
@@ -393,7 +393,7 @@ test('resetAuth (Abmelden): hebt die Sperre auf, der nächste Abruf holt sofort
|
||||
const failing = { on: true }
|
||||
const getJwtFlaky = async (o) => {
|
||||
asked.push(o?.fresh === true)
|
||||
if (failing.on) throw new Error('429')
|
||||
if (failing.on) throw Object.assign(new Error('429'), { code: 429 })
|
||||
return 'jwt-neu'
|
||||
}
|
||||
const f = fakeFetch(okReplies(2))
|
||||
@@ -427,3 +427,128 @@ test('ein hängendes createJWT (Zeitgrenze) ist kein Scheitern und löst keine S
|
||||
await assert.rejects(api.state(), { message: DOWN, status: 0 })
|
||||
assert.equal(asked, 2) // die Zeitgrenze (Netz/Ruhezustand) sperrt nichts: ein nächster Abruf versucht es wieder
|
||||
})
|
||||
|
||||
// ---- Endprüfung P3 (W1): gesperrt wird nur, wenn Appwrite createJWT mit einem HTTP-Code ab 400 abgelehnt hat; Übertragungsfehler nicht ----
|
||||
|
||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
||||
|
||||
test('createJWT: Übertragungsfehler (TypeError, SDK-Fehler mit code 0, Fehler ohne Code) geben DOWN/0 und sperren nichts', async () => {
|
||||
const transport = [
|
||||
new TypeError('Failed to fetch'),
|
||||
Object.assign(new Error('Load failed'), { code: 0 }), // so meldet das Appwrite-SDK einen Netzfehler (client.ts: AppwriteException mit code 0)
|
||||
new Error('irgendwas'),
|
||||
Object.assign(new Error('Code ist keine Zahl'), { code: '429' }),
|
||||
Object.assign(new Error('Code nicht ganzzahlig'), { code: 429.5 }),
|
||||
Object.assign(new Error('Code unter 400'), { code: 399 }),
|
||||
null,
|
||||
undefined,
|
||||
]
|
||||
for (const failure of transport) {
|
||||
const calls = []
|
||||
const getJwtFlaky = async () => { calls.push(1); throw failure }
|
||||
const f = fakeFetch(okReplies(1))
|
||||
const api = createWorkersApi({ getJwt: getJwtFlaky, fetchImpl: f, now: () => 0 })
|
||||
await assert.rejects(api.state(), { message: DOWN, status: 0 }, String(failure))
|
||||
await assert.rejects(api.state(), { message: DOWN, status: 0 }, String(failure)) // zweiter Abruf: nicht gesperrt, getJwt wird wieder gerufen
|
||||
assert.equal(calls.length, 2, String(failure))
|
||||
assert.equal(f.calls.length, 0)
|
||||
}
|
||||
})
|
||||
|
||||
test('createJWT: jeder HTTP-Code ab 400 sperrt (401, 403, 429, 500, 503), nur Appwrites echte Ablehnung zählt', async () => {
|
||||
for (const code of [400, 401, 403, 429, 500, 503]) {
|
||||
let t = 0
|
||||
let creates = 0
|
||||
const getJwtRefused = async () => { creates += 1; throw Object.assign(new Error('abgelehnt'), { code }) }
|
||||
const api = createWorkersApi({ getJwt: getJwtRefused, fetchImpl: fakeFetch([]), now: () => t, authCooldownMs: 60000 })
|
||||
await assert.rejects(api.state(), { message: SESSION, status: 401 }, `code ${code}`)
|
||||
t += 10000
|
||||
await assert.rejects(api.events(null), { message: SESSION, status: 401 }, `code ${code}`)
|
||||
assert.equal(creates, 1, `code ${code}`) // gesperrt: kein zweiter createJWT-Aufruf
|
||||
}
|
||||
})
|
||||
|
||||
test('createJWT: offline (TypeError) gibt DOWN/0, ein Abruf 10 s später mit zurückgekehrtem Netz klappt sofort', async () => {
|
||||
let t = 0
|
||||
let online = false
|
||||
let creates = 0
|
||||
const getJwtNet = async () => {
|
||||
creates += 1
|
||||
if (!online) throw new TypeError('Failed to fetch')
|
||||
return 'jwt-1'
|
||||
}
|
||||
const f = fakeFetch(okReplies(2))
|
||||
const api = createWorkersApi({ getJwt: getJwtNet, fetchImpl: f, now: () => t }) // Standard-Sperre 2 min: sie darf nicht greifen
|
||||
await assert.rejects(api.state(), { message: DOWN, status: 0 })
|
||||
assert.equal(creates, 1)
|
||||
assert.equal(f.calls.length, 0)
|
||||
t += 10000
|
||||
online = true
|
||||
assert.deepEqual(await api.state(), {})
|
||||
assert.equal(creates, 2)
|
||||
assert.equal(f.calls.length, 1)
|
||||
assert.equal(f.calls[0].init.headers['X-Appwrite-JWT'], 'jwt-1')
|
||||
})
|
||||
|
||||
test('createJWT: späte Ablehnung nach der Zeitgrenze (TypeError und code 0) sperrt nicht', async () => {
|
||||
for (const failure of [new TypeError('Failed to fetch'), Object.assign(new Error('Load failed'), { code: 0 })]) {
|
||||
let t = 0
|
||||
let creates = 0
|
||||
let online = false
|
||||
const getJwtLate = async () => {
|
||||
creates += 1
|
||||
if (online) return 'jwt-1'
|
||||
await sleep(25) // trifft erst nach der Zeitgrenze (10 ms) ein: der Aufrufer hat DOWN/0 schon bekommen
|
||||
throw failure
|
||||
}
|
||||
const f = fakeFetch(okReplies(1))
|
||||
const api = createWorkersApi({ getJwt: getJwtLate, fetchImpl: f, now: () => t, timeoutMs: 10 })
|
||||
await assert.rejects(api.state(), { message: DOWN, status: 0 })
|
||||
await sleep(40) // die späte Ablehnung ist inzwischen angekommen
|
||||
t += 5000
|
||||
online = true
|
||||
assert.deepEqual(await api.state(), {}) // nicht gesperrt: der nächste Takt holt ein JWT
|
||||
assert.equal(creates, 2)
|
||||
assert.equal(f.calls.length, 1)
|
||||
}
|
||||
})
|
||||
|
||||
test('createJWT: späte Ablehnung nach der Zeitgrenze mit code 429 sperrt weiter', async () => {
|
||||
let t = 0
|
||||
let creates = 0
|
||||
const getJwtLate429 = async () => {
|
||||
creates += 1
|
||||
await sleep(25)
|
||||
throw Object.assign(new Error('Rate limit'), { code: 429 })
|
||||
}
|
||||
const f = fakeFetch(okReplies(2))
|
||||
const api = createWorkersApi({ getJwt: getJwtLate429, fetchImpl: f, now: () => t, timeoutMs: 10, authCooldownMs: 60000 })
|
||||
await assert.rejects(api.state(), { message: DOWN, status: 0 }) // dem Aufrufer wurde schon DOWN/0 gemeldet
|
||||
await sleep(40)
|
||||
t += 5000
|
||||
await assert.rejects(api.state(), { message: SESSION, status: 401 }) // die echte Ablehnung zählt gegen die Begrenzung: Sperre
|
||||
assert.equal(creates, 1)
|
||||
assert.equal(f.calls.length, 0)
|
||||
t += 60000
|
||||
await assert.rejects(api.state(), { message: DOWN, status: 0 }) // nach der Sperre genau ein neuer Versuch
|
||||
assert.equal(creates, 2)
|
||||
})
|
||||
|
||||
test('createJWT: Übertragungsfehler beim frischen JWT nach einer 401 sperrt nicht (nur die 401-Folge selbst)', async () => {
|
||||
let t = 0
|
||||
const mints = []
|
||||
let online = true
|
||||
const getJwtFresh = async (o) => {
|
||||
mints.push(o?.fresh === true)
|
||||
if (o?.fresh && !online) throw new TypeError('Failed to fetch')
|
||||
return o?.fresh ? 'jwt-neu' : 'jwt-alt'
|
||||
}
|
||||
const f = fakeFetch([{ status: 401, body: {} }, { status: 401, body: {} }, { status: 200, body: { ok: 1 } }])
|
||||
const api = createWorkersApi({ getJwt: getJwtFresh, fetchImpl: f, now: () => t })
|
||||
online = false
|
||||
await assert.rejects(api.state(), { message: DOWN, status: 0 }) // 401, dann Netz weg beim frischen JWT
|
||||
t += 5000
|
||||
online = true
|
||||
assert.deepEqual(await api.state(), { ok: 1 }) // 401 mit dem gespeicherten JWT, die Wiederholung mit frischem JWT darf wieder erfolgen und klappt
|
||||
assert.deepEqual(mints, [false, true, false, true]) // keine Sperre: weder für frische JWTs noch für createJWT überhaupt
|
||||
})
|
||||
Reference in new issue
Block a user