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
JavaScript
// 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'
};
}
}