UNPKG

@emmahyde/thinking-patterns

Version:

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

698 lines (697 loc) 31.1 kB
import { DomainModelingSchema, EntitySchema, RelationshipSchema, DomainRuleSchema, BoundarySchema, ModelValidationSchema } from '../../src/schemas/DomainModelingSchema.js'; describe('DomainModelingSchema', () => { describe('EntitySchema', () => { it('should validate a valid entity', () => { const validEntity = { name: 'User', description: 'A system user', attributes: ['id', 'name', 'email'] }; expect(() => EntitySchema.parse(validEntity)).not.toThrow(); }); it('should validate entity with optional fields', () => { const validEntity = { id: 'user-entity', name: 'User', description: 'A system user', attributes: ['id', 'name', 'email'], behaviors: ['login', 'logout', 'updateProfile'], constraints: ['email must be unique'], invariants: ['id is immutable'] }; expect(() => EntitySchema.parse(validEntity)).not.toThrow(); }); it('should reject entity without required fields', () => { const invalidEntity = { name: 'User' // missing description and attributes }; expect(() => EntitySchema.parse(invalidEntity)).toThrow(); }); it('should reject entity with empty attributes array', () => { const invalidEntity = { name: 'User', description: 'A system user', attributes: [] }; expect(() => EntitySchema.parse(invalidEntity)).not.toThrow(); // Empty arrays are allowed }); }); describe('RelationshipSchema', () => { it('should validate a valid relationship', () => { const validRelationship = { name: 'UserOrder', type: 'one-to-many', sourceEntity: 'User', targetEntity: 'Order', description: 'A user can have multiple orders' }; expect(() => RelationshipSchema.parse(validRelationship)).not.toThrow(); }); it('should validate all relationship types', () => { const relationshipTypes = [ 'one-to-one', 'one-to-many', 'many-to-many', 'inheritance', 'composition', 'aggregation', 'dependency' ]; relationshipTypes.forEach(type => { const relationship = { name: 'TestRelationship', type, sourceEntity: 'EntityA', targetEntity: 'EntityB', description: 'Test relationship' }; expect(() => RelationshipSchema.parse(relationship)).not.toThrow(); }); }); it('should reject invalid relationship type', () => { const invalidRelationship = { name: 'TestRelationship', type: 'invalid-type', sourceEntity: 'EntityA', targetEntity: 'EntityB', description: 'Test relationship' }; expect(() => RelationshipSchema.parse(invalidRelationship)).toThrow(); }); it('should validate relationship with optional fields', () => { const validRelationship = { id: 'rel-1', name: 'UserOrder', type: 'one-to-many', sourceEntity: 'User', targetEntity: 'Order', description: 'A user can have multiple orders', constraints: ['cascade delete'], cardinality: '1:N' }; expect(() => RelationshipSchema.parse(validRelationship)).not.toThrow(); }); }); describe('DomainRuleSchema', () => { it('should validate a valid domain rule', () => { const validRule = { name: 'Order Total Rule', description: 'Order total must be positive', type: 'business-rule', entities: ['Order'], condition: 'total > 0' }; expect(() => DomainRuleSchema.parse(validRule)).not.toThrow(); }); it('should validate all rule types', () => { const ruleTypes = ['business-rule', 'validation-rule', 'constraint', 'invariant', 'axiom']; ruleTypes.forEach(type => { const rule = { name: 'Test Rule', description: 'Test rule description', type, entities: ['TestEntity'], condition: 'test condition' }; expect(() => DomainRuleSchema.parse(rule)).not.toThrow(); }); }); it('should validate all priority levels', () => { const priorities = ['low', 'medium', 'high', 'critical']; priorities.forEach(priority => { const rule = { name: 'Test Rule', description: 'Test rule description', type: 'business-rule', entities: ['TestEntity'], condition: 'test condition', priority }; expect(() => DomainRuleSchema.parse(rule)).not.toThrow(); }); }); it('should validate rule with optional fields', () => { const validRule = { id: 'rule-1', name: 'Order Total Rule', description: 'Order total must be positive', type: 'business-rule', entities: ['Order', 'OrderItem'], condition: 'total > 0', consequence: 'reject order', priority: 'high' }; expect(() => DomainRuleSchema.parse(validRule)).not.toThrow(); }); }); describe('BoundarySchema', () => { it('should validate a valid boundary', () => { const validBoundary = { name: 'Order Management', description: 'Handles order processing', includedEntities: ['Order', 'OrderItem', 'Customer'] }; expect(() => BoundarySchema.parse(validBoundary)).not.toThrow(); }); it('should validate boundary with optional fields', () => { const validBoundary = { name: 'Order Management', description: 'Handles order processing', includedEntities: ['Order', 'OrderItem', 'Customer'], excludedConcepts: ['Payment', 'Shipping'], interfaces: ['OrderAPI', 'CustomerAPI'], responsibilities: ['Process orders', 'Validate customers'] }; expect(() => BoundarySchema.parse(validBoundary)).not.toThrow(); }); it('should reject boundary without required fields', () => { const invalidBoundary = { name: 'Order Management' // missing description and includedEntities }; expect(() => BoundarySchema.parse(invalidBoundary)).toThrow(); }); }); describe('ModelValidationSchema', () => { it('should validate a valid model validation', () => { const validValidation = { completeness: 0.85, consistency: 0.92, correctness: 0.78 }; expect(() => ModelValidationSchema.parse(validValidation)).not.toThrow(); }); it('should validate model validation with optional fields', () => { const validValidation = { completeness: 0.85, consistency: 0.92, correctness: 0.78, issues: ['Missing relationship between User and Profile'], suggestions: ['Add validation rules for email format'] }; expect(() => ModelValidationSchema.parse(validValidation)).not.toThrow(); }); it('should reject validation scores outside 0-1 range', () => { const invalidValidation = { completeness: 1.5, consistency: 0.92, correctness: 0.78 }; expect(() => ModelValidationSchema.parse(invalidValidation)).toThrow(); const invalidValidation2 = { completeness: 0.85, consistency: -0.1, correctness: 0.78 }; expect(() => ModelValidationSchema.parse(invalidValidation2)).toThrow(); }); }); describe('DomainModelingSchema', () => { it('should validate a minimal valid domain model', () => { const validModel = { domainName: 'E-commerce', description: 'Online shopping domain', modelingId: 'model-123', iteration: 1, stage: 'conceptual', entities: [{ name: 'Product', description: 'A sellable item', attributes: ['id', 'name', 'price'] }], abstractionLevel: 'high', paradigm: 'object-oriented', nextStageNeeded: true }; expect(() => DomainModelingSchema.parse(validModel)).not.toThrow(); }); it('should validate a complete domain model', () => { const completeModel = { domainName: 'E-commerce', description: 'Online shopping domain', modelingId: 'model-123', iteration: 2, stage: 'logical', entities: [ { id: 'user-entity', name: 'User', description: 'System user', attributes: ['id', 'name', 'email'], behaviors: ['login', 'logout'], constraints: ['email unique'], invariants: ['id immutable'] }, { name: 'Order', description: 'Customer order', attributes: ['id', 'total', 'date'] } ], relationships: [{ id: 'user-order-rel', name: 'UserOrders', type: 'one-to-many', sourceEntity: 'User', targetEntity: 'Order', description: 'User can have multiple orders', constraints: ['cascade delete'], cardinality: '1:N' }], domainRules: [{ id: 'order-total-rule', name: 'Positive Order Total', description: 'Order total must be positive', type: 'business-rule', entities: ['Order'], condition: 'total > 0', consequence: 'reject order', priority: 'high' }], boundaries: { name: 'Order Management', description: 'Order processing boundary', includedEntities: ['User', 'Order'], excludedConcepts: ['Payment'], interfaces: ['OrderAPI'], responsibilities: ['Process orders'] }, assumptions: ['Users have valid email addresses'], stakeholders: ['Customers', 'Sales Team', 'IT Department'], useCases: ['Place Order', 'View Order History'], modelValidation: { completeness: 0.85, consistency: 0.92, correctness: 0.78, issues: ['Missing payment entity'], suggestions: ['Add payment processing'] }, abstractionLevel: 'medium', paradigm: 'domain-driven', nextStageNeeded: true, suggestedNextStage: 'Add payment processing entities', modelingNotes: ['Consider adding inventory management'] }; expect(() => DomainModelingSchema.parse(completeModel)).not.toThrow(); }); it('should validate all stage values', () => { const stages = ['analysis', 'conceptual', 'logical', 'physical', 'validation', 'refinement']; stages.forEach(stage => { const model = { domainName: 'Test Domain', description: 'Test description', modelingId: 'test-123', iteration: 1, stage, entities: [{ name: 'TestEntity', description: 'Test entity', attributes: ['id'] }], abstractionLevel: 'high', paradigm: 'object-oriented', nextStageNeeded: false }; expect(() => DomainModelingSchema.parse(model)).not.toThrow(); }); }); it('should validate all abstraction levels', () => { const levels = ['high', 'medium', 'low']; levels.forEach(abstractionLevel => { const model = { domainName: 'Test Domain', description: 'Test description', modelingId: 'test-123', iteration: 1, stage: 'conceptual', entities: [{ name: 'TestEntity', description: 'Test entity', attributes: ['id'] }], abstractionLevel, paradigm: 'object-oriented', nextStageNeeded: false }; expect(() => DomainModelingSchema.parse(model)).not.toThrow(); }); }); it('should validate all paradigm values', () => { const paradigms = [ 'object-oriented', 'relational', 'functional', 'event-driven', 'service-oriented', 'domain-driven' ]; paradigms.forEach(paradigm => { const model = { domainName: 'Test Domain', description: 'Test description', modelingId: 'test-123', iteration: 1, stage: 'conceptual', entities: [{ name: 'TestEntity', description: 'Test entity', attributes: ['id'] }], abstractionLevel: 'high', paradigm, nextStageNeeded: false }; expect(() => DomainModelingSchema.parse(model)).not.toThrow(); }); }); it('should reject model without required fields', () => { const invalidModel = { domainName: 'Test Domain' // missing required fields }; expect(() => DomainModelingSchema.parse(invalidModel)).toThrow(); }); it('should reject model with empty entities array', () => { const invalidModel = { domainName: 'Test Domain', description: 'Test description', modelingId: 'test-123', iteration: 1, stage: 'conceptual', entities: [], // empty entities array abstractionLevel: 'high', paradigm: 'object-oriented', nextStageNeeded: false }; expect(() => DomainModelingSchema.parse(invalidModel)).not.toThrow(); // Empty arrays are allowed }); it('should reject invalid stage value', () => { const invalidModel = { domainName: 'Test Domain', description: 'Test description', modelingId: 'test-123', iteration: 1, stage: 'invalid-stage', entities: [{ name: 'TestEntity', description: 'Test entity', attributes: ['id'] }], abstractionLevel: 'high', paradigm: 'object-oriented', nextStageNeeded: false }; expect(() => DomainModelingSchema.parse(invalidModel)).toThrow(); }); it('should reject invalid abstraction level', () => { const invalidModel = { domainName: 'Test Domain', description: 'Test description', modelingId: 'test-123', iteration: 1, stage: 'conceptual', entities: [{ name: 'TestEntity', description: 'Test entity', attributes: ['id'] }], abstractionLevel: 'invalid-level', paradigm: 'object-oriented', nextStageNeeded: false }; expect(() => DomainModelingSchema.parse(invalidModel)).toThrow(); }); it('should reject invalid paradigm', () => { const invalidModel = { domainName: 'Test Domain', description: 'Test description', modelingId: 'test-123', iteration: 1, stage: 'conceptual', entities: [{ name: 'TestEntity', description: 'Test entity', attributes: ['id'] }], abstractionLevel: 'high', paradigm: 'invalid-paradigm', nextStageNeeded: false }; expect(() => DomainModelingSchema.parse(invalidModel)).toThrow(); }); }); describe('Type inference', () => { it('should infer correct types for domain modeling data', () => { const model = { domainName: 'Test', description: 'Test domain', modelingId: 'test-123', iteration: 1, stage: 'conceptual', entities: [{ name: 'TestEntity', description: 'Test entity', attributes: ['id'] }], abstractionLevel: 'high', paradigm: 'object-oriented', nextStageNeeded: false }; // Type assertions to verify correct inference expect(typeof model.domainName).toBe('string'); expect(typeof model.iteration).toBe('number'); expect(typeof model.nextStageNeeded).toBe('boolean'); expect(Array.isArray(model.entities)).toBe(true); }); }); describe('Edge cases', () => { it('should handle large domain models', () => { const largeModel = { domainName: 'Large E-commerce System', description: 'Complex e-commerce domain with many entities', modelingId: 'large-model-123', iteration: 5, stage: 'physical', entities: Array.from({ length: 50 }, (_, i) => ({ name: `Entity${i}`, description: `Description for entity ${i}`, attributes: Array.from({ length: 10 }, (_, j) => `attr${j}`) })), relationships: Array.from({ length: 75 }, (_, i) => ({ name: `Relationship${i}`, type: 'one-to-many', sourceEntity: `Entity${i % 25}`, targetEntity: `Entity${(i + 1) % 25}`, description: `Relationship ${i}` })), domainRules: Array.from({ length: 30 }, (_, i) => ({ name: `Rule${i}`, description: `Business rule ${i}`, type: 'business-rule', entities: [`Entity${i % 10}`], condition: `condition${i}` })), abstractionLevel: 'low', paradigm: 'domain-driven', nextStageNeeded: false }; expect(() => DomainModelingSchema.parse(largeModel)).not.toThrow(); }); it('should handle model with complex validation data', () => { const modelWithValidation = { domainName: 'Validated Domain', description: 'Domain with comprehensive validation', modelingId: 'validated-123', iteration: 3, stage: 'validation', entities: [{ name: 'ValidatedEntity', description: 'An entity with validation', attributes: ['id', 'name'] }], modelValidation: { completeness: 0.95, consistency: 0.88, correctness: 0.92, issues: [ 'Entity X is missing required attribute Y', 'Relationship Z has inconsistent cardinality', 'Business rule A conflicts with rule B' ], suggestions: [ 'Add missing attributes to complete the model', 'Review relationship cardinalities for consistency', 'Resolve conflicting business rules' ] }, abstractionLevel: 'medium', paradigm: 'object-oriented', nextStageNeeded: true, suggestedNextStage: 'Implement suggested improvements and re-validate' }; expect(() => DomainModelingSchema.parse(modelWithValidation)).not.toThrow(); }); }); describe('Real-world examples', () => { it('should validate a banking domain model', () => { const bankingModel = { domainName: 'Banking System', description: 'Core banking operations domain', modelingId: 'banking-v2.1', iteration: 3, stage: 'logical', entities: [ { name: 'Customer', description: 'Bank customer', attributes: ['customerId', 'name', 'address', 'phone', 'email'], behaviors: ['openAccount', 'closeAccount', 'updateProfile'], constraints: ['email must be unique', 'phone must be valid'], invariants: ['customerId is immutable'] }, { name: 'Account', description: 'Bank account', attributes: ['accountNumber', 'balance', 'accountType', 'openDate'], behaviors: ['deposit', 'withdraw', 'transfer'], constraints: ['balance cannot be negative for savings accounts'], invariants: ['accountNumber is immutable', 'openDate is immutable'] }, { name: 'Transaction', description: 'Financial transaction', attributes: ['transactionId', 'amount', 'timestamp', 'type', 'description'], constraints: ['amount must be positive', 'timestamp cannot be future'], invariants: ['transactionId is immutable', 'timestamp is immutable'] } ], relationships: [ { name: 'CustomerAccounts', type: 'one-to-many', sourceEntity: 'Customer', targetEntity: 'Account', description: 'Customer can have multiple accounts', cardinality: '1:N' }, { name: 'AccountTransactions', type: 'one-to-many', sourceEntity: 'Account', targetEntity: 'Transaction', description: 'Account has transaction history', cardinality: '1:N' } ], domainRules: [ { name: 'Minimum Balance Rule', description: 'Savings accounts must maintain minimum balance', type: 'business-rule', entities: ['Account'], condition: 'accountType = "savings" AND balance >= 100', priority: 'high' }, { name: 'Daily Withdrawal Limit', description: 'ATM withdrawals limited to $500 per day', type: 'business-rule', entities: ['Transaction', 'Account'], condition: 'transactionType = "withdrawal" AND dailyTotal <= 500', priority: 'critical' } ], boundaries: { name: 'Core Banking', description: 'Core banking operations boundary', includedEntities: ['Customer', 'Account', 'Transaction'], excludedConcepts: ['Loans', 'Investments', 'Insurance'], interfaces: ['CustomerAPI', 'AccountAPI', 'TransactionAPI'], responsibilities: ['Manage customer accounts', 'Process transactions', 'Maintain balances'] }, assumptions: [ 'Customers have valid identification', 'All transactions are in USD', 'System operates during business hours' ], stakeholders: ['Bank Customers', 'Tellers', 'Account Managers', 'Compliance Team'], useCases: [ 'Open new account', 'Deposit funds', 'Withdraw funds', 'Transfer between accounts', 'View account balance', 'View transaction history' ], abstractionLevel: 'medium', paradigm: 'domain-driven', nextStageNeeded: true, suggestedNextStage: 'Add loan and investment entities', modelingNotes: [ 'Consider adding audit trail for all transactions', 'May need to add support for multiple currencies', 'Compliance requirements may require additional constraints' ] }; expect(() => DomainModelingSchema.parse(bankingModel)).not.toThrow(); }); it('should validate a healthcare domain model', () => { const healthcareModel = { domainName: 'Healthcare Management', description: 'Patient care and medical records management', modelingId: 'healthcare-v1.0', iteration: 1, stage: 'conceptual', entities: [ { name: 'Patient', description: 'Healthcare patient', attributes: ['patientId', 'name', 'dateOfBirth', 'gender', 'address'], behaviors: ['scheduleAppointment', 'updateContactInfo'], constraints: ['dateOfBirth must be in the past'], invariants: ['patientId is immutable'] }, { name: 'Doctor', description: 'Medical practitioner', attributes: ['doctorId', 'name', 'specialty', 'licenseNumber'], behaviors: ['diagnose', 'prescribe', 'scheduleAppointment'], constraints: ['licenseNumber must be valid'], invariants: ['doctorId is immutable', 'licenseNumber is immutable'] }, { name: 'Appointment', description: 'Medical appointment', attributes: ['appointmentId', 'dateTime', 'duration', 'status', 'notes'], constraints: ['dateTime must be in the future when scheduled'], invariants: ['appointmentId is immutable'] } ], relationships: [ { name: 'PatientAppointments', type: 'one-to-many', sourceEntity: 'Patient', targetEntity: 'Appointment', description: 'Patient can have multiple appointments' }, { name: 'DoctorAppointments', type: 'one-to-many', sourceEntity: 'Doctor', targetEntity: 'Appointment', description: 'Doctor can have multiple appointments' } ], domainRules: [ { name: 'No Double Booking', description: 'Doctor cannot have overlapping appointments', type: 'constraint', entities: ['Doctor', 'Appointment'], condition: 'no overlapping appointment times for same doctor', priority: 'critical' } ], abstractionLevel: 'high', paradigm: 'object-oriented', nextStageNeeded: true, stakeholders: ['Patients', 'Doctors', 'Nurses', 'Administrators'], useCases: ['Schedule appointment', 'View patient history', 'Update medical records'] }; expect(() => DomainModelingSchema.parse(healthcareModel)).not.toThrow(); }); }); });