UNPKG

yoda-mcp

Version:

Intelligent Planning MCP with Optional Dependencies and Graceful Fallbacks - wise planning through the Force of lean excellence

440 lines (383 loc) 16.8 kB
/** * SmartResourceOptimizer Tests * * RED PHASE: Write failing tests for genuine Pareto analysis * These tests define what real resource optimization means (not hardcoded sorting) */ import { SmartResourceOptimizer } from '../src/smart/smart-resource-optimizer'; import { Plan, Task, ProjectContext, ParetoAnalysis } from '../src/types'; describe('SmartResourceOptimizer', () => { let optimizer: SmartResourceOptimizer; beforeEach(() => { optimizer = new SmartResourceOptimizer(); }); // RED: This test MUST fail - SmartResourceOptimizer doesn't exist yet describe('Dynamic Business Value Analysis', () => { test('should calculate higher value for user-facing features', () => { const userFacingTask: Task = { id: 'user_dashboard', title: 'Build user dashboard', description: 'Main interface users interact with daily', estimatedHours: 12, skills: ['frontend'], dependencies: [], deliverable: 'Dashboard UI' }; const internalTask: Task = { id: 'logging_system', title: 'Implement logging', description: 'Internal system for error tracking', estimatedHours: 8, skills: ['backend'], dependencies: [], deliverable: 'Logging service' }; const context: ProjectContext = { userPriorities: ['usability', 'performance'], timeline: 'normal', businessModel: 'saas', industry: 'technology' }; const userValue = optimizer.calculateBusinessValue(userFacingTask, context); const internalValue = optimizer.calculateBusinessValue(internalTask, context); expect(userValue).toBeGreaterThan(internalValue); expect(userValue).toBeGreaterThanOrEqual(80); // User-facing should be high value expect(internalValue).toBeLessThan(70); // Internal should be lower value }); test('should calculate higher value for revenue-impacting features', () => { const paymentTask: Task = { id: 'payment_processing', title: 'Implement payment system', description: 'Process customer payments and subscriptions', estimatedHours: 16, skills: ['backend', 'payments'], dependencies: [], deliverable: 'Payment system' }; const documentationTask: Task = { id: 'api_docs', title: 'Write API documentation', description: 'Document REST API endpoints', estimatedHours: 6, skills: ['documentation'], dependencies: [], deliverable: 'API documentation' }; const context: ProjectContext = { userPriorities: [], timeline: 'normal', businessModel: 'ecommerce', industry: 'retail' }; const paymentValue = optimizer.calculateBusinessValue(paymentTask, context); const docsValue = optimizer.calculateBusinessValue(documentationTask, context); expect(paymentValue).toBeGreaterThan(docsValue); expect(paymentValue).toBeGreaterThanOrEqual(90); // Revenue impact should be very high }); test('should adjust value based on industry context', () => { const securityTask: Task = { id: 'security_audit', title: 'Security compliance audit', description: 'Implement security controls and audit logging', estimatedHours: 20, skills: ['security', 'compliance'], dependencies: [], deliverable: 'Compliance report' }; const financialContext: ProjectContext = { userPriorities: [], timeline: 'normal', businessModel: 'saas', industry: 'financial' // High security requirements }; const techContext: ProjectContext = { userPriorities: [], timeline: 'normal', businessModel: 'saas', industry: 'technology' // Lower security requirements }; const financialValue = optimizer.calculateBusinessValue(securityTask, financialContext); const techValue = optimizer.calculateBusinessValue(securityTask, techContext); expect(financialValue).toBeGreaterThan(techValue); expect(financialValue).toBeGreaterThanOrEqual(85); // Critical for financial industry }); }); // RED: This test MUST fail - calculateRealEffort doesn't exist yet describe('Real Effort Calculation', () => { test('should adjust effort based on task complexity type', () => { const integrationTask: Task = { id: 'legacy_integration', title: 'Integrate with legacy system', description: 'Connect new API to old mainframe system', estimatedHours: 10, skills: ['integration', 'legacy'], dependencies: [], deliverable: 'Integration layer' }; const simpleTask: Task = { id: 'ui_update', title: 'Update button color', description: 'Change primary button from blue to green', estimatedHours: 2, skills: ['ui'], dependencies: [], deliverable: 'Updated button' }; const integrationEffort = optimizer.calculateRealEffort(integrationTask); const simpleEffort = optimizer.calculateRealEffort(simpleTask); expect(integrationEffort).toBeGreaterThan(integrationTask.estimatedHours); // Should be multiplied expect(simpleEffort).toBeLessThanOrEqual(simpleTask.estimatedHours * 1.2); // Minimal multiplier expect(integrationEffort / integrationTask.estimatedHours).toBeGreaterThan(1.4); // At least 40% increase }); test('should consider team experience in effort calculation', () => { const complexTask: Task = { id: 'ml_model', title: 'Build machine learning model', description: 'Train ML model for recommendation system', estimatedHours: 40, skills: ['machine-learning', 'python', 'data-science'], dependencies: [], deliverable: 'ML model' }; const experiencedTeamMultiplier = 0.8; // Team has ML experience const inexperiencedTeamMultiplier = 2.0; // Team is new to ML optimizer.setTeamExperienceMultiplier('machine-learning', experiencedTeamMultiplier); const experiencedEffort = optimizer.calculateRealEffort(complexTask); optimizer.setTeamExperienceMultiplier('machine-learning', inexperiencedTeamMultiplier); const inexperiencedEffort = optimizer.calculateRealEffort(complexTask); expect(inexperiencedEffort).toBeGreaterThan(experiencedEffort); expect(inexperiencedEffort / experiencedEffort).toBeGreaterThanOrEqual(2.0); }); }); // RED: This test MUST fail - analyzeParetoImpact doesn't exist yet describe('True Pareto Analysis', () => { test('should find actual 80% value threshold, not just top 20% of tasks', async () => { const tasks: Task[] = [ // These 2 tasks should deliver 80%+ of total value (true Pareto) { id: 'core_feature', title: 'Core product feature', description: 'Main value proposition', estimatedHours: 20, skills: [], dependencies: [], deliverable: 'Core feature', businessValue: 50 // Will be calculated dynamically }, { id: 'payment_system', title: 'Payment processing', description: 'Revenue generation', estimatedHours: 16, skills: [], dependencies: [], deliverable: 'Payments', businessValue: 40 // Will be calculated dynamically }, // These 3 tasks should deliver remaining 20% of value { id: 'admin_panel', title: 'Admin interface', description: 'Internal admin tools', estimatedHours: 12, skills: [], dependencies: [], deliverable: 'Admin panel', businessValue: 6 // Will be calculated dynamically }, { id: 'reports', title: 'Reporting system', description: 'Generate usage reports', estimatedHours: 8, skills: [], dependencies: [], deliverable: 'Reports', businessValue: 3 // Will be calculated dynamically }, { id: 'documentation', title: 'User documentation', description: 'Help and tutorial content', estimatedHours: 6, skills: [], dependencies: [], deliverable: 'Docs', businessValue: 1 // Will be calculated dynamically } ]; const plan: Plan = { requirements: [], tasks, timeline: { totalEstimate: '8 weeks', phases: [], criticalPath: [] }, risks: [], metadata: { planId: 'pareto_test', generatedAt: new Date(), estimatedReadTime: '10 min', complexity: 'moderate' } }; const context: ProjectContext = { userPriorities: ['core-functionality', 'revenue'], timeline: 'normal', businessModel: 'saas', industry: 'technology' }; const analysis: ParetoAnalysis = await optimizer.analyzeParetoImpact(plan, context); // Should identify true vital tasks (not just 20% of count) expect(analysis.vitalTasks).toHaveLength(2); // Should be 2, not Math.ceil(5 * 0.2) = 1 expect(analysis.vitalTasks.map(t => t.id)).toContain('core_feature'); expect(analysis.vitalTasks.map(t => t.id)).toContain('payment_system'); // Should achieve true 80% value concentration expect(analysis.valueConcentration).toBeGreaterThanOrEqual(0.8); // Actual Pareto ratio should be 40% (2 out of 5), not forced 20% expect(analysis.actualParetoRatio).toBeCloseTo(0.4, 1); // Should provide actionable recommendations expect(analysis.recommendations.some(rec => rec.includes('low-ROI'))).toBe(true); expect(analysis.recommendations.length).toBeGreaterThan(0); }); test('should handle edge case where all tasks have similar value', async () => { const similarValueTasks: Task[] = Array.from({ length: 5 }, (_, i) => ({ id: `task_${i + 1}`, title: `Similar task ${i + 1}`, description: 'All tasks have similar business value', estimatedHours: 8, skills: [], dependencies: [], deliverable: `Output ${i + 1}`, businessValue: 20 // All similar value })); const plan: Plan = { requirements: [], tasks: similarValueTasks, timeline: { totalEstimate: '5 weeks', phases: [], criticalPath: [] }, risks: [], metadata: { planId: 'similar_value', generatedAt: new Date(), estimatedReadTime: '6 min', complexity: 'simple' } }; const context: ProjectContext = { userPriorities: [], timeline: 'normal', businessModel: 'saas', industry: 'technology' }; const analysis: ParetoAnalysis = await optimizer.analyzeParetoImpact(plan, context); // When values are similar, should recommend focusing on efficiency expect(analysis.recommendations.some(rec => rec.includes('similar value'))).toBe(true); expect(analysis.actualParetoRatio).toBeGreaterThanOrEqual(0.8); // Most tasks needed for 80% value expect(analysis.vitalTasks.length).toBeGreaterThanOrEqual(4); // Most tasks are vital }); }); // RED: This test MUST fail - generateResourceRecommendations doesn't exist yet describe('Actionable Resource Recommendations', () => { test('should provide specific guidance based on Pareto analysis results', () => { const analysis: ParetoAnalysis = { vitalTasks: [ { id: 'vital_1', title: 'Vital Task 1', businessValue: 60, effortComplexity: 10, roi: 6.0 }, { id: 'vital_2', title: 'Vital Task 2', businessValue: 40, effortComplexity: 15, roi: 2.67 } ], importantTasks: [ { id: 'important_1', title: 'Important Task 1', businessValue: 15, effortComplexity: 20, roi: 0.75 } ], trivialTasks: [ { id: 'trivial_1', title: 'Trivial Task 1', businessValue: 5, effortComplexity: 25, roi: 0.2 } ], actualParetoRatio: 0.5, // 2 out of 4 tasks valueConcentration: 0.83, // 100 out of 120 total value effortRequired: 0.42, // 25 out of 60 total effort recommendations: [] // Will be generated }; const recommendations = optimizer.generateResourceRecommendations( analysis.vitalTasks, [...analysis.vitalTasks, ...analysis.importantTasks, ...analysis.trivialTasks] ); expect(recommendations.some(rec => rec.includes('36% of effort'))).toBe(true); // Actual efficiency expect(recommendations.length).toBeGreaterThan(0); // Should identify low-ROI tasks to defer expect(recommendations.some(rec => rec.includes('low-ROI'))).toBe(true); expect(recommendations.some(rec => rec.includes('trivial_1'))).toBe(true); }); test('should warn when vital tasks require too much effort', () => { const analysis: ParetoAnalysis = { vitalTasks: [ { id: 'complex_vital', title: 'Complex Vital Task', businessValue: 80, effortComplexity: 50, roi: 1.6 } ], importantTasks: [ { id: 'simple_important', title: 'Simple Important Task', businessValue: 15, effortComplexity: 5, roi: 3.0 } ], trivialTasks: [ { id: 'simple_trivial', title: 'Simple Trivial Task', businessValue: 5, effortComplexity: 5, roi: 1.0 } ], actualParetoRatio: 0.33, // 1 out of 3 tasks valueConcentration: 0.8, // 80 out of 100 total value effortRequired: 0.83, // 50 out of 60 total effort - TOO HIGH recommendations: [] }; const recommendations = optimizer.generateResourceRecommendations( analysis.vitalTasks, [...analysis.vitalTasks, ...analysis.importantTasks, ...analysis.trivialTasks] ); expect(recommendations.some(rec => rec.includes('Warning'))).toBe(true); expect(recommendations.some(rec => rec.includes('83% of effort'))).toBe(true); expect(recommendations.some(rec => rec.includes('scope reduction'))).toBe(true); }); }); // RED: This test MUST fail - contextual adaptation doesn't exist yet describe('Context-Aware Value Calculation', () => { test('should adapt recommendations based on timeline urgency', () => { const task: Task = { id: 'feature_x', title: 'Complex feature', description: 'Feature that takes significant development time', estimatedHours: 40, skills: ['full-stack'], dependencies: [], deliverable: 'Complex feature' }; const urgentContext: ProjectContext = { userPriorities: [], timeline: 'urgent', // Urgent timeline businessModel: 'saas', industry: 'technology' }; const normalContext: ProjectContext = { userPriorities: [], timeline: 'normal', // Normal timeline businessModel: 'saas', industry: 'technology' }; const urgentValue = optimizer.calculateBusinessValue(task, urgentContext); const normalValue = optimizer.calculateBusinessValue(task, normalContext); // If it's on the critical path for urgent timeline, value should increase if (optimizer.isOnCriticalPath(task)) { expect(urgentValue).toBeGreaterThan(normalValue); } else { // If not on critical path, urgent timeline might decrease value (delay other priorities) expect(urgentValue).toBeLessThanOrEqual(normalValue); } }); test('should provide different recommendations for different business models', () => { const tasks = [ { id: 'analytics', title: 'User analytics', description: 'Track user behavior', estimatedHours: 12, skills: [], dependencies: [], deliverable: 'Analytics system' } ]; const saasContext: ProjectContext = { userPriorities: [], timeline: 'normal', businessModel: 'saas', // Analytics important for retention industry: 'technology' }; const ecommerceContext: ProjectContext = { userPriorities: [], timeline: 'normal', businessModel: 'ecommerce', // Analytics less critical than payment/inventory industry: 'retail' }; const saasValue = optimizer.calculateBusinessValue(tasks[0], saasContext); const ecommerceValue = optimizer.calculateBusinessValue(tasks[0], ecommerceContext); expect(saasValue).toBeGreaterThanOrEqual(ecommerceValue + 5); // At least 5 points difference }); }); });