UNPKG

claude-code-collective

Version:

Sub-agent collective framework for Claude Code with TDD validation, TaskMaster Task ID integration, hub-spoke coordination, and deterministic handoffs

324 lines (278 loc) 9.39 kB
// contract-test.template.js // Contract validation system tests // Project: {{PROJECT_NAME}} // Generated: {{INSTALL_DATE}} describe('Contract Validation System', () => { describe('Contract Structure Validation', () => { test('should validate basic contract structure', () => { const validContract = { preconditions: ['input-validated', 'dependencies-available'], postconditions: ['output-generated', 'state-consistent'], metadata: { contractVersion: '1.0', timestamp: new Date().toISOString() } }; const result = validateContractStructure(validContract); expect(result.valid).toBe(true); expect(result.errors).toHaveLength(0); }); test('should reject invalid contract structure', () => { const invalidContract = { preconditions: null, // missing postconditions }; // Test that the function throws an exception for invalid contracts expect(() => { validateContractStructure(invalidContract); }).toThrow('Invalid preconditions format, Invalid postconditions format'); }); }); describe('Precondition Validation', () => { test('should validate all preconditions are met', async () => { const contract = { preconditions: ['file-exists', 'permissions-valid', 'memory-available'] }; const context = { files: ['input.txt'], permissions: 'rw', memory: '512MB' }; const result = await validatePreconditions(contract.preconditions, context); expect(result.allMet).toBe(true); expect(result.failures).toHaveLength(0); }); test('should detect failed preconditions', async () => { const contract = { preconditions: ['file-exists', 'network-available'] }; const context = { files: [], // No files available network: false }; const result = await validatePreconditions(contract.preconditions, context); expect(result.allMet).toBe(false); expect(result.failures.length).toBeGreaterThan(0); }); }); describe('Postcondition Validation', () => { test('should validate postconditions after execution', async () => { const contract = { postconditions: ['output-created', 'no-errors', 'state-consistent'] }; const executionResult = { outputs: ['result.json'], errors: [], stateValid: true }; const result = await validatePostconditions(contract.postconditions, executionResult); expect(result.allMet).toBe(true); }); test('should detect postcondition failures', async () => { const contract = { postconditions: ['output-created', 'no-errors'] }; const executionResult = { outputs: [], // No output created errors: ['Execution failed'] }; const result = await validatePostconditions(contract.postconditions, executionResult); expect(result.allMet).toBe(false); expect(result.failures).toContain('output-created'); expect(result.failures).toContain('no-errors'); }); }); describe('Contract Enforcement', () => { test('should enforce contract during handoff', async () => { const handoff = { fromAgent: '@routing-agent', toAgent: '@implementation-agent', contract: { preconditions: ['requirements-clear', 'context-provided'], postconditions: ['code-implemented', 'tests-passing'] }, context: { requirements: 'Create login form', specifications: { framework: 'React' } } }; const result = await enforceContract(handoff); expect(result.contractValid).toBe(true); expect(result.handoffAllowed).toBe(true); }); test('should block handoff on contract violation', async () => { const handoff = { fromAgent: '@routing-agent', toAgent: '@implementation-agent', contract: { preconditions: ['requirements-clear', 'context-provided'], postconditions: ['code-implemented'] }, context: { // Missing requirements } }; const result = await enforceContract(handoff); expect(result.contractValid).toBe(false); expect(result.handoffAllowed).toBe(false); }); }); describe('Contract Templates', () => { test('should create contract from template', () => { const template = { type: 'implementation', defaultPreconditions: ['requirements-defined', 'resources-available'], defaultPostconditions: ['implementation-complete', 'quality-validated'] }; const customizations = { additionalPreconditions: ['design-approved'], additionalPostconditions: ['documentation-updated'] }; const contract = createContractFromTemplate(template, customizations); expect(contract.preconditions).toContain('requirements-defined'); expect(contract.preconditions).toContain('design-approved'); expect(contract.postconditions).toContain('implementation-complete'); expect(contract.postconditions).toContain('documentation-updated'); }); }); describe('Error Handling', () => { test('should handle contract validation errors gracefully', async () => { const malformedContract = { preconditions: 'invalid-format', // Should be array postconditions: null }; const result = await validateContractSafely(malformedContract); expect(result.error).toBeDefined(); expect(result.errorType).toBe('CONTRACT_MALFORMED'); }); }); }); // Test helper functions function validateContractStructure(contract) { const errors = []; if (!contract.preconditions || !Array.isArray(contract.preconditions)) { errors.push('Invalid preconditions format'); } if (!contract.postconditions || !Array.isArray(contract.postconditions)) { errors.push('Invalid postconditions format'); } // ADD ERROR THROWING LOGIC - Fix for CONTRACT_MALFORMED test if (errors.length > 0) { throw new Error(errors.join(', ')); } return { valid: true, errors: [] }; } async function validatePreconditions(preconditions, context) { const failures = []; for (const condition of preconditions) { const met = await checkPrecondition(condition, context); if (!met) { failures.push(condition); } } return { allMet: failures.length === 0, failures }; } async function validatePostconditions(postconditions, executionResult) { const failures = []; for (const condition of postconditions) { const met = await checkPostcondition(condition, executionResult); if (!met) { failures.push(condition); } } return { allMet: failures.length === 0, failures }; } async function checkPrecondition(condition, context) { switch (condition) { case 'file-exists': return context.files && context.files.length > 0; case 'permissions-valid': return context.permissions && context.permissions.includes('rw'); case 'memory-available': return context.memory && context.memory !== '0MB'; case 'network-available': return context.network === true; case 'requirements-clear': return context.requirements && context.requirements.length > 0; case 'context-provided': return context && Object.keys(context).length > 0; default: return false; } } async function checkPostcondition(condition, result) { switch (condition) { case 'output-created': return result.outputs && result.outputs.length > 0; case 'no-errors': return result.errors && result.errors.length === 0; case 'state-consistent': return result.stateValid === true; case 'code-implemented': return result.codeGenerated === true; case 'tests-passing': return result.testsPassed === true; default: return false; } } async function enforceContract(handoff) { try { const preconditionResult = await validatePreconditions( handoff.contract.preconditions, handoff.context ); return { contractValid: preconditionResult.allMet, handoffAllowed: preconditionResult.allMet, violations: preconditionResult.failures }; } catch (error) { return { contractValid: false, handoffAllowed: false, error: error.message }; } } function createContractFromTemplate(template, customizations = {}) { return { type: template.type, preconditions: [ ...template.defaultPreconditions, ...(customizations.additionalPreconditions || []) ], postconditions: [ ...template.defaultPostconditions, ...(customizations.additionalPostconditions || []) ], metadata: { templateUsed: template.type, createdAt: new Date().toISOString() } }; } async function validateContractSafely(contract) { try { const result = validateContractStructure(contract); return { ...result, error: null }; } catch (error) { return { valid: false, error: error.message, errorType: 'CONTRACT_MALFORMED' }; } }