Files
timmy-talking-turd/tests/rate-limit.test.js
T
timmy ba9afe0a1d feat(ingress): decide photo validity from bytes, budget, and header before any provider work (#16)
The declared data-URL MIME was a claim the boundary believed. Every image decision now comes from
the bytes: the container is sniffed, walked to its own end marker, refused when metadata segments
remain, and refused above a 4096px edge or 16MP. The request path adds a per-caller sliding-window
budget (429 + Retry-After) and a body deadline (408), so a flood, a stalled body, or a decode bomb
fails before a provider call.

Log hygiene is part of the contract: refusal copy is fixed prose, the bucket key is a salted hash
kept in memory only, and the acceptance test asserts no image bytes reach the process log.
2026-10-20 08:21:20 +00:00

92 lines
3.5 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { createRateLimiter } from '../src/rate-limit.js';
// A clock the test owns, so no test sleeps and every window boundary is exact.
function manualClock(start = 1_700_000_000_000) {
let current = start;
return { now: () => current, advance: ms => { current += ms; } };
}
test('allows requests up to the limit and refuses the next one', () => {
const clock = manualClock();
const limiter = createRateLimiter({ limit: 3, windowMs: 60_000, now: clock.now });
for (let attempt = 1; attempt <= 3; attempt += 1) {
const verdict = limiter.take('device-a');
assert.equal(verdict.allowed, true, `attempt ${attempt} must be allowed`);
assert.equal(verdict.remaining, 3 - attempt);
}
const refused = limiter.take('device-a');
assert.equal(refused.allowed, false);
assert.equal(refused.remaining, 0);
assert.equal(refused.retryAfterMs, 60_000);
});
test('counts each caller on its own budget', () => {
const clock = manualClock();
const limiter = createRateLimiter({ limit: 1, windowMs: 60_000, now: clock.now });
assert.equal(limiter.take('device-a').allowed, true);
assert.equal(limiter.take('device-a').allowed, false);
assert.equal(limiter.take('device-b').allowed, true);
});
test('lets a caller back in once its window has passed', () => {
const clock = manualClock();
const limiter = createRateLimiter({ limit: 1, windowMs: 60_000, now: clock.now });
assert.equal(limiter.take('device-a').allowed, true);
assert.equal(limiter.take('device-a').allowed, false);
clock.advance(60_001);
const allowedAgain = limiter.take('device-a');
assert.equal(allowedAgain.allowed, true);
assert.equal(allowedAgain.remaining, 0);
});
test('a refused request does not extend the block', () => {
const clock = manualClock();
const limiter = createRateLimiter({ limit: 1, windowMs: 1_000, now: clock.now });
assert.equal(limiter.take('device-a').allowed, true);
clock.advance(400);
assert.equal(limiter.take('device-a').allowed, false);
clock.advance(601);
// The original hit is now 1001ms old, so the window has moved on even though the caller hammered
// the endpoint in between. A refusal must never buy a longer penalty than the caller earned.
assert.equal(limiter.take('device-a').allowed, true);
});
test('reports the wait from the oldest hit still inside the window', () => {
const clock = manualClock();
const limiter = createRateLimiter({ limit: 2, windowMs: 10_000, now: clock.now });
limiter.take('device-a');
clock.advance(4_000);
limiter.take('device-a');
clock.advance(1_000);
const refused = limiter.take('device-a');
assert.equal(refused.allowed, false);
assert.equal(refused.retryAfterMs, 5_000);
});
test('keeps its caller map bounded when many callers appear', () => {
const clock = manualClock();
const limiter = createRateLimiter({ limit: 5, windowMs: 60_000, maxKeys: 8, now: clock.now });
for (let index = 0; index < 50; index += 1) limiter.take(`device-${index}`);
assert.ok(limiter.size() <= 8, `expected at most 8 tracked callers, saw ${limiter.size()}`);
});
test('prunes expired callers instead of growing for the life of the process', () => {
const clock = manualClock();
const limiter = createRateLimiter({ limit: 5, windowMs: 1_000, now: clock.now });
for (let index = 0; index < 20; index += 1) limiter.take(`device-${index}`);
clock.advance(1_001);
limiter.take('device-fresh');
assert.equal(limiter.size(), 1);
});