chore: establish learning module baseline
This commit is contained in:
68
OpenMAIC/tests/server/access-code-rate-limit.test.ts
Normal file
68
OpenMAIC/tests/server/access-code-rate-limit.test.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import {
|
||||
AccessCodeRateLimiter,
|
||||
resolveAccessCodeRateLimitConfig,
|
||||
resolveAccessCodeRateLimitKey,
|
||||
} from '@/lib/server/access-code-rate-limit';
|
||||
|
||||
describe('ACCESS_CODE login rate limiting', () => {
|
||||
test('blocks after the configured attempt count and reopens after the window', () => {
|
||||
const limiter = new AccessCodeRateLimiter();
|
||||
const config = { maxAttempts: 2, windowMs: 10_000, maxClients: 100 };
|
||||
|
||||
expect(limiter.consume('client-a', config, 1_000)).toMatchObject({
|
||||
allowed: true,
|
||||
remaining: 1,
|
||||
});
|
||||
expect(limiter.consume('client-a', config, 2_000)).toMatchObject({
|
||||
allowed: true,
|
||||
remaining: 0,
|
||||
});
|
||||
expect(limiter.consume('client-a', config, 3_000)).toMatchObject({
|
||||
allowed: false,
|
||||
retryAfterSeconds: 8,
|
||||
});
|
||||
expect(limiter.consume('client-a', config, 11_000)).toMatchObject({
|
||||
allowed: true,
|
||||
remaining: 1,
|
||||
});
|
||||
});
|
||||
|
||||
test('keeps the in-process client map bounded', () => {
|
||||
const limiter = new AccessCodeRateLimiter();
|
||||
const config = { maxAttempts: 3, windowMs: 10_000, maxClients: 2 };
|
||||
|
||||
limiter.consume('client-a', config, 1_000);
|
||||
limiter.consume('client-b', config, 1_001);
|
||||
limiter.consume('client-c', config, 1_002);
|
||||
|
||||
expect(limiter.size).toBe(2);
|
||||
expect(limiter.consume('client-a', config, 1_003)).toMatchObject({
|
||||
allowed: true,
|
||||
remaining: 2,
|
||||
});
|
||||
expect(limiter.size).toBe(2);
|
||||
});
|
||||
|
||||
test('trusts proxy IP headers only through explicit opt-in', () => {
|
||||
const request = new Request('https://ops.example/api/access-code/verify', {
|
||||
headers: {
|
||||
'x-forwarded-for': '203.0.113.8, 10.0.0.2',
|
||||
'x-real-ip': '203.0.113.9',
|
||||
},
|
||||
});
|
||||
|
||||
expect(resolveAccessCodeRateLimitKey(request, false)).toBe('shared:unknown-client');
|
||||
expect(resolveAccessCodeRateLimitKey(request, true)).toBe('ip:203.0.113.8');
|
||||
});
|
||||
|
||||
test('falls back safely for invalid configuration', () => {
|
||||
expect(
|
||||
resolveAccessCodeRateLimitConfig({
|
||||
ACCESS_CODE_RATE_LIMIT_MAX_ATTEMPTS: '-1',
|
||||
ACCESS_CODE_RATE_LIMIT_WINDOW_SECONDS: 'not-a-number',
|
||||
ACCESS_CODE_RATE_LIMIT_MAX_CLIENTS: '0',
|
||||
}),
|
||||
).toEqual({ maxAttempts: 10, windowMs: 900_000, maxClients: 10_000 });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user