mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-02-06 13:33:11 +00:00
revert: restore rate-limiting test to original beforeAll approach
Root Cause:
- Test isolation changes (beforeEach + unique ports) caused CI failures
- Random port allocation unreliable in CI environment
- 3 out of 4 tests failing with ECONNREFUSED errors
Revert Changes:
- Restored beforeAll/afterAll from commit 06cbb40
- Fixed port 3001 instead of random ports per test
- Removed startServer helper function
- Removed per-test server spawning
- Re-enabled all 4 tests (removed .skip)
Rationale:
- Original shared server approach was stable in CI
- Test isolation improvement not worth CI instability
- Keeping all other security improvements (IPv6, cloud metadata)
Test Status:
- Rate limiting tests should now pass in CI ✅
- All other security fixes remain intact ✅
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -10,17 +10,17 @@ import axios from 'axios';
|
|||||||
*/
|
*/
|
||||||
describe('Integration: Rate Limiting', () => {
|
describe('Integration: Rate Limiting', () => {
|
||||||
let serverProcess: ChildProcess;
|
let serverProcess: ChildProcess;
|
||||||
let testPort: number;
|
const port = 3001;
|
||||||
const baseAuthToken = 'test-token-for-rate-limiting-test-32-chars';
|
const authToken = 'test-token-for-rate-limiting-test-32-chars';
|
||||||
|
|
||||||
// Helper to start a fresh server on a unique port
|
beforeAll(async () => {
|
||||||
const startServer = async (port: number, token: string): Promise<ChildProcess> => {
|
// Start HTTP server with rate limiting
|
||||||
const childProcess = spawn('node', ['dist/http-server-single-session.js'], {
|
serverProcess = spawn('node', ['dist/http-server-single-session.js'], {
|
||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
MCP_MODE: 'http',
|
MCP_MODE: 'http',
|
||||||
PORT: port.toString(),
|
PORT: port.toString(),
|
||||||
AUTH_TOKEN: token,
|
AUTH_TOKEN: authToken,
|
||||||
NODE_ENV: 'test',
|
NODE_ENV: 'test',
|
||||||
AUTH_RATE_LIMIT_WINDOW: '900000', // 15 minutes
|
AUTH_RATE_LIMIT_WINDOW: '900000', // 15 minutes
|
||||||
AUTH_RATE_LIMIT_MAX: '20', // 20 attempts
|
AUTH_RATE_LIMIT_MAX: '20', // 20 attempts
|
||||||
@@ -29,24 +29,17 @@ describe('Integration: Rate Limiting', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Wait for server to start
|
// Wait for server to start
|
||||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||||
return childProcess;
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
// Use unique port for each test to ensure isolation
|
|
||||||
testPort = 3001 + Math.floor(Math.random() * 100);
|
|
||||||
serverProcess = await startServer(testPort, baseAuthToken);
|
|
||||||
}, 15000);
|
}, 15000);
|
||||||
|
|
||||||
afterEach(() => {
|
afterAll(() => {
|
||||||
if (serverProcess) {
|
if (serverProcess) {
|
||||||
serverProcess.kill();
|
serverProcess.kill();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should block after max authentication attempts (sequential requests)', async () => {
|
it('should block after max authentication attempts (sequential requests)', async () => {
|
||||||
const baseUrl = `http://localhost:${testPort}/mcp`;
|
const baseUrl = `http://localhost:${port}/mcp`;
|
||||||
|
|
||||||
// IMPORTANT: Use sequential requests to ensure deterministic order
|
// IMPORTANT: Use sequential requests to ensure deterministic order
|
||||||
// Parallel requests can cause race conditions with in-memory rate limiter
|
// Parallel requests can cause race conditions with in-memory rate limiter
|
||||||
@@ -73,7 +66,7 @@ describe('Integration: Rate Limiting', () => {
|
|||||||
}, 60000);
|
}, 60000);
|
||||||
|
|
||||||
it('should include rate limit headers', async () => {
|
it('should include rate limit headers', async () => {
|
||||||
const baseUrl = `http://localhost:${testPort}/mcp`;
|
const baseUrl = `http://localhost:${port}/mcp`;
|
||||||
|
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
baseUrl,
|
baseUrl,
|
||||||
@@ -90,9 +83,8 @@ describe('Integration: Rate Limiting', () => {
|
|||||||
expect(response.headers['ratelimit-reset']).toBeDefined();
|
expect(response.headers['ratelimit-reset']).toBeDefined();
|
||||||
}, 15000);
|
}, 15000);
|
||||||
|
|
||||||
// TODO: Fix 406 error - investigate Express content negotiation issue
|
it('should accept valid tokens within rate limit', async () => {
|
||||||
it.skip('should accept valid tokens within rate limit', async () => {
|
const baseUrl = `http://localhost:${port}/mcp`;
|
||||||
const baseUrl = `http://localhost:${testPort}/mcp`;
|
|
||||||
|
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
baseUrl,
|
baseUrl,
|
||||||
@@ -107,11 +99,7 @@ describe('Integration: Rate Limiting', () => {
|
|||||||
id: 1,
|
id: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: { Authorization: `Bearer ${authToken}` },
|
||||||
Authorization: `Bearer ${baseAuthToken}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Accept': 'application/json',
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -120,7 +108,7 @@ describe('Integration: Rate Limiting', () => {
|
|||||||
}, 15000);
|
}, 15000);
|
||||||
|
|
||||||
it('should return JSON-RPC formatted error on rate limit', async () => {
|
it('should return JSON-RPC formatted error on rate limit', async () => {
|
||||||
const baseUrl = `http://localhost:${testPort}/mcp`;
|
const baseUrl = `http://localhost:${port}/mcp`;
|
||||||
|
|
||||||
// Exhaust rate limit
|
// Exhaust rate limit
|
||||||
for (let i = 0; i < 21; i++) {
|
for (let i = 0; i < 21; i++) {
|
||||||
|
|||||||
Reference in New Issue
Block a user