UNPKG

@emmahyde/thinking-patterns

Version:

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

496 lines (495 loc) 24.1 kB
/** * Integration tests for all schemas * Tests schema interactions, compatibility, and integration patterns */ import { z } from 'zod'; import { SequentialThoughtSchema, StepRecommendationSchema, CurrentStepSchema } from '../../src/schemas/SequentialThoughtSchema.js'; import { ToolRecommendationSchema, ToolUsageHistorySchema, ToolContextSchema } from '../../src/schemas/ToolSchemas.js'; import { createMockThoughtData, createMockCurrentStep, generateLargeThoughtHistory, generateLargeToolRecommendations } from '../helpers/mockFactories.js'; describe('Schema Integration Tests', () => { describe('cross-schema validation', () => { it('should validate complete thought workflow with all schemas', () => { // Create a complete workflow that uses all schemas const toolRecommendations = [ ToolRecommendationSchema.parse({ toolName: "sequential_thinking", confidence: 0.9, rationale: "Best for systematic analysis", priority: 1, alternativeTools: ["mental_model", "debugging"] }), ToolRecommendationSchema.parse({ toolName: "mental_model", confidence: 0.7, rationale: "Good for understanding structure", priority: 2 }) ]; const currentStep = CurrentStepSchema.parse({ stepDescription: "Analyze the problem systematically", recommendedTools: toolRecommendations, expectedOutcome: "Clear understanding of the problem", nextStepConditions: ["Analysis complete", "Key insights identified"], stepNumber: 2, estimatedDuration: "15 minutes", complexityLevel: "high" }); const previousSteps = [ StepRecommendationSchema.parse({ stepDescription: "Initial problem identification", recommendedTools: toolRecommendations.slice(1), expectedOutcome: "Problem clearly defined", nextStepConditions: ["Problem scope understood"] }) ]; const toolHistory = [ ToolUsageHistorySchema.parse({ toolName: "mental_model", usedAt: "2024-01-01T10:00:00Z", effectivenessScore: 0.85 }), ToolUsageHistorySchema.parse({ toolName: "sequential_thinking", usedAt: "2024-01-01T10:15:00Z", effectivenessScore: 0.92 }) ]; const thoughtData = SequentialThoughtSchema.parse({ thought: "Based on the mental model analysis, I need to break this down systematically", thoughtNumber: 2, totalThoughts: 5, nextThoughtNeeded: true, isRevision: false, currentStep: currentStep, previousSteps: previousSteps, remainingSteps: ["Synthesis", "Conclusion", "Validation"], toolUsageHistory: toolHistory }); // Verify the integrated structure expect(thoughtData).toMatchObject({ thought: expect.any(String), thoughtNumber: expect.any(Number), totalThoughts: expect.any(Number), nextThoughtNeeded: expect.any(Boolean) }); expect(thoughtData.currentStep?.recommendedTools).toHaveLength(2); expect(thoughtData.previousSteps).toHaveLength(1); expect(thoughtData.toolUsageHistory).toHaveLength(2); expect(thoughtData.remainingSteps).toHaveLength(3); // Verify tool recommendations are properly nested expect(thoughtData.currentStep?.recommendedTools[0].toolName).toBe("sequential_thinking"); expect(thoughtData.currentStep?.recommendedTools[0].confidence).toBe(0.9); // Verify tool history integration expect(thoughtData.toolUsageHistory?.[1].toolName).toBe("sequential_thinking"); expect(thoughtData.toolUsageHistory?.[1].effectivenessScore).toBe(0.92); }); it('should validate tool context integration with other schemas', () => { const context = ToolContextSchema.parse({ availableTools: ["sequential_thinking", "mental_model", "debugging", "stochastic"], userPreferences: { style: "detailed", format: "structured", verbosity: "high" }, sessionHistory: [ "Previous analysis of similar problem", "Used mental model successfully", "Sequential thinking helped break down complexity" ], problemDomain: "complexAnalysis" }); // Create thought data that references tools from the context const thoughtWithContext = SequentialThoughtSchema.parse({ thought: "Using the available tools from context to approach this problem", thoughtNumber: 1, totalThoughts: 3, nextThoughtNeeded: true, currentStep: { stepDescription: "Select appropriate tools based on context", recommendedTools: context.availableTools.slice(0, 2).map((toolName) => ({ toolName: toolName, confidence: 0.8, rationale: `Tool ${toolName} is available in context`, priority: 1 })), expectedOutcome: "Optimal tool selection", nextStepConditions: ["Tools selected", "Context considered"] } }); expect(thoughtWithContext).toMatchObject({ thought: expect.any(String), thoughtNumber: expect.any(Number), totalThoughts: expect.any(Number), nextThoughtNeeded: expect.any(Boolean) }); expect(thoughtWithContext.currentStep?.recommendedTools).toHaveLength(2); expect(thoughtWithContext.currentStep?.recommendedTools[0].toolName).toBe("sequential_thinking"); expect(thoughtWithContext.currentStep?.recommendedTools[1].toolName).toBe("mental_model"); }); }); describe('schema composition and nesting', () => { it('should handle deeply nested schema structures', () => { const deeplyNestedThought = { thought: "This tests deep nesting of all schema types", thoughtNumber: 3, totalThoughts: 5, nextThoughtNeeded: true, currentStep: { stepDescription: "Deep analysis step", recommendedTools: [ { toolName: "sequential_thinking", confidence: 0.95, rationale: "Perfect for deep analysis", priority: 1, alternativeTools: ["mental_model", "debugging", "stochastic"] }, { toolName: "collaborative", confidence: 0.8, rationale: "Good for complex problems", priority: 2, alternativeTools: ["mental_model"] } ], expectedOutcome: "Comprehensive understanding", nextStepConditions: [ "All aspects analyzed", "Edge cases considered", "Alternative approaches evaluated" ], stepNumber: 3, estimatedDuration: "25 minutes", complexityLevel: "high" }, previousSteps: [ { stepDescription: "Initial exploration", recommendedTools: [ { toolName: "mental_model", confidence: 0.7, rationale: "Good starting point", priority: 1 } ], expectedOutcome: "Problem framework", nextStepConditions: ["Framework established"] }, { stepDescription: "Detailed analysis", recommendedTools: [ { toolName: "debugging", confidence: 0.85, rationale: "Systematic approach needed", priority: 1 }, { toolName: "sequential_thinking", confidence: 0.9, rationale: "Step-by-step breakdown", priority: 2 } ], expectedOutcome: "Detailed understanding", nextStepConditions: ["Analysis complete", "Patterns identified"] } ], remainingSteps: [ "Synthesis and integration", "Solution formulation" ], toolUsageHistory: [ { toolName: "mental_model", usedAt: "2024-01-01T09:00:00Z", effectivenessScore: 0.7 }, { toolName: "debugging", usedAt: "2024-01-01T09:30:00Z", effectivenessScore: 0.85 }, { toolName: "sequential_thinking", usedAt: "2024-01-01T10:00:00Z", effectivenessScore: 0.9 } ] }; const result = SequentialThoughtSchema.parse(deeplyNestedThought); expect(result).toMatchObject({ thought: expect.any(String), thoughtNumber: expect.any(Number), totalThoughts: expect.any(Number), nextThoughtNeeded: expect.any(Boolean) }); expect(result.currentStep?.recommendedTools).toHaveLength(2); expect(result.previousSteps).toHaveLength(2); expect(result.toolUsageHistory).toHaveLength(3); // Verify nested tool recommendations expect(result.currentStep?.recommendedTools[0].alternativeTools).toHaveLength(3); expect(result.previousSteps?.[1].recommendedTools).toHaveLength(2); // Verify consistency across the structure const allRecommendedTools = [ ...(result.currentStep?.recommendedTools || []), ...(result.previousSteps?.flatMap(step => step.recommendedTools) || []) ]; const allHistoryTools = result.toolUsageHistory?.map(h => h.toolName) || []; // Some recommended tools should appear in history const recommendedToolNames = allRecommendedTools.map(t => t.toolName); const intersection = recommendedToolNames.filter(name => allHistoryTools.includes(name)); expect(intersection.length).toBeGreaterThan(0); }); it('should validate schema arrays with mixed complexity', () => { const mixedComplexitySteps = [ // Simple step { stepDescription: "Simple initial step", recommendedTools: [{ toolName: "mental_model", confidence: 0.8, rationale: "Basic framework", priority: 1 }], expectedOutcome: "Basic understanding", nextStepConditions: ["Basics covered"] }, // Complex step { stepDescription: "Complex analytical step", recommendedTools: [ { toolName: "sequential_thinking", confidence: 0.95, rationale: "Systematic breakdown needed", priority: 1, alternativeTools: ["debugging", "stochastic", "collaborative"] }, { toolName: "debugging", confidence: 0.85, rationale: "Error checking required", priority: 2, alternativeTools: ["sequential_thinking"] } ], expectedOutcome: "Comprehensive analysis complete", nextStepConditions: [ "All variables considered", "Edge cases identified", "Alternative solutions explored" ] } ]; // Validate each step individually const validatedSteps = mixedComplexitySteps.map(step => StepRecommendationSchema.parse(step)); expect(validatedSteps).toHaveLength(2); expect(validatedSteps[0].recommendedTools).toHaveLength(1); expect(validatedSteps[1].recommendedTools).toHaveLength(2); expect(validatedSteps[1].recommendedTools[0].alternativeTools).toHaveLength(3); // Use in a ThoughtData structure const thoughtWithMixedSteps = SequentialThoughtSchema.parse({ thought: "Processing steps of varying complexity", thoughtNumber: 2, totalThoughts: 3, nextThoughtNeeded: true, previousSteps: validatedSteps }); expect(thoughtWithMixedSteps.previousSteps).toHaveLength(2); }); }); describe('schema performance and scalability', () => { it('should handle large-scale schema validation efficiently', () => { const start = Date.now(); // Generate large datasets const largeThoughtHistory = generateLargeThoughtHistory(50); const largeToolRecommendations = generateLargeToolRecommendations(25); // Validate all thought data const validatedThoughts = largeThoughtHistory.map(thought => SequentialThoughtSchema.parse(thought)); // Validate all tool recommendations const validatedRecommendations = largeToolRecommendations.map(rec => ToolRecommendationSchema.parse(rec)); const elapsed = Date.now() - start; expect(validatedThoughts).toHaveLength(50); expect(validatedRecommendations).toHaveLength(25); expect(elapsed).toBeLessThan(2000); // Should complete within 2 seconds }); it('should handle complex nested structures without performance degradation', () => { const complexThoughtData = createMockThoughtData({ currentStep: createMockCurrentStep({ recommendedTools: generateLargeToolRecommendations(10) }), previousSteps: Array.from({ length: 5 }, () => ({ stepDescription: "Previous step", recommendedTools: generateLargeToolRecommendations(5), expectedOutcome: "Step outcome", nextStepConditions: ["Condition 1", "Condition 2"] })), toolUsageHistory: Array.from({ length: 20 }, (_, i) => ({ toolName: `tool_${i}`, usedAt: new Date().toISOString(), effectivenessScore: Math.random() })) }); const start = Date.now(); const result = SequentialThoughtSchema.parse(complexThoughtData); const elapsed = Date.now() - start; expect(result).toMatchObject({ thought: expect.any(String), thoughtNumber: expect.any(Number), totalThoughts: expect.any(Number), nextThoughtNeeded: expect.any(Boolean) }); expect(result.currentStep?.recommendedTools).toHaveLength(10); expect(result.previousSteps).toHaveLength(5); expect(result.toolUsageHistory).toHaveLength(20); expect(elapsed).toBeLessThan(100); // Should be very fast for single validation }); }); describe('schema error handling and recovery', () => { it('should provide detailed error paths for nested validation failures', () => { const invalidNestedStructure = { thought: "Valid thought", thoughtNumber: 1, totalThoughts: 3, nextThoughtNeeded: true, currentStep: { stepDescription: "Valid description", recommendedTools: [ { toolName: "validTool", confidence: 1.5, // Invalid - greater than 1 rationale: "Valid rationale", priority: 1 }, { toolName: "", // Invalid - empty string confidence: 0.8, rationale: "Valid rationale", priority: "invalid" // Invalid - should be number } ], expectedOutcome: "Valid outcome", nextStepConditions: ["Valid condition"] } }; try { SequentialThoughtSchema.parse(invalidNestedStructure); expect.fail('Should have thrown validation error'); } catch (error) { expect(error.errors).toBeDefined(); // Should have errors for nested paths const errorPaths = error.errors.map((err) => err.path.join('.')); expect(errorPaths.some((path) => path.includes('currentStep'))).toBe(true); expect(errorPaths.some((path) => path.includes('recommendedTools'))).toBe(true); } }); it('should validate partial schemas when some optional fields fail', () => { const partiallyValidData = { thought: "Valid thought", thoughtNumber: 1, totalThoughts: 3, nextThoughtNeeded: true, // Valid optional field isRevision: false, // Invalid optional field that should be ignored in this test toolUsageHistory: [ { toolName: "validTool", usedAt: "2024-01-01T10:00:00Z", effectivenessScore: 0.8 }, { toolName: "anotherTool", usedAt: "invalidDate", // This might be okay as it's just a string effectivenessScore: 1.2 // This might be okay as there's no constraint } ] }; // This should pass because the schema doesn't enforce date format or score range for tool usage const result = SequentialThoughtSchema.parse(partiallyValidData); expect(result).toMatchObject({ thought: expect.any(String), thoughtNumber: expect.any(Number), totalThoughts: expect.any(Number), nextThoughtNeeded: expect.any(Boolean) }); expect(result.toolUsageHistory).toHaveLength(2); }); }); describe('schema compatibility and evolution', () => { it('should maintain backward compatibility with simpler schema versions', () => { // Test with minimal required fields (like an older version might have) const minimalThought = { thought: "Minimal thought for compatibility test", thoughtNumber: 1, totalThoughts: 1, nextThoughtNeeded: false }; const result = SequentialThoughtSchema.parse(minimalThought); expect(result).toMatchObject({ thought: expect.any(String), thoughtNumber: expect.any(Number), totalThoughts: expect.any(Number), nextThoughtNeeded: expect.any(Boolean) }); // All optional fields should be undefined expect(result.isRevision).toBeUndefined(); expect(result.currentStep).toBeUndefined(); expect(result.previousSteps).toBeUndefined(); expect(result.toolUsageHistory).toBeUndefined(); }); it('should support schema extension patterns', () => { // Test that the schema can be extended for future use const extendedThoughtSchema = SequentialThoughtSchema.extend({ // Future fields that might be added experimentalField: z.string().optional(), version: z.string().default("1.0") }); const extendedData = { thought: "Extended thought", thoughtNumber: 1, totalThoughts: 1, nextThoughtNeeded: false, experimentalField: "testValue", version: "1.1" }; const result = extendedThoughtSchema.parse(extendedData); expect(result.thought).toBe("Extended thought"); expect(result.experimentalField).toBe("testValue"); expect(result.version).toBe("1.1"); }); it('should handle schema transformations for data migration', () => { // Simulate old data format that needs transformation const oldFormatData = { // Old camelCase format thought: "Legacy data", thoughtNumber: 1, // Old field name totalThoughts: 3, // Old field name nextThoughtNeeded: true, // Old field name isRevision: false // Old field name }; // Transform to new format const transformedData = { thought: oldFormatData.thought, thoughtNumber: oldFormatData.thoughtNumber, totalThoughts: oldFormatData.totalThoughts, nextThoughtNeeded: oldFormatData.nextThoughtNeeded, isRevision: oldFormatData.isRevision }; const result = SequentialThoughtSchema.parse(transformedData); expect(result).toMatchObject({ thought: expect.any(String), thoughtNumber: expect.any(Number), totalThoughts: expect.any(Number), nextThoughtNeeded: expect.any(Boolean) }); expect(result.thoughtNumber).toBe(1); expect(result.totalThoughts).toBe(3); expect(result.nextThoughtNeeded).toBe(true); expect(result.isRevision).toBe(false); }); }); });