102 lines
2.8 KiB
JavaScript
102 lines
2.8 KiB
JavaScript
|
|
import { describe, test } from 'node:test';
|
||
|
|
import assert from 'node:assert/strict';
|
||
|
|
import { PeggyDSLParser } from '../src/parser/PeggyDSLParser.js';
|
||
|
|
|
||
|
|
describe('Peggy DSL Parser', () => {
|
||
|
|
const parser = new PeggyDSLParser();
|
||
|
|
|
||
|
|
test('Basic parsing', () => {
|
||
|
|
const dsl = `
|
||
|
|
definition User {
|
||
|
|
role: string
|
||
|
|
isActive: boolean
|
||
|
|
}
|
||
|
|
|
||
|
|
fact hasRole(user: User, role: string)
|
||
|
|
|
||
|
|
evidence canRead(user: User, doc: Document) {
|
||
|
|
hasRole(user, 'admin')
|
||
|
|
}
|
||
|
|
`;
|
||
|
|
|
||
|
|
const program = parser.parse(dsl);
|
||
|
|
assert.ok(program !== null, 'Program should be created');
|
||
|
|
assert.ok(program.definitions.length === 1, 'Should have 1 definition');
|
||
|
|
assert.ok(program.facts.length === 1, 'Should have 1 fact');
|
||
|
|
assert.ok(program.evidence.length === 1, 'Should have 1 evidence');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('Complex DSL parsing', () => {
|
||
|
|
const dsl = `
|
||
|
|
definition User {
|
||
|
|
role: string
|
||
|
|
isActive: boolean
|
||
|
|
clearance: string BEHAVES {
|
||
|
|
blurring adaptive confidence_95
|
||
|
|
} CACHE eager
|
||
|
|
}
|
||
|
|
|
||
|
|
fact hasRole(user: User, role: string) CACHE eager
|
||
|
|
fact isMember(user: User, group: Group) transitive CACHE lazy
|
||
|
|
|
||
|
|
evidence canRead(user: User, doc: Document) {
|
||
|
|
hasRole(user, 'admin')
|
||
|
|
|
||
|
|
isMember(user, *group) {
|
||
|
|
canRead(group, doc)
|
||
|
|
} limit 5
|
||
|
|
|
||
|
|
WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
|
||
|
|
}
|
||
|
|
`;
|
||
|
|
|
||
|
|
const program = parser.parse(dsl);
|
||
|
|
assert.ok(program !== null, 'Program should be created');
|
||
|
|
assert.ok(program.definitions.length === 1, 'Should have 1 definition');
|
||
|
|
assert.ok(program.facts.length === 2, 'Should have 2 facts');
|
||
|
|
assert.ok(program.evidence.length === 1, 'Should have 1 evidence');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('Error handling', () => {
|
||
|
|
const invalidDSL = `
|
||
|
|
definition User {
|
||
|
|
role: string
|
||
|
|
// Missing closing brace
|
||
|
|
|
||
|
|
fact hasRole(user: User, role: string)
|
||
|
|
// Missing semicolon
|
||
|
|
`;
|
||
|
|
|
||
|
|
assert.throws(
|
||
|
|
() => parser.parse(invalidDSL),
|
||
|
|
/Parsing failed/,
|
||
|
|
'Should have parsing error message'
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('Validation', () => {
|
||
|
|
const validDSL = `
|
||
|
|
definition User {
|
||
|
|
role: string
|
||
|
|
isActive: boolean
|
||
|
|
}
|
||
|
|
|
||
|
|
fact hasRole(user: User, role: string)
|
||
|
|
`;
|
||
|
|
|
||
|
|
const invalidDSL = `
|
||
|
|
definition User {
|
||
|
|
role: string
|
||
|
|
// Missing closing brace
|
||
|
|
`;
|
||
|
|
|
||
|
|
const validResult = parser.validate(validDSL);
|
||
|
|
assert.ok(validResult.success, 'Valid DSL should pass validation');
|
||
|
|
assert.ok(validResult.program !== null, 'Valid DSL should return program');
|
||
|
|
|
||
|
|
const invalidResult = parser.validate(invalidDSL);
|
||
|
|
assert.ok(!invalidResult.success, 'Invalid DSL should fail validation');
|
||
|
|
assert.ok(invalidResult.errors.length > 0, 'Should have validation errors');
|
||
|
|
});
|
||
|
|
});
|