@emmahyde/thinking-patterns
Version:
MCP server combining systematic thinking, mental models, debugging approaches, and stochastic algorithms for comprehensive cognitive pattern support
395 lines (394 loc) • 16.9 kB
JavaScript
import { DecisionFrameworkServer } from '../../src/servers/DecisionFrameworkServer.js';
describe('DecisionFrameworkServer', () => {
let server;
beforeEach(() => {
server = new DecisionFrameworkServer();
});
describe('process', () => {
it('should process complete decision framework data correctly', () => {
const input = {
decisionStatement: 'Choose the best cloud provider for our application',
decisionId: 'cloud-decision-001',
analysisType: 'multi-criteria',
stage: 'evaluation',
iteration: 2,
nextStageNeeded: true,
options: [
{
id: 'aws',
name: 'Amazon Web Services',
description: 'Comprehensive cloud platform with extensive services'
},
{
id: 'azure',
name: 'Microsoft Azure',
description: 'Enterprise-focused cloud platform with strong hybrid capabilities'
},
{
id: 'gcp',
name: 'Google Cloud Platform',
description: 'Data and AI-focused cloud platform with competitive pricing'
}
],
criteria: [
{
id: 'cost',
name: 'Cost',
description: 'Total cost of ownership including compute, storage, and data transfer',
weight: 0.3,
evaluationMethod: 'quantitative'
},
{
id: 'performance',
name: 'Performance',
description: 'Latency, throughput, and reliability metrics',
weight: 0.25,
evaluationMethod: 'quantitative'
},
{
id: 'security',
name: 'Security',
description: 'Security features, compliance, and data protection',
weight: 0.25,
evaluationMethod: 'qualitative'
},
{
id: 'integration',
name: 'Integration',
description: 'Ease of integration with existing systems',
weight: 0.2,
evaluationMethod: 'qualitative'
}
],
criteriaEvaluations: [
{
criterionId: 'cost',
optionId: 'aws',
score: 0.7,
justification: 'Competitive pricing with reserved instances'
},
{
criterionId: 'cost',
optionId: 'azure',
score: 0.8,
justification: 'Good enterprise pricing and hybrid cost benefits'
},
{
criterionId: 'cost',
optionId: 'gcp',
score: 0.9,
justification: 'Most competitive pricing for our workload'
}
],
stakeholders: ['Engineering Team', 'Finance Department', 'Security Team'],
constraints: ['Budget under $10K/month', 'GDPR compliance required'],
timeHorizon: '3 years',
riskTolerance: 'risk-neutral'
};
const result = server.process(input);
expect(result.status).toBe('success');
expect(result.decisionStatement).toBe('Choose the best cloud provider for our application');
expect(result.decisionId).toBe('cloud-decision-001');
expect(result.analysisType).toBe('multi-criteria');
expect(result.stage).toBe('evaluation');
expect(result.iteration).toBe(2);
expect(result.nextStageNeeded).toBe(true);
expect(result.optionCount).toBe(3);
expect(result.criteriaCount).toBe(4);
expect(result.hasStakeholders).toBe(true);
expect(result.hasConstraints).toBe(true);
expect(result.hasTimeHorizon).toBe(true);
expect(result.hasRiskTolerance).toBe(true);
expect(result.hasCriteriaEvaluations).toBe(true);
expect(result.timestamp).toBeDefined();
});
it('should handle minimal decision framework data', () => {
const input = {
decisionStatement: 'Simple binary choice',
decisionId: 'simple-decision-001',
analysisType: 'expected-utility',
stage: 'problem-definition',
iteration: 1,
nextStageNeeded: true,
options: [
{
id: 'option-a',
name: 'Option A',
description: 'First choice'
},
{
id: 'option-b',
name: 'Option B',
description: 'Second choice'
}
]
};
const result = server.process(input);
expect(result.status).toBe('success');
expect(result.decisionStatement).toBe('Simple binary choice');
expect(result.analysisType).toBe('expected-utility');
expect(result.stage).toBe('problem-definition');
expect(result.optionCount).toBe(2);
expect(result.criteriaCount).toBe(0);
expect(result.hasStakeholders).toBe(false);
expect(result.hasConstraints).toBe(false);
});
it('should handle expected utility analysis', () => {
const input = {
decisionStatement: 'Investment decision with uncertainty',
decisionId: 'investment-decision-001',
analysisType: 'expected-utility',
stage: 'analysis',
iteration: 3,
nextStageNeeded: true,
options: [
{
id: 'conservative',
name: 'Conservative Investment',
description: 'Low risk, low return investment'
},
{
id: 'aggressive',
name: 'Aggressive Investment',
description: 'High risk, high return investment'
}
],
possibleOutcomes: [
{
id: 'market-up',
description: 'Market performs well',
probability: 0.6,
value: 15000,
optionId: 'aggressive',
confidenceInEstimate: 0.8
},
{
id: 'market-down',
description: 'Market performs poorly',
probability: 0.4,
value: -5000,
optionId: 'aggressive',
confidenceInEstimate: 0.7
},
{
id: 'conservative-outcome',
description: 'Stable conservative return',
probability: 0.9,
value: 3000,
optionId: 'conservative',
confidenceInEstimate: 0.95
}
],
riskTolerance: 'risk-averse'
};
const result = server.process(input);
expect(result.status).toBe('success');
expect(result.analysisType).toBe('expected-utility');
expect(result.riskTolerance).toBe('risk-averse');
expect(result.hasPossibleOutcomes).toBe(true);
});
it('should handle decision with information gaps', () => {
const input = {
decisionStatement: 'Technology adoption with uncertainties',
decisionId: 'tech-adoption-001',
analysisType: 'maximin',
stage: 'evaluation',
iteration: 2,
nextStageNeeded: true,
options: [
{
id: 'new-tech',
name: 'New Technology',
description: 'Cutting-edge but unproven technology'
},
{
id: 'current-tech',
name: 'Current Technology',
description: 'Proven but potentially outdated technology'
}
],
informationGaps: [
{
description: 'Long-term support and maintenance costs for new technology',
impact: 0.8,
researchMethod: 'Vendor interviews and case studies'
},
{
description: 'Market adoption rate and ecosystem development',
impact: 0.6,
researchMethod: 'Industry reports and trend analysis'
}
]
};
const result = server.process(input);
expect(result.status).toBe('success');
expect(result.analysisType).toBe('maximin');
expect(result.hasInformationGaps).toBe(true);
expect(result.informationGaps).toHaveLength(2);
});
it('should handle recommendation stage', () => {
const input = {
decisionStatement: 'Final recommendation for hiring platform',
decisionId: 'hiring-platform-001',
analysisType: 'multi-criteria',
stage: 'recommendation',
iteration: 4,
nextStageNeeded: false,
options: [
{
id: 'platform-a',
name: 'Platform A',
description: 'Comprehensive HR platform'
},
{
id: 'platform-b',
name: 'Platform B',
description: 'Lightweight recruiting tool'
}
],
recommendation: 'Choose Platform A due to superior integration capabilities and long-term scalability',
sensitivityInsights: [
'Decision is robust across different weight assignments',
'Platform A maintains advantage even with 20% cost increase'
]
};
const result = server.process(input);
expect(result.status).toBe('success');
expect(result.stage).toBe('recommendation');
expect(result.nextStageNeeded).toBe(false);
expect(result.hasRecommendation).toBe(true);
expect(result.hasSensitivityInsights).toBe(true);
expect(result.recommendation).toContain('Platform A');
});
});
describe('edge cases and error handling', () => {
it('should handle null input', () => {
const response = server.run(null);
expect(response.isError).toBe(true);
});
it('should handle undefined input', () => {
const response = server.run(undefined);
expect(response.isError).toBe(true);
});
it('should handle empty object input', () => {
const response = server.run({});
expect(response.isError).toBe(true);
});
it('should handle missing required fields', () => {
const incompleteInput = {
decisionStatement: 'Test decision'
// Missing required fields
};
const response = server.run(incompleteInput);
expect(response.isError).toBe(true);
});
it('should handle invalid analysis type', () => {
const invalidInput = {
decisionStatement: 'Test decision',
decisionId: 'test-001',
analysisType: 'invalid-type',
stage: 'analysis',
iteration: 1,
nextStageNeeded: true,
options: []
};
const response = server.run(invalidInput);
expect(response.isError).toBe(true);
});
it('should handle invalid stage', () => {
const invalidInput = {
decisionStatement: 'Test decision',
decisionId: 'test-001',
analysisType: 'multi-criteria',
stage: 'invalid-stage',
iteration: 1,
nextStageNeeded: true,
options: []
};
const response = server.run(invalidInput);
expect(response.isError).toBe(true);
});
});
describe('output formatting', () => {
it('should return properly formatted JSON content', () => {
const input = {
decisionStatement: 'Test decision',
decisionId: 'test-001',
analysisType: 'multi-criteria',
stage: 'analysis',
iteration: 1,
nextStageNeeded: true,
options: [
{
id: 'option-1',
name: 'Option 1',
description: 'First option'
}
]
};
const response = server.run(input);
expect(response.content).toHaveLength(1);
expect(response.content[0].type).toBe('text');
expect(() => JSON.parse(response.content[0].text)).not.toThrow();
const parsedContent = JSON.parse(response.content[0].text);
expect(parsedContent.status).toBe('success');
expect(parsedContent.decisionStatement).toBe('Test decision');
expect(parsedContent.decisionId).toBe('test-001');
expect(parsedContent.analysisType).toBe('multi-criteria');
expect(parsedContent.optionCount).toBe(1);
expect(parsedContent.timestamp).toBeDefined();
});
});
describe('analysis types', () => {
it('should handle satisficing analysis', () => {
const input = {
decisionStatement: 'Find acceptable solution quickly',
decisionId: 'satisficing-001',
analysisType: 'satisficing',
stage: 'analysis',
iteration: 1,
nextStageNeeded: true,
options: [
{
id: 'quick-fix',
name: 'Quick Fix',
description: 'Fast implementation, meets minimum requirements'
},
{
id: 'optimal',
name: 'Optimal Solution',
description: 'Best possible solution but takes longer'
}
]
};
const result = server.process(input);
expect(result.status).toBe('success');
expect(result.analysisType).toBe('satisficing');
});
it('should handle minimax-regret analysis', () => {
const input = {
decisionStatement: 'Minimize worst-case regret',
decisionId: 'minimax-regret-001',
analysisType: 'minimax-regret',
stage: 'analysis',
iteration: 1,
nextStageNeeded: true,
options: [
{
id: 'conservative',
name: 'Conservative Choice',
description: 'Safe option with limited upside'
},
{
id: 'aggressive',
name: 'Aggressive Choice',
description: 'Risky option with high potential'
}
]
};
const result = server.process(input);
expect(result.status).toBe('success');
expect(result.analysisType).toBe('minimax-regret');
});
});
});