aec04474be
- Add tests/unit/utils/poller.test.js covering background polling lock, registry, error recovery, webhook bypasses, and global fallbacks - Add tests/integration/rateLimiter.test.js verifying 429 response rate-limiting in an isolated production environment - Add tests/integration/ombiDecoration.test.js covering deep links and admin role checks - Expand tests/frontend/ui/downloads.test.js covering createServiceIcons() and createClientLogo() fallbacks - Expand tests/integration/dashboard.test.js verifying SSE heartbeats, payload schema contract, and listener cleanup on client disconnect
66 lines
2.3 KiB
JavaScript
66 lines
2.3 KiB
JavaScript
// Copyright (c) 2026 Gordon Bolton. MIT License.
|
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
import request from 'supertest';
|
|
import nock from 'nock';
|
|
|
|
describe('Rate Limiting Integration Tests', () => {
|
|
let app;
|
|
let originalSkipRateLimit;
|
|
|
|
beforeEach(async () => {
|
|
// Save current rate limiting skip flag
|
|
originalSkipRateLimit = process.env.SKIP_RATE_LIMIT;
|
|
// Explicitly delete it before loading the app so rate limiters are active
|
|
delete process.env.SKIP_RATE_LIMIT;
|
|
process.env.EMBY_URL = 'https://emby.test';
|
|
|
|
// Dynamically import createApp so that routes/auth.js evaluates process.env.SKIP_RATE_LIMIT as undefined
|
|
const appModule = await import('../../server/app.js');
|
|
const createApp = appModule.createApp;
|
|
|
|
// Create a new app instance with rate limiting enabled
|
|
app = createApp({ skipRateLimits: false });
|
|
|
|
nock.cleanAll();
|
|
});
|
|
|
|
afterEach(() => {
|
|
// Restore rate limit skip flag
|
|
if (originalSkipRateLimit !== undefined) {
|
|
process.env.SKIP_RATE_LIMIT = originalSkipRateLimit;
|
|
} else {
|
|
delete process.env.SKIP_RATE_LIMIT;
|
|
}
|
|
delete process.env.EMBY_URL;
|
|
nock.cleanAll();
|
|
});
|
|
|
|
it('triggers a 429 Too Many Requests error on the auth endpoint after 10 failed requests', async () => {
|
|
// Mock Emby server auth endpoint to return 401 (failed credentials).
|
|
// The login rate limiter has `skipSuccessfulRequests: true`, meaning ONLY failed login attempts
|
|
// count toward the rate limit window of 10 requests.
|
|
nock('https://emby.test')
|
|
.post('/Users/authenticatebyname')
|
|
.reply(401, { error: 'Unauthorized' })
|
|
.persist();
|
|
|
|
// Fire 10 rapid failed login requests (the limit is 10)
|
|
for (let i = 0; i < 10; i++) {
|
|
const res = await request(app)
|
|
.post('/api/auth/login')
|
|
.send({ username: 'TestUser', password: 'wrongpassword' });
|
|
|
|
expect(res.status).toBe(401);
|
|
expect(res.body.error).toBe('Invalid username or password');
|
|
}
|
|
|
|
// The 11th request must be rate limited and return 429
|
|
const limitRes = await request(app)
|
|
.post('/api/auth/login')
|
|
.send({ username: 'TestUser', password: 'wrongpassword' });
|
|
|
|
expect(limitRes.status).toBe(429);
|
|
expect(limitRes.body.error).toContain('Too many login attempts');
|
|
});
|
|
});
|