feat(mcp): add rate limiting and auth guard tests for MCP transport controller

MCP endpoints already had JwtAuthGuard applied but lacked per-route rate
limiting and test coverage for security behavior. Add @Throttle decorators
with appropriate limits (5 req/min for SSE connections, 30 req/min for
server list and messages), unit tests verifying guard/throttle metadata,
and E2E tests confirming 401 rejection for unauthenticated requests.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-10 18:12:19 +07:00
parent 2432a20b45
commit 3418ab30b0
3 changed files with 111 additions and 0 deletions

54
e2e/api/mcp.spec.ts Normal file
View File

@@ -0,0 +1,54 @@
import { test, expect, registerUser } from '../fixtures';
test.describe('MCP API — Auth Guards', () => {
test.describe('GET /mcp/servers', () => {
test('rejects unauthenticated request with 401', async ({ request }) => {
const res = await request.get('/mcp/servers');
expect(res.status()).toBe(401);
});
test('returns server list for authenticated user', async ({ request }) => {
const { accessToken } = await registerUser(request);
const res = await request.get('/mcp/servers', {
headers: { Authorization: `Bearer ${accessToken}` },
});
expect(res.status()).toBe(200);
const body = await res.json();
expect(body).toHaveProperty('servers');
expect(Array.isArray(body.servers)).toBeTruthy();
});
});
test.describe('GET /mcp/:serverName/sse', () => {
test('rejects unauthenticated SSE connection with 401', async ({ request }) => {
const res = await request.get('/mcp/search/sse');
expect(res.status()).toBe(401);
});
});
test.describe('POST /mcp/:serverName/messages', () => {
test('rejects unauthenticated message with 401', async ({ request }) => {
const res = await request.post('/mcp/search/messages', {
params: { sessionId: 'fake-session' },
data: {},
});
expect(res.status()).toBe(401);
});
test('returns 400 when sessionId is missing for authenticated user', async ({ request }) => {
const { accessToken } = await registerUser(request);
const res = await request.post('/mcp/search/messages', {
data: {},
headers: { Authorization: `Bearer ${accessToken}` },
});
expect(res.status()).toBe(400);
});
});
});