cleanup: remove dead code, stale shipped scaffolding, internal docs
CI / test (push) Successful in 5m45s
CI / benchmark (push) Successful in 43s
CI / publish (push) Has been skipped

Dead code with zero callers (deprecation notes promised removal):
- RelationCSR index: always-off option (useRelationCsrIndex), never
  enabled in production, wired through RelationManager/RelationUpdates/
  RelationLookup. Removed the module and all wiring.
- getAggregatedBlurredValue (RelationManager) and aggregateBlurredValues
  (ValueManager): @deprecated shims, zero callers.
- QualitativeRelationalComparatorRule._aggregateBlurredValues:
  @deprecated shim, zero callers.

Kept compareRelationValues: non-deprecated public API, coherent and
clock-threaded, just currently callerless.

Stale scaffolding shipping in the published artifact (files: src/):
- src/ast/tests/* and src/ast/examples/*: orphaned duplicates of
  tests/ast/, zero references anywhere, 11 files in the tarball.
  Removed; the live copies live in tests/ast/.

Internal docs moved out of the shipped surface (1266 lines) to
docs/internal/: VALUE_OPTIMIZATION_SUMMARY, rules API_SPECIFICATION,
ast README, qualitative README — repo-kept, not packaged.

Tarball .md count: 11 -> 1. Rigor 251/251, full suite 853/791/0.
This commit is contained in:
John Dvorak
2026-08-02 21:03:37 -07:00
parent 33dd15bba5
commit f8f6c5cb1b
21 changed files with 0 additions and 3843 deletions
-352
View File
@@ -1,352 +0,0 @@
import { DSLCompiler } from '../DSLCompiler.js';
import { Arbiter } from '../../core/Arbiter.js';
/**
* Progressive Test Suite - Tests each language feature incrementally
*/
export function runProgressiveTestSuite() {
console.log('=== Progressive DSL Test Suite ===\n');
const arbiter = new Arbiter();
const compiler = new DSLCompiler(arbiter);
const tests = [
{
name: '1. Basic Definitions',
dsl: `
definition User {
name: string
age: number
}`,
expected: { definitions: 1, facts: 0, evidence: 0, measures: 0 }
},
{
name: '2. Definitions with Arrays',
dsl: `
definition User {
name: string
roles: string[]
permissions: Permission[]
}`,
expected: { definitions: 1, facts: 0, evidence: 0, measures: 0 }
},
{
name: '3. Definitions with Behaviors',
dsl: `
definition User {
name: string
lastActive: timestamp BEHAVES {
decaying down hourly
}
score: number BEHAVES {
blurring adaptive confidence_95
}
session: string BEHAVES {
ttl 24h
}
}`,
expected: { definitions: 1, facts: 0, evidence: 0, measures: 0 }
},
{
name: '4. Definitions with Cache Directives',
dsl: `
definition User {
name: string
lastActive: timestamp BEHAVES {
decaying down hourly
} CACHE lazy
score: number BEHAVES {
blurring adaptive confidence_95
} CACHE eager
}`,
expected: { definitions: 1, facts: 0, evidence: 0, measures: 0 }
},
{
name: '5. Basic Facts',
dsl: `
definition User {
name: string
}
fact hasRole(user: User, role: string)
fact isActive(user: User)`,
expected: { definitions: 1, facts: 2, evidence: 0, measures: 0 }
},
{
name: '6. Facts with Properties',
dsl: `
definition User {
name: string
}
fact hasRole(user: User, role: string) CACHE eager
fact isMember(user: User, group: Group) transitive CACHE lazy
fact isFriend(user: User, friend: User) symmetrical`,
expected: { definitions: 1, facts: 3, evidence: 0, measures: 0 }
},
{
name: '7. Facts with Limits',
dsl: `
definition User {
name: string
}
fact isMember(user: User, group: Group) transitive limit 10
fact isFriend(user: User, friend: User) symmetrical limit 100`,
expected: { definitions: 1, facts: 2, evidence: 0, measures: 0 }
},
{
name: '8. Simple Evidence',
dsl: `
definition User {
name: string
}
fact hasRole(user: User, role: string)
evidence canRead(user: User, doc: Document) {
hasRole(user, 'admin')
}`,
expected: { definitions: 1, facts: 1, evidence: 1, measures: 0 }
},
{
name: '9. Evidence with Multiple Statements',
dsl: `
definition User {
name: string
}
fact hasRole(user: User, role: string)
fact owns(user: User, doc: Document)
evidence canRead(user: User, doc: Document) {
owns(user, doc)
hasRole(user, 'admin')
}`,
expected: { definitions: 1, facts: 2, evidence: 1, measures: 0 }
},
{
name: '10. Evidence with ALWAYS',
dsl: `
definition User {
name: string
}
fact isActive(user: User)
evidence canRead(user: User, doc: Document) {
ALWAYS isActive(user)
}`,
expected: { definitions: 1, facts: 1, evidence: 1, measures: 0 }
},
{
name: '11. Evidence with REQUIRES',
dsl: `
definition User {
name: string
}
fact hasClearance(user: User, level: string)
evidence canRead(user: User, doc: Document) {
REQUIRES hasClearance(user, doc.level)
}`,
expected: { definitions: 1, facts: 1, evidence: 1, measures: 0 }
},
{
name: '12. Evidence with WHEN',
dsl: `
definition User {
name: string
}
fact hasRole(user: User, role: string)
evidence canRead(user: User, doc: Document) {
WHEN hasRole(user, 'admin')
}`,
expected: { definitions: 1, facts: 1, evidence: 1, measures: 0 }
},
{
name: '13. Evidence with WHEN UNLESS',
dsl: `
definition User {
name: string
}
fact hasRole(user: User, role: string)
fact isSuspended(user: User)
evidence canRead(user: User, doc: Document) {
WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
}`,
expected: { definitions: 1, facts: 2, evidence: 1, measures: 0 }
},
{
name: '14. Evidence with Pattern Matching',
dsl: `
definition User {
name: string
}
fact isMember(user: User, group: Group)
evidence canRead(user: User, doc: Document) {
isMember(user, *group) {
canRead(group, doc)
}
}`,
expected: { definitions: 1, facts: 1, evidence: 1, measures: 0 }
},
{
name: '15. Evidence with Pattern Matching and Limits',
dsl: `
definition User {
name: string
}
fact isMember(user: User, group: Group)
evidence canRead(user: User, doc: Document) {
isMember(user, *group) {
canRead(group, doc)
} limit 5
}`,
expected: { definitions: 1, facts: 1, evidence: 1, measures: 0 }
},
{
name: '16. Evidence with Fusion',
dsl: `
definition User {
name: string
}
fact hasRole(user: User, role: string)
fact isMember(user: User, group: Group)
evidence canRead(user: User, doc: Document) {
fusion max {
hasRole(user, 'admin')
isMember(user, *group) {
canRead(group, doc)
}
}
}`,
expected: { definitions: 1, facts: 2, evidence: 1, measures: 0 }
},
{
name: '17. Basic Measures',
dsl: `
definition User {
name: string
role: string
}
measure userRole(user: User) {
user.role
} PROVIDES string`,
expected: { definitions: 1, facts: 0, evidence: 0, measures: 1 }
},
{
name: '18. Measures with Fusion',
dsl: `
definition User {
name: string
role: string
}
fact hasRole(user: User, role: string)
measure userPermissions(user: User) {
fusion max {
user.role.permissions
hasRole(user, 'admin')
}
} PROVIDES Permission[]`,
expected: { definitions: 1, facts: 1, evidence: 0, measures: 1 }
},
{
name: '19. Complete Example',
dsl: `
definition User {
role: string
isActive: boolean
lastActive: timestamp BEHAVES {
decaying down hourly
} CACHE lazy
}
definition Document {
level: string
owner: User
}
fact hasRole(user: User, role: string) CACHE eager
fact owns(user: User, doc: Document) CACHE eager
evidence canRead(user: User, doc: Document) {
owns(user, doc)
WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
}
measure userRole(user: User) {
user.role
} PROVIDES string`,
expected: { definitions: 2, facts: 2, evidence: 1, measures: 1 }
}
];
let passed = 0;
let failed = 0;
for (const test of tests) {
console.log(`\n--- ${test.name} ---`);
try {
const result = compiler.compile(test.dsl, `test-${passed + failed + 1}`);
const actual = {
definitions: result.program.definitions.length,
facts: result.program.facts.length,
evidence: result.program.evidence.length,
measures: result.program.measures.length
};
const success = result.success &&
actual.definitions === test.expected.definitions &&
actual.facts === test.expected.facts &&
actual.evidence === test.expected.evidence &&
actual.measures === test.expected.measures;
if (success) {
console.log('✅ PASSED');
console.log(` Definitions: ${actual.definitions}, Facts: ${actual.facts}, Evidence: ${actual.evidence}, Measures: ${actual.measures}`);
passed++;
} else {
console.log('❌ FAILED');
console.log(` Expected: ${JSON.stringify(test.expected)}`);
console.log(` Actual: ${JSON.stringify(actual)}`);
if (result.errors.length > 0) {
console.log(` Errors: ${result.errors.join(', ')}`);
}
failed++;
}
} catch (error) {
console.log('❌ FAILED');
console.log(` Error: ${error.message}`);
failed++;
}
}
console.log(`\n=== Test Suite Results ===`);
console.log(`Total Tests: ${passed + failed}`);
console.log(`Passed: ${passed}`);
console.log(`Failed: ${failed}`);
console.log(`Success Rate: ${((passed / (passed + failed)) * 100).toFixed(1)}%`);
return { passed, failed, total: passed + failed };
}
// Run the test suite
runProgressiveTestSuite();
-53
View File
@@ -1,53 +0,0 @@
import { DSLCompiler } from '../DSLCompiler.js';
import { Arbiter } from '../../core/Arbiter.js';
/**
* Test with simple evidence containing predicate calls
*/
export function runSimpleEvidenceTest() {
console.log('=== Simple Evidence Test ===\n');
// Create arbiter instance
const arbiter = new Arbiter();
// Create compiler
const compiler = new DSLCompiler(arbiter);
// Simple evidence with predicate call
const dsl = `
definition User {
name: string
}
fact hasRole(user: User, role: string)
evidence canRead(user: User, doc: Document) {
hasRole(user, 'admin')
}
`;
console.log('1. Compiling DSL with simple evidence...');
try {
const result = compiler.compile(dsl, 'simple-evidence');
console.log('Result:', result.success ? 'SUCCESS' : 'FAILED');
console.log('Generated rules:', result.generatedRules.size);
console.log('Program evidence:', result.program.evidence.length);
if (result.errors.length > 0) {
console.log('Errors:', result.errors);
}
if (result.warnings.length > 0) {
console.log('Warnings:', result.warnings);
}
return result;
} catch (error) {
console.error('Compilation failed:', error.message);
return { success: false, error: error.message };
}
}
// Run the test
runSimpleEvidenceTest();
-381
View File
@@ -1,381 +0,0 @@
/**
* 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();
}
-464
View File
@@ -1,464 +0,0 @@
/**
* Evidence Rule Tests
*
* Tests the evidence rule system of the Evidence DSL,
* including defeasible logic, pattern matching, fusion, and complex evidence composition.
*/
import { DSLCompiler } from '../DSLCompiler.js';
export class EvidenceTests {
constructor() {
this.arbiter = null;
this.compiler = null;
this.testResults = [];
}
setup(arbiter) {
this.arbiter = arbiter;
this.compiler = new DSLCompiler(arbiter);
}
runAllTests() {
console.log('=== Evidence Rule Tests ===\n');
this.testBasicEvidence();
this.testDefeasibleLogic();
this.testPatternMatching();
this.testFusion();
this.testComplexEvidence();
this.testEvidenceErrors();
return this.getTestResults();
}
testBasicEvidence() {
console.log('Testing Basic Evidence...');
const testCases = [
{
input: `evidence canRead(user: User, doc: Document) {
hasRole(user, 'admin')
}`,
description: 'Simple evidence with function call'
},
{
input: `evidence canAccess(user: User, resource: Resource) {
user.isActive
}`,
description: 'Evidence with attribute access'
},
{
input: `evidence canModify(user: User, doc: Document) {
user.isActive
hasRole(user, 'admin')
}`,
description: 'Evidence with multiple conditions'
},
{
input: `evidence canDelete(user: User, doc: Document) {
owns(user, doc)
user.isActive
}`,
description: 'Evidence with ownership and status'
}
];
testCases.forEach(({ input, description }) => {
try {
const result = this.compiler.compile(input, `test-basic-evidence-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
this.assert(result.program.evidence.length > 0, 'Should have evidence');
console.log(`${description}`);
} catch (error) {
this.fail(`Basic evidence test: ${description}`, error);
}
});
}
testDefeasibleLogic() {
console.log('Testing Defeasible Logic...');
const testCases = [
{
input: `evidence canAccess(user: User, resource: Resource) {
ALWAYS user.isActive
}`,
description: 'ALWAYS rule - strict requirement'
},
{
input: `evidence canAccess(user: User, resource: Resource) {
WHEN hasRole(user, 'admin')
}`,
description: 'WHEN rule - defeasible condition'
},
{
input: `evidence canAccess(user: User, resource: Resource) {
WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
}`,
description: 'WHEN/UNLESS rule - defeasible with defeater'
},
{
input: `evidence canAccess(user: User, resource: Resource) {
REQUIRES hasClearance(user, resource.level)
}`,
description: 'REQUIRES rule - inverse defeater'
},
{
input: `evidence canAccessCritical(user: User, resource: Resource) {
ALWAYS user.isActive
WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
REQUIRES hasClearance(user, resource.level)
}`,
description: 'Complex defeasible logic with all rule types'
},
{
input: `evidence canAccessSensitive(user: User, doc: Document) {
ALWAYS user.isActive
WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
REQUIRES hasClearance(user, doc.level)
fusion majority {
user.isTrusted
user.hasRecentActivity
}
}`,
description: 'Defeasible logic with fusion'
}
];
testCases.forEach(({ input, description }) => {
try {
const result = this.compiler.compile(input, `test-defeasible-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
console.log(`${description}`);
} catch (error) {
this.fail(`Defeasible logic test: ${description}`, error);
}
});
}
testPatternMatching() {
console.log('Testing Pattern Matching...');
const testCases = [
{
input: `evidence canRead(user: User, doc: Document) {
isMember(user, *group) {
canRead(group, doc)
}
}`,
description: 'Basic pattern matching with wildcard'
},
{
input: `evidence canRead(user: User, doc: Document) {
isMember(user, *group) {
canRead(group, doc)
} limit 5
}`,
description: 'Pattern matching with limit'
},
{
input: `evidence canRead(user: User, doc: Document) {
similar(doc, *similar) |similarity| {
canRead(user, similar)
} with similarity > 0.7
}`,
description: 'Pattern matching with binding and condition'
},
{
input: `evidence canRead(user: User, doc: Document) {
similar(doc, *similar) |similarity| {
canRead(user, similar)
} with similarity > 0.7 limit 5
}`,
description: 'Pattern matching with binding, condition, and limit'
},
{
input: `evidence canRead(user: User, doc: Document) {
isMember(user, *group) {
isMember(group, *parentGroup) {
canRead(parentGroup, doc)
} limit 2
} limit 3
}`,
description: 'Nested pattern matching'
},
{
input: `evidence canRead(user: User, doc: Document) {
isFriend(user, *friend) {
isMember(friend, *group) {
canRead(group, doc)
} limit 1
} limit 5
}`,
description: 'Multi-hop pattern matching'
}
];
testCases.forEach(({ input, description }) => {
try {
const result = this.compiler.compile(input, `test-pattern-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
console.log(`${description}`);
} catch (error) {
this.fail(`Pattern matching test: ${description}`, error);
}
});
}
testFusion() {
console.log('Testing Fusion...');
const testCases = [
{
input: `evidence canAccess(user: User, resource: Resource) {
fusion min {
hasClearance(user, resource.level)
user.isActive
}
}`,
description: 'Min fusion - all conditions must be true'
},
{
input: `evidence canAccess(user: User, resource: Resource) {
fusion max {
hasRole(user, 'admin')
hasRole(user, 'superuser')
}
}`,
description: 'Max fusion - any condition can be true'
},
{
input: `evidence canAccess(user: User, resource: Resource) {
fusion majority {
hasClearance(user, 'secret')
user.isTrusted
user.hasRecentActivity
}
}`,
description: 'Majority fusion - most conditions must be true'
},
{
input: `evidence canAccessCritical(user: User, resource: Resource) {
fusion min {
hasClearance(user, resource.level)
user.isActive
NOT user.isBlacklisted
}
fusion max {
hasRole(user, 'admin')
fusion majority {
hasClearance(user, 'secret')
user.isTrusted
user.lastActive within 1hr
}
}
}`,
description: 'Nested fusion with different strategies'
},
{
input: `evidence canAccess(user: User, resource: Resource) {
fusion average {
user.reputation
user.activityScore
user.verificationLevel
}
}`,
description: 'Average fusion for numeric values'
}
];
testCases.forEach(({ input, description }) => {
try {
const result = this.compiler.compile(input, `test-fusion-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
console.log(`${description}`);
} catch (error) {
this.fail(`Fusion test: ${description}`, error);
}
});
}
testComplexEvidence() {
console.log('Testing Complex Evidence...');
const testCases = [
{
input: `evidence canRead(user: User, doc: Document) {
owns(user, doc)
isMember(user, *group) {
canRead(group, doc)
} limit 5
parentOf(user, *parent) {
canRead(parent, doc)
} limit 3
similar(doc, *similar) |similarity| {
canRead(user, similar)
} with similarity > 0.7 limit 5
WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
}`,
description: 'Complex evidence with all features'
},
{
input: `evidence canAccessCritical(user: User, resource: Resource) {
ALWAYS user.isActive
WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
REQUIRES hasClearance(user, resource.level)
fusion min {
hasClearance(user, resource.level)
user.isActive
NOT user.isBlacklisted
}
fusion max {
hasRole(user, 'admin')
fusion majority {
hasClearance(user, 'secret')
user.isTrusted
user.lastActive within 1hr
}
}
}`,
description: 'Critical access with all rule types and fusion'
},
{
input: `evidence canModify(user: User, doc: Document) {
owns(user, doc)
isMember(user, *group) {
canModify(group, doc)
} limit 3
similar(doc, *similar) |similarity| {
canModify(user, similar)
similar.isEditable
} with similarity > 0.8 limit 2
fusion majority {
user.isTrusted
user.hasRecentActivity
doc.isPublic
}
}`,
description: 'Modification access with similarity and fusion'
}
];
testCases.forEach(({ input, description }) => {
try {
const result = this.compiler.compile(input, `test-complex-evidence-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
console.log(`${description}`);
} catch (error) {
this.fail(`Complex evidence test: ${description}`, error);
}
});
}
testEvidenceErrors() {
console.log('Testing Evidence Error Handling...');
const testCases = [
{
input: `evidence canRead(user: User, doc: Document) {
hasRole(user, 'admin'
}`,
description: 'Missing closing parenthesis should fail'
},
{
input: `evidence canRead(user: User, doc: Document) {
WHEN hasRole(user, 'admin') UNLESS
}`,
description: 'Incomplete UNLESS condition should fail'
},
{
input: `evidence canRead(user: User, doc: Document) {
fusion min {
hasRole(user, 'admin')
}`,
description: 'Incomplete fusion should fail'
},
{
input: `evidence canRead(user: User, doc: Document) {
isMember(user, *group) {
canRead(group, doc)
} with
}`,
description: 'Incomplete with clause should fail'
},
{
input: `evidence canRead(user: User, doc: Document) {
isMember(user, *group) {
canRead(group, doc)
} limit
}`,
description: 'Incomplete limit should fail'
},
{
input: `evidence canRead(user: User, doc: Document) {
invalid syntax here
}`,
description: 'Invalid syntax should fail'
}
];
testCases.forEach(({ input, description }) => {
try {
const result = this.compiler.compile(input, `test-evidence-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 runEvidenceTests(arbiter) {
const test = new EvidenceTests();
test.setup(arbiter);
return test.runAllTests();
}
-347
View File
@@ -1,347 +0,0 @@
/**
* Expression Parsing Tests
*
* Tests the expression parsing capabilities of the Evidence DSL,
* focusing on operator precedence, associativity, and complex expressions.
*/
import { DSLCompiler } from '../DSLCompiler.js';
export class ExpressionTests {
constructor() {
this.arbiter = null;
this.compiler = null;
this.testResults = [];
}
setup(arbiter) {
this.arbiter = arbiter;
this.compiler = new DSLCompiler(arbiter);
}
runAllTests() {
console.log('=== Expression Parsing Tests ===\n');
this.testArithmeticPrecedence();
this.testLogicalPrecedence();
this.testComparisonOperators();
this.testTemporalExpressions();
this.testUnaryOperators();
this.testAttributeAccess();
this.testFunctionCalls();
this.testComplexExpressions();
this.testExpressionErrors();
return this.getTestResults();
}
testArithmeticPrecedence() {
console.log('Testing Arithmetic Operator Precedence...');
const testCases = [
{
input: '1 + 2 * 3',
expected: 'Should evaluate as 1 + (2 * 3) = 7',
description: 'Multiplication before addition'
},
{
input: '10 - 3 * 2',
expected: 'Should evaluate as 10 - (3 * 2) = 4',
description: 'Multiplication before subtraction'
},
{
input: '8 / 2 * 4',
expected: 'Should evaluate as (8 / 2) * 4 = 16',
description: 'Left-associative division and multiplication'
},
{
input: '2 + 3 * 4 - 5',
expected: 'Should evaluate as 2 + (3 * 4) - 5 = 9',
description: 'Mixed arithmetic with correct precedence'
},
{
input: '(1 + 2) * 3',
expected: 'Should evaluate as (1 + 2) * 3 = 9',
description: 'Parentheses override precedence'
}
];
testCases.forEach(({ input, expected, description }) => {
try {
const dsl = `evidence test() { ${input} }`;
const result = this.compiler.compile(dsl, `test-arithmetic-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
console.log(`${description}`);
} catch (error) {
this.fail(`Arithmetic precedence test: ${description}`, error);
}
});
}
testLogicalPrecedence() {
console.log('Testing Logical Operator Precedence...');
const testCases = [
{
input: 'true && false || true',
expected: 'Should evaluate as (true && false) || true = true',
description: 'AND before OR'
},
{
input: 'false || true && false',
expected: 'Should evaluate as false || (true && false) = false',
description: 'AND before OR (alternative)'
},
{
input: 'NOT true && false',
expected: 'Should evaluate as (NOT true) && false = false',
description: 'NOT before AND'
},
{
input: 'true && NOT false',
expected: 'Should evaluate as true && (NOT false) = true',
description: 'NOT before AND (alternative)'
},
{
input: '(true || false) && true',
expected: 'Should evaluate as (true || false) && true = true',
description: 'Parentheses override logical precedence'
}
];
testCases.forEach(({ input, expected, description }) => {
try {
const dsl = `evidence test() { ${input} }`;
const result = this.compiler.compile(dsl, `test-logical-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
console.log(`${description}`);
} catch (error) {
this.fail(`Logical precedence test: ${description}`, error);
}
});
}
testComparisonOperators() {
console.log('Testing Comparison Operators...');
const testCases = [
{ input: '1 == 1', description: 'Equality comparison' },
{ input: '1 != 2', description: 'Inequality comparison' },
{ input: '5 > 3', description: 'Greater than' },
{ input: '3 < 5', description: 'Less than' },
{ input: '4 >= 4', description: 'Greater than or equal' },
{ input: '4 <= 4', description: 'Less than or equal' },
{ input: '1 == 1 && 2 > 1', description: 'Comparison with logical operators' },
{ input: '1 + 2 == 3', description: 'Arithmetic in comparison' }
];
testCases.forEach(({ input, description }) => {
try {
const dsl = `evidence test() { ${input} }`;
const result = this.compiler.compile(dsl, `test-comparison-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
console.log(`${description}`);
} catch (error) {
this.fail(`Comparison test: ${description}`, error);
}
});
}
testTemporalExpressions() {
console.log('Testing Temporal Expressions...');
const testCases = [
{ input: 'user.lastActive within 1h', description: 'Temporal within expression' },
{ input: 'user.lastLogin within 24h', description: 'Temporal within with hours' },
{ input: 'user.createdAt within 7d', description: 'Temporal within with days' },
{ input: 'user.lastActivity within 1h && user.isActive', description: 'Temporal with logical operators' }
];
testCases.forEach(({ input, description }) => {
try {
const dsl = `evidence test() { ${input} }`;
const result = this.compiler.compile(dsl, `test-temporal-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
console.log(`${description}`);
} catch (error) {
this.fail(`Temporal test: ${description}`, error);
}
});
}
testUnaryOperators() {
console.log('Testing Unary Operators...');
const testCases = [
{ input: 'NOT true', description: 'NOT operator' },
{ input: '!false', description: 'Alternative NOT operator' },
{ input: 'NOT (true && false)', description: 'NOT with parenthesized expression' },
{ input: 'NOT user.isSuspended', description: 'NOT with attribute access' }
];
testCases.forEach(({ input, description }) => {
try {
const dsl = `evidence test() { ${input} }`;
const result = this.compiler.compile(dsl, `test-unary-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
console.log(`${description}`);
} catch (error) {
this.fail(`Unary test: ${description}`, error);
}
});
}
testAttributeAccess() {
console.log('Testing Attribute Access...');
const testCases = [
{ input: 'user.role', description: 'Simple attribute access' },
{ input: 'user.profile.name', description: 'Nested attribute access' },
{ input: 'user.permissions[0]', description: 'Array access' },
{ input: 'user.role.permissions[0]', description: 'Nested attribute with array access' },
{ input: 'user.isActive && user.role == "admin"', description: 'Attribute access in logical expression' }
];
testCases.forEach(({ input, description }) => {
try {
const dsl = `evidence test() { ${input} }`;
const result = this.compiler.compile(dsl, `test-attribute-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
console.log(`${description}`);
} catch (error) {
this.fail(`Attribute access test: ${description}`, error);
}
});
}
testFunctionCalls() {
console.log('Testing Function Calls...');
const testCases = [
{ input: 'hasRole(user, "admin")', description: 'Simple function call' },
{ input: 'isMember(user, group)', description: 'Function call with variables' },
{ input: 'hasPermission(user, resource, "read")', description: 'Function call with multiple arguments' },
{ input: 'hasRole(user, "admin") && isActive(user)', description: 'Multiple function calls' },
{ input: 'hasRole(user, user.role)', description: 'Function call with attribute access' }
];
testCases.forEach(({ input, description }) => {
try {
const dsl = `evidence test() { ${input} }`;
const result = this.compiler.compile(dsl, `test-function-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
console.log(`${description}`);
} catch (error) {
this.fail(`Function call test: ${description}`, error);
}
});
}
testComplexExpressions() {
console.log('Testing Complex Expressions...');
const testCases = [
{
input: 'user.isActive && (hasRole(user, "admin") || hasPermission(user, resource, "read"))',
description: 'Complex logical expression with function calls'
},
{
input: 'user.balance > 100 && user.isActive && NOT user.isSuspended',
description: 'Multiple conditions with NOT'
},
{
input: 'user.lastActive within 1h && (user.role == "admin" || user.hasEmergencyAccess)',
description: 'Temporal with logical conditions'
},
{
input: 'hasRole(user, "admin") && user.isActive && NOT (user.isSuspended || user.isBlacklisted)',
description: 'Complex negation with multiple conditions'
},
{
input: 'user.score > 0.8 && user.isTrusted && user.lastActivity within 24h',
description: 'Multiple attribute conditions with temporal'
}
];
testCases.forEach(({ input, description }) => {
try {
const dsl = `evidence test() { ${input} }`;
const result = this.compiler.compile(dsl, `test-complex-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
console.log(`${description}`);
} catch (error) {
this.fail(`Complex expression test: ${description}`, error);
}
});
}
testExpressionErrors() {
console.log('Testing Expression Error Handling...');
const testCases = [
{
input: 'user.role ==',
description: 'Incomplete comparison should fail'
},
{
input: 'user.role &&',
description: 'Incomplete logical expression should fail'
},
{
input: 'hasRole(user,)',
description: 'Function call with missing argument should fail'
},
{
input: 'user.role == "admin" &&',
description: 'Incomplete logical expression should fail'
}
];
testCases.forEach(({ input, description }) => {
try {
const dsl = `evidence test() { ${input} }`;
const result = this.compiler.compile(dsl, `test-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 runExpressionTests(arbiter) {
const test = new ExpressionTests();
test.setup(arbiter);
return test.runAllTests();
}
-330
View File
@@ -1,330 +0,0 @@
/**
* Fact Declaration Tests
*
* Tests the fact declaration system of the Evidence DSL,
* including properties, caching, limits, and parameter types.
*/
import { DSLCompiler } from '../DSLCompiler.js';
export class FactTests {
constructor() {
this.arbiter = null;
this.compiler = null;
this.testResults = [];
}
setup(arbiter) {
this.arbiter = arbiter;
this.compiler = new DSLCompiler(arbiter);
}
runAllTests() {
console.log('=== Fact Declaration Tests ===\n');
this.testBasicFacts();
this.testFactProperties();
this.testFactCaching();
this.testFactLimits();
this.testParameterTypes();
this.testComplexFacts();
this.testFactErrors();
return this.getTestResults();
}
testBasicFacts() {
console.log('Testing Basic Facts...');
const testCases = [
{
input: `fact hasRole(user: User, role: string)`,
description: 'Simple fact with two parameters'
},
{
input: `fact isMember(user: User, group: Group)`,
description: 'Fact with custom types'
},
{
input: `fact owns(user: User, doc: Document)`,
description: 'Fact with multiple custom types'
},
{
input: `fact isActive(user: User)`,
description: 'Fact with single parameter'
},
{
input: `fact hasPermission(user: User, resource: Resource, action: string)`,
description: 'Fact with three parameters'
}
];
testCases.forEach(({ input, description }) => {
try {
const result = this.compiler.compile(input, `test-basic-fact-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
this.assert(result.program.facts.length > 0, 'Should have facts');
console.log(`${description}`);
} catch (error) {
this.fail(`Basic fact test: ${description}`, error);
}
});
}
testFactProperties() {
console.log('Testing Fact Properties...');
const testCases = [
{
input: `fact isMember(user: User, group: Group) transitive`,
description: 'Transitive fact'
},
{
input: `fact isFriend(user: User, friend: User) symmetrical`,
description: 'Symmetrical fact'
},
{
input: `fact isMember(user: User, group: Group) transitive symmetrical`,
description: 'Fact with multiple properties'
},
{
input: `fact isColleague(user: User, colleague: User) symmetrical`,
description: 'Symmetrical relationship fact'
},
{
input: `fact isParentOf(parent: User, child: User) transitive`,
description: 'Transitive hierarchical fact'
}
];
testCases.forEach(({ input, description }) => {
try {
const result = this.compiler.compile(input, `test-fact-property-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
console.log(`${description}`);
} catch (error) {
this.fail(`Fact property test: ${description}`, error);
}
});
}
testFactCaching() {
console.log('Testing Fact Caching...');
const testCases = [
{
input: `fact hasRole(user: User, role: string) CACHE eager`,
description: 'Eager cached fact'
},
{
input: `fact isMember(user: User, group: Group) CACHE lazy`,
description: 'Lazy cached fact'
},
{
input: `fact isMember(user: User, group: Group) transitive CACHE eager`,
description: 'Transitive fact with eager caching'
},
{
input: `fact isFriend(user: User, friend: User) symmetrical CACHE lazy`,
description: 'Symmetrical fact with lazy caching'
},
{
input: `fact hasPermission(user: User, resource: Resource, action: string) CACHE eager`,
description: 'Multi-parameter fact with eager caching'
}
];
testCases.forEach(({ input, description }) => {
try {
const result = this.compiler.compile(input, `test-fact-cache-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
console.log(`${description}`);
} catch (error) {
this.fail(`Fact cache test: ${description}`, error);
}
});
}
testFactLimits() {
console.log('Testing Fact Limits...');
const testCases = [
{
input: `fact isMember(user: User, group: Group) limit 10`,
description: 'Fact with simple limit'
},
{
input: `fact isFriend(user: User, friend: User) limit 100`,
description: 'Fact with higher limit'
},
{
input: `fact isMember(user: User, group: Group) transitive limit 5`,
description: 'Transitive fact with limit'
},
{
input: `fact isFriend(user: User, friend: User) symmetrical limit 50`,
description: 'Symmetrical fact with limit'
},
{
input: `fact isMember(user: User, group: Group) transitive CACHE lazy limit 3`,
description: 'Fact with properties, caching, and limit'
}
];
testCases.forEach(({ input, description }) => {
try {
const result = this.compiler.compile(input, `test-fact-limit-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
console.log(`${description}`);
} catch (error) {
this.fail(`Fact limit test: ${description}`, error);
}
});
}
testParameterTypes() {
console.log('Testing Parameter Types...');
const testCases = [
{ type: 'string', description: 'String parameter' },
{ type: 'number', description: 'Number parameter' },
{ type: 'boolean', description: 'Boolean parameter' },
{ type: 'timestamp', description: 'Timestamp parameter' },
{ type: 'User', description: 'Custom type parameter' },
{ type: 'Group', description: 'Another custom type parameter' },
{ type: 'Permission[]', description: 'Array type parameter' }
];
testCases.forEach(({ type, description }) => {
try {
const dsl = `fact test(param: ${type})`;
const result = this.compiler.compile(dsl, `test-param-type-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
console.log(`${description}`);
} catch (error) {
this.fail(`Parameter type test: ${description}`, error);
}
});
}
testComplexFacts() {
console.log('Testing Complex Facts...');
const testCases = [
{
input: `fact hasRole(user: User, role: string) CACHE eager
fact isMember(user: User, group: Group) transitive CACHE lazy limit 10
fact isFriend(user: User, friend: User) symmetrical CACHE eager limit 100
fact owns(user: User, doc: Document) CACHE eager
fact isSuspended(user: User) CACHE lazy`,
description: 'Multiple facts with different configurations'
},
{
input: `fact hasPermission(user: User, resource: Resource, action: string) CACHE eager
fact isAdmin(user: User) CACHE eager
fact isOwner(user: User, resource: Resource) CACHE eager
fact hasAccess(user: User, resource: Resource, level: string) CACHE lazy`,
description: 'Permission-related facts'
},
{
input: `fact isMember(user: User, group: Group) transitive CACHE lazy limit 5
fact isFriend(user: User, friend: User) symmetrical CACHE eager limit 50
fact isColleague(user: User, colleague: User) symmetrical CACHE lazy limit 20
fact isParentOf(parent: User, child: User) transitive CACHE eager limit 3`,
description: 'Relationship facts with various properties'
}
];
testCases.forEach(({ input, description }) => {
try {
const result = this.compiler.compile(input, `test-complex-facts-${Date.now()}`);
this.assert(result.success, `${description} should parse successfully`);
this.assert(result.program.facts.length > 0, 'Should have facts');
console.log(`${description}`);
} catch (error) {
this.fail(`Complex facts test: ${description}`, error);
}
});
}
testFactErrors() {
console.log('Testing Fact Error Handling...');
const testCases = [
{
input: `fact hasRole(user: User, role: string`,
description: 'Missing closing parenthesis should fail'
},
{
input: `fact hasRole(user: User, )`,
description: 'Missing parameter name should fail'
},
{
input: `fact hasRole(user: User, role: )`,
description: 'Missing parameter type should fail'
},
{
input: `fact hasRole(, role: string)`,
description: 'Missing parameter name should fail'
},
{
input: `fact hasRole(user: User, role: string) CACHE`,
description: 'Incomplete cache directive should fail'
},
{
input: `fact hasRole(user: User, role: string) limit`,
description: 'Incomplete limit should fail'
},
{
input: `fact hasRole(user: User, role: string) invalid`,
description: 'Invalid property should fail'
}
];
testCases.forEach(({ input, description }) => {
try {
const result = this.compiler.compile(input, `test-fact-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 runFactTests(arbiter) {
const test = new FactTests();
test.setup(arbiter);
return test.runAllTests();
}
-635
View File
@@ -1,635 +0,0 @@
/**
* Integration Tests
*
* Tests complex combinations of multiple language features,
* simulating real-world authorization scenarios.
*/
import { DSLCompiler } from '../DSLCompiler.js';
export class IntegrationTests {
constructor() {
this.arbiter = null;
this.compiler = null;
this.testResults = [];
}
setup(arbiter) {
this.arbiter = arbiter;
this.compiler = new DSLCompiler(arbiter);
}
runAllTests() {
console.log('=== Integration Tests ===\n');
this.testCompleteAuthorizationSystem();
this.testMultiDomainSystem();
this.testHierarchicalAccess();
this.testSimilarityBasedAccess();
this.testTemporalAccess();
this.testComplexBehaviors();
this.testPerformanceScenarios();
return this.getTestResults();
}
testCompleteAuthorizationSystem() {
console.log('Testing Complete Authorization System...');
const completeSystem = `
// Type definitions with complex behaviors
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
clearance: string BEHAVES {
blurring fixed
} CACHE eager
reputation: number BEHAVES {
decaying up daily
} CACHE lazy
}
definition Group {
name: string
permissions: Permission[]
level: string
isPublic: boolean CACHE eager
created: timestamp BEHAVES {
decaying stable monthly
} CACHE lazy
}
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
isPublic: boolean CACHE eager
}
definition Resource {
level: string
owner: User
permissions: Permission[]
isPublic: boolean CACHE eager
accessCount: number BEHAVES {
decaying up daily
} CACHE eager
}
// Facts with various properties and caching
fact hasRole(user: User, role: string) CACHE eager
fact isMember(user: User, group: Group) transitive CACHE lazy limit 10
fact isFriend(user: User, friend: User) symmetrical CACHE eager limit 100
fact owns(user: User, doc: Document) CACHE eager
fact isSuspended(user: User) CACHE lazy
fact hasPermission(user: User, resource: Resource, action: string) CACHE eager
fact isAdmin(user: User) CACHE eager
fact isOwner(user: User, resource: Resource) CACHE eager
fact hasAccess(user: User, resource: Resource, level: string) CACHE lazy
fact isColleague(user: User, colleague: User) symmetrical CACHE lazy limit 50
fact isParentOf(parent: User, child: User) transitive CACHE eager limit 3
// Evidence rules with complex logic
evidence canRead(user: User, doc: Document) {
owns(user, doc)
isMember(user, *group) {
canRead(group, doc)
} limit 5
parentOf(user, *parent) {
canRead(parent, doc)
} limit 3
similar(doc, *similar) |similarity| {
canRead(user, similar)
} with similarity > 0.7 limit 5
WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
}
evidence canWrite(user: User, doc: Document) {
owns(user, doc)
isMember(user, *group) {
canWrite(group, doc)
} limit 3
WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
REQUIRES user.isActive
}
evidence canDelete(user: User, doc: Document) {
owns(user, doc)
ALWAYS user.isActive
WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
REQUIRES user.isActive
}
evidence canAccessCritical(user: User, resource: Resource) {
fusion min {
hasClearance(user, resource.level)
user.isActive
NOT user.isBlacklisted
}
fusion max {
hasRole(user, 'admin')
fusion majority {
hasClearance(user, 'secret')
user.isTrusted
user.lastActive within 1hr
}
}
}
evidence canAccessSensitive(user: User, doc: Document) {
ALWAYS user.isActive
WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
REQUIRES hasClearance(user, doc.level)
fusion majority {
user.isTrusted
user.hasRecentActivity
}
}
// Measures for computed values
measure userRole(user: User) {
user.role
} PROVIDES string
measure userPermissions(user: User) {
fusion max {
user.role.permissions
user.group.permissions
}
} PROVIDES Permission[]
measure effectiveClearance(user: User) {
fusion majority {
user.clearance
user.role.clearance
user.group.clearance
}
} PROVIDES string
measure userTrustScore(user: User) {
fusion average {
user.reputation
user.activityScore
user.verificationLevel
}
} PROVIDES number
measure userBalance(user: User) {
user.balance
} PROVIDES number
measure userScore(user: User) {
user.score
} PROVIDES number
`;
try {
const result = this.compiler.compile(completeSystem, 'test-complete-system');
this.assert(result.success, 'Complete authorization system should compile successfully');
this.assert(result.program.definitions.length >= 4, 'Should have multiple definitions');
this.assert(result.program.facts.length >= 10, 'Should have multiple facts');
this.assert(result.program.evidence.length >= 5, 'Should have multiple evidence rules');
this.assert(result.program.measures.length >= 6, 'Should have multiple measures');
console.log(' ✓ Complete authorization system');
} catch (error) {
this.fail('Complete authorization system test', error);
}
}
testMultiDomainSystem() {
console.log('Testing Multi-Domain System...');
const multiDomain = `
// Authentication domain
definition User {
role: string
isActive: boolean
lastActive: timestamp BEHAVES { decaying down hourly } CACHE lazy
session: string BEHAVES { ttl 24h } CACHE eager
}
fact hasRole(user: User, role: string) CACHE eager
fact isActive(user: User) CACHE eager
evidence canAuthenticate(user: User) {
user.isActive
user.session within 24h
}
// Authorization domain
definition Resource {
level: string
owner: User
permissions: Permission[]
}
fact owns(user: User, resource: Resource) CACHE eager
fact hasPermission(user: User, resource: Resource, action: string) CACHE eager
evidence canAccess(user: User, resource: Resource) {
owns(user, resource)
hasPermission(user, resource, 'read')
}
// Finance domain
definition Account {
balance: number BEHAVES { decaying down hourly } CACHE eager
owner: User
isActive: boolean CACHE eager
}
fact hasAccount(user: User, account: Account) CACHE eager
fact hasBalance(user: User, amount: number) CACHE eager
evidence canWithdraw(user: User, amount: number) {
hasBalance(user, amount)
user.isActive
}
// Social domain
definition Group {
name: string
members: User[]
isPublic: boolean CACHE eager
}
fact isMember(user: User, group: Group) transitive CACHE lazy limit 10
fact isFriend(user: User, friend: User) symmetrical CACHE eager limit 100
evidence canAccessGroup(user: User, group: Group) {
isMember(user, group)
group.isPublic
}
`;
try {
const result = this.compiler.compile(multiDomain, 'test-multi-domain');
this.assert(result.success, 'Multi-domain system should compile successfully');
this.assert(result.program.definitions.length >= 4, 'Should have multiple domain definitions');
this.assert(result.program.facts.length >= 8, 'Should have multiple domain facts');
this.assert(result.program.evidence.length >= 4, 'Should have multiple domain evidence rules');
console.log(' ✓ Multi-domain system');
} catch (error) {
this.fail('Multi-domain system test', error);
}
}
testHierarchicalAccess() {
console.log('Testing Hierarchical Access...');
const hierarchicalSystem = `
definition User {
role: string
level: string
isActive: boolean
clearance: string
}
definition Organization {
name: string
level: string
parent: Organization
}
fact isMember(user: User, org: Organization) transitive CACHE lazy limit 5
fact isParentOf(parent: Organization, child: Organization) transitive CACHE eager limit 3
fact hasRole(user: User, role: string) CACHE eager
fact hasClearance(user: User, level: string) CACHE eager
evidence canAccessOrg(user: User, org: Organization) {
isMember(user, org)
isParentOf(org, *parentOrg) {
canAccessOrg(user, parentOrg)
} limit 3
WHEN hasRole(user, 'admin') UNLESS user.isSuspended
}
evidence canAccessResource(user: User, resource: Resource) {
isMember(user, *org) {
canAccessResource(org, resource)
} limit 5
parentOf(user, *parent) {
canAccessResource(parent, resource)
} limit 2
}
`;
try {
const result = this.compiler.compile(hierarchicalSystem, 'test-hierarchical');
this.assert(result.success, 'Hierarchical access system should compile successfully');
console.log(' ✓ Hierarchical access system');
} catch (error) {
this.fail('Hierarchical access test', error);
}
}
testSimilarityBasedAccess() {
console.log('Testing Similarity-Based Access...');
const similaritySystem = `
definition User {
profile: string
interests: string[]
isActive: boolean
}
definition Document {
content: string
tags: string[]
isPublic: boolean
owner: User
}
fact isFriend(user: User, friend: User) symmetrical CACHE eager limit 100
fact hasInterest(user: User, interest: string) CACHE lazy
fact hasTag(doc: Document, tag: string) CACHE lazy
evidence canRead(user: User, doc: Document) {
owns(user, doc)
similar(doc, *similar) |similarity| {
canRead(user, similar)
similar.isPublic
} with similarity > 0.7 limit 10
isFriend(user, *friend) {
canRead(friend, doc)
} limit 5
fusion majority {
user.interests
doc.tags
}
}
evidence canRecommend(user: User, doc: Document) {
similar(user, *similarUser) |similarity| {
canRead(similarUser, doc)
} with similarity > 0.8 limit 20
fusion average {
user.profile
doc.content
}
}
`;
try {
const result = this.compiler.compile(similaritySystem, 'test-similarity');
this.assert(result.success, 'Similarity-based access system should compile successfully');
console.log(' ✓ Similarity-based access system');
} catch (error) {
this.fail('Similarity-based access test', error);
}
}
testTemporalAccess() {
console.log('Testing Temporal Access...');
const temporalSystem = `
definition User {
lastActive: timestamp BEHAVES { decaying down hourly } CACHE lazy
session: string BEHAVES { ttl 24h } CACHE eager
isActive: boolean
}
definition Event {
startTime: timestamp
endTime: timestamp
isPublic: boolean
}
fact hasAccess(user: User, event: Event) CACHE lazy
fact isParticipant(user: User, event: Event) CACHE eager
evidence canAccessEvent(user: User, event: Event) {
user.lastActive within 1h
isParticipant(user, event)
WHEN event.isPublic UNLESS user.isSuspended
fusion min {
user.session within 24h
user.isActive
}
}
evidence canAccessHistorical(user: User, event: Event) {
user.lastActive within 24h
fusion majority {
user.isActive
user.session within 24h
event.isPublic
}
}
`;
try {
const result = this.compiler.compile(temporalSystem, 'test-temporal');
this.assert(result.success, 'Temporal access system should compile successfully');
console.log(' ✓ Temporal access system');
} catch (error) {
this.fail('Temporal access test', error);
}
}
testComplexBehaviors() {
console.log('Testing Complex Behaviors...');
const behaviorSystem = `
definition User {
balance: number BEHAVES { decaying down hourly } CACHE eager
score: number BEHAVES { blurring adaptive confidence_95 } CACHE lazy
session: string BEHAVES { ttl 24h } CACHE eager
reputation: number BEHAVES { decaying up daily } CACHE lazy
clearance: string BEHAVES { blurring fixed } CACHE eager
lastActive: timestamp BEHAVES { decaying down hourly } CACHE lazy
}
definition Document {
content: string BEHAVES { blurring fixed } CACHE lazy
accessCount: number BEHAVES { decaying up daily } CACHE eager
expiresAt: timestamp BEHAVES { ttl 30d } CACHE eager
isPublic: boolean CACHE eager
}
fact hasBalance(user: User, amount: number) CACHE eager
fact hasScore(user: User, score: number) CACHE lazy
fact hasReputation(user: User, reputation: number) CACHE lazy
evidence canAccessDocument(user: User, doc: Document) {
user.balance > 0
user.score > 0.5
user.reputation > 0.3
doc.accessCount < 1000
fusion majority {
user.isActive
user.lastActive within 1h
doc.isPublic
}
}
measure userEffectiveScore(user: User) {
fusion average {
user.score
user.reputation
user.balance
}
} PROVIDES number
measure documentPopularity(doc: Document) {
doc.accessCount
} PROVIDES number
`;
try {
const result = this.compiler.compile(behaviorSystem, 'test-behaviors');
this.assert(result.success, 'Complex behaviors system should compile successfully');
console.log(' ✓ Complex behaviors system');
} catch (error) {
this.fail('Complex behaviors test', error);
}
}
testPerformanceScenarios() {
console.log('Testing Performance Scenarios...');
const performanceSystem = `
definition User {
role: string
isActive: boolean
permissions: Permission[] CACHE eager
}
definition Resource {
level: string
owner: User
permissions: Permission[] CACHE eager
}
// High-frequency facts with limits
fact isMember(user: User, group: Group) transitive CACHE lazy limit 5
fact isFriend(user: User, friend: User) symmetrical CACHE eager limit 50
fact hasPermission(user: User, resource: Resource, action: string) CACHE eager
fact owns(user: User, resource: Resource) CACHE eager
// Optimized evidence rules
evidence canAccess(user: User, resource: Resource) {
owns(user, resource)
isMember(user, *group) {
canAccess(group, resource)
} limit 3
WHEN hasPermission(user, resource, 'read')
}
evidence canModify(user: User, resource: Resource) {
owns(user, resource)
isMember(user, *group) {
canModify(group, resource)
} limit 2
WHEN hasPermission(user, resource, 'write')
}
// Efficient measures
measure userEffectivePermissions(user: User) {
user.permissions
} PROVIDES Permission[]
measure resourceAccessLevel(resource: Resource) {
resource.level
} PROVIDES string
`;
try {
const result = this.compiler.compile(performanceSystem, 'test-performance');
this.assert(result.success, 'Performance scenarios should compile successfully');
console.log(' ✓ Performance scenarios');
} catch (error) {
this.fail('Performance scenarios test', error);
}
}
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 runIntegrationTests(arbiter) {
const test = new IntegrationTests();
test.setup(arbiter);
return test.runAllTests();
}
-393
View File
@@ -1,393 +0,0 @@
/**
* 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();
}
-249
View File
@@ -1,249 +0,0 @@
# Evidence DSL Test Suite
## Overview
This comprehensive test suite follows a **structural linguistic approach** to validate the Evidence DSL (Domain Specific Language) for authorization policies. The tests are organized incrementally from basic language primitives to complex integration scenarios.
## Test Structure
### 1. Structural Linguistic Tests (`StructuralLinguisticTests.js`)
**Level: Comprehensive**
- **Lexical Primitives**: Identifiers, literals, keywords, whitespace
- **Basic Expressions**: Arithmetic, logical, comparison, temporal
- **Type System**: Definitions, fields, behaviors, caching
- **Fact System**: Declarations, properties, caching, limits
- **Evidence System**: Rules, defeasible logic, pattern matching
- **Measure System**: Aggregation, fusion, return types
- **Complex Integration**: Multi-feature combinations
### 2. Expression Tests (`ExpressionTests.js`)
**Level: Focused**
- Arithmetic operator precedence
- Logical operator precedence
- Comparison operators
- Temporal expressions
- Unary operators
- Attribute access
- Function calls
- Complex expressions
- Error handling
### 3. Definition Tests (`DefinitionTests.js`)
**Level: Focused**
- Basic type definitions
- Field types (string, number, boolean, timestamp, custom)
- Array types
- Behaviors (decay, blur, TTL)
- Caching (eager, lazy)
- Complex definitions
- Error handling
### 4. Fact Tests (`FactTests.js`)
**Level: Focused**
- Basic fact declarations
- Fact properties (transitive, symmetrical)
- Fact caching
- Fact limits
- Parameter types
- Complex facts
- Error handling
### 5. Evidence Tests (`EvidenceTests.js`)
**Level: Focused**
- Basic evidence rules
- Defeasible logic (ALWAYS, WHEN/UNLESS, REQUIRES)
- Pattern matching with wildcards
- Fusion strategies (min, max, majority, average)
- Complex evidence composition
- Error handling
### 6. Measure Tests (`MeasureTests.js`)
**Level: Focused**
- Basic measure definitions
- Return types
- Aggregation with different strategies
- Fusion with different strategies
- Complex measures
- Error handling
### 7. Integration Tests (`IntegrationTests.js`)
**Level: Integration**
- Complete authorization systems
- Multi-domain systems
- Hierarchical access patterns
- Similarity-based access
- Temporal access patterns
- Complex behaviors
- Performance scenarios
## Test Runner (`TestRunner.js`)
The test runner orchestrates all test suites and provides:
- **Comprehensive Testing**: Run all test suites
- **Selective Testing**: Run specific test suites
- **Level-based Testing**: Run tests by complexity level
- **Detailed Reporting**: Summary and detailed results
- **Coverage Analysis**: Language feature coverage
## Usage
### Run All Tests
```javascript
import { runAllTests } from './tests/TestRunner.js';
const results = runAllTests(arbiter);
console.log(`Tests: ${results.passed}/${results.total} passed`);
```
### Run Specific Test Suites
```javascript
import { runSpecificTests } from './tests/TestRunner.js';
const results = runSpecificTests(arbiter, [
'Expression Tests',
'Definition Tests'
]);
```
### Run Tests by Level
```javascript
import { runTestsByLevel } from './tests/TestRunner.js';
// Run only focused tests
const results = runTestsByLevel(arbiter, 'focused');
// Run only integration tests
const results = runTestsByLevel(arbiter, 'integration');
```
## Language Feature Coverage
### ✅ Lexical Primitives
- Identifiers (simple, with underscores, with numbers)
- Literals (string, number, boolean, duration)
- Keywords (reserved words)
- Whitespace and comments
### ✅ Expression System
- Arithmetic operators (+, -, *, /) with precedence
- Logical operators (&&, ||, NOT) with precedence
- Comparison operators (==, !=, >, <, >=, <=)
- Temporal expressions (within)
- Unary operators (NOT, !)
- Attribute access (object.attribute)
- Function calls (predicate(args))
### ✅ Type System
- Type definitions with fields
- Field types (string, number, boolean, timestamp, custom)
- Array types (Type[])
- Behaviors (decay, blur, TTL)
- Caching directives (eager, lazy)
### ✅ Fact System
- Fact declarations with parameters
- Fact properties (transitive, symmetrical)
- Caching directives
- Limits for performance
- Parameter types
### ✅ Evidence System
- Basic evidence rules
- Defeasible logic (ALWAYS, WHEN/UNLESS, REQUIRES)
- Pattern matching with wildcards (*)
- Binding clauses (|variable|)
- With clauses (with condition)
- Limits for pattern matching
- Fusion strategies (min, max, majority, average)
### ✅ Measure System
- Measure definitions
- Return type specifications (PROVIDES)
- Aggregation with strategies (USING)
- Fusion with strategies
- Complex value computation
### ✅ Integration Features
- Multi-domain systems
- Hierarchical access patterns
- Similarity-based access
- Temporal access patterns
- Complex behavior combinations
- Performance optimization scenarios
## Test Philosophy
### Structural Linguistic Approach
The tests follow a structural linguistic methodology:
1. **Phonological Level**: Basic lexical elements (identifiers, literals)
2. **Morphological Level**: Word formation (operators, keywords)
3. **Syntactic Level**: Grammar rules (expressions, statements)
4. **Semantic Level**: Meaning (types, behaviors, logic)
5. **Pragmatic Level**: Usage (integration, real-world scenarios)
### Incremental Complexity
Tests progress from simple to complex:
- **Level 1**: Lexical primitives
- **Level 2**: Basic expressions
- **Level 3**: Type system
- **Level 4**: Fact system
- **Level 5**: Evidence system
- **Level 6**: Measure system
- **Level 7**: Complex integration
### Comprehensive Coverage
Each language feature is tested for:
- **Valid cases**: Correct syntax and semantics
- **Invalid cases**: Error handling and recovery
- **Edge cases**: Boundary conditions
- **Integration**: Multi-feature combinations
## Running Tests
### Prerequisites
- Node.js environment
- Arbiter instance for testing
- All dependencies installed
### Basic Usage
```bash
# Run all tests
npm test
# Run specific test file
node src/ast/tests/StructuralLinguisticTests.js
# Run with specific arbiter
node -e "
import { runAllTests } from './src/ast/tests/TestRunner.js';
const results = runAllTests(arbiter);
console.log(results);
"
```
### Test Output
The test runner provides:
- **Progress indicators**: Real-time test execution
- **Detailed results**: Pass/fail status for each test
- **Error reporting**: Specific error messages for failures
- **Performance metrics**: Execution time for each suite
- **Coverage analysis**: Language feature coverage
## Contributing
When adding new tests:
1. Follow the structural linguistic approach
2. Test both valid and invalid cases
3. Include error handling tests
4. Document test purpose and expected behavior
5. Maintain incremental complexity
6. Update coverage documentation
## Test Maintenance
- **Regular Updates**: Keep tests current with language changes
- **Performance Monitoring**: Track test execution time
- **Coverage Analysis**: Ensure comprehensive feature coverage
- **Error Handling**: Validate error messages and recovery
- **Integration Testing**: Test real-world scenarios
-413
View File
@@ -1,413 +0,0 @@
/**
* Comprehensive Test Runner for Evidence DSL
*
* Orchestrates all test suites in a structural linguistic approach,
* from basic primitives to complex integration scenarios.
*/
import { runStructuralLinguisticTests } from './StructuralLinguisticTests.js';
import { runExpressionTests } from './ExpressionTests.js';
import { runDefinitionTests } from './DefinitionTests.js';
import { runFactTests } from './FactTests.js';
import { runEvidenceTests } from './EvidenceTests.js';
import { runMeasureTests } from './MeasureTests.js';
import { runIntegrationTests } from './IntegrationTests.js';
export class TestRunner {
constructor() {
this.arbiter = null;
this.testSuites = [];
this.results = {
total: 0,
passed: 0,
failed: 0,
success: false,
suites: []
};
}
setup(arbiter) {
this.arbiter = arbiter;
this.testSuites = [
{
name: 'Structural Linguistic Tests',
description: 'Comprehensive tests from basic primitives to complex features',
runner: runStructuralLinguisticTests,
level: 'comprehensive'
},
{
name: 'Expression Tests',
description: 'Expression parsing, operator precedence, and complex expressions',
runner: runExpressionTests,
level: 'focused'
},
{
name: 'Definition Tests',
description: 'Type definitions, fields, behaviors, and caching',
runner: runDefinitionTests,
level: 'focused'
},
{
name: 'Fact Tests',
description: 'Fact declarations, properties, and caching',
runner: runFactTests,
level: 'focused'
},
{
name: 'Evidence Tests',
description: 'Evidence rules, defeasible logic, and pattern matching',
runner: runEvidenceTests,
level: 'focused'
},
{
name: 'Measure Tests',
description: 'Measure definitions, aggregation, and fusion',
runner: runMeasureTests,
level: 'focused'
},
{
name: 'Integration Tests',
description: 'Complex multi-feature integration scenarios',
runner: runIntegrationTests,
level: 'integration'
}
];
}
/**
* Run all test suites
* @returns {Object} Comprehensive test results
*/
runAllTests() {
console.log('='.repeat(80));
console.log('EVIDENCE DSL COMPREHENSIVE TEST SUITE');
console.log('='.repeat(80));
console.log('Structural Linguistic Approach: Testing from primitives to integration\n');
const startTime = Date.now();
for (const suite of this.testSuites) {
console.log(`\n${'='.repeat(60)}`);
console.log(`Running: ${suite.name}`);
console.log(`Level: ${suite.level.toUpperCase()}`);
console.log(`Description: ${suite.description}`);
console.log(`${'='.repeat(60)}`);
try {
const suiteStartTime = Date.now();
const suiteResults = suite.runner(this.arbiter);
const suiteEndTime = Date.now();
const suiteDuration = suiteEndTime - suiteStartTime;
this.results.suites.push({
name: suite.name,
level: suite.level,
duration: suiteDuration,
results: suiteResults
});
this.results.total += suiteResults.total;
this.results.passed += suiteResults.passed;
this.results.failed += suiteResults.failed;
console.log(`\n${suite.name} completed in ${suiteDuration}ms`);
console.log(`Results: ${suiteResults.passed}/${suiteResults.total} passed, ${suiteResults.failed} failed`);
if (suiteResults.success) {
console.log(`${suite.name} PASSED`);
} else {
console.log(`${suite.name} FAILED`);
}
} catch (error) {
console.error(`\n${suite.name} ERROR: ${error.message}`);
this.results.suites.push({
name: suite.name,
level: suite.level,
duration: 0,
results: {
total: 0,
passed: 0,
failed: 1,
success: false,
error: error.message
}
});
this.results.failed += 1;
}
}
const endTime = Date.now();
const totalDuration = endTime - startTime;
this.results.success = this.results.failed === 0;
this.printSummary(totalDuration);
this.printDetailedResults();
return this.results;
}
/**
* Run specific test suites
* @param {string[]} suiteNames - Names of test suites to run
* @returns {Object} Test results for specified suites
*/
runSpecificTests(suiteNames) {
console.log('='.repeat(80));
console.log('EVIDENCE DSL SELECTIVE TEST SUITE');
console.log('='.repeat(80));
console.log(`Running: ${suiteNames.join(', ')}\n`);
const startTime = Date.now();
const selectedSuites = this.testSuites.filter(suite => suiteNames.includes(suite.name));
for (const suite of selectedSuites) {
console.log(`\n${'='.repeat(60)}`);
console.log(`Running: ${suite.name}`);
console.log(`${'='.repeat(60)}`);
try {
const suiteStartTime = Date.now();
const suiteResults = suite.runner(this.arbiter);
const suiteEndTime = Date.now();
const suiteDuration = suiteEndTime - suiteStartTime;
this.results.suites.push({
name: suite.name,
level: suite.level,
duration: suiteDuration,
results: suiteResults
});
this.results.total += suiteResults.total;
this.results.passed += suiteResults.passed;
this.results.failed += suiteResults.failed;
console.log(`\n${suite.name} completed in ${suiteDuration}ms`);
console.log(`Results: ${suiteResults.passed}/${suiteResults.total} passed, ${suiteResults.failed} failed`);
} catch (error) {
console.error(`\n${suite.name} ERROR: ${error.message}`);
this.results.failed += 1;
}
}
const endTime = Date.now();
const totalDuration = endTime - startTime;
this.results.success = this.results.failed === 0;
this.printSummary(totalDuration);
return this.results;
}
/**
* Run tests by level
* @param {string} level - Test level to run ('comprehensive', 'focused', 'integration')
* @returns {Object} Test results for specified level
*/
runTestsByLevel(level) {
const levelSuites = this.testSuites.filter(suite => suite.level === level);
const suiteNames = levelSuites.map(suite => suite.name);
return this.runSpecificTests(suiteNames);
}
/**
* Print test summary
* @param {number} totalDuration - Total test duration in milliseconds
*/
printSummary(totalDuration) {
console.log('\n' + '='.repeat(80));
console.log('TEST SUMMARY');
console.log('='.repeat(80));
console.log(`Total Tests: ${this.results.total}`);
console.log(`Passed: ${this.results.passed}`);
console.log(`Failed: ${this.results.failed}`);
console.log(`Success Rate: ${((this.results.passed / this.results.total) * 100).toFixed(2)}%`);
console.log(`Total Duration: ${totalDuration}ms`);
console.log(`Status: ${this.results.success ? '✓ ALL TESTS PASSED' : '✗ SOME TESTS FAILED'}`);
console.log('='.repeat(80));
}
/**
* Print detailed results for each test suite
*/
printDetailedResults() {
console.log('\n' + '='.repeat(80));
console.log('DETAILED RESULTS');
console.log('='.repeat(80));
this.results.suites.forEach(suite => {
console.log(`\n${suite.name} (${suite.level}):`);
console.log(` Duration: ${suite.duration}ms`);
console.log(` Total: ${suite.results.total}`);
console.log(` Passed: ${suite.results.passed}`);
console.log(` Failed: ${suite.results.failed}`);
console.log(` Success: ${suite.results.success ? '✓' : '✗'}`);
if (suite.results.error) {
console.log(` Error: ${suite.results.error}`);
}
if (suite.results.results && suite.results.results.length > 0) {
console.log(' Individual Results:');
suite.results.results.forEach(result => {
const status = result.status === 'PASSED' ? '✓' : '✗';
console.log(` ${status} ${result.test}`);
if (result.error) {
console.log(` Error: ${result.error}`);
}
});
}
});
}
/**
* Get test coverage report
* @returns {Object} Coverage report
*/
getCoverageReport() {
const coverage = {
lexical: {
identifiers: 'tested',
literals: 'tested',
keywords: 'tested',
whitespace: 'tested'
},
expressions: {
arithmetic: 'tested',
logical: 'tested',
comparison: 'tested',
temporal: 'tested',
unary: 'tested',
attributeAccess: 'tested',
functionCalls: 'tested'
},
types: {
definitions: 'tested',
fields: 'tested',
behaviors: 'tested',
caching: 'tested',
arrays: 'tested'
},
facts: {
declarations: 'tested',
properties: 'tested',
caching: 'tested',
limits: 'tested',
parameters: 'tested'
},
evidence: {
basic: 'tested',
defeasibleLogic: 'tested',
patternMatching: 'tested',
fusion: 'tested',
complex: 'tested'
},
measures: {
basic: 'tested',
aggregation: 'tested',
fusion: 'tested',
returnTypes: 'tested',
complex: 'tested'
},
integration: {
completeSystems: 'tested',
multiDomain: 'tested',
hierarchical: 'tested',
similarity: 'tested',
temporal: 'tested',
behaviors: 'tested',
performance: 'tested'
}
};
return coverage;
}
/**
* Get language feature coverage
* @returns {Object} Feature coverage report
*/
getFeatureCoverage() {
return {
languagePrimitives: {
identifiers: '✓',
literals: '✓',
keywords: '✓',
operators: '✓',
expressions: '✓'
},
typeSystem: {
definitions: '✓',
fields: '✓',
behaviors: '✓',
caching: '✓',
arrays: '✓'
},
factSystem: {
declarations: '✓',
properties: '✓',
caching: '✓',
limits: '✓',
parameters: '✓'
},
evidenceSystem: {
rules: '✓',
defeasibleLogic: '✓',
patternMatching: '✓',
fusion: '✓',
complex: '✓'
},
measureSystem: {
definitions: '✓',
aggregation: '✓',
fusion: '✓',
returnTypes: '✓',
complex: '✓'
},
integration: {
multiFeature: '✓',
realWorld: '✓',
performance: '✓',
scalability: '✓'
}
};
}
}
/**
* Run all tests
* @param {Object} arbiter - Arbiter instance for testing
* @returns {Object} Comprehensive test results
*/
export function runAllTests(arbiter) {
const runner = new TestRunner();
runner.setup(arbiter);
return runner.runAllTests();
}
/**
* Run specific test suites
* @param {Object} arbiter - Arbiter instance for testing
* @param {string[]} suiteNames - Names of test suites to run
* @returns {Object} Test results for specified suites
*/
export function runSpecificTests(arbiter, suiteNames) {
const runner = new TestRunner();
runner.setup(arbiter);
return runner.runSpecificTests(suiteNames);
}
/**
* Run tests by level
* @param {Object} arbiter - Arbiter instance for testing
* @param {string} level - Test level to run
* @returns {Object} Test results for specified level
*/
export function runTestsByLevel(arbiter, level) {
const runner = new TestRunner();
runner.setup(arbiter);
return runner.runTestsByLevel(level);
}
@@ -579,17 +579,6 @@ export class QualitativeRelationalComparatorRule extends BaseRule {
};
}
/**
* @deprecated Use _aggregateCrispValues instead. Removal after Stage 2.
*/
_aggregateBlurredValues(blurredValues, operandConfig, scale) {
if (!this._warnedAggregateBlurredValues) {
this._warnedAggregateBlurredValues = true;
console.warn('[QualitativeRelationalComparatorRule] _aggregateBlurredValues is deprecated; use _aggregateCrispValues instead.');
}
return this._aggregateCrispValues(blurredValues, operandConfig, scale);
}
/**
* Compare blurred qualitative intervals
* @private
-39
View File
@@ -1,4 +1,3 @@
import { RelationCSR } from './relation/RelationCSR.js';
import { RelationSnapshotAccess } from './relation/RelationSnapshotAccess.js';
import { RelationCaches } from './relation/RelationCaches.js';
import { RelationLookup } from './relation/RelationLookup.js';
@@ -22,16 +21,10 @@ export class RelationManager {
this._relationKeyToIndex = new Map();
this._relationNameToId = new Map();
this._relationCsr = new RelationCSR(this);
this._snapshotAccess = new RelationSnapshotAccess(this);
this._lookup = new RelationLookup(this);
this._updates = new RelationUpdates(this);
this._relationGraph = new RelationGraphTraversal(this);
if (this._relationCsr.enabled && !RelationManager._warnedRelationCsrDeprecated) {
RelationManager._warnedRelationCsrDeprecated = true;
console.warn('[RelationManager] relation CSR index is deprecated; avoid useRelationCsrIndex and related options.');
}
this._cacheHits = 0;
this._cacheMisses = 0;
@@ -60,26 +53,6 @@ export class RelationManager {
return this._relationGraph.shouldUseTraversal(nodeId, relation, reverse);
}
_buildRelationCsrIndex(relation) {
return this._relationCsr.buildIndex(relation);
}
_getRelationCsrIndex(relation) {
return this._relationCsr.getIndex(relation);
}
_recordRelationCsrAdd(relationObj) {
this._relationCsr.recordAdd(relationObj);
}
_recordRelationCsrRemove(srcId, relation, dstId) {
this._relationCsr.recordRemove(srcId, relation, dstId);
}
_getRelationsFromCsr(csr, nodeId, reverse = false) {
return this._relationCsr.getRelationsFromCsr(csr, nodeId, reverse);
}
_getRelationDegreeFromIndices(nodeId, relation, reverse = false) {
this._ensureIndicesBuilt();
const indices = this.arbiter.indices;
@@ -525,17 +498,6 @@ export class RelationManager {
return this.arbiter.valueManager.aggregateCrispValues(intervalValues, aggregator);
}
/**
* @deprecated Use getAggregatedIntervalValue instead. Removal after Stage 2.
*/
getAggregatedBlurredValue(srcId, relation, aggregator = 'max') {
if (!this._warnedAggregatedBlurredValue) {
this._warnedAggregatedBlurredValue = true;
console.warn('[RelationManager] getAggregatedBlurredValue is deprecated; use getAggregatedIntervalValue instead.');
}
return this.getAggregatedIntervalValue(srcId, relation, aggregator);
}
/**
* Compare values between two relations using interval arithmetic
* @param {Object} leftRelation - Left relation object or { srcId, relation, dstId }
@@ -747,7 +709,6 @@ export class RelationManager {
_clearAllCaches() {
this._caches.clearAll();
this._relationCsr.clear();
this._relationGraph.clear();
}
-11
View File
@@ -1169,17 +1169,6 @@ export class ValueManager {
};
}
/**
* @deprecated Use aggregateCrispValues instead. Removal after Stage 2.
*/
aggregateBlurredValues(blurredValues, aggregator = 'max') {
if (!this._warnedAggregateBlurredValues) {
this._warnedAggregateBlurredValues = true;
console.warn('[ValueManager] aggregateBlurredValues is deprecated; use aggregateCrispValues instead.');
}
return this.aggregateCrispValues(blurredValues, aggregator);
}
/**
* Compare two intervals using a comparator
* @param {Object} leftInterval - { min, max }
-154
View File
@@ -1,154 +0,0 @@
export class RelationCSR {
constructor(manager) {
this.manager = manager;
const options = manager.arbiter?.options || {};
this.enabled = !!options.useRelationCsrIndex;
this.deltaThreshold = Number.isFinite(options.relationCsrDeltaThreshold)
? options.relationCsrDeltaThreshold
: 1000;
this.minDegree = Number.isFinite(options.relationCsrMinDegree)
? options.relationCsrMinDegree
: 200;
this.byName = new Map();
}
buildIndex(relation) {
const relSet = this.manager.arbiter.indices?.relationsByRel?.get(relation);
const relations = relSet ? Array.from(relSet) : [];
const bySrcMap = new Map();
const byDstMap = new Map();
for (const rel of relations) {
let srcList = bySrcMap.get(rel.src);
if (!srcList) {
srcList = [];
bySrcMap.set(rel.src, srcList);
}
srcList.push(rel);
let dstList = byDstMap.get(rel.dst);
if (!dstList) {
dstList = [];
byDstMap.set(rel.dst, dstList);
}
dstList.push(rel);
}
const bySrcEntries = [];
const bySrcOffsets = new Map();
for (const [srcId, list] of bySrcMap.entries()) {
const start = bySrcEntries.length;
for (const rel of list) bySrcEntries.push(rel);
bySrcOffsets.set(srcId, { start, end: bySrcEntries.length });
}
const byDstEntries = [];
const byDstOffsets = new Map();
for (const [dstId, list] of byDstMap.entries()) {
const start = byDstEntries.length;
for (const rel of list) byDstEntries.push(rel);
byDstOffsets.set(dstId, { start, end: byDstEntries.length });
}
return {
relation,
bySrcEntries,
bySrcOffsets,
byDstEntries,
byDstOffsets,
addDeltaBySrc: new Map(),
addDeltaByDst: new Map(),
removeDelta: new Set(),
deltaCount: 0
};
}
getIndex(relation) {
if (!this.enabled) return null;
let csr = this.byName.get(relation);
if (!csr || csr.deltaCount > this.deltaThreshold) {
csr = this.buildIndex(relation);
this.byName.set(relation, csr);
}
return csr;
}
recordAdd(relationObj) {
if (!this.enabled) return;
const csr = this.byName.get(relationObj.rel);
if (!csr) return;
const key = this.manager._makeRelationKey(relationObj.src, relationObj.rel, relationObj.dst);
if (csr.removeDelta.has(key)) {
csr.removeDelta.delete(key);
}
let srcList = csr.addDeltaBySrc.get(relationObj.src);
if (!srcList) {
srcList = [];
csr.addDeltaBySrc.set(relationObj.src, srcList);
}
srcList.push(relationObj);
let dstList = csr.addDeltaByDst.get(relationObj.dst);
if (!dstList) {
dstList = [];
csr.addDeltaByDst.set(relationObj.dst, dstList);
}
dstList.push(relationObj);
csr.deltaCount++;
}
recordRemove(srcId, relation, dstId) {
if (!this.enabled) return;
const csr = this.byName.get(relation);
if (!csr) return;
const key = this.manager._makeRelationKey(srcId, relation, dstId);
csr.removeDelta.add(key);
csr.deltaCount++;
}
getRelationsFromCsr(csr, nodeId, reverse = false) {
const entries = reverse ? csr.byDstEntries : csr.bySrcEntries;
const offsets = reverse ? csr.byDstOffsets : csr.bySrcOffsets;
const deltaMap = reverse ? csr.addDeltaByDst : csr.addDeltaBySrc;
const range = offsets.get(nodeId);
const removeDelta = csr.removeDelta;
const result = [];
if (range) {
for (let i = range.start; i < range.end; i++) {
const rel = entries[i];
const key = this.manager._makeRelationKey(rel.src, rel.rel, rel.dst);
if (!removeDelta.has(key)) {
result.push(rel);
}
}
}
const delta = deltaMap.get(nodeId);
if (delta && delta.length) {
for (const rel of delta) {
const key = this.manager._makeRelationKey(rel.src, rel.rel, rel.dst);
if (!removeDelta.has(key)) {
result.push(rel);
}
}
}
return result;
}
getRelationsForNode(nodeId, relation, reverse = false) {
if (!this.enabled) return null;
const degree = this.manager._getRelationDegreeFromIndices(nodeId, relation, reverse);
if (degree < this.minDegree) return null;
const csr = this.getIndex(relation);
if (!csr) return null;
return this.getRelationsFromCsr(csr, nodeId, reverse);
}
clear() {
this.byName.clear();
}
}
-8
View File
@@ -47,10 +47,6 @@ export class RelationLookup {
const persistent = this.manager.arbiter.indices.getRelationsFromSrc(srcId, relation);
return this.manager._mergeRelationLists(persistent, partial);
}
const csrResult = this.manager._relationCsr.getRelationsForNode(srcId, relation, false);
if (csrResult !== null) {
return csrResult;
}
// Try cache first for frequently accessed patterns - use numeric key
const cacheKey = this.manager._makeSrcRelCacheKey(srcId, relation);
const cached = this.manager._caches.relationLookupCache.get(cacheKey);
@@ -84,10 +80,6 @@ export class RelationLookup {
const persistent = this.manager.arbiter.indices.getRelationsToDst(dstId, relation);
return this.manager._mergeRelationLists(persistent, partial);
}
const csrResult = this.manager._relationCsr.getRelationsForNode(dstId, relation, true);
if (csrResult !== null) {
return csrResult;
}
// Try cache first - use numeric key
const cacheKey = this.manager._makeDstRelCacheKey(dstId, relation);
const cached = this.manager._caches.relationLookupCache.get(cacheKey);
-3
View File
@@ -71,7 +71,6 @@ export class RelationUpdates {
this.manager.arbiter.indicesBuilt = true;
this.manager._addRelationToGraph(srcId, relation, dstId);
this.manager._recordRelationCsrAdd(relationObj);
// Update PLTC indices incrementally (if initialized and not in batch mode)
if (!this.manager.arbiter.batchUpdateInProgress) {
@@ -124,7 +123,6 @@ export class RelationUpdates {
this.manager._invalidateValueRelationCaches(srcId, dstId, relationName);
this.manager._removeRelationFromGraph(srcId, relationName, dstId);
this.manager._recordRelationCsrRemove(srcId, relationName, dstId);
// Notify ValueManager about the removed relation's last state
if (relationObjectToRemove.value !== undefined && relationObjectToRemove.stateId) {
@@ -368,7 +366,6 @@ export class RelationUpdates {
this.manager.arbiter.indicesBuilt = true;
this.manager._addRelationToGraph(srcId, relation, dstId);
this.manager._recordRelationCsrAdd(relationObj);
// Update PLTC indices incrementally (if initialized and not in batch mode)
// For updates, only update if it's a new relation (not an existing one being modified)