import test from 'brittle'
import { RateLimiter } from '../../lib/auth/rate-limiter.js'
test('RateLimiter - allows requests within limit', async (t) => {
const limiter = new RateLimiter({
limits: {
'notes': { tokens: 5, refillRate: 1, refillInterval: 60000 }
}
})
const result = await limiter.check('user1', 'notes')
t.ok(result.allowed, 'first request allowed')
t.is(result.remaining, 4, 'remaining tokens correct')
limiter.destroy()
})
test('RateLimiter - blocks after exhaustion', async (t) => {
const limiter = new RateLimiter({
limits: {
'notes': { tokens: 2, refillRate: 1, refillInterval: 60000 }
}
})
await limiter.check('user1', 'notes')
await limiter.check('user1', 'notes')
const result = await limiter.check('user1', 'notes')
t.absent(result.allowed, 'third request blocked')
t.is(result.remaining, 0, 'no remaining tokens')
t.ok(result.retryAfter > 0, 'has retryAfter')
limiter.destroy()
})
test('RateLimiter - different keys tracked independently', async (t) => {
const limiter = new RateLimiter({
limits: {
'notes': { tokens: 1, refillRate: 1, refillInterval: 60000 }
}
})
const r1 = await limiter.check('user1', 'notes')
const r2 = await limiter.check('user2', 'notes')
t.ok(r1.allowed, 'user1 allowed')
t.ok(r2.allowed, 'user2 allowed')
limiter.destroy()
})
test('RateLimiter - unknown operation returns allowed', async (t) => {
const limiter = new RateLimiter()
const result = await limiter.check('user1', 'unknown-op')
t.ok(result.allowed, 'unknown operations allowed')
limiter.destroy()
})
test('RateLimiter - reset restores tokens', async (t) => {
const limiter = new RateLimiter({
limits: {
'notes': { tokens: 1, refillRate: 0, refillInterval: 60000 }
}
})
await limiter.check('user1', 'notes') // exhaust
const blocked = await limiter.check('user1', 'notes')
t.absent(blocked.allowed, 'blocked after exhaustion')
await limiter.reset('user1', 'notes')
const afterReset = await limiter.check('user1', 'notes')
t.ok(afterReset.allowed, 'allowed after reset')
limiter.destroy()
})
test('RateLimiter - notes operation exists in defaults', async (t) => {
const limiter = new RateLimiter()
t.ok(limiter.limits['notes'], 'notes operation exists in defaults')
t.absent(limiter.limits['git'], 'git operation does NOT exist in defaults')
limiter.destroy()
})