"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); require("reflect-metadata"); const supertest_1 = __importDefault(require("supertest")); const express_1 = __importDefault(require("express")); // Mock services const mockAITaskService = { createTaskFromNaturalLanguage: jest.fn(), updateTaskContext: jest.fn(), getTaskSuggestions: jest.fn(), breakdownTask: jest.fn(), getHealthStatus: jest.fn(), getUsageStats: jest.fn(), warmCache: jest.fn(), }; const mockOpenAIService = { testConnection: jest.fn(), }; const mockLangChainService = { testConnection: jest.fn(), }; // Mock DI container before importing routes jest.mock('tsyringe', () => ({ container: { resolve: jest.fn((token) => { if (token === 'AITaskService') return mockAITaskService; if (token === 'OpenAIService') return mockOpenAIService; if (token === 'LangChainService') return mockLangChainService; return null; }), register: jest.fn(), }, injectable: jest.fn(() => (target) => target), inject: jest.fn(() => (target, key, index) => { }), })); // Import routes after mocking const aiRoutes = require('../../src/api/routes/ai.routes').default; describe('AI API Integration Tests', () => { let app; beforeEach(() => { jest.clearAllMocks(); // Create Express app app = (0, express_1.default)(); app.use(express_1.default.json()); app.use('/api/ai', aiRoutes); // Add error handler app.use((err, req, res, next) => { res.status(err.status || 500).json({ success: false, error: err.message || 'Internal server error', }); }); }); describe('POST /api/ai/create-task', () => { it('should create a task from natural language successfully', async () => { const mockTask = { title: 'Implement user authentication', description: 'Add JWT-based authentication to the application', priority: 'high', complexity: 'major', estimatedHours: 16, acceptanceCriteria: [ 'Users can register', 'Users can login', 'JWT tokens are secure', ], tags: ['auth', 'security'], }; mockAITaskService.createTaskFromNaturalLanguage.mockResolvedValue(mockTask); const response = await (0, supertest_1.default)(app) .post('/api/ai/create-task') .send({ prompt: 'Implement user authentication with JWT', projectId: 'project-123', context: { projectDescription: 'E-commerce platform', currentTasks: ['Database setup', 'API structure'], }, }) .expect(201); expect(response.body).toEqual({ success: true, data: mockTask, message: 'Task created successfully from natural language', }); expect(mockAITaskService.createTaskFromNaturalLanguage).toHaveBeenCalledWith({ prompt: 'Implement user authentication with JWT', projectId: 'project-123', context: { projectDescription: 'E-commerce platform', currentTasks: ['Database setup', 'API structure'], }, }); }); it('should return 400 for invalid request', async () => { const response = await (0, supertest_1.default)(app) .post('/api/ai/create-task') .send({ // Missing required 'prompt' field projectId: 'project-123', }) .expect(400); expect(response.body).toMatchObject({ success: false, error: 'Validation error', details: expect.any(Array), }); expect(mockAITaskService.createTaskFromNaturalLanguage).not.toHaveBeenCalled(); }); it('should handle rate limiting', async () => { mockAITaskService.createTaskFromNaturalLanguage.mockRejectedValue(new Error('Rate limit exceeded for task_creation')); const response = await (0, supertest_1.default)(app) .post('/api/ai/create-task') .send({ prompt: 'Create a task', }) .expect(429); expect(response.body).toEqual({ success: false, error: 'Rate limit exceeded', message: 'Too many requests. Please try again later.', }); }); }); describe('PUT /api/ai/update-context/:taskId', () => { it('should update task context with feedback', async () => { mockAITaskService.updateTaskContext.mockResolvedValue(undefined); const response = await (0, supertest_1.default)(app) .put('/api/ai/update-context/task-123') .send({ feedback: 'The task needs more specific acceptance criteria', }) .expect(200); expect(response.body).toEqual({ success: true, message: 'Task context updated successfully', }); expect(mockAITaskService.updateTaskContext).toHaveBeenCalledWith('task-123', 'The task needs more specific acceptance criteria'); }); it('should validate feedback is provided', async () => { const response = await (0, supertest_1.default)(app) .put('/api/ai/update-context/task-123') .send({}) .expect(400); expect(response.body).toMatchObject({ success: false, error: 'Validation error', }); }); }); describe('GET /api/ai/suggestions/:projectId', () => { it('should get task suggestions for a project', async () => { const mockSuggestions = [ { id: 'suggestion-1', title: 'Add error handling', description: 'Implement comprehensive error handling', rationale: 'Improves user experience and debugging', priority: 'medium', confidence: 0.85, }, { id: 'suggestion-2', title: 'Setup logging', description: 'Add structured logging system', rationale: 'Essential for production monitoring', priority: 'high', confidence: 0.92, }, ]; mockAITaskService.getTaskSuggestions.mockResolvedValue(mockSuggestions); const response = await (0, supertest_1.default)(app) .get('/api/ai/suggestions/project-123') .query({ projectDescription: 'E-commerce platform', completedTasks: 'Setup database,Create API structure', currentTasks: 'Build UI components', goals: 'Launch MVP,Scale to 1000 users', }) .expect(200); expect(response.body).toEqual({ success: true, data: mockSuggestions, projectId: 'project-123', }); expect(mockAITaskService.getTaskSuggestions).toHaveBeenCalledWith('project-123', { projectDescription: 'E-commerce platform', completedTasks: ['Setup database', 'Create API structure'], currentTasks: ['Build UI components'], goals: ['Launch MVP', 'Scale to 1000 users'], }); }); it('should handle missing query parameters', async () => { const response = await (0, supertest_1.default)(app) .get('/api/ai/suggestions/project-123') .expect(400); expect(response.body).toMatchObject({ success: false, error: 'Validation error', }); }); }); describe('POST /api/ai/breakdown/:taskId', () => { it('should break down a task into subtasks', async () => { const mockBreakdown = { title: 'Implement authentication', description: 'Create secure authentication system', subtasks: [ { id: '1', title: 'Setup database schema', description: 'Create user tables and indexes', estimatedHours: 2, }, { id: '2', title: 'Implement JWT logic', description: 'Create token generation and validation', estimatedHours: 4, }, { id: '3', title: 'Create API endpoints', description: 'Build login, register, and refresh endpoints', estimatedHours: 3, }, ], complexity: 'major', estimatedTotalHours: 9, suggestedApproach: 'Use industry-standard JWT with refresh tokens', }; mockAITaskService.breakdownTask.mockResolvedValue(mockBreakdown); const response = await (0, supertest_1.default)(app) .post('/api/ai/breakdown/task-456') .send({ taskDescription: 'Implement user authentication with JWT tokens', context: 'Node.js Express application with PostgreSQL database', }) .expect(200); expect(response.body).toEqual({ success: true, data: mockBreakdown, taskId: 'task-456', }); expect(mockAITaskService.breakdownTask).toHaveBeenCalledWith('task-456', 'Implement user authentication with JWT tokens', 'Node.js Express application with PostgreSQL database'); }); }); describe('GET /api/ai/health', () => { it('should return healthy status when all services are operational', async () => { mockAITaskService.getHealthStatus.mockResolvedValue({ openai: true, langchain: true, redis: true, rateLimiter: true, }); const response = await (0, supertest_1.default)(app) .get('/api/ai/health') .expect(200); expect(response.body).toMatchObject({ success: true, healthy: true, services: { openai: true, langchain: true, redis: true, rateLimiter: true, }, timestamp: expect.any(String), }); }); it('should return 503 when services are degraded', async () => { mockAITaskService.getHealthStatus.mockResolvedValue({ openai: false, langchain: true, redis: true, rateLimiter: true, }); const response = await (0, supertest_1.default)(app) .get('/api/ai/health') .expect(503); expect(response.body).toMatchObject({ success: true, healthy: false, services: expect.any(Object), }); }); }); describe('GET /api/ai/usage', () => { it('should return usage statistics', async () => { const mockStats = { openai: { requestCount: 150, totalTokensUsed: 75000, estimatedCost: 1.5, }, totalContextsSaved: 300, cacheHitRate: 0.68, }; mockAITaskService.getUsageStats.mockResolvedValue(mockStats); const response = await (0, supertest_1.default)(app) .get('/api/ai/usage') .expect(200); expect(response.body).toEqual({ success: true, data: mockStats, timestamp: expect.any(String), }); }); }); describe('POST /api/ai/warm-cache', () => { it('should initiate cache warming for a project', async () => { mockAITaskService.warmCache.mockResolvedValue(undefined); const response = await (0, supertest_1.default)(app) .post('/api/ai/warm-cache') .send({ projectId: 'project-789', }) .expect(202); expect(response.body).toEqual({ success: true, message: 'Cache warming initiated', projectId: 'project-789', }); expect(mockAITaskService.warmCache).toHaveBeenCalledWith('project-789'); }); it('should require projectId', async () => { const response = await (0, supertest_1.default)(app) .post('/api/ai/warm-cache') .send({}) .expect(400); expect(response.body).toEqual({ success: false, error: 'Project ID is required', }); }); }); describe('POST /api/ai/test-connection', () => { it('should test connections to all AI services', async () => { mockOpenAIService.testConnection.mockResolvedValue(true); mockLangChainService.testConnection.mockResolvedValue(true); const response = await (0, supertest_1.default)(app) .post('/api/ai/test-connection') .expect(200); expect(response.body).toEqual({ success: true, connections: { openai: true, langchain: true, }, message: 'All AI services are connected', }); }); it('should report partial connectivity', async () => { mockOpenAIService.testConnection.mockResolvedValue(true); mockLangChainService.testConnection.mockResolvedValue(false); const response = await (0, supertest_1.default)(app) .post('/api/ai/test-connection') .expect(200); expect(response.body).toEqual({ success: true, connections: { openai: true, langchain: false, }, message: 'Some AI services are not available', }); }); }); describe('Error handling', () => { it('should handle internal server errors gracefully', async () => { mockAITaskService.createTaskFromNaturalLanguage.mockRejectedValue(new Error('Database connection failed')); const response = await (0, supertest_1.default)(app) .post('/api/ai/create-task') .send({ prompt: 'Create a task', }) .expect(500); expect(response.body).toMatchObject({ success: false, error: 'Database connection failed', }); }); it('should validate request data types', async () => { const response = await (0, supertest_1.default)(app) .post('/api/ai/create-task') .send({ prompt: 123, // Should be string }) .expect(400); expect(response.body).toMatchObject({ success: false, error: 'Validation error', }); }); }); describe('End-to-end workflow', () => { it('should complete full task creation and breakdown flow', async () => { // Step 1: Create task from natural language const createdTask = { id: 'task-new-123', title: 'Build user dashboard', description: 'Create interactive dashboard for users', priority: 'high', complexity: 'major', }; mockAITaskService.createTaskFromNaturalLanguage.mockResolvedValue(createdTask); const createResponse = await (0, supertest_1.default)(app) .post('/api/ai/create-task') .send({ prompt: 'Build a user dashboard with charts and metrics', }) .expect(201); expect(createResponse.body.success).toBe(true); expect(createResponse.body.data).toMatchObject(createdTask); // Step 2: Break down the created task const breakdown = { title: createdTask.title, description: createdTask.description, subtasks: [ { id: '1', title: 'Design dashboard layout' }, { id: '2', title: 'Implement chart components' }, { id: '3', title: 'Add metrics calculations' }, ], complexity: 'major', estimatedTotalHours: 24, }; mockAITaskService.breakdownTask.mockResolvedValue(breakdown); const breakdownResponse = await (0, supertest_1.default)(app) .post(`/api/ai/breakdown/${createdTask.id}`) .send({ taskDescription: createdTask.description, context: 'React application with D3.js for charts', }) .expect(200); expect(breakdownResponse.body.success).toBe(true); expect(breakdownResponse.body.data.subtasks).toHaveLength(3); // Step 3: Update context with feedback mockAITaskService.updateTaskContext.mockResolvedValue(undefined); const updateResponse = await (0, supertest_1.default)(app) .put(`/api/ai/update-context/${createdTask.id}`) .send({ feedback: 'Focus on real-time data updates for the dashboard', }) .expect(200); expect(updateResponse.body.success).toBe(true); }); }); }); //# sourceMappingURL=ai.integration.test.js.map