// 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'); }); });