Skip to main content
Glama

mcp-github-project-manager

TaskGenerationService.test.ts13.6 kB
import { describe, it, expect, beforeEach, jest } from '@jest/globals'; import { TaskGenerationService } from '../../src/services/TaskGenerationService'; import { AIServiceFactory } from '../../src/services/ai/AIServiceFactory'; import { TaskStatus, TaskPriority, TaskComplexity } from '../../src/domain/ai-types'; // Mock the AI service factory jest.mock('../../src/services/ai/AIServiceFactory'); // Mock the ai package jest.mock('ai', () => ({ generateObject: jest.fn(), generateText: jest.fn() })); describe('TaskGenerationService', () => { let service: TaskGenerationService; let mockAIService: any; beforeEach(() => { jest.clearAllMocks(); // Mock AI service - just needs to be a valid model object mockAIService = { modelId: 'test-model', provider: 'test-provider' }; // Mock AIServiceFactory const mockFactory = { getMainModel: jest.fn().mockReturnValue(mockAIService), getFallbackModel: jest.fn().mockReturnValue(mockAIService), getModel: jest.fn().mockReturnValue(mockAIService), getBestAvailableModel: jest.fn().mockReturnValue(mockAIService), getPRDModel: jest.fn().mockReturnValue(mockAIService), getResearchModel: jest.fn().mockReturnValue(mockAIService) }; (AIServiceFactory.getInstance as jest.Mock).mockReturnValue(mockFactory); service = new TaskGenerationService(); }); describe('generateTasksFromPRD', () => { it('should generate tasks from PRD content', async () => { const mockTasks = [ { id: 'task-1', title: 'Setup project infrastructure', description: 'Initialize project structure and CI/CD pipeline', complexity: 4, estimatedHours: 8, priority: 'high', status: 'pending', dependencies: [], acceptanceCriteria: ['Project structure is created', 'CI/CD pipeline is configured'], tags: ['setup', 'infrastructure'], aiGenerated: true, subtasks: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }, { id: 'task-2', title: 'Implement user authentication', description: 'Create secure login and registration functionality', complexity: 6, estimatedHours: 16, priority: 'critical', status: 'pending', dependencies: ['task-1'], acceptanceCriteria: ['Users can register with email', 'Users can login securely'], tags: ['auth', 'security'], aiGenerated: true, subtasks: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() } ]; const { generateObject } = require('ai'); generateObject.mockResolvedValue({ object: mockTasks }); const input = { prd: 'Task management application with user authentication and task CRUD operations', maxTasks: 10, includeSubtasks: true, autoEstimate: true, autoPrioritize: true }; const result = await service.generateTasksFromPRD(input); expect(result).toHaveLength(2); expect(result[0].title).toBe('Setup project infrastructure'); expect(result[0].complexity).toBe(4); expect(result[0].estimatedHours).toBe(8); expect(result[1].dependencies).toEqual(expect.arrayContaining([ expect.objectContaining({ id: 'task-1' }) ])); expect(generateObject).toHaveBeenCalledTimes(1); }); it('should respect maxTasks limit', async () => { const mockManyTasks = Array.from({ length: 10 }, (_, i) => ({ id: `task-${i + 1}`, title: `Task ${i + 1}`, description: `Description for task ${i + 1}`, complexity: 3, estimatedHours: 6, priority: 'medium', status: 'pending', dependencies: [], acceptanceCriteria: [`Task ${i + 1} is completed`], tags: ['generated'], aiGenerated: true, subtasks: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() })); const { generateObject } = require('ai'); generateObject.mockResolvedValue({ object: mockManyTasks.slice(0, 5) }); // Respect maxTasks limit const input = { prd: 'Complex application with many features', maxTasks: 5, includeSubtasks: false, autoEstimate: true, autoPrioritize: true }; const result = await service.generateTasksFromPRD(input); expect(result.length).toBeLessThanOrEqual(5); }); it('should include subtasks when requested', async () => { const mockTasksWithSubtasks = [ { id: 'task-1', title: 'Complex feature implementation', description: 'Implement a complex feature with multiple components', complexity: 8, estimatedHours: 24, priority: 'high', status: 'pending', dependencies: [], acceptanceCriteria: ['Feature is fully implemented'], tags: ['complex'], aiGenerated: true, subtasks: [ { id: 'subtask-1', title: 'Design component architecture', description: 'Design the overall architecture for the feature', complexity: 4, estimatedHours: 8, priority: 'high', status: 'pending', dependencies: [], acceptanceCriteria: ['Architecture is documented'], tags: ['design'], aiGenerated: true, subtasks: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() } ], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() } ]; const { generateObject } = require('ai'); generateObject.mockResolvedValue({ object: mockTasksWithSubtasks }); const input = { prd: 'Application with complex features requiring subtask breakdown', maxTasks: 5, includeSubtasks: true, autoEstimate: true, autoPrioritize: true }; const result = await service.generateTasksFromPRD(input); expect(result[0].subtasks).toBeDefined(); expect(result[0].subtasks.length).toBeGreaterThan(0); // The service actually returns SubTask objects, not string IDs expect(result[0].subtasks[0]).toEqual(expect.objectContaining({ id: 'subtask-1', title: 'Design component architecture' })); }); }); describe('analyzeTaskComplexity', () => { it('should analyze task complexity and provide recommendations', async () => { const mockAnalysis = { originalComplexity: 5, newComplexity: 7, estimatedHours: 16, analysis: 'Task complexity analysis shows increased complexity due to real-time features', recommendations: [ 'Consider breaking into smaller tasks', 'Ensure team has WebSocket experience', 'Plan for additional testing time' ] }; const { generateText } = require('ai'); generateText.mockResolvedValue({ text: 'Task complexity analysis: complexity 7, estimated 16 hours. Real-time functionality adds complexity. Consider breaking into smaller tasks.' }); const task = { id: 'task-1', title: 'Implement real-time collaboration', description: 'Build WebSocket-based real-time collaboration with conflict resolution', complexity: 5 as TaskComplexity, estimatedHours: 12, priority: TaskPriority.HIGH, status: TaskStatus.PENDING, dependencies: [], acceptanceCriteria: [{ id: 'ac-1', description: 'Real-time collaboration works', completed: false }], tags: ['realtime'], aiGenerated: false, subtasks: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; const result = await service.analyzeTaskComplexity(task); expect(result.newComplexity).toBe(7); expect(result.estimatedHours).toBe(16); expect(result.analysis).toBeDefined(); expect(result.recommendations).toEqual([]); // AITaskProcessor returns empty array for now expect(require('ai').generateText).toHaveBeenCalledTimes(1); }); it('should handle simple tasks with low complexity', async () => { const mockSimpleAnalysis = { originalComplexity: 2, newComplexity: 2, estimatedHours: 3, analysis: 'Simple task with low complexity', recommendations: ['Straightforward implementation', 'Good starter task'] }; require('ai').generateText.mockResolvedValue({ text: 'Task complexity analysis: complexity 2, estimated 3 hours. Straightforward implementation.' }); const simpleTask = { id: 'task-1', title: 'Update button color', description: 'Change the primary button color from blue to green', complexity: 2 as TaskComplexity, estimatedHours: 2, priority: TaskPriority.LOW, status: TaskStatus.PENDING, dependencies: [], acceptanceCriteria: [{ id: 'ac-2', description: 'Button color is updated', completed: false }], tags: ['ui'], aiGenerated: false, subtasks: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; const result = await service.analyzeTaskComplexity(simpleTask); expect(result.newComplexity).toBe(2); expect(result.analysis).toBeDefined(); expect(result.recommendations).toEqual([]); // AITaskProcessor returns empty array for now }); }); describe('expandTaskIntoSubtasks', () => { it('should break down complex task into manageable subtasks', async () => { const mockSubtasks = [ { id: 'subtask-1', title: 'Design dashboard layout', description: 'Create wireframes and design for the dashboard layout', complexity: 3, estimatedHours: 6, priority: 'high', status: 'pending', dependencies: [], acceptanceCriteria: ['Wireframes are approved', 'Design is finalized'], tags: ['design'], aiGenerated: true, subtasks: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }, { id: 'subtask-2', title: 'Implement dashboard components', description: 'Build React components for the dashboard', complexity: 5, estimatedHours: 12, priority: 'high', status: 'pending', dependencies: ['subtask-1'], acceptanceCriteria: ['Components are implemented', 'Components are tested'], tags: ['frontend'], aiGenerated: true, subtasks: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() } ]; require('ai').generateText.mockResolvedValue({ text: 'Subtasks: 1. Design dashboard layout 2. Implement dashboard components' }); const complexTask = { id: 'task-1', title: 'Build analytics dashboard', description: 'Create comprehensive analytics dashboard with charts and metrics', complexity: 8 as TaskComplexity, estimatedHours: 24, priority: TaskPriority.HIGH, status: TaskStatus.PENDING, dependencies: [], acceptanceCriteria: [{ id: 'ac-3', description: 'Dashboard displays analytics', completed: false }], tags: ['analytics'], aiGenerated: false, subtasks: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; const input = { task: complexTask, maxDepth: 2, autoEstimate: true }; const result = await service.expandTaskIntoSubtasks(input); expect(result).toHaveLength(3); // Service generates 3 subtasks: Setup, Implementation, Testing expect(result[0].title).toContain('Setup'); expect(result[0].complexity).toBeLessThan(complexTask.complexity); expect(result[1].title).toContain('Implementation'); expect(require('ai').generateText).toHaveBeenCalledTimes(1); }); }); // Note: generateTaskRecommendations method doesn't exist in TaskGenerationService describe('error handling', () => { it('should handle AI service errors gracefully', async () => { const { generateObject } = require('ai'); generateObject.mockRejectedValue(new Error('AI service error')); const input = { prd: 'Test PRD content', maxTasks: 5, includeSubtasks: false, autoEstimate: true, autoPrioritize: true }; await expect(service.generateTasksFromPRD(input)).rejects.toThrow('AI service error'); }); it('should handle empty PRD input gracefully', async () => { const { generateObject } = require('ai'); generateObject.mockResolvedValue({ object: [] }); // Return empty tasks for empty PRD const emptyInput = { prd: '', // Empty PRD maxTasks: 0, // Invalid max tasks includeSubtasks: false, autoEstimate: true, autoPrioritize: true }; const result = await service.generateTasksFromPRD(emptyInput); expect(result).toEqual([]); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kunwarVivek/mcp-github-project-manager'

If you have feedback or need assistance with the MCP directory API, please join our Discord server