UNPKG

@emmahyde/thinking-patterns

Version:

MCP server combining systematic thinking, mental models, debugging approaches, and stochastic algorithms for comprehensive cognitive pattern support

350 lines (349 loc) 15.7 kB
/** * Tests for RecursiveThinkingServer * Tests recursive problem-solving and algorithmic thinking */ import { RecursiveThinkingServer } from '../../src/servers/RecursiveThinkingServer.js'; describe('RecursiveThinkingServer', () => { let server; beforeEach(() => { server = new RecursiveThinkingServer(); }); describe('process', () => { it('should process valid recursive thinking data correctly', () => { const validInput = { problem: 'Calculate factorial of a number', baseCases: [ { condition: 'n <= 1', solution: 'return 1', complexity: 'O(1)', examples: ['factorial(0) = 1', 'factorial(1) = 1'] } ], recursiveCases: [ { condition: 'n > 1', decomposition: 'Break down into factorial(n-1)', recombination: 'Multiply n by factorial(n-1)', reductionFactor: 'Problem size reduced by 1 each call', examples: ['factorial(5) = 5 * factorial(4)'] } ], terminationConditions: ['n <= 1'], complexityAnalysis: { timeComplexity: 'O(n)', spaceComplexity: 'O(n)', maxStackDepth: 'n', worstCaseInput: 'Large positive integer', bestCaseInput: 'n <= 1' }, optimizations: [ { technique: 'memoization', description: 'Cache previously computed results to avoid redundant calculations', complexityImprovement: 'Reduces time complexity for repeated calls', tradeoffs: ['Increases space complexity for caching', 'Memory overhead'] }, { technique: 'tail-recursion', description: 'Convert to tail-recursive form to optimize stack usage', complexityImprovement: 'Reduces space complexity in languages with tail-call optimization', tradeoffs: ['May be less intuitive to understand'] } ], iterativeAlternatives: [ { approach: 'loop-based', description: 'Use a simple for loop to calculate factorial', advantages: ['No stack overflow risk', 'Better space complexity', 'Easier to understand'], disadvantages: ['Less elegant for naturally recursive problems'], complexity: { timeComplexity: 'O(n)', spaceComplexity: 'O(1)' } } ] }; const result = server.process(validInput); expect(result.problem).toBe('Calculate factorial of a number'); expect(result.baseCases).toHaveLength(1); expect(result.recursiveCases).toHaveLength(1); expect(result.terminationConditions).toHaveLength(1); expect(result.iterativeAlternatives).toHaveLength(1); expect(result.status).toBe('success'); expect(result.timestamp).toBeDefined(); }); it('should handle minimal recursive thinking data', () => { const minimalInput = { problem: 'Simple recursive problem', baseCases: [ { condition: 'base condition', solution: 'base solution' } ], recursiveCases: [ { condition: 'recursive condition', decomposition: 'break down problem', recombination: 'combine results' } ], terminationConditions: ['base condition'] }; const result = server.process(minimalInput); expect(result.problem).toBe('Simple recursive problem'); expect(result.baseCases).toHaveLength(1); expect(result.recursiveCases).toHaveLength(1); expect(result.terminationConditions).toHaveLength(1); expect(result.status).toBe('success'); }); it('should handle multiple base and recursive cases', () => { const input = { problem: 'Fibonacci sequence calculation', baseCases: [ { condition: 'n == 0', solution: 'return 0', examples: ['fib(0) = 0'] }, { condition: 'n == 1', solution: 'return 1', examples: ['fib(1) = 1'] } ], recursiveCases: [ { condition: 'n > 1', decomposition: 'Break into fib(n-1) and fib(n-2)', recombination: 'Add fib(n-1) + fib(n-2)', examples: ['fib(5) = fib(4) + fib(3)'] } ], terminationConditions: ['n == 0', 'n == 1'], complexityAnalysis: { timeComplexity: 'O(2^n)', spaceComplexity: 'O(n)', maxStackDepth: 'n', worstCaseInput: 'Large positive integer' } }; const result = server.process(input); expect(result.baseCases).toHaveLength(2); expect(result.recursiveCases).toHaveLength(1); expect(result.terminationConditions).toHaveLength(2); }); it('should handle validation errors gracefully', () => { const invalidInput = { // Missing required problem field baseCases: [], recursiveCases: [], terminationConditions: [] }; const result = server.run(invalidInput); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Validation failed'); }); it('should return properly formatted output', () => { const input = { problem: 'Test problem', baseCases: [ { condition: 'test condition', solution: 'test solution' } ], recursiveCases: [ { condition: 'recursive condition', decomposition: 'test decomposition', recombination: 'test recombination' } ], terminationConditions: ['test condition'] }; const result = server.process(input); expect(result).toHaveProperty('problem'); expect(result).toHaveProperty('baseCases'); expect(result).toHaveProperty('recursiveCases'); expect(result).toHaveProperty('terminationConditions'); expect(result).toHaveProperty('status'); expect(result).toHaveProperty('timestamp'); expect(Array.isArray(result.baseCases)).toBe(true); expect(Array.isArray(result.recursiveCases)).toBe(true); expect(Array.isArray(result.terminationConditions)).toBe(true); }); }); describe('edge cases and error handling', () => { it('should handle null input', () => { expect(() => server.process(null)).toThrow(); }); it('should handle undefined input', () => { expect(() => server.process(undefined)).toThrow(); }); it('should handle empty object input', () => { const result = server.run({}); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Validation failed'); }); it('should handle invalid field types', () => { const invalidInput = { problem: 123, // Should be string baseCases: 'not-array', // Should be array recursiveCases: [], terminationConditions: [] }; expect(() => server.process(invalidInput)).toThrow(); }); it('should handle empty base cases and recursive cases', () => { const input = { problem: 'Problem with no cases', baseCases: [], recursiveCases: [], terminationConditions: [] }; const result = server.process(input); expect(result.baseCases).toHaveLength(0); expect(result.recursiveCases).toHaveLength(0); expect(result.terminationConditions).toHaveLength(0); expect(result.status).toBe('success'); }); it('should handle complex optimization scenarios', () => { const input = { problem: 'Complex recursive algorithm with many optimizations', baseCases: [ { condition: 'input.length <= 1', solution: 'return input' } ], recursiveCases: [ { condition: 'input.length > 1', decomposition: 'Split input into left and right halves', recombination: 'Merge processed left and right results' } ], terminationConditions: ['input.length <= 1'], optimizations: Array.from({ length: 5 }, (_, i) => ({ technique: `optimization-${i + 1}`, description: `Description for optimization ${i + 1}`, complexityImprovement: `Improvement for optimization ${i + 1}`, tradeoffs: [`Tradeoff 1 for optimization ${i + 1}`, `Tradeoff 2 for optimization ${i + 1}`] })), iterativeAlternatives: Array.from({ length: 3 }, (_, i) => ({ approach: `iterative-approach-${i + 1}`, description: `Description for iterative approach ${i + 1}`, advantages: [`Advantage 1 for approach ${i + 1}`, `Advantage 2 for approach ${i + 1}`], disadvantages: [`Disadvantage 1 for approach ${i + 1}`], complexity: { timeComplexity: 'O(n)', spaceComplexity: 'O(1)' } })) }; const result = server.process(input); expect(result.optimizations).toHaveLength(5); expect(result.iterativeAlternatives).toHaveLength(3); expect(result.status).toBe('success'); }); it('should handle detailed complexity analysis', () => { const input = { problem: 'Algorithm with detailed complexity analysis', baseCases: [ { condition: 'n <= threshold', solution: 'direct_solution(n)' } ], recursiveCases: [ { condition: 'n > threshold', decomposition: 'Split problem into two halves', recombination: 'Combine results from both halves' } ], terminationConditions: ['n <= threshold'], complexityAnalysis: { timeComplexity: 'O(n log n)', spaceComplexity: 'O(log n)', maxStackDepth: 'log n', worstCaseInput: 'Large input requiring maximum recursion depth', bestCaseInput: 'Input at or below threshold' } }; const result = server.process(input); expect(result.complexityAnalysis?.timeComplexity).toBe('O(n log n)'); expect(result.complexityAnalysis?.spaceComplexity).toBe('O(log n)'); expect(result.complexityAnalysis?.maxStackDepth).toBe('log n'); }); it('should handle very long problem descriptions', () => { const longDescription = 'A'.repeat(5000); const input = { problem: longDescription, baseCases: [ { condition: 'base condition', solution: 'base solution' } ], recursiveCases: [ { condition: 'recursive condition', decomposition: longDescription, recombination: 'combine results' } ], terminationConditions: ['base condition'] }; const result = server.process(input); expect(result.problem).toBe(longDescription); expect(result.recursiveCases[0].decomposition).toBe(longDescription); expect(result.status).toBe('success'); }); it('should handle additional optional fields', () => { const input = { problem: 'Problem with all optional fields', problemId: 'recursive-001', domain: 'algorithms', baseCases: [ { id: 'base-1', condition: 'n <= 1', solution: 'return 1', complexity: 'O(1)', examples: ['n=0', 'n=1'] } ], recursiveCases: [ { id: 'recursive-1', condition: 'n > 1', decomposition: 'factorial(n-1)', recombination: 'n * result', reductionFactor: '1', examples: ['n=5 -> 5 * factorial(4)'] } ], terminationConditions: ['n <= 1'], stackOverflowRisk: 'medium', inputSizeLimit: '10000', tailRecursionOptimizable: true, invariants: ['n >= 0', 'result > 0'], recursionPattern: 'linear', similarProblems: ['power calculation', 'sum of numbers'], learningObjectives: ['understand recursion', 'analyze complexity'], commonMistakes: ['forgetting base case', 'infinite recursion'] }; const result = server.process(input); expect(result.problemId).toBe('recursive-001'); expect(result.domain).toBe('algorithms'); expect(result.stackOverflowRisk).toBe('medium'); expect(result.tailRecursionOptimizable).toBe(true); expect(result.invariants).toHaveLength(2); expect(result.recursionPattern).toBe('linear'); expect(result.status).toBe('success'); }); }); });