import { describe, test } from 'node:test'; import assert from 'node:assert/strict'; import { DSLCompiler } from '../src/DSLCompiler.js'; function createMockArbiter() { const relationConfigs = new Map(); return { relationConfigs, setRelationConfig(relation, config) { relationConfigs.set(relation, config); } }; } 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); test('Basic facts', () => { const testCases = [ { input: `fact hasRole(user: User, role: string)`, description: 'Simple fact with two parameters' }, { input: `fact isMember(user: User, group: Group)`, description: 'Fact with custom types' }, { input: `fact owns(user: User, doc: Document)`, description: 'Fact with multiple custom types' }, { input: `fact isActive(user: User)`, description: 'Fact with single parameter' }, { input: `fact hasPermission(user: User, resource: Resource, action: string)`, description: 'Fact with three parameters' } ]; testCases.forEach(({ input, description }) => { 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'); }); }); test('Fact properties', () => { const testCases = [ { input: `fact isMember(user: User, group: Group) transitive`, description: 'Transitive fact' }, { input: `fact isFriend(user: User, friend: User) symmetrical`, description: 'Symmetrical fact' }, { input: `fact isMember(user: User, group: Group) transitive symmetrical`, description: 'Fact with multiple properties' }, { input: `fact isColleague(user: User, colleague: User) symmetrical`, description: 'Symmetrical relationship fact' }, { input: `fact isParentOf(parent: User, child: User) transitive`, description: 'Transitive hierarchical fact' } ]; testCases.forEach(({ input, description }) => { const result = compiler.compile(DSL_SUPPORT + input, `test-fact-property-${Date.now()}`); assert.ok(result.success, `${description} should parse successfully`); }); }); test('Fact caching', () => { const testCases = [ { input: `fact hasRole(user: User, role: string) CACHE eager`, description: 'Eager cached fact' }, { input: `fact isMember(user: User, group: Group) CACHE lazy`, description: 'Lazy cached fact' }, { input: `fact isMember(user: User, group: Group) transitive CACHE eager`, description: 'Transitive fact with eager caching' }, { input: `fact isFriend(user: User, friend: User) symmetrical CACHE lazy`, description: 'Symmetrical fact with lazy caching' }, { input: `fact hasPermission(user: User, resource: Resource, action: string) CACHE eager`, description: 'Multi-parameter fact with eager caching' } ]; testCases.forEach(({ input, description }) => { const result = compiler.compile(DSL_SUPPORT + input, `test-fact-cache-${Date.now()}`); assert.ok(result.success, `${description} should parse successfully`); }); }); test('Fact limits', () => { const testCases = [ { input: `fact isMember(user: User, group: Group) limit 10`, description: 'Fact with simple limit' }, { input: `fact isFriend(user: User, friend: User) limit 100`, description: 'Fact with higher limit' }, { input: `fact isMember(user: User, group: Group) transitive limit 5`, description: 'Transitive fact with limit' }, { input: `fact isFriend(user: User, friend: User) symmetrical limit 50`, description: 'Symmetrical fact with limit' }, { input: `fact isMember(user: User, group: Group) transitive CACHE lazy limit 3`, description: 'Fact with properties, caching, and limit' } ]; testCases.forEach(({ input, description }) => { const result = compiler.compile(DSL_SUPPORT + input, `test-fact-limit-${Date.now()}`); assert.ok(result.success, `${description} should parse successfully`); }); }); test('Parameter types', () => { const testCases = [ { type: 'string', description: 'String parameter' }, { type: 'number', description: 'Number parameter' }, { type: 'boolean', description: 'Boolean parameter' }, { type: 'timestamp', description: 'Timestamp parameter' }, { type: 'User', description: 'Custom type parameter' }, { type: 'Group', description: 'Another custom type parameter' }, { type: 'Permission[]', description: 'Array type parameter' } ]; testCases.forEach(({ type, description }) => { 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`); }); }); test('Complex facts', () => { const testCases = [ { input: `fact hasRole(user: User, role: string) CACHE eager fact isMember(user: User, group: Group) transitive CACHE lazy limit 10 fact isFriend(user: User, friend: User) symmetrical CACHE eager limit 100 fact owns(user: User, doc: Document) CACHE eager fact isSuspended(user: User) CACHE lazy`, description: 'Multiple facts with different configurations' }, { input: `fact hasPermission(user: User, resource: Resource, action: string) CACHE eager fact isAdmin(user: User) CACHE eager fact isOwner(user: User, resource: Resource) CACHE eager fact hasAccess(user: User, resource: Resource, level: string) CACHE lazy`, description: 'Permission-related facts' }, { input: `fact isMember(user: User, group: Group) transitive CACHE lazy limit 5 fact isFriend(user: User, friend: User) symmetrical CACHE eager limit 50 fact isColleague(user: User, colleague: User) symmetrical CACHE lazy limit 20 fact isParentOf(parent: User, child: User) transitive CACHE eager limit 3`, description: 'Relationship facts with various properties' } ]; testCases.forEach(({ input, description }) => { 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'); }); }); test('Fact error handling', () => { const testCases = [ { input: `fact hasRole(user: User, role: string`, description: 'Missing closing parenthesis should fail' }, { input: `fact hasRole(user: User, )`, description: 'Missing parameter name should fail' }, { input: `fact hasRole(user: User, role: )`, description: 'Missing parameter type should fail' }, { input: `fact hasRole(, role: string)`, description: 'Missing parameter name should fail' }, { input: `fact hasRole(user: User, role: string) CACHE`, description: 'Incomplete cache directive should fail' }, { input: `fact hasRole(user: User, role: string) limit`, description: 'Incomplete limit should fail' }, { input: `fact hasRole(user: User, role: string) invalid`, description: 'Invalid property should fail' } ]; testCases.forEach(({ input, description }) => { try { const result = compiler.compile(DSL_SUPPORT + input, `test-fact-error-${Date.now()}`); assert.ok(!result.success, `${description} should fail to parse`); } catch { // Expected to fail } }); }); });