import { test } from 'node:test'; import assert from 'node:assert/strict'; const { rateLimit } = await import('../src/ratelimit.js'); function fakeCtx() { const headers = {}; return { req: { header: () => undefined }, header: (k, v) => { headers[k] = v; }, json: (body, status = 200) => ({ __status: status, body }), _headers: headers, }; } test('rateLimit: blockt nach Überschreiten mit 429 + Retry-After', async () => { const mw = rateLimit({ max: 2, windowMs: 10_000, keyFn: () => 'fix' }); let calls = 0; const run = async () => { const c = fakeCtx(); const r = await mw(c, async () => { calls++; }); return { c, r }; }; assert.equal((await run()).r, undefined); // 1 → durch (next, kein Return) assert.equal((await run()).r, undefined); // 2 → durch const { c, r } = await run(); // 3 → blockiert assert.equal(r.__status, 429); assert.ok(c._headers['Retry-After']); assert.equal(calls, 2); // next nur zweimal aufgerufen }); test('rateLimit: getrennte Schlüssel zählen getrennt', async () => { let key = 'a'; const mw = rateLimit({ max: 1, windowMs: 10_000, keyFn: () => key }); assert.equal((await mw(fakeCtx(), async () => {})), undefined); // a:1 ok assert.equal((await mw(fakeCtx(), async () => {})).__status, 429); // a:2 blockiert key = 'b'; assert.equal((await mw(fakeCtx(), async () => {})), undefined); // b:1 ok }); test('rateLimit: Fenster läuft ab → wieder frei', async () => { const mw = rateLimit({ max: 1, windowMs: 30, keyFn: () => 'win' }); assert.equal((await mw(fakeCtx(), async () => {})), undefined); assert.equal((await mw(fakeCtx(), async () => {})).__status, 429); await new Promise((r) => setTimeout(r, 40)); assert.equal((await mw(fakeCtx(), async () => {})), undefined); // Fenster neu });