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