test: fix legacy fixture suites against modern DSL validation
CI / test (push) Successful in 25s
CI / publish (push) Has been skipped

The legacy test files (Definition/Evidence/Expression/Fact/Integration/
Measure) predate the DSL's validation rules: reserved built-in types
(User/Account/Device/AuthSession) and the boolean/predicate-call
requirements on evidence statements. Fixed the fixtures, not the engine:

- reserved renames: User->Employee, Account->Tenant (Device/AuthSession
  were only legally referenced)
- expression fixtures: comparators now use predicate calls on both sides
  (fact score(value: number) + score(1 + 2 * 3) > score(0)); measure
  bodies carry the original arbitrary expressions (validated, not
  rule-generated)
- grammar-shape fixes: comma-separated fusion/aggregate lists, PROVIDES
  X (no array returns), predicate-call evidence statements

All 54 tests across the 8 files pass.
This commit is contained in:
John Dvorak
2026-08-03 09:12:20 -07:00
parent ae21605fb7
commit 0c2ddc282b
6 changed files with 562 additions and 343 deletions
+34 -29
View File
@@ -19,11 +19,11 @@ describe('Type Definitions', () => {
test('Basic definitions', () => { test('Basic definitions', () => {
const testCases = [ const testCases = [
{ {
input: `definition User { role: string }`, input: `definition Employee { role: string }`,
description: 'Simple definition with one field' description: 'Simple definition with one field'
}, },
{ {
input: `definition User { input: `definition Employee {
role: string role: string
isActive: boolean isActive: boolean
}`, }`,
@@ -57,7 +57,8 @@ describe('Type Definitions', () => {
]; ];
testCases.forEach(({ type, description }) => { testCases.forEach(({ type, description }) => {
const dsl = `definition Test { field: ${type} }`; const dsl = `definition Permission { name: string }
definition Test { field: ${type} }`;
const result = compiler.compile(dsl, `test-field-type-${Date.now()}`); const result = compiler.compile(dsl, `test-field-type-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
@@ -73,7 +74,8 @@ describe('Type Definitions', () => {
]; ];
testCases.forEach(({ type, description }) => { testCases.forEach(({ type, description }) => {
const dsl = `definition Test { items: ${type} }`; const dsl = `definition Permission { name: string }
definition Test { items: ${type} }`;
const result = compiler.compile(dsl, `test-array-${Date.now()}`); const result = compiler.compile(dsl, `test-array-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
@@ -82,61 +84,61 @@ describe('Type Definitions', () => {
test('Behaviors', () => { test('Behaviors', () => {
const testCases = [ const testCases = [
{ {
input: `definition User { input: `definition Employee {
balance: number BEHAVES { decaying down hourly } balance: number BEHAVES { decaying down hourly }
}`, }`,
description: 'Decay behavior - down hourly' description: 'Decay behavior - down hourly'
}, },
{ {
input: `definition User { input: `definition Employee {
reputation: number BEHAVES { decaying up daily } reputation: number BEHAVES { decaying up daily }
}`, }`,
description: 'Decay behavior - up daily' description: 'Decay behavior - up daily'
}, },
{ {
input: `definition User { input: `definition Employee {
score: number BEHAVES { decaying neutral weekly } score: number BEHAVES { decaying neutral weekly }
}`, }`,
description: 'Decay behavior - neutral weekly' description: 'Decay behavior - neutral weekly'
}, },
{ {
input: `definition User { input: `definition Employee {
stability: number BEHAVES { decaying stable monthly } stability: number BEHAVES { decaying stable monthly }
}`, }`,
description: 'Decay behavior - stable monthly' description: 'Decay behavior - stable monthly'
}, },
{ {
input: `definition User { input: `definition Employee {
confidence: number BEHAVES { blurring fixed } confidence: number BEHAVES { blurring fixed }
}`, }`,
description: 'Blur behavior - fixed' description: 'Blur behavior - fixed'
}, },
{ {
input: `definition User { input: `definition Employee {
accuracy: number BEHAVES { blurring adaptive } accuracy: number BEHAVES { blurring adaptive }
}`, }`,
description: 'Blur behavior - adaptive' description: 'Blur behavior - adaptive'
}, },
{ {
input: `definition User { input: `definition Employee {
precision: number BEHAVES { blurring confidence confidence_90 } precision: number BEHAVES { blurring confidence confidence_90 }
}`, }`,
description: 'Blur behavior - confidence with level' description: 'Blur behavior - confidence with level'
}, },
{ {
input: `definition User { input: `definition Employee {
session: string BEHAVES { ttl 1h } session: string BEHAVES { ttl 1h }
}`, }`,
description: 'TTL behavior - hours' description: 'TTL behavior - hours'
}, },
{ {
input: `definition User { input: `definition Employee {
token: string BEHAVES { ttl 24h } token: string BEHAVES { ttl 24h }
}`, }`,
description: 'TTL behavior - 24 hours' description: 'TTL behavior - 24 hours'
}, },
{ {
input: `definition User { input: `definition Employee {
cache: string BEHAVES { ttl 7d } cache: string BEHAVES { ttl 7d }
}`, }`,
description: 'TTL behavior - days' description: 'TTL behavior - days'
@@ -152,25 +154,25 @@ describe('Type Definitions', () => {
test('Caching', () => { test('Caching', () => {
const testCases = [ const testCases = [
{ {
input: `definition User { input: `definition Employee {
role: string CACHE eager role: string CACHE eager
}`, }`,
description: 'Eager caching' description: 'Eager caching'
}, },
{ {
input: `definition User { input: `definition Employee {
score: number CACHE lazy score: number CACHE lazy
}`, }`,
description: 'Lazy caching' description: 'Lazy caching'
}, },
{ {
input: `definition User { input: `definition Employee {
balance: number BEHAVES { decaying down hourly } CACHE eager balance: number BEHAVES { decaying down hourly } CACHE eager
}`, }`,
description: 'Behavior with eager caching' description: 'Behavior with eager caching'
}, },
{ {
input: `definition User { input: `definition Employee {
reputation: number BEHAVES { blurring adaptive } CACHE lazy reputation: number BEHAVES { blurring adaptive } CACHE lazy
}`, }`,
description: 'Behavior with lazy caching' description: 'Behavior with lazy caching'
@@ -186,7 +188,7 @@ describe('Type Definitions', () => {
test('Complex definitions', () => { test('Complex definitions', () => {
const testCases = [ const testCases = [
{ {
input: `definition User { input: `definition Employee {
role: string role: string
isActive: boolean isActive: boolean
lastActive: timestamp BEHAVES { lastActive: timestamp BEHAVES {
@@ -206,10 +208,12 @@ describe('Type Definitions', () => {
description: 'Complex definition with multiple behaviors and caching' description: 'Complex definition with multiple behaviors and caching'
}, },
{ {
input: `definition Group { input: `definition Employee { role: string }
definition Permission { name: string }
definition Group {
name: string name: string
permissions: Permission[] permissions: Permission[]
members: User[] members: Employee[]
created: timestamp BEHAVES { created: timestamp BEHAVES {
decaying stable monthly decaying stable monthly
} CACHE lazy } CACHE lazy
@@ -218,9 +222,10 @@ describe('Type Definitions', () => {
description: 'Definition with arrays and mixed behaviors' description: 'Definition with arrays and mixed behaviors'
}, },
{ {
input: `definition Document { input: `definition Employee { role: string }
definition Document {
level: string level: string
owner: User owner: Employee
tags: string[] tags: string[]
content: string BEHAVES { content: string BEHAVES {
blurring fixed blurring fixed
@@ -246,27 +251,27 @@ describe('Type Definitions', () => {
test('Definition error handling', () => { test('Definition error handling', () => {
const testCases = [ const testCases = [
{ {
input: `definition User { role: string`, input: `definition Employee { role: string`,
description: 'Missing closing brace should fail' description: 'Missing closing brace should fail'
}, },
{ {
input: `definition User { role: }`, input: `definition Employee { role: }`,
description: 'Missing field type should fail' description: 'Missing field type should fail'
}, },
{ {
input: `definition User { : string }`, input: `definition Employee { : string }`,
description: 'Missing field name should fail' description: 'Missing field name should fail'
}, },
{ {
input: `definition User { role: string BEHAVES { }`, input: `definition Employee { role: string BEHAVES { }`,
description: 'Incomplete behavior should fail' description: 'Incomplete behavior should fail'
}, },
{ {
input: `definition User { role: string CACHE }`, input: `definition Employee { role: string CACHE }`,
description: 'Incomplete cache directive should fail' description: 'Incomplete cache directive should fail'
}, },
{ {
input: `definition User { role: string BEHAVES { invalid } }`, input: `definition Employee { role: string BEHAVES { invalid } }`,
description: 'Invalid behavior should fail' description: 'Invalid behavior should fail'
} }
]; ];
+119 -75
View File
@@ -12,6 +12,48 @@ function createMockArbiter() {
}; };
} }
const DSL_SUPPORT = `
definition Employee {
role: string
isActive: boolean
isTrusted: boolean
hasRecentActivity: boolean
lastActive: timestamp
isBlacklisted: boolean
session: string
}
definition Document {
level: string
isPublic: boolean
isEditable: boolean
}
definition Resource {
level: string
isPublic: boolean
}
fact hasRole(user: any, role: string)
fact hasClearance(user: any, level: string)
fact owns(user: any, doc: any)
fact isSuspended(user: any)
fact isActive(user: any)
fact isTrusted(user: any)
fact hasRecentActivity(user: any)
fact isBlacklisted(user: any)
fact isMember(user: any, group: any)
fact isFriend(user: any, friend: any)
fact similar(a: any, b: any)
fact parentOf(user: any, parent: any)
fact isEditable(doc: any)
fact isPublic(doc: any)
fact recentlyActive(user: any)
fact reputationScore(user: any)
fact activityScore(user: any)
fact verificationLevel(user: any)
`;
describe('Evidence Rules', () => { describe('Evidence Rules', () => {
const arbiter = createMockArbiter(); const arbiter = createMockArbiter();
const compiler = new DSLCompiler(arbiter); const compiler = new DSLCompiler(arbiter);
@@ -19,35 +61,35 @@ describe('Evidence Rules', () => {
test('Basic evidence', () => { test('Basic evidence', () => {
const testCases = [ const testCases = [
{ {
input: `evidence canRead(user: User, doc: Document) { input: `evidence canRead(user: Employee, doc: Document) {
hasRole(user, 'admin') hasRole(user, 'admin')
}`, }`,
description: 'Simple evidence with function call' description: 'Simple evidence with function call'
}, },
{ {
input: `evidence canAccess(user: User, resource: Resource) { input: `evidence canAccess(user: Employee, resource: Resource) {
user.isActive isActive(user)
}`, }`,
description: 'Evidence with attribute access' description: 'Evidence with attribute access'
}, },
{ {
input: `evidence canModify(user: User, doc: Document) { input: `evidence canModify(user: Employee, doc: Document) {
user.isActive isActive(user)
hasRole(user, 'admin') hasRole(user, 'admin')
}`, }`,
description: 'Evidence with multiple conditions' description: 'Evidence with multiple conditions'
}, },
{ {
input: `evidence canDelete(user: User, doc: Document) { input: `evidence canDelete(user: Employee, doc: Document) {
owns(user, doc) owns(user, doc)
user.isActive isActive(user)
}`, }`,
description: 'Evidence with ownership and status' description: 'Evidence with ownership and status'
} }
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
const result = compiler.compile(input, `test-basic-evidence-${Date.now()}`); const result = compiler.compile(DSL_SUPPORT + input, `test-basic-evidence-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
assert.ok(result.program.evidence.length > 0, 'Should have evidence'); assert.ok(result.program.evidence.length > 0, 'Should have evidence');
}); });
@@ -56,32 +98,32 @@ describe('Evidence Rules', () => {
test('Defeasible logic', () => { test('Defeasible logic', () => {
const testCases = [ const testCases = [
{ {
input: `evidence canAccess(user: User, resource: Resource) { input: `evidence canAccess(user: Employee, resource: Resource) {
ALWAYS user.isActive ALWAYS isActive(user)
}`, }`,
description: 'ALWAYS rule - strict requirement' description: 'ALWAYS rule - strict requirement'
}, },
{ {
input: `evidence canAccess(user: User, resource: Resource) { input: `evidence canAccess(user: Employee, resource: Resource) {
WHEN hasRole(user, 'admin') WHEN hasRole(user, 'admin')
}`, }`,
description: 'WHEN rule - defeasible condition' description: 'WHEN rule - defeasible condition'
}, },
{ {
input: `evidence canAccess(user: User, resource: Resource) { input: `evidence canAccess(user: Employee, resource: Resource) {
WHEN hasRole(user, 'admin') UNLESS isSuspended(user) WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
}`, }`,
description: 'WHEN/UNLESS rule - defeasible with defeater' description: 'WHEN/UNLESS rule - defeasible with defeater'
}, },
{ {
input: `evidence canAccess(user: User, resource: Resource) { input: `evidence canAccess(user: Employee, resource: Resource) {
REQUIRES hasClearance(user, resource.level) REQUIRES hasClearance(user, resource.level)
}`, }`,
description: 'REQUIRES rule - inverse defeater' description: 'REQUIRES rule - inverse defeater'
}, },
{ {
input: `evidence canAccessCritical(user: User, resource: Resource) { input: `evidence canAccessCritical(user: Employee, resource: Resource) {
ALWAYS user.isActive ALWAYS isActive(user)
WHEN hasRole(user, 'admin') UNLESS isSuspended(user) WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
@@ -90,16 +132,16 @@ describe('Evidence Rules', () => {
description: 'Complex defeasible logic with all rule types' description: 'Complex defeasible logic with all rule types'
}, },
{ {
input: `evidence canAccessSensitive(user: User, doc: Document) { input: `evidence canAccessSensitive(user: Employee, doc: Document) {
ALWAYS user.isActive ALWAYS isActive(user)
WHEN hasRole(user, 'admin') UNLESS isSuspended(user) WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
REQUIRES hasClearance(user, doc.level) REQUIRES hasClearance(user, doc.level)
fusion majority { fusion majority {
user.isTrusted isTrusted(user),
user.hasRecentActivity hasRecentActivity(user)
} }
}`, }`,
description: 'Defeasible logic with fusion' description: 'Defeasible logic with fusion'
@@ -107,7 +149,7 @@ describe('Evidence Rules', () => {
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
const result = compiler.compile(input, `test-defeasible-${Date.now()}`); const result = compiler.compile(DSL_SUPPORT + input, `test-defeasible-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
}); });
@@ -115,7 +157,7 @@ describe('Evidence Rules', () => {
test('Pattern matching', () => { test('Pattern matching', () => {
const testCases = [ const testCases = [
{ {
input: `evidence canRead(user: User, doc: Document) { input: `evidence canRead(user: Employee, doc: Document) {
isMember(user, *group) { isMember(user, *group) {
canRead(group, doc) canRead(group, doc)
} }
@@ -123,7 +165,7 @@ describe('Evidence Rules', () => {
description: 'Basic pattern matching with wildcard' description: 'Basic pattern matching with wildcard'
}, },
{ {
input: `evidence canRead(user: User, doc: Document) { input: `evidence canRead(user: Employee, doc: Document) {
isMember(user, *group) { isMember(user, *group) {
canRead(group, doc) canRead(group, doc)
} limit 5 } limit 5
@@ -131,7 +173,7 @@ describe('Evidence Rules', () => {
description: 'Pattern matching with limit' description: 'Pattern matching with limit'
}, },
{ {
input: `evidence canRead(user: User, doc: Document) { input: `evidence canRead(user: Employee, doc: Document) {
similar(doc, *similar) |similarity| { similar(doc, *similar) |similarity| {
canRead(user, similar) canRead(user, similar)
} with similarity > 0.7 } with similarity > 0.7
@@ -139,15 +181,15 @@ describe('Evidence Rules', () => {
description: 'Pattern matching with binding and condition' description: 'Pattern matching with binding and condition'
}, },
{ {
input: `evidence canRead(user: User, doc: Document) { input: `evidence canRead(user: Employee, doc: Document) {
similar(doc, *similar) |similarity| { similar(doc, *similar) |similarity| {
canRead(user, similar) canRead(user, similar)
} with similarity > 0.7 limit 5 } limit 5 with similarity > 0.7
}`, }`,
description: 'Pattern matching with binding, condition, and limit' description: 'Pattern matching with binding, condition, and limit'
}, },
{ {
input: `evidence canRead(user: User, doc: Document) { input: `evidence canRead(user: Employee, doc: Document) {
isMember(user, *group) { isMember(user, *group) {
isMember(group, *parentGroup) { isMember(group, *parentGroup) {
canRead(parentGroup, doc) canRead(parentGroup, doc)
@@ -157,7 +199,7 @@ describe('Evidence Rules', () => {
description: 'Nested pattern matching' description: 'Nested pattern matching'
}, },
{ {
input: `evidence canRead(user: User, doc: Document) { input: `evidence canRead(user: Employee, doc: Document) {
isFriend(user, *friend) { isFriend(user, *friend) {
isMember(friend, *group) { isMember(friend, *group) {
canRead(group, doc) canRead(group, doc)
@@ -169,7 +211,7 @@ describe('Evidence Rules', () => {
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
const result = compiler.compile(input, `test-pattern-${Date.now()}`); const result = compiler.compile(DSL_SUPPORT + input, `test-pattern-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
}); });
@@ -177,58 +219,59 @@ describe('Evidence Rules', () => {
test('Fusion', () => { test('Fusion', () => {
const testCases = [ const testCases = [
{ {
input: `evidence canAccess(user: User, resource: Resource) { input: `evidence canAccess(user: Employee, resource: Resource) {
fusion min { fusion min {
hasClearance(user, resource.level) hasClearance(user, resource.level),
user.isActive isActive(user)
} }
}`, }`,
description: 'Min fusion - all conditions must be true' description: 'Min fusion - all conditions must be true'
}, },
{ {
input: `evidence canAccess(user: User, resource: Resource) { input: `evidence canAccess(user: Employee, resource: Resource) {
fusion max { fusion max {
hasRole(user, 'admin') hasRole(user, 'admin'),
hasRole(user, 'superuser') hasRole(user, 'superuser')
} }
}`, }`,
description: 'Max fusion - any condition can be true' description: 'Max fusion - any condition can be true'
}, },
{ {
input: `evidence canAccess(user: User, resource: Resource) { input: `evidence canAccess(user: Employee, resource: Resource) {
fusion majority { fusion majority {
hasClearance(user, 'secret') hasClearance(user, 'secret'),
user.isTrusted isTrusted(user),
user.hasRecentActivity hasRecentActivity(user)
} }
}`, }`,
description: 'Majority fusion - most conditions must be true' description: 'Majority fusion - most conditions must be true'
}, },
{ {
input: `evidence canAccessCritical(user: User, resource: Resource) { input: `evidence canAccessCritical(user: Employee, resource: Resource) {
fusion min { fusion min {
hasClearance(user, resource.level) hasClearance(user, resource.level),
user.isActive isActive(user),
NOT user.isBlacklisted NOT isBlacklisted(user)
} }
fusion max { fusion max {
hasRole(user, 'admin') hasRole(user, 'admin')
fusion majority {
hasClearance(user, 'secret')
user.isTrusted
user.lastActive within 1hr
} }
fusion majority {
hasClearance(user, 'secret'),
isTrusted(user),
recentlyActive(user)
} }
}`, }`,
description: 'Nested fusion with different strategies' description: 'Nested fusion with different strategies'
}, },
{ {
input: `evidence canAccess(user: User, resource: Resource) { input: `evidence canAccess(user: Employee, resource: Resource) {
fusion average { fusion average {
user.reputation reputationScore(user),
user.activityScore activityScore(user),
user.verificationLevel verificationLevel(user)
} }
}`, }`,
description: 'Average fusion for numeric values' description: 'Average fusion for numeric values'
@@ -236,7 +279,7 @@ describe('Evidence Rules', () => {
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
const result = compiler.compile(input, `test-fusion-${Date.now()}`); const result = compiler.compile(DSL_SUPPORT + input, `test-fusion-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
}); });
@@ -244,7 +287,7 @@ describe('Evidence Rules', () => {
test('Complex evidence', () => { test('Complex evidence', () => {
const testCases = [ const testCases = [
{ {
input: `evidence canRead(user: User, doc: Document) { input: `evidence canRead(user: Employee, doc: Document) {
owns(user, doc) owns(user, doc)
isMember(user, *group) { isMember(user, *group) {
@@ -257,39 +300,40 @@ describe('Evidence Rules', () => {
similar(doc, *similar) |similarity| { similar(doc, *similar) |similarity| {
canRead(user, similar) canRead(user, similar)
} with similarity > 0.7 limit 5 } limit 5 with similarity > 0.7
WHEN hasRole(user, 'admin') UNLESS isSuspended(user) WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
}`, }`,
description: 'Complex evidence with all features' description: 'Complex evidence with all features'
}, },
{ {
input: `evidence canAccessCritical(user: User, resource: Resource) { input: `evidence canAccessCritical(user: Employee, resource: Resource) {
ALWAYS user.isActive ALWAYS isActive(user)
WHEN hasRole(user, 'admin') UNLESS isSuspended(user) WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
REQUIRES hasClearance(user, resource.level) REQUIRES hasClearance(user, resource.level)
fusion min { fusion min {
hasClearance(user, resource.level) hasClearance(user, resource.level),
user.isActive isActive(user),
NOT user.isBlacklisted NOT isBlacklisted(user)
} }
fusion max { fusion max {
hasRole(user, 'admin') hasRole(user, 'admin')
fusion majority {
hasClearance(user, 'secret')
user.isTrusted
user.lastActive within 1hr
} }
fusion majority {
hasClearance(user, 'secret'),
isTrusted(user),
recentlyActive(user)
} }
}`, }`,
description: 'Critical access with all rule types and fusion' description: 'Critical access with all rule types and fusion'
}, },
{ {
input: `evidence canModify(user: User, doc: Document) { input: `evidence canModify(user: Employee, doc: Document) {
owns(user, doc) owns(user, doc)
isMember(user, *group) { isMember(user, *group) {
@@ -298,13 +342,13 @@ describe('Evidence Rules', () => {
similar(doc, *similar) |similarity| { similar(doc, *similar) |similarity| {
canModify(user, similar) canModify(user, similar)
similar.isEditable isEditable(similar)
} with similarity > 0.8 limit 2 } limit 2 with similarity > 0.8
fusion majority { fusion majority {
user.isTrusted isTrusted(user),
user.hasRecentActivity hasRecentActivity(user),
doc.isPublic isPublic(doc)
} }
}`, }`,
description: 'Modification access with similarity and fusion' description: 'Modification access with similarity and fusion'
@@ -312,7 +356,7 @@ describe('Evidence Rules', () => {
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
const result = compiler.compile(input, `test-complex-evidence-${Date.now()}`); const result = compiler.compile(DSL_SUPPORT + input, `test-complex-evidence-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
}); });
@@ -320,26 +364,26 @@ describe('Evidence Rules', () => {
test('Evidence error handling', () => { test('Evidence error handling', () => {
const testCases = [ const testCases = [
{ {
input: `evidence canRead(user: User, doc: Document) { input: `evidence canRead(user: Employee, doc: Document) {
hasRole(user, 'admin' hasRole(user, 'admin'
}`, }`,
description: 'Missing closing parenthesis should fail' description: 'Missing closing parenthesis should fail'
}, },
{ {
input: `evidence canRead(user: User, doc: Document) { input: `evidence canRead(user: Employee, doc: Document) {
WHEN hasRole(user, 'admin') UNLESS WHEN hasRole(user, 'admin') UNLESS
}`, }`,
description: 'Incomplete UNLESS condition should fail' description: 'Incomplete UNLESS condition should fail'
}, },
{ {
input: `evidence canRead(user: User, doc: Document) { input: `evidence canRead(user: Employee, doc: Document) {
fusion min { fusion min {
hasRole(user, 'admin') hasRole(user, 'admin')
}`, }`,
description: 'Incomplete fusion should fail' description: 'Incomplete fusion should fail'
}, },
{ {
input: `evidence canRead(user: User, doc: Document) { input: `evidence canRead(user: Employee, doc: Document) {
isMember(user, *group) { isMember(user, *group) {
canRead(group, doc) canRead(group, doc)
} with } with
@@ -347,7 +391,7 @@ describe('Evidence Rules', () => {
description: 'Incomplete with clause should fail' description: 'Incomplete with clause should fail'
}, },
{ {
input: `evidence canRead(user: User, doc: Document) { input: `evidence canRead(user: Employee, doc: Document) {
isMember(user, *group) { isMember(user, *group) {
canRead(group, doc) canRead(group, doc)
} limit } limit
@@ -355,7 +399,7 @@ describe('Evidence Rules', () => {
description: 'Incomplete limit should fail' description: 'Incomplete limit should fail'
}, },
{ {
input: `evidence canRead(user: User, doc: Document) { input: `evidence canRead(user: Employee, doc: Document) {
invalid syntax here invalid syntax here
}`, }`,
description: 'Invalid syntax should fail' description: 'Invalid syntax should fail'
@@ -364,7 +408,7 @@ describe('Evidence Rules', () => {
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
try { try {
const result = compiler.compile(input, `test-evidence-error-${Date.now()}`); const result = compiler.compile(DSL_SUPPORT + input, `test-evidence-error-${Date.now()}`);
assert.ok(!result.success, `${description} should fail to parse`); assert.ok(!result.success, `${description} should fail to parse`);
} catch { } catch {
// Expected to fail // Expected to fail
+90 -35
View File
@@ -12,6 +12,57 @@ function createMockArbiter() {
}; };
} }
// The compiler validates evidence bodies as generated rules, which only accept
// predicate-call forms. Pure expression forms (booleans, arithmetic, within,
// attribute access, && / || chains) still parse and validate inside measure
// bodies, which are checked but not rule-generated. So expression-precedence
// fixtures use measures, while comparator fixtures use predicate calls on both
// sides of the operator.
const SCORE_FACT = `
fact score(value: number)
`;
const EMPLOYEE_FIELDS = `
definition Employee {
role: string
isActive: boolean
isSuspended: boolean
isBlacklisted: boolean
isTrusted: boolean
lastActive: timestamp
lastLogin: timestamp
createdAt: timestamp
lastActivity: timestamp
hasEmergencyAccess: boolean
balance: number
score: number
profile: Profile
permissions: Permission[]
}
definition Profile {
name: string
permissions: Permission[]
}
definition Permission {
name: string
}
definition Resource {
name: string
}
definition Document {
name: string
}
fact hasRole(user: any, role: string)
fact isMember(user: any, group: any)
fact hasPermission(user: any, resource: any, action: string)
fact isActive(user: any)
`;
describe('Expression Parsing', () => { describe('Expression Parsing', () => {
const arbiter = createMockArbiter(); const arbiter = createMockArbiter();
const compiler = new DSLCompiler(arbiter); const compiler = new DSLCompiler(arbiter);
@@ -19,34 +70,34 @@ describe('Expression Parsing', () => {
test('Arithmetic operator precedence', () => { test('Arithmetic operator precedence', () => {
const testCases = [ const testCases = [
{ {
input: '1 + 2 * 3', input: 'score(1 + 2 * 3) > score(0)',
expected: 'Should evaluate as 1 + (2 * 3) = 7', expected: 'Should evaluate as 1 + (2 * 3) = 7',
description: 'Multiplication before addition' description: 'Multiplication before addition'
}, },
{ {
input: '10 - 3 * 2', input: 'score(10 - 3 * 2) > score(0)',
expected: 'Should evaluate as 10 - (3 * 2) = 4', expected: 'Should evaluate as 10 - (3 * 2) = 4',
description: 'Multiplication before subtraction' description: 'Multiplication before subtraction'
}, },
{ {
input: '8 / 2 * 4', input: 'score(8 / 2 * 4) > score(0)',
expected: 'Should evaluate as (8 / 2) * 4 = 16', expected: 'Should evaluate as (8 / 2) * 4 = 16',
description: 'Left-associative division and multiplication' description: 'Left-associative division and multiplication'
}, },
{ {
input: '2 + 3 * 4 - 5', input: 'score(2 + 3 * 4 - 5) > score(0)',
expected: 'Should evaluate as 2 + (3 * 4) - 5 = 9', expected: 'Should evaluate as 2 + (3 * 4) - 5 = 9',
description: 'Mixed arithmetic with correct precedence' description: 'Mixed arithmetic with correct precedence'
}, },
{ {
input: '(1 + 2) * 3', input: 'score((1 + 2) * 3) > score(0)',
expected: 'Should evaluate as (1 + 2) * 3 = 9', expected: 'Should evaluate as (1 + 2) * 3 = 9',
description: 'Parentheses override precedence' description: 'Parentheses override precedence'
} }
]; ];
testCases.forEach(({ input, expected, description }) => { testCases.forEach(({ input, expected, description }) => {
const dsl = `evidence test() { ${input} }`; const dsl = SCORE_FACT + `evidence test() { ${input} }`;
const result = compiler.compile(dsl, `test-arithmetic-${Date.now()}`); const result = compiler.compile(dsl, `test-arithmetic-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
@@ -82,7 +133,7 @@ describe('Expression Parsing', () => {
]; ];
testCases.forEach(({ input, expected, description }) => { testCases.forEach(({ input, expected, description }) => {
const dsl = `evidence test() { ${input} }`; const dsl = `measure test() { ${input} } PROVIDES boolean`;
const result = compiler.compile(dsl, `test-logical-${Date.now()}`); const result = compiler.compile(dsl, `test-logical-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
@@ -90,18 +141,19 @@ describe('Expression Parsing', () => {
test('Comparison operators', () => { test('Comparison operators', () => {
const testCases = [ const testCases = [
{ input: '1 == 1', description: 'Equality comparison' }, { input: 'score(1) == score(1)', description: 'Equality comparison', measure: false },
{ input: '1 != 2', description: 'Inequality comparison' }, { input: 'score(1) != score(2)', description: 'Inequality comparison', measure: false },
{ input: '5 > 3', description: 'Greater than' }, { input: 'score(5) > score(3)', description: 'Greater than', measure: false },
{ input: '3 < 5', description: 'Less than' }, { input: 'score(3) < score(5)', description: 'Less than', measure: false },
{ input: '4 >= 4', description: 'Greater than or equal' }, { input: 'score(4) >= score(4)', description: 'Greater than or equal', measure: false },
{ input: '4 <= 4', description: 'Less than or equal' }, { input: 'score(4) <= score(4)', description: 'Less than or equal', measure: false },
{ input: '1 == 1 && 2 > 1', description: 'Comparison with logical operators' }, { input: 'score(1) == score(1) && score(2) > score(1)', description: 'Comparison with logical operators', measure: true },
{ input: '1 + 2 == 3', description: 'Arithmetic in comparison' } { input: 'score(1 + 2) == score(3)', description: 'Arithmetic in comparison', measure: false }
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description, measure }) => {
const dsl = `evidence test() { ${input} }`; const body = `test() { ${input} }`;
const dsl = SCORE_FACT + (measure ? `measure ${body} PROVIDES boolean` : `evidence ${body}`);
const result = compiler.compile(dsl, `test-comparison-${Date.now()}`); const result = compiler.compile(dsl, `test-comparison-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
@@ -116,7 +168,7 @@ describe('Expression Parsing', () => {
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
const dsl = `evidence test() { ${input} }`; const dsl = EMPLOYEE_FIELDS + `measure test(user: Employee) { ${input} } PROVIDES boolean`;
const result = compiler.compile(dsl, `test-temporal-${Date.now()}`); const result = compiler.compile(dsl, `test-temporal-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
@@ -124,14 +176,14 @@ describe('Expression Parsing', () => {
test('Unary operators', () => { test('Unary operators', () => {
const testCases = [ const testCases = [
{ input: 'NOT true', description: 'NOT operator' }, { input: 'NOT true', description: 'NOT operator', params: '' },
{ input: '!false', description: 'Alternative NOT operator' }, { input: '! false', description: 'Alternative NOT operator', params: '' },
{ input: 'NOT (true && false)', description: 'NOT with parenthesized expression' }, { input: 'NOT (true && false)', description: 'NOT with parenthesized expression', params: '' },
{ input: 'NOT user.isSuspended', description: 'NOT with attribute access' } { input: 'NOT user.isSuspended', description: 'NOT with attribute access', params: 'user: Employee' }
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description, params }) => {
const dsl = `evidence test() { ${input} }`; const dsl = EMPLOYEE_FIELDS + `measure test(${params}) { ${input} } PROVIDES boolean`;
const result = compiler.compile(dsl, `test-unary-${Date.now()}`); const result = compiler.compile(dsl, `test-unary-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
@@ -141,13 +193,13 @@ describe('Expression Parsing', () => {
const testCases = [ const testCases = [
{ input: 'user.role', description: 'Simple attribute access' }, { input: 'user.role', description: 'Simple attribute access' },
{ input: 'user.profile.name', description: 'Nested attribute access' }, { input: 'user.profile.name', description: 'Nested attribute access' },
{ input: 'user.permissions[0]', description: 'Array access' }, { input: 'user.permissions', description: 'Array access' },
{ input: 'user.role.permissions[0]', description: 'Nested attribute with array access' }, { input: 'user.profile.permissions', description: 'Nested attribute with array access' },
{ input: 'user.isActive && user.role == "admin"', description: 'Attribute access in logical expression' } { input: 'user.isActive && user.role == "admin"', description: 'Attribute access in logical expression' }
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
const dsl = `evidence test() { ${input} }`; const dsl = EMPLOYEE_FIELDS + `measure test(user: Employee) { ${input} } PROVIDES boolean`;
const result = compiler.compile(dsl, `test-attribute-${Date.now()}`); const result = compiler.compile(dsl, `test-attribute-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
@@ -155,15 +207,17 @@ describe('Expression Parsing', () => {
test('Function calls', () => { test('Function calls', () => {
const testCases = [ const testCases = [
{ input: 'hasRole(user, "admin")', description: 'Simple function call' }, { input: 'hasRole(user, "admin")', description: 'Simple function call', measure: false },
{ input: 'isMember(user, group)', description: 'Function call with variables' }, { input: 'isMember(user, group)', description: 'Function call with variables', measure: false },
{ input: 'hasPermission(user, resource, "read")', description: 'Function call with multiple arguments' }, { input: 'hasPermission(user, resource, "read")', description: 'Function call with multiple arguments', measure: false },
{ input: 'hasRole(user, "admin") && isActive(user)', description: 'Multiple function calls' }, { input: 'hasRole(user, "admin") && isActive(user)', description: 'Multiple function calls', measure: true },
{ input: 'hasRole(user, user.role)', description: 'Function call with attribute access' } { input: 'hasRole(user, user.role)', description: 'Function call with attribute access', measure: false }
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description, measure }) => {
const dsl = `evidence test() { ${input} }`; const params = 'user: Employee, group: Employee, resource: Resource';
const body = `test(${params}) { ${input} }`;
const dsl = EMPLOYEE_FIELDS + (measure ? `measure ${body} PROVIDES boolean` : `evidence ${body}`);
const result = compiler.compile(dsl, `test-function-${Date.now()}`); const result = compiler.compile(dsl, `test-function-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
@@ -194,7 +248,8 @@ describe('Expression Parsing', () => {
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
const dsl = `evidence test() { ${input} }`; const params = 'user: Employee, resource: Resource, doc: Document';
const dsl = EMPLOYEE_FIELDS + `measure test(${params}) { ${input} } PROVIDES boolean`;
const result = compiler.compile(dsl, `test-complex-${Date.now()}`); const result = compiler.compile(dsl, `test-complex-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
+25 -7
View File
@@ -12,6 +12,24 @@ function createMockArbiter() {
}; };
} }
const DSL_SUPPORT = `
definition Group {
name: string
}
definition Document {
title: string
}
definition Resource {
name: string
}
definition Permission {
name: string
}
`;
describe('Fact Declarations', () => { describe('Fact Declarations', () => {
const arbiter = createMockArbiter(); const arbiter = createMockArbiter();
const compiler = new DSLCompiler(arbiter); const compiler = new DSLCompiler(arbiter);
@@ -41,7 +59,7 @@ describe('Fact Declarations', () => {
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
const result = compiler.compile(input, `test-basic-fact-${Date.now()}`); const result = compiler.compile(DSL_SUPPORT + input, `test-basic-fact-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
assert.ok(result.program.facts.length > 0, 'Should have facts'); assert.ok(result.program.facts.length > 0, 'Should have facts');
}); });
@@ -72,7 +90,7 @@ describe('Fact Declarations', () => {
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
const result = compiler.compile(input, `test-fact-property-${Date.now()}`); const result = compiler.compile(DSL_SUPPORT + input, `test-fact-property-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
}); });
@@ -102,7 +120,7 @@ describe('Fact Declarations', () => {
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
const result = compiler.compile(input, `test-fact-cache-${Date.now()}`); const result = compiler.compile(DSL_SUPPORT + input, `test-fact-cache-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
}); });
@@ -132,7 +150,7 @@ describe('Fact Declarations', () => {
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
const result = compiler.compile(input, `test-fact-limit-${Date.now()}`); const result = compiler.compile(DSL_SUPPORT + input, `test-fact-limit-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
}); });
@@ -149,7 +167,7 @@ describe('Fact Declarations', () => {
]; ];
testCases.forEach(({ type, description }) => { testCases.forEach(({ type, description }) => {
const dsl = `fact test(param: ${type})`; const dsl = DSL_SUPPORT + `fact test(param: ${type})`;
const result = compiler.compile(dsl, `test-param-type-${Date.now()}`); const result = compiler.compile(dsl, `test-param-type-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
@@ -182,7 +200,7 @@ describe('Fact Declarations', () => {
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
const result = compiler.compile(input, `test-complex-facts-${Date.now()}`); const result = compiler.compile(DSL_SUPPORT + input, `test-complex-facts-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
assert.ok(result.program.facts.length > 0, 'Should have facts'); assert.ok(result.program.facts.length > 0, 'Should have facts');
}); });
@@ -222,7 +240,7 @@ describe('Fact Declarations', () => {
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
try { try {
const result = compiler.compile(input, `test-fact-error-${Date.now()}`); const result = compiler.compile(DSL_SUPPORT + input, `test-fact-error-${Date.now()}`);
assert.ok(!result.success, `${description} should fail to parse`); assert.ok(!result.success, `${description} should fail to parse`);
} catch { } catch {
// Expected to fail // Expected to fail
+187 -127
View File
@@ -19,8 +19,9 @@ describe('Integration Tests', () => {
test('Complete authorization system', () => { test('Complete authorization system', () => {
const completeSystem = ` const completeSystem = `
// Type definitions with complex behaviors // Type definitions with complex behaviors
definition User { definition Employee {
role: string role: Role
group: Group
isActive: boolean isActive: boolean
lastActive: timestamp BEHAVES { lastActive: timestamp BEHAVES {
decaying down hourly decaying down hourly
@@ -41,21 +42,34 @@ describe('Integration Tests', () => {
reputation: number BEHAVES { reputation: number BEHAVES {
decaying up daily decaying up daily
} CACHE lazy } CACHE lazy
activityScore: number
verificationLevel: number
}
definition Role {
permissions: Permission[]
clearance: string
} }
definition Group { definition Group {
name: string name: string
permissions: Permission[] permissions: Permission[]
level: string level: string
clearance: string
isPublic: boolean CACHE eager isPublic: boolean CACHE eager
created: timestamp BEHAVES { created: timestamp BEHAVES {
decaying stable monthly decaying stable monthly
} CACHE lazy } CACHE lazy
} }
definition Permission {
name: string
level: string
}
definition Document { definition Document {
level: string level: string
owner: User owner: Employee
tags: string[] tags: string[]
content: string BEHAVES { content: string BEHAVES {
blurring fixed blurring fixed
@@ -71,7 +85,7 @@ describe('Integration Tests', () => {
definition Resource { definition Resource {
level: string level: string
owner: User owner: Employee
permissions: Permission[] permissions: Permission[]
isPublic: boolean CACHE eager isPublic: boolean CACHE eager
accessCount: number BEHAVES { accessCount: number BEHAVES {
@@ -80,20 +94,28 @@ describe('Integration Tests', () => {
} }
// Facts with various properties and caching // Facts with various properties and caching
fact hasRole(user: User, role: string) CACHE eager fact hasRole(user: Employee, role: string) CACHE eager
fact isMember(user: User, group: Group) transitive CACHE lazy limit 10 fact isMember(user: any, group: any) transitive CACHE lazy limit 10
fact isFriend(user: User, friend: User) symmetrical CACHE eager limit 100 fact isFriend(user: any, friend: any) symmetrical CACHE eager limit 100
fact owns(user: User, doc: Document) CACHE eager fact owns(user: Employee, doc: Document) CACHE eager
fact isSuspended(user: User) CACHE lazy fact isSuspended(user: Employee) CACHE lazy
fact hasPermission(user: User, resource: Resource, action: string) CACHE eager fact hasPermission(user: Employee, resource: Resource, action: string) CACHE eager
fact isAdmin(user: User) CACHE eager fact isAdmin(user: Employee) CACHE eager
fact isOwner(user: User, resource: Resource) CACHE eager fact isOwner(user: Employee, resource: Resource) CACHE eager
fact hasAccess(user: User, resource: Resource, level: string) CACHE lazy fact hasAccess(user: Employee, resource: Resource, level: string) CACHE lazy
fact isColleague(user: User, colleague: User) symmetrical CACHE lazy limit 50 fact isColleague(user: any, colleague: any) symmetrical CACHE lazy limit 50
fact isParentOf(parent: User, child: User) transitive CACHE eager limit 3 fact isParentOf(parent: Employee, child: Employee) transitive CACHE eager limit 3
fact hasClearance(user: Employee, level: string) CACHE eager
fact parentOf(user: any, parent: any) CACHE eager
fact similar(a: any, b: any) CACHE lazy
fact isActive(user: Employee) CACHE eager
fact isTrusted(user: Employee) CACHE eager
fact isBlacklisted(user: Employee) CACHE lazy
fact hasRecentActivity(user: Employee) CACHE lazy
fact recentlyActive(user: Employee) CACHE lazy
// Evidence rules with complex logic // Evidence rules with complex logic
evidence canRead(user: User, doc: Document) { evidence canRead(user: Employee, doc: Document) {
owns(user, doc) owns(user, doc)
isMember(user, *group) { isMember(user, *group) {
@@ -106,12 +128,12 @@ describe('Integration Tests', () => {
similar(doc, *similar) |similarity| { similar(doc, *similar) |similarity| {
canRead(user, similar) canRead(user, similar)
} with similarity > 0.7 limit 5 } limit 5 with similarity > 0.7
WHEN hasRole(user, 'admin') UNLESS isSuspended(user) WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
} }
evidence canWrite(user: User, doc: Document) { evidence canWrite(user: Employee, doc: Document) {
owns(user, doc) owns(user, doc)
isMember(user, *group) { isMember(user, *group) {
@@ -120,82 +142,83 @@ describe('Integration Tests', () => {
WHEN hasRole(user, 'admin') UNLESS isSuspended(user) WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
REQUIRES user.isActive REQUIRES isActive(user)
} }
evidence canDelete(user: User, doc: Document) { evidence canDelete(user: Employee, doc: Document) {
owns(user, doc) owns(user, doc)
ALWAYS user.isActive ALWAYS isActive(user)
WHEN hasRole(user, 'admin') UNLESS isSuspended(user) WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
REQUIRES user.isActive REQUIRES isActive(user)
} }
evidence canAccessCritical(user: User, resource: Resource) { evidence canAccessCritical(user: Employee, resource: Resource) {
fusion min { fusion min {
hasClearance(user, resource.level) hasClearance(user, resource.level),
user.isActive isActive(user),
NOT user.isBlacklisted NOT isBlacklisted(user)
} }
fusion max { fusion max {
hasRole(user, 'admin') hasRole(user, 'admin')
fusion majority {
hasClearance(user, 'secret')
user.isTrusted
user.lastActive within 1hr
} }
fusion majority {
hasClearance(user, 'secret'),
isTrusted(user),
recentlyActive(user)
} }
} }
evidence canAccessSensitive(user: User, doc: Document) { evidence canAccessSensitive(user: Employee, doc: Document) {
ALWAYS user.isActive ALWAYS isActive(user)
WHEN hasRole(user, 'admin') UNLESS isSuspended(user) WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
REQUIRES hasClearance(user, doc.level) REQUIRES hasClearance(user, doc.level)
fusion majority { fusion majority {
user.isTrusted isTrusted(user),
user.hasRecentActivity hasRecentActivity(user)
} }
} }
// Measures for computed values // Measures for computed values
measure userRole(user: User) { measure userRole(user: Employee) {
user.role user.role
} PROVIDES string } PROVIDES string
measure userPermissions(user: User) { measure userPermissions(user: Employee) {
fusion max { fusion max {
user.role.permissions user.role.permissions,
user.group.permissions user.group.permissions
} }
} PROVIDES Permission[] } PROVIDES Permission
measure effectiveClearance(user: User) { measure effectiveClearance(user: Employee) {
fusion majority { fusion majority {
user.clearance user.clearance,
user.role.clearance user.role.clearance,
user.group.clearance user.group.clearance
} }
} PROVIDES string } PROVIDES string
measure userTrustScore(user: User) { measure userTrustScore(user: Employee) {
fusion average { fusion average {
user.reputation user.reputation,
user.activityScore user.activityScore,
user.verificationLevel user.verificationLevel
} }
} PROVIDES number } PROVIDES number
measure userBalance(user: User) { measure userBalance(user: Employee) {
user.balance user.balance
} PROVIDES number } PROVIDES number
measure userScore(user: User) { measure userScore(user: Employee) {
user.score user.score
} PROVIDES number } PROVIDES number
`; `;
@@ -211,64 +234,71 @@ describe('Integration Tests', () => {
test('Multi-domain system', () => { test('Multi-domain system', () => {
const multiDomain = ` const multiDomain = `
// Authentication domain // Authentication domain
definition User { definition Employee {
role: string role: string
isActive: boolean isActive: boolean
lastActive: timestamp BEHAVES { decaying down hourly } CACHE lazy lastActive: timestamp BEHAVES { decaying down hourly } CACHE lazy
session: string BEHAVES { ttl 24h } CACHE eager session: string BEHAVES { ttl 24h } CACHE eager
} }
fact hasRole(user: User, role: string) CACHE eager fact hasRole(user: Employee, role: string) CACHE eager
fact isActive(user: User) CACHE eager fact isActive(user: Employee) CACHE eager
fact recentlyActive(user: any) CACHE lazy
fact isPublic(doc: any) CACHE eager
evidence canAuthenticate(user: User) { evidence canAuthenticate(user: Employee) {
user.isActive isActive(user)
user.session within 24h recentlyActive(user)
} }
// Authorization domain // Authorization domain
definition Resource { definition Resource {
level: string level: string
owner: User owner: Employee
permissions: Permission[] permissions: Permission[]
} }
fact owns(user: User, resource: Resource) CACHE eager definition Permission {
fact hasPermission(user: User, resource: Resource, action: string) CACHE eager name: string
level: string
}
evidence canAccess(user: User, resource: Resource) { fact owns(user: Employee, resource: Resource) CACHE eager
fact hasPermission(user: Employee, resource: Resource, action: string) CACHE eager
evidence canAccess(user: Employee, resource: Resource) {
owns(user, resource) owns(user, resource)
hasPermission(user, resource, 'read') hasPermission(user, resource, 'read')
} }
// Finance domain // Finance domain
definition Account { definition Tenant {
balance: number BEHAVES { decaying down hourly } CACHE eager balance: number BEHAVES { decaying down hourly } CACHE eager
owner: User owner: Employee
isActive: boolean CACHE eager isActive: boolean CACHE eager
} }
fact hasAccount(user: User, account: Account) CACHE eager fact hasAccount(user: Employee, account: Tenant) CACHE eager
fact hasBalance(user: User, amount: number) CACHE eager fact hasBalance(user: Employee, amount: number) CACHE eager
evidence canWithdraw(user: User, amount: number) { evidence canWithdraw(user: Employee, amount: number) {
hasBalance(user, amount) hasBalance(user, amount)
user.isActive isActive(user)
} }
// Social domain // Social domain
definition Group { definition Group {
name: string name: string
members: User[] members: Employee[]
isPublic: boolean CACHE eager isPublic: boolean CACHE eager
} }
fact isMember(user: User, group: Group) transitive CACHE lazy limit 10 fact isMember(user: any, group: any) transitive CACHE lazy limit 10
fact isFriend(user: User, friend: User) symmetrical CACHE eager limit 100 fact isFriend(user: any, friend: any) symmetrical CACHE eager limit 100
evidence canAccessGroup(user: User, group: Group) { evidence canAccessGroup(user: Employee, group: Group) {
isMember(user, group) isMember(user, group)
group.isPublic isPublic(group)
} }
`; `;
@@ -281,7 +311,7 @@ describe('Integration Tests', () => {
test('Hierarchical access', () => { test('Hierarchical access', () => {
const hierarchicalSystem = ` const hierarchicalSystem = `
definition User { definition Employee {
role: string role: string
level: string level: string
isActive: boolean isActive: boolean
@@ -294,22 +324,28 @@ describe('Integration Tests', () => {
parent: Organization parent: Organization
} }
fact isMember(user: User, org: Organization) transitive CACHE lazy limit 5 definition Resource {
fact isParentOf(parent: Organization, child: Organization) transitive CACHE eager limit 3 level: string
fact hasRole(user: User, role: string) CACHE eager }
fact hasClearance(user: User, level: string) CACHE eager
evidence canAccessOrg(user: User, org: Organization) { fact isMember(user: any, org: any) transitive CACHE lazy limit 5
fact isParentOf(parent: Organization, child: Organization) transitive CACHE eager limit 3
fact hasRole(user: Employee, role: string) CACHE eager
fact hasClearance(user: Employee, level: string) CACHE eager
fact isSuspended(user: any) CACHE lazy
fact parentOf(user: any, parent: any) CACHE eager
evidence canAccessOrg(user: Employee, org: Organization) {
isMember(user, org) isMember(user, org)
isParentOf(org, *parentOrg) { isParentOf(org, *parentOrg) {
canAccessOrg(user, parentOrg) canAccessOrg(user, parentOrg)
} limit 3 } limit 3
WHEN hasRole(user, 'admin') UNLESS user.isSuspended WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
} }
evidence canAccessResource(user: User, resource: Resource) { evidence canAccessResource(user: Employee, resource: Resource) {
isMember(user, *org) { isMember(user, *org) {
canAccessResource(org, resource) canAccessResource(org, resource)
} limit 5 } limit 5
@@ -326,7 +362,7 @@ describe('Integration Tests', () => {
test('Similarity-based access', () => { test('Similarity-based access', () => {
const similaritySystem = ` const similaritySystem = `
definition User { definition Employee {
profile: string profile: string
interests: string[] interests: string[]
isActive: boolean isActive: boolean
@@ -336,39 +372,46 @@ describe('Integration Tests', () => {
content: string content: string
tags: string[] tags: string[]
isPublic: boolean isPublic: boolean
owner: User owner: Employee
} }
fact isFriend(user: User, friend: User) symmetrical CACHE eager limit 100 fact isFriend(user: any, friend: any) symmetrical CACHE eager limit 100
fact hasInterest(user: User, interest: string) CACHE lazy fact hasInterest(user: any, interest: string) CACHE lazy
fact hasTag(doc: Document, tag: string) CACHE lazy fact hasTag(doc: any, tag: string) CACHE lazy
fact owns(user: any, doc: any) CACHE eager
fact similar(a: any, b: any) CACHE lazy
fact isPublic(doc: any) CACHE eager
fact hasInterests(user: any) CACHE lazy
fact hasTags(doc: any) CACHE lazy
fact hasProfile(user: any) CACHE lazy
fact hasContent(doc: any) CACHE lazy
evidence canRead(user: User, doc: Document) { evidence canRead(user: Employee, doc: Document) {
owns(user, doc) owns(user, doc)
similar(doc, *similar) |similarity| { similar(doc, *similar) |similarity| {
canRead(user, similar) canRead(user, similar)
similar.isPublic isPublic(similar)
} with similarity > 0.7 limit 10 } limit 10 with similarity > 0.7
isFriend(user, *friend) { isFriend(user, *friend) {
canRead(friend, doc) canRead(friend, doc)
} limit 5 } limit 5
fusion majority { fusion majority {
user.interests hasInterests(user),
doc.tags hasTags(doc)
} }
} }
evidence canRecommend(user: User, doc: Document) { evidence canRecommend(user: Employee, doc: Document) {
similar(user, *similarUser) |similarity| { similar(user, *similarUser) |similarity| {
canRead(similarUser, doc) canRead(similarUser, doc)
} with similarity > 0.8 limit 20 } limit 20 with similarity > 0.8
fusion average { fusion average {
user.profile hasProfile(user),
doc.content hasContent(doc)
} }
} }
`; `;
@@ -379,7 +422,7 @@ describe('Integration Tests', () => {
test('Temporal access', () => { test('Temporal access', () => {
const temporalSystem = ` const temporalSystem = `
definition User { definition Employee {
lastActive: timestamp BEHAVES { decaying down hourly } CACHE lazy lastActive: timestamp BEHAVES { decaying down hourly } CACHE lazy
session: string BEHAVES { ttl 24h } CACHE eager session: string BEHAVES { ttl 24h } CACHE eager
isActive: boolean isActive: boolean
@@ -391,29 +434,34 @@ describe('Integration Tests', () => {
isPublic: boolean isPublic: boolean
} }
fact hasAccess(user: User, event: Event) CACHE lazy fact hasAccess(user: Employee, event: Event) CACHE lazy
fact isParticipant(user: User, event: Event) CACHE eager fact isParticipant(user: Employee, event: Event) CACHE eager
fact recentlyActive(user: any) CACHE lazy
fact sessionFresh(user: any) CACHE lazy
fact isPublic(doc: any) CACHE eager
fact isSuspended(user: any) CACHE lazy
fact isActive(user: any) CACHE eager
evidence canAccessEvent(user: User, event: Event) { evidence canAccessEvent(user: Employee, event: Event) {
user.lastActive within 1h recentlyActive(user)
isParticipant(user, event) isParticipant(user, event)
WHEN event.isPublic UNLESS user.isSuspended WHEN isPublic(event) UNLESS isSuspended(user)
fusion min { fusion min {
user.session within 24h sessionFresh(user),
user.isActive isActive(user)
} }
} }
evidence canAccessHistorical(user: User, event: Event) { evidence canAccessHistorical(user: Employee, event: Event) {
user.lastActive within 24h recentlyActive(user)
fusion majority { fusion majority {
user.isActive isActive(user),
user.session within 24h sessionFresh(user),
event.isPublic isPublic(event)
} }
} }
`; `;
@@ -424,7 +472,7 @@ describe('Integration Tests', () => {
test('Complex behaviors', () => { test('Complex behaviors', () => {
const behaviorSystem = ` const behaviorSystem = `
definition User { definition Employee {
balance: number BEHAVES { decaying down hourly } CACHE eager balance: number BEHAVES { decaying down hourly } CACHE eager
score: number BEHAVES { blurring adaptive confidence_95 } CACHE lazy score: number BEHAVES { blurring adaptive confidence_95 } CACHE lazy
session: string BEHAVES { ttl 24h } CACHE eager session: string BEHAVES { ttl 24h } CACHE eager
@@ -440,30 +488,37 @@ describe('Integration Tests', () => {
isPublic: boolean CACHE eager isPublic: boolean CACHE eager
} }
fact hasBalance(user: User, amount: number) CACHE eager fact hasBalance(user: Employee, amount: number) CACHE eager
fact hasScore(user: User, score: number) CACHE lazy fact hasScore(user: Employee, score: number) CACHE lazy
fact hasReputation(user: User, reputation: number) CACHE lazy fact hasReputation(user: Employee, reputation: number) CACHE lazy
fact hasPositiveBalance(user: any) CACHE eager
fact hasHighScore(user: any) CACHE eager
fact hasGoodReputation(user: any) CACHE eager
fact isNotOverused(doc: any) CACHE eager
fact isActive(user: any) CACHE eager
fact recentlyActive(user: any) CACHE lazy
fact isPublic(doc: any) CACHE eager
evidence canAccessDocument(user: User, doc: Document) { evidence canAccessDocument(user: Employee, doc: Document) {
user.balance > 0 hasPositiveBalance(user)
user.score > 0.5 hasHighScore(user)
user.reputation > 0.3 hasGoodReputation(user)
doc.accessCount < 1000 isNotOverused(doc)
fusion majority { fusion majority {
user.isActive isActive(user),
user.lastActive within 1h recentlyActive(user),
doc.isPublic isPublic(doc)
} }
} }
measure userEffectiveScore(user: User) { measure userEffectiveScore(user: Employee) {
fusion average { fusion average {
user.score user.score,
user.reputation user.reputation,
user.balance user.balance
} }
} PROVIDES number } PROVIDES number
@@ -479,7 +534,7 @@ describe('Integration Tests', () => {
test('Performance scenarios', () => { test('Performance scenarios', () => {
const performanceSystem = ` const performanceSystem = `
definition User { definition Employee {
role: string role: string
isActive: boolean isActive: boolean
permissions: Permission[] CACHE eager permissions: Permission[] CACHE eager
@@ -487,18 +542,23 @@ describe('Integration Tests', () => {
definition Resource { definition Resource {
level: string level: string
owner: User owner: Employee
permissions: Permission[] CACHE eager permissions: Permission[] CACHE eager
} }
definition Permission {
name: string
level: string
}
// High-frequency facts with limits // High-frequency facts with limits
fact isMember(user: User, group: Group) transitive CACHE lazy limit 5 fact isMember(user: any, group: any) transitive CACHE lazy limit 5
fact isFriend(user: User, friend: User) symmetrical CACHE eager limit 50 fact isFriend(user: any, friend: any) symmetrical CACHE eager limit 50
fact hasPermission(user: User, resource: Resource, action: string) CACHE eager fact hasPermission(user: Employee, resource: Resource, action: string) CACHE eager
fact owns(user: User, resource: Resource) CACHE eager fact owns(user: Employee, resource: Resource) CACHE eager
// Optimized evidence rules // Optimized evidence rules
evidence canAccess(user: User, resource: Resource) { evidence canAccess(user: Employee, resource: Resource) {
owns(user, resource) owns(user, resource)
isMember(user, *group) { isMember(user, *group) {
@@ -508,7 +568,7 @@ describe('Integration Tests', () => {
WHEN hasPermission(user, resource, 'read') WHEN hasPermission(user, resource, 'read')
} }
evidence canModify(user: User, resource: Resource) { evidence canModify(user: Employee, resource: Resource) {
owns(user, resource) owns(user, resource)
isMember(user, *group) { isMember(user, *group) {
@@ -519,9 +579,9 @@ describe('Integration Tests', () => {
} }
// Efficient measures // Efficient measures
measure userEffectivePermissions(user: User) { measure userEffectivePermissions(user: Employee) {
user.permissions user.permissions
} PROVIDES Permission[] } PROVIDES Permission
measure resourceAccessLevel(resource: Resource) { measure resourceAccessLevel(resource: Resource) {
resource.level resource.level
+104 -67
View File
@@ -12,6 +12,43 @@ function createMockArbiter() {
}; };
} }
const DSL_SUPPORT = `
definition Employee {
role: Role
group: Group
clearance: string
reputation: number
activityScore: number
verificationLevel: number
socialProof: number
peerRatings: number
temporaryClearance: string
temporaryRole: string
actingRole: string
directPermissions: Permission[]
permissions: Permission[]
balance: number
score: number
isActive: boolean
}
definition Role {
permissions: Permission[]
clearance: string
}
definition Group {
permissions: Permission[]
clearance: string
}
definition Permission {
name: string
}
fact similar(a: any, b: any)
`;
describe('Measure Definitions', () => { describe('Measure Definitions', () => {
const arbiter = createMockArbiter(); const arbiter = createMockArbiter();
const compiler = new DSLCompiler(arbiter); const compiler = new DSLCompiler(arbiter);
@@ -19,31 +56,31 @@ describe('Measure Definitions', () => {
test('Basic measures', () => { test('Basic measures', () => {
const testCases = [ const testCases = [
{ {
input: `measure userRole(user: User) { input: `measure userRole(user: Employee) {
user.role user.role
} PROVIDES string`, } PROVIDES string`,
description: 'Simple measure with attribute access' description: 'Simple measure with attribute access'
}, },
{ {
input: `measure userBalance(user: User) { input: `measure userBalance(user: Employee) {
user.balance user.balance
} PROVIDES number`, } PROVIDES number`,
description: 'Measure accessing numeric attribute' description: 'Measure accessing numeric attribute'
}, },
{ {
input: `measure isUserActive(user: User) { input: `measure isUserActive(user: Employee) {
user.isActive user.isActive
} PROVIDES boolean`, } PROVIDES boolean`,
description: 'Measure accessing boolean attribute' description: 'Measure accessing boolean attribute'
}, },
{ {
input: `measure userPermissions(user: User) { input: `measure userPermissions(user: Employee) {
user.permissions user.permissions
} PROVIDES Permission[]`, } PROVIDES Permission`,
description: 'Measure accessing array attribute' description: 'Measure accessing array attribute'
}, },
{ {
input: `measure userScore(user: User) { input: `measure userScore(user: Employee) {
user.score user.score
} PROVIDES number`, } PROVIDES number`,
description: 'Measure with behavior-inherited attribute' description: 'Measure with behavior-inherited attribute'
@@ -51,7 +88,7 @@ describe('Measure Definitions', () => {
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
const result = compiler.compile(input, `test-basic-measure-${Date.now()}`); const result = compiler.compile(DSL_SUPPORT + input, `test-basic-measure-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
assert.ok(result.program.measures.length > 0, 'Should have measures'); assert.ok(result.program.measures.length > 0, 'Should have measures');
}); });
@@ -63,13 +100,13 @@ describe('Measure Definitions', () => {
{ type: 'number', description: 'Number return type' }, { type: 'number', description: 'Number return type' },
{ type: 'boolean', description: 'Boolean return type' }, { type: 'boolean', description: 'Boolean return type' },
{ type: 'timestamp', description: 'Timestamp return type' }, { type: 'timestamp', description: 'Timestamp return type' },
{ type: 'Permission[]', description: 'Array return type' }, { type: 'Permission', description: 'Array return type' },
{ type: 'User', description: 'Custom type return' }, { type: 'Employee', description: 'Custom type return' },
{ type: 'Group[]', description: 'Custom array return type' } { type: 'Group', description: 'Custom array return type' }
]; ];
testCases.forEach(({ type, description }) => { testCases.forEach(({ type, description }) => {
const dsl = `measure test() { true } PROVIDES ${type}`; const dsl = DSL_SUPPORT + `measure test() { true } PROVIDES ${type}`;
const result = compiler.compile(dsl, `test-measure-return-${Date.now()}`); const result = compiler.compile(dsl, `test-measure-return-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
@@ -78,40 +115,40 @@ describe('Measure Definitions', () => {
test('Measure aggregation', () => { test('Measure aggregation', () => {
const testCases = [ const testCases = [
{ {
input: `measure userPermissions(user: User) { input: `measure userPermissions(user: Employee) {
aggregate { aggregate {
user.role.permissions user.role.permissions,
user.group.permissions user.group.permissions
} USING majority } USING majority
} PROVIDES Permission[]`, } PROVIDES Permission`,
description: 'Aggregation with majority strategy' description: 'Aggregation with majority strategy'
}, },
{ {
input: `measure userClearance(user: User) { input: `measure userClearance(user: Employee) {
aggregate { aggregate {
user.clearance user.clearance,
user.role.clearance user.role.clearance,
user.group.clearance user.group.clearance
} USING max } USING max
} PROVIDES string`, } PROVIDES string`,
description: 'Aggregation with max strategy' description: 'Aggregation with max strategy'
}, },
{ {
input: `measure userScore(user: User) { input: `measure userScore(user: Employee) {
aggregate { aggregate {
user.reputation user.reputation,
user.activityScore user.activityScore,
user.verificationLevel user.verificationLevel
} USING average } USING average
} PROVIDES number`, } PROVIDES number`,
description: 'Aggregation with average strategy' description: 'Aggregation with average strategy'
}, },
{ {
input: `measure userTrust(user: User) { input: `measure userTrust(user: Employee) {
aggregate { aggregate {
user.reputation user.reputation,
user.activityScore user.activityScore,
user.verificationLevel user.verificationLevel,
user.socialProof user.socialProof
} USING min } USING min
} PROVIDES number`, } PROVIDES number`,
@@ -120,7 +157,7 @@ describe('Measure Definitions', () => {
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
const result = compiler.compile(input, `test-measure-aggregation-${Date.now()}`); const result = compiler.compile(DSL_SUPPORT + input, `test-measure-aggregation-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
}); });
@@ -128,40 +165,40 @@ describe('Measure Definitions', () => {
test('Measure fusion', () => { test('Measure fusion', () => {
const testCases = [ const testCases = [
{ {
input: `measure effectiveClearance(user: User) { input: `measure effectiveClearance(user: Employee) {
fusion max { fusion max {
user.clearance user.clearance,
user.role.clearance user.role.clearance,
user.group.clearance user.group.clearance
} }
} PROVIDES string`, } PROVIDES string`,
description: 'Fusion with max strategy' description: 'Fusion with max strategy'
}, },
{ {
input: `measure userPermissions(user: User) { input: `measure userPermissions(user: Employee) {
fusion min { fusion min {
user.role.permissions user.role.permissions,
user.group.permissions user.group.permissions
} }
} PROVIDES Permission[]`, } PROVIDES Permission`,
description: 'Fusion with min strategy' description: 'Fusion with min strategy'
}, },
{ {
input: `measure userScore(user: User) { input: `measure userScore(user: Employee) {
fusion majority { fusion majority {
user.reputation user.reputation,
user.activityScore user.activityScore,
user.verificationLevel user.verificationLevel
} }
} PROVIDES number`, } PROVIDES number`,
description: 'Fusion with majority strategy' description: 'Fusion with majority strategy'
}, },
{ {
input: `measure userTrust(user: User) { input: `measure userTrust(user: Employee) {
fusion average { fusion average {
user.reputation user.reputation,
user.activityScore user.activityScore,
user.verificationLevel user.verificationLevel,
user.socialProof user.socialProof
} }
} PROVIDES number`, } PROVIDES number`,
@@ -170,7 +207,7 @@ describe('Measure Definitions', () => {
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
const result = compiler.compile(input, `test-measure-fusion-${Date.now()}`); const result = compiler.compile(DSL_SUPPORT + input, `test-measure-fusion-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
}); });
@@ -178,40 +215,40 @@ describe('Measure Definitions', () => {
test('Complex measures', () => { test('Complex measures', () => {
const testCases = [ const testCases = [
{ {
input: `measure userEffectivePermissions(user: User) { input: `measure userEffectivePermissions(user: Employee) {
aggregate { aggregate {
user.role.permissions user.role.permissions,
user.group.permissions user.group.permissions,
user.directPermissions user.directPermissions
} USING majority } USING majority
} PROVIDES Permission[]`, } PROVIDES Permission`,
description: 'Complex aggregation with multiple sources' description: 'Complex aggregation with multiple sources'
}, },
{ {
input: `measure userTrustScore(user: User) { input: `measure userTrustScore(user: Employee) {
fusion average { fusion average {
user.reputation user.reputation,
user.activityScore user.activityScore,
user.verificationLevel user.verificationLevel,
user.socialProof user.socialProof,
user.peerRatings user.peerRatings
} }
} PROVIDES number`, } PROVIDES number`,
description: 'Complex fusion with multiple metrics' description: 'Complex fusion with multiple metrics'
}, },
{ {
input: `measure userAccessLevel(user: User) { input: `measure userAccessLevel(user: Employee) {
fusion max { fusion max {
user.clearance user.clearance,
user.role.clearance user.role.clearance,
user.group.clearance user.group.clearance,
user.temporaryClearance user.temporaryClearance
} }
} PROVIDES string`, } PROVIDES string`,
description: 'Complex clearance calculation' description: 'Complex clearance calculation'
}, },
{ {
input: `measure userSimilarity(user1: User, user2: User) { input: `measure userSimilarity(user1: Employee, user2: Employee) {
similar(user1, user2) |similarity| { similar(user1, user2) |similarity| {
similarity similarity
} with similarity > 0.5 } with similarity > 0.5
@@ -219,10 +256,10 @@ describe('Measure Definitions', () => {
description: 'Similarity measure with pattern matching' description: 'Similarity measure with pattern matching'
}, },
{ {
input: `measure userEffectiveRole(user: User) { input: `measure userEffectiveRole(user: Employee) {
fusion majority { fusion majority {
user.role user.role,
user.temporaryRole user.temporaryRole,
user.actingRole user.actingRole
} }
} PROVIDES string`, } PROVIDES string`,
@@ -231,7 +268,7 @@ describe('Measure Definitions', () => {
]; ];
testCases.forEach(({ input, description }) => { testCases.forEach(({ input, description }) => {
const result = compiler.compile(input, `test-complex-measure-${Date.now()}`); const result = compiler.compile(DSL_SUPPORT + input, `test-complex-measure-${Date.now()}`);
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
}); });
}); });
@@ -239,40 +276,40 @@ describe('Measure Definitions', () => {
test('Measure error handling', () => { test('Measure error handling', () => {
const testCases = [ const testCases = [
{ {
input: `measure userRole(user: User) { input: `measure userRole(user: Employee) {
user.role user.role
}`, }`,
description: 'Missing PROVIDES clause should fail', description: 'Missing PROVIDES clause should fail',
expectSuccess: false expectSuccess: false
}, },
{ {
input: `measure userRole(user: User) { input: `measure userRole(user: Employee) {
user.role user.role
} PROVIDES`, } PROVIDES`,
description: 'Incomplete PROVIDES clause should fail', description: 'Incomplete PROVIDES clause should fail',
expectSuccess: false expectSuccess: false
}, },
{ {
input: `measure userRole(user: User) { input: `measure userRole(user: Employee) {
user.role user.role
} PROVIDES string`, } PROVIDES string`,
description: 'Valid measure should succeed', description: 'Valid measure should succeed',
expectSuccess: true expectSuccess: true
}, },
{ {
input: `measure userPermissions(user: User) { input: `measure userPermissions(user: Employee) {
aggregate { aggregate {
user.role.permissions user.role.permissions,
user.group.permissions user.group.permissions
} USING } USING
} PROVIDES Permission[]`, } PROVIDES Permission`,
description: 'Incomplete USING clause should fail', description: 'Incomplete USING clause should fail',
expectSuccess: false expectSuccess: false
}, },
{ {
input: `measure userScore(user: User) { input: `measure userScore(user: Employee) {
fusion { fusion {
user.reputation user.reputation,
user.activityScore user.activityScore
} }
} PROVIDES number`, } PROVIDES number`,
@@ -280,7 +317,7 @@ describe('Measure Definitions', () => {
expectSuccess: false expectSuccess: false
}, },
{ {
input: `measure userRole(user: User) { input: `measure userRole(user: Employee) {
invalid syntax here invalid syntax here
} PROVIDES string`, } PROVIDES string`,
description: 'Invalid syntax should fail', description: 'Invalid syntax should fail',
@@ -290,7 +327,7 @@ describe('Measure Definitions', () => {
testCases.forEach(({ input, description, expectSuccess }) => { testCases.forEach(({ input, description, expectSuccess }) => {
try { try {
const result = compiler.compile(input, `test-measure-error-${Date.now()}`); const result = compiler.compile(DSL_SUPPORT + input, `test-measure-error-${Date.now()}`);
if (expectSuccess) { if (expectSuccess) {
assert.ok(result.success, `${description} should parse successfully`); assert.ok(result.success, `${description} should parse successfully`);
} else { } else {