// API Gateway Integration Tests const TestEnvironment = require('./setup'); const TestHelpers = require('./helpers'); const apiGatewayApp = require('../../services/api-gateway/src/app'); describe('API Gateway Integration Tests', () => { let testEnv; let helpers; let apiClient; let testUser; let apiKey; const API_GATEWAY_PORT = 13000; const API_BASE_URL = `http://localhost:${API_GATEWAY_PORT}/api/v1`; beforeAll(async () => { testEnv = new TestEnvironment(); helpers = new TestHelpers(testEnv); // Setup test environment await testEnv.setup(); // Setup mocks helpers.setupMocks(); // Start API Gateway await testEnv.startService('api-gateway', apiGatewayApp, API_GATEWAY_PORT); // Create test user and API key testUser = await testEnv.createTestUser(); apiKey = await testEnv.createTestApiKey(testUser.id); // Create authenticated client apiClient = await helpers.createAuthenticatedClient(`http://localhost:${API_GATEWAY_PORT}`, testUser); }); afterAll(async () => { helpers.cleanupMocks(); await testEnv.cleanup(); }); describe('Authentication', () => { test('should authenticate with JWT token', async () => { const response = await apiClient.get('/api/v1/auth/me'); const data = helpers.expectApiSuccess(response); expect(data.user).toBeDefined(); expect(data.user.id).toBe(testUser.id); expect(data.user.username).toBe(testUser.username); }); test('should reject invalid token', async () => { const invalidClient = require('axios').create({ baseURL: `http://localhost:${API_GATEWAY_PORT}`, headers: { 'Authorization': 'Bearer invalid-token' }, validateStatus: () => true }); const response = await invalidClient.get('/api/v1/auth/me'); helpers.expectApiError(response, 401, 'Invalid token'); }); test('should authenticate with API key', async () => { const apiKeyClient = require('axios').create({ baseURL: `http://localhost:${API_GATEWAY_PORT}`, headers: { 'X-API-Key': apiKey.apiKey }, validateStatus: () => true }); const response = await apiKeyClient.get('/api/v1/auth/me'); const data = helpers.expectApiSuccess(response); expect(data.user).toBeDefined(); expect(data.apiKey).toBeDefined(); }); }); describe('Rate Limiting', () => { test('should enforce rate limits', async () => { const requests = []; // Make many requests quickly for (let i = 0; i < 105; i++) { requests.push( apiClient.get('/api/v1/health').catch(e => e.response) ); } const responses = await Promise.all(requests); // Check that some requests were rate limited const rateLimited = responses.filter(r => r.status === 429); expect(rateLimited.length).toBeGreaterThan(0); // Verify rate limit headers const limitedResponse = rateLimited[0]; expect(limitedResponse.headers['x-ratelimit-limit']).toBeDefined(); expect(limitedResponse.headers['x-ratelimit-remaining']).toBeDefined(); expect(limitedResponse.headers['x-ratelimit-reset']).toBeDefined(); }); }); describe('Service Routing', () => { test('should route to orchestrator service', async () => { const campaign = await helpers.createTestCampaign(); const response = await apiClient.get(`/api/v1/campaigns/${campaign.campaignId}`); const data = helpers.expectApiSuccess(response); expect(data.campaign).toBeDefined(); expect(data.campaign.campaignId).toBe(campaign.campaignId); }); test('should route to analytics service', async () => { const response = await apiClient.get('/api/v1/analytics/metrics', { params: { startDate: new Date(Date.now() - 86400000).toISOString(), endDate: new Date().toISOString() } }); const data = helpers.expectApiSuccess(response); expect(data.metrics).toBeDefined(); }); test('should handle service unavailable', async () => { // Try to access a non-existent service endpoint const response = await apiClient.get('/api/v1/nonexistent/endpoint'); helpers.expectApiError(response, 404); }); }); describe('CORS', () => { test('should handle CORS preflight requests', async () => { const axios = require('axios'); const response = await axios.options(`http://localhost:${API_GATEWAY_PORT}/api/v1/health`, { headers: { 'Origin': 'http://localhost:3008', 'Access-Control-Request-Method': 'GET', 'Access-Control-Request-Headers': 'authorization' }, validateStatus: () => true }); expect(response.status).toBe(204); expect(response.headers['access-control-allow-origin']).toBe('http://localhost:3008'); expect(response.headers['access-control-allow-methods']).toContain('GET'); expect(response.headers['access-control-allow-headers']).toContain('authorization'); }); test('should reject unauthorized origins', async () => { const axios = require('axios'); const response = await axios.get(`http://localhost:${API_GATEWAY_PORT}/api/v1/health`, { headers: { 'Origin': 'http://unauthorized.com' }, validateStatus: () => true }); expect(response.headers['access-control-allow-origin']).toBeUndefined(); }); }); describe('Error Handling', () => { test('should handle validation errors', async () => { const response = await apiClient.post('/api/v1/campaigns', { // Missing required fields name: 'Test Campaign' }); helpers.expectApiError(response, 400); expect(response.data.errors).toBeDefined(); }); test('should handle internal server errors gracefully', async () => { // Force an error by sending invalid data type const response = await apiClient.post('/api/v1/campaigns', 'invalid-data-type'); helpers.expectApiError(response, 400); expect(response.data.error).toBeDefined(); }); }); describe('Health Checks', () => { test('should return health status', async () => { const response = await apiClient.get('/api/v1/health'); const data = helpers.expectApiSuccess(response); expect(data.status).toBe('healthy'); expect(data.timestamp).toBeDefined(); expect(data.version).toBeDefined(); }); test('should return detailed service health', async () => { const response = await apiClient.get('/api/v1/health/services'); const data = helpers.expectApiSuccess(response); expect(data.services).toBeDefined(); expect(data.services['api-gateway']).toBeDefined(); expect(data.services['api-gateway'].status).toBe('healthy'); }); }); describe('Request Logging', () => { test('should log requests with correlation ID', async () => { const correlationId = 'test-correlation-' + Date.now(); const response = await apiClient.get('/api/v1/health', { headers: { 'X-Correlation-ID': correlationId } }); helpers.expectApiSuccess(response); expect(response.headers['x-correlation-id']).toBe(correlationId); }); }); describe('API Versioning', () => { test('should support multiple API versions', async () => { // Test v1 const v1Response = await apiClient.get('/api/v1/health'); helpers.expectApiSuccess(v1Response); // Test v2 (if implemented) const v2Response = await apiClient.get('/api/v2/health'); if (v2Response.status === 200) { const data = helpers.expectApiSuccess(v2Response); expect(data.apiVersion).toBe('v2'); } else { // v2 not implemented yet expect(v2Response.status).toBe(404); } }); }); });