/** * Measure Definition Tests * * Tests the measure system of the Evidence DSL, * including aggregation, fusion, return types, and value computation. */ import { DSLCompiler } from '../DSLCompiler.js'; export class MeasureTests { constructor() { this.arbiter = null; this.compiler = null; this.testResults = []; } setup(arbiter) { this.arbiter = arbiter; this.compiler = new DSLCompiler(arbiter); } runAllTests() { console.log('=== Measure Definition Tests ===\n'); this.testBasicMeasures(); this.testMeasureReturnTypes(); this.testMeasureAggregation(); this.testMeasureFusion(); this.testComplexMeasures(); this.testMeasureErrors(); return this.getTestResults(); } testBasicMeasures() { console.log('Testing Basic Measures...'); const testCases = [ { input: `measure userRole(user: User) { user.role } PROVIDES string`, description: 'Simple measure with attribute access' }, { input: `measure userBalance(user: User) { user.balance } PROVIDES number`, description: 'Measure accessing numeric attribute' }, { input: `measure isUserActive(user: User) { user.isActive } PROVIDES boolean`, description: 'Measure accessing boolean attribute' }, { input: `measure userPermissions(user: User) { user.permissions } PROVIDES Permission[]`, description: 'Measure accessing array attribute' }, { input: `measure userScore(user: User) { user.score } PROVIDES number`, description: 'Measure with behavior-inherited attribute' } ]; testCases.forEach(({ input, description }) => { try { const result = this.compiler.compile(input, `test-basic-measure-${Date.now()}`); this.assert(result.success, `${description} should parse successfully`); this.assert(result.program.measures.length > 0, 'Should have measures'); console.log(` ✓ ${description}`); } catch (error) { this.fail(`Basic measure test: ${description}`, error); } }); } testMeasureReturnTypes() { console.log('Testing Measure Return Types...'); const testCases = [ { type: 'string', description: 'String return type' }, { type: 'number', description: 'Number return type' }, { type: 'boolean', description: 'Boolean return type' }, { type: 'timestamp', description: 'Timestamp return type' }, { type: 'Permission[]', description: 'Array return type' }, { type: 'User', description: 'Custom type return' }, { type: 'Group[]', description: 'Custom array return type' } ]; testCases.forEach(({ type, description }) => { try { const dsl = `measure test() { true } PROVIDES ${type}`; const result = this.compiler.compile(dsl, `test-measure-return-${Date.now()}`); this.assert(result.success, `${description} should parse successfully`); console.log(` ✓ ${description}`); } catch (error) { this.fail(`Measure return type test: ${description}`, error); } }); } testMeasureAggregation() { console.log('Testing Measure Aggregation...'); const testCases = [ { input: `measure userPermissions(user: User) { aggregate { user.role.permissions user.group.permissions } USING majority } PROVIDES Permission[]`, description: 'Aggregation with majority strategy' }, { input: `measure userClearance(user: User) { aggregate { user.clearance user.role.clearance user.group.clearance } USING max } PROVIDES string`, description: 'Aggregation with max strategy' }, { input: `measure userScore(user: User) { aggregate { user.reputation user.activityScore user.verificationLevel } USING average } PROVIDES number`, description: 'Aggregation with average strategy' }, { input: `measure userTrust(user: User) { aggregate { user.reputation user.activityScore user.verificationLevel user.socialProof } USING min } PROVIDES number`, description: 'Aggregation with min strategy' } ]; testCases.forEach(({ input, description }) => { try { const result = this.compiler.compile(input, `test-measure-aggregation-${Date.now()}`); this.assert(result.success, `${description} should parse successfully`); console.log(` ✓ ${description}`); } catch (error) { this.fail(`Measure aggregation test: ${description}`, error); } }); } testMeasureFusion() { console.log('Testing Measure Fusion...'); const testCases = [ { input: `measure effectiveClearance(user: User) { fusion max { user.clearance user.role.clearance user.group.clearance } } PROVIDES string`, description: 'Fusion with max strategy' }, { input: `measure userPermissions(user: User) { fusion min { user.role.permissions user.group.permissions } } PROVIDES Permission[]`, description: 'Fusion with min strategy' }, { input: `measure userScore(user: User) { fusion majority { user.reputation user.activityScore user.verificationLevel } } PROVIDES number`, description: 'Fusion with majority strategy' }, { input: `measure userTrust(user: User) { fusion average { user.reputation user.activityScore user.verificationLevel user.socialProof } } PROVIDES number`, description: 'Fusion with average strategy' } ]; testCases.forEach(({ input, description }) => { try { const result = this.compiler.compile(input, `test-measure-fusion-${Date.now()}`); this.assert(result.success, `${description} should parse successfully`); console.log(` ✓ ${description}`); } catch (error) { this.fail(`Measure fusion test: ${description}`, error); } }); } testComplexMeasures() { console.log('Testing Complex Measures...'); const testCases = [ { input: `measure userEffectivePermissions(user: User) { aggregate { user.role.permissions user.group.permissions user.directPermissions } USING majority } PROVIDES Permission[]`, description: 'Complex aggregation with multiple sources' }, { input: `measure userTrustScore(user: User) { fusion average { user.reputation user.activityScore user.verificationLevel user.socialProof user.peerRatings } } PROVIDES number`, description: 'Complex fusion with multiple metrics' }, { input: `measure userAccessLevel(user: User) { fusion max { user.clearance user.role.clearance user.group.clearance user.temporaryClearance } } PROVIDES string`, description: 'Complex clearance calculation' }, { input: `measure userSimilarity(user1: User, user2: User) { similar(user1, user2) |similarity| { similarity } with similarity > 0.5 } PROVIDES number`, description: 'Similarity measure with pattern matching' }, { input: `measure userEffectiveRole(user: User) { fusion majority { user.role user.temporaryRole user.actingRole } } PROVIDES string`, description: 'Role determination with multiple sources' } ]; testCases.forEach(({ input, description }) => { try { const result = this.compiler.compile(input, `test-complex-measure-${Date.now()}`); this.assert(result.success, `${description} should parse successfully`); console.log(` ✓ ${description}`); } catch (error) { this.fail(`Complex measure test: ${description}`, error); } }); } testMeasureErrors() { console.log('Testing Measure Error Handling...'); const testCases = [ { input: `measure userRole(user: User) { user.role }`, description: 'Missing PROVIDES clause should fail' }, { input: `measure userRole(user: User) { user.role } PROVIDES`, description: 'Incomplete PROVIDES clause should fail' }, { input: `measure userRole(user: User) { user.role } PROVIDES string`, description: 'Valid measure should succeed' }, { input: `measure userPermissions(user: User) { aggregate { user.role.permissions user.group.permissions } USING } PROVIDES Permission[]`, description: 'Incomplete USING clause should fail' }, { input: `measure userScore(user: User) { fusion { user.reputation user.activityScore } } PROVIDES number`, description: 'Missing fusion strategy should fail' }, { input: `measure userRole(user: User) { invalid syntax here } PROVIDES string`, description: 'Invalid syntax should fail' } ]; testCases.forEach(({ input, description }) => { try { const result = this.compiler.compile(input, `test-measure-error-${Date.now()}`); if (description.includes('should succeed')) { this.assert(result.success, `${description} should parse successfully`); console.log(` ✓ ${description}`); } else { this.assert(!result.success, `${description} should fail to parse`); console.log(` ✓ ${description} (correctly failed)`); } } catch (error) { if (description.includes('should succeed')) { this.fail(`Measure error test: ${description}`, error); } else { // Expected to fail console.log(` ✓ ${description} (correctly failed)`); } } }); } assert(condition, message) { if (!condition) { throw new Error(`Assertion failed: ${message}`); } } fail(testName, error) { console.log(` ✗ ${testName} failed: ${error.message}`); this.testResults.push({ test: testName, status: 'FAILED', error: error.message }); } getTestResults() { const passed = this.testResults.filter(r => r.status === 'PASSED').length; const failed = this.testResults.filter(r => r.status === 'FAILED').length; const total = this.testResults.length; return { total: total, passed: passed, failed: failed, success: failed === 0, results: this.testResults }; } } export function runMeasureTests(arbiter) { const test = new MeasureTests(); test.setup(arbiter); return test.runAllTests(); }