382 lines
11 KiB
JavaScript
382 lines
11 KiB
JavaScript
|
|
/**
|
||
|
|
* Type Definition Tests
|
||
|
|
*
|
||
|
|
* Tests the type definition system of the Evidence DSL,
|
||
|
|
* including fields, behaviors, caching, and complex type structures.
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { DSLCompiler } from '../DSLCompiler.js';
|
||
|
|
|
||
|
|
export class DefinitionTests {
|
||
|
|
constructor() {
|
||
|
|
this.arbiter = null;
|
||
|
|
this.compiler = null;
|
||
|
|
this.testResults = [];
|
||
|
|
}
|
||
|
|
|
||
|
|
setup(arbiter) {
|
||
|
|
this.arbiter = arbiter;
|
||
|
|
this.compiler = new DSLCompiler(arbiter);
|
||
|
|
}
|
||
|
|
|
||
|
|
runAllTests() {
|
||
|
|
console.log('=== Type Definition Tests ===\n');
|
||
|
|
|
||
|
|
this.testBasicDefinitions();
|
||
|
|
this.testFieldTypes();
|
||
|
|
this.testArrayTypes();
|
||
|
|
this.testBehaviors();
|
||
|
|
this.testCaching();
|
||
|
|
this.testComplexDefinitions();
|
||
|
|
this.testDefinitionErrors();
|
||
|
|
|
||
|
|
return this.getTestResults();
|
||
|
|
}
|
||
|
|
|
||
|
|
testBasicDefinitions() {
|
||
|
|
console.log('Testing Basic Definitions...');
|
||
|
|
|
||
|
|
const testCases = [
|
||
|
|
{
|
||
|
|
input: `definition User { role: string }`,
|
||
|
|
description: 'Simple definition with one field'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition User {
|
||
|
|
role: string
|
||
|
|
isActive: boolean
|
||
|
|
}`,
|
||
|
|
description: 'Definition with multiple fields'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition Group {
|
||
|
|
name: string
|
||
|
|
description: string
|
||
|
|
created: timestamp
|
||
|
|
}`,
|
||
|
|
description: 'Definition with different field types'
|
||
|
|
}
|
||
|
|
];
|
||
|
|
|
||
|
|
testCases.forEach(({ input, description }) => {
|
||
|
|
try {
|
||
|
|
const result = this.compiler.compile(input, `test-basic-def-${Date.now()}`);
|
||
|
|
this.assert(result.success, `${description} should parse successfully`);
|
||
|
|
this.assert(result.program.definitions.length > 0, 'Should have definitions');
|
||
|
|
console.log(` ✓ ${description}`);
|
||
|
|
} catch (error) {
|
||
|
|
this.fail(`Basic definition test: ${description}`, error);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
testFieldTypes() {
|
||
|
|
console.log('Testing Field Types...');
|
||
|
|
|
||
|
|
const testCases = [
|
||
|
|
{ type: 'string', description: 'String field type' },
|
||
|
|
{ type: 'number', description: 'Number field type' },
|
||
|
|
{ type: 'boolean', description: 'Boolean field type' },
|
||
|
|
{ type: 'timestamp', description: 'Timestamp field type' },
|
||
|
|
{ type: 'User', description: 'Custom type field' },
|
||
|
|
{ type: 'Permission', description: 'Another custom type field' }
|
||
|
|
];
|
||
|
|
|
||
|
|
testCases.forEach(({ type, description }) => {
|
||
|
|
try {
|
||
|
|
const dsl = `definition Test { field: ${type} }`;
|
||
|
|
const result = this.compiler.compile(dsl, `test-field-type-${Date.now()}`);
|
||
|
|
this.assert(result.success, `${description} should parse successfully`);
|
||
|
|
console.log(` ✓ ${description}`);
|
||
|
|
} catch (error) {
|
||
|
|
this.fail(`Field type test: ${description}`, error);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
testArrayTypes() {
|
||
|
|
console.log('Testing Array Types...');
|
||
|
|
|
||
|
|
const testCases = [
|
||
|
|
{ type: 'string[]', description: 'String array' },
|
||
|
|
{ type: 'number[]', description: 'Number array' },
|
||
|
|
{ type: 'boolean[]', description: 'Boolean array' },
|
||
|
|
{ type: 'Permission[]', description: 'Custom type array' },
|
||
|
|
{ type: 'User[]', description: 'User array' }
|
||
|
|
];
|
||
|
|
|
||
|
|
testCases.forEach(({ type, description }) => {
|
||
|
|
try {
|
||
|
|
const dsl = `definition Test { items: ${type} }`;
|
||
|
|
const result = this.compiler.compile(dsl, `test-array-${Date.now()}`);
|
||
|
|
this.assert(result.success, `${description} should parse successfully`);
|
||
|
|
console.log(` ✓ ${description}`);
|
||
|
|
} catch (error) {
|
||
|
|
this.fail(`Array type test: ${description}`, error);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
testBehaviors() {
|
||
|
|
console.log('Testing Behaviors...');
|
||
|
|
|
||
|
|
const testCases = [
|
||
|
|
{
|
||
|
|
input: `definition User {
|
||
|
|
balance: number BEHAVES { decaying down hourly }
|
||
|
|
}`,
|
||
|
|
description: 'Decay behavior - down hourly'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition User {
|
||
|
|
reputation: number BEHAVES { decaying up daily }
|
||
|
|
}`,
|
||
|
|
description: 'Decay behavior - up daily'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition User {
|
||
|
|
score: number BEHAVES { decaying neutral weekly }
|
||
|
|
}`,
|
||
|
|
description: 'Decay behavior - neutral weekly'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition User {
|
||
|
|
stability: number BEHAVES { decaying stable monthly }
|
||
|
|
}`,
|
||
|
|
description: 'Decay behavior - stable monthly'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition User {
|
||
|
|
confidence: number BEHAVES { blurring fixed }
|
||
|
|
}`,
|
||
|
|
description: 'Blur behavior - fixed'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition User {
|
||
|
|
accuracy: number BEHAVES { blurring adaptive }
|
||
|
|
}`,
|
||
|
|
description: 'Blur behavior - adaptive'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition User {
|
||
|
|
precision: number BEHAVES { blurring confidence confidence_90 }
|
||
|
|
}`,
|
||
|
|
description: 'Blur behavior - confidence with level'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition User {
|
||
|
|
session: string BEHAVES { ttl 1h }
|
||
|
|
}`,
|
||
|
|
description: 'TTL behavior - hours'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition User {
|
||
|
|
token: string BEHAVES { ttl 24h }
|
||
|
|
}`,
|
||
|
|
description: 'TTL behavior - 24 hours'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition User {
|
||
|
|
cache: string BEHAVES { ttl 7d }
|
||
|
|
}`,
|
||
|
|
description: 'TTL behavior - days'
|
||
|
|
}
|
||
|
|
];
|
||
|
|
|
||
|
|
testCases.forEach(({ input, description }) => {
|
||
|
|
try {
|
||
|
|
const result = this.compiler.compile(input, `test-behavior-${Date.now()}`);
|
||
|
|
this.assert(result.success, `${description} should parse successfully`);
|
||
|
|
console.log(` ✓ ${description}`);
|
||
|
|
} catch (error) {
|
||
|
|
this.fail(`Behavior test: ${description}`, error);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
testCaching() {
|
||
|
|
console.log('Testing Caching...');
|
||
|
|
|
||
|
|
const testCases = [
|
||
|
|
{
|
||
|
|
input: `definition User {
|
||
|
|
role: string CACHE eager
|
||
|
|
}`,
|
||
|
|
description: 'Eager caching'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition User {
|
||
|
|
score: number CACHE lazy
|
||
|
|
}`,
|
||
|
|
description: 'Lazy caching'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition User {
|
||
|
|
balance: number BEHAVES { decaying down hourly } CACHE eager
|
||
|
|
}`,
|
||
|
|
description: 'Behavior with eager caching'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition User {
|
||
|
|
reputation: number BEHAVES { blurring adaptive } CACHE lazy
|
||
|
|
}`,
|
||
|
|
description: 'Behavior with lazy caching'
|
||
|
|
}
|
||
|
|
];
|
||
|
|
|
||
|
|
testCases.forEach(({ input, description }) => {
|
||
|
|
try {
|
||
|
|
const result = this.compiler.compile(input, `test-cache-${Date.now()}`);
|
||
|
|
this.assert(result.success, `${description} should parse successfully`);
|
||
|
|
console.log(` ✓ ${description}`);
|
||
|
|
} catch (error) {
|
||
|
|
this.fail(`Cache test: ${description}`, error);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
testComplexDefinitions() {
|
||
|
|
console.log('Testing Complex Definitions...');
|
||
|
|
|
||
|
|
const testCases = [
|
||
|
|
{
|
||
|
|
input: `definition User {
|
||
|
|
role: string
|
||
|
|
isActive: boolean
|
||
|
|
lastActive: timestamp BEHAVES {
|
||
|
|
decaying down hourly
|
||
|
|
} CACHE lazy
|
||
|
|
isSuspended: boolean
|
||
|
|
balance: number BEHAVES {
|
||
|
|
decaying down hourly
|
||
|
|
} CACHE eager
|
||
|
|
score: number BEHAVES {
|
||
|
|
blurring adaptive confidence_95
|
||
|
|
} CACHE lazy
|
||
|
|
session: string BEHAVES {
|
||
|
|
ttl 24h
|
||
|
|
} CACHE eager
|
||
|
|
}`,
|
||
|
|
description: 'Complex definition with multiple behaviors and caching'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition Group {
|
||
|
|
name: string
|
||
|
|
permissions: Permission[]
|
||
|
|
members: User[]
|
||
|
|
created: timestamp BEHAVES {
|
||
|
|
decaying stable monthly
|
||
|
|
} CACHE lazy
|
||
|
|
isPublic: boolean CACHE eager
|
||
|
|
}`,
|
||
|
|
description: 'Definition with arrays and mixed behaviors'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition Document {
|
||
|
|
level: string
|
||
|
|
owner: User
|
||
|
|
tags: string[]
|
||
|
|
content: string BEHAVES {
|
||
|
|
blurring fixed
|
||
|
|
} CACHE lazy
|
||
|
|
accessCount: number BEHAVES {
|
||
|
|
decaying up daily
|
||
|
|
} CACHE eager
|
||
|
|
expiresAt: timestamp BEHAVES {
|
||
|
|
ttl 30d
|
||
|
|
} CACHE eager
|
||
|
|
}`,
|
||
|
|
description: 'Definition with all behavior types'
|
||
|
|
}
|
||
|
|
];
|
||
|
|
|
||
|
|
testCases.forEach(({ input, description }) => {
|
||
|
|
try {
|
||
|
|
const result = this.compiler.compile(input, `test-complex-def-${Date.now()}`);
|
||
|
|
this.assert(result.success, `${description} should parse successfully`);
|
||
|
|
this.assert(result.program.definitions.length > 0, 'Should have definitions');
|
||
|
|
console.log(` ✓ ${description}`);
|
||
|
|
} catch (error) {
|
||
|
|
this.fail(`Complex definition test: ${description}`, error);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
testDefinitionErrors() {
|
||
|
|
console.log('Testing Definition Error Handling...');
|
||
|
|
|
||
|
|
const testCases = [
|
||
|
|
{
|
||
|
|
input: `definition User { role: string`,
|
||
|
|
description: 'Missing closing brace should fail'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition User { role: }`,
|
||
|
|
description: 'Missing field type should fail'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition User { : string }`,
|
||
|
|
description: 'Missing field name should fail'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition User { role: string BEHAVES { }`,
|
||
|
|
description: 'Incomplete behavior should fail'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition User { role: string CACHE }`,
|
||
|
|
description: 'Incomplete cache directive should fail'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
input: `definition User { role: string BEHAVES { invalid } }`,
|
||
|
|
description: 'Invalid behavior should fail'
|
||
|
|
}
|
||
|
|
];
|
||
|
|
|
||
|
|
testCases.forEach(({ input, description }) => {
|
||
|
|
try {
|
||
|
|
const result = this.compiler.compile(input, `test-def-error-${Date.now()}`);
|
||
|
|
this.assert(!result.success, `${description} should fail to parse`);
|
||
|
|
console.log(` ✓ ${description} (correctly failed)`);
|
||
|
|
} catch (error) {
|
||
|
|
// 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 runDefinitionTests(arbiter) {
|
||
|
|
const test = new DefinitionTests();
|
||
|
|
test.setup(arbiter);
|
||
|
|
return test.runAllTests();
|
||
|
|
}
|