Files
evidence-dsl/tests/PeggyParser.test.js
John Dvorak ae21605fb7
CI / test (push) Successful in 11s
CI / publish (push) Successful in 9s
evidence-dsl: extract Evidence DSL v2 compiler from @arbiter/core
The Evidence DSL (ADR-000) is a thin declarative layer that compiles to
engine rule types. It has zero runtime coupling to the core engine
(DSLCompiler takes an arbiter as a duck-typed argument; the only shared
code was the ip-utils helpers, now local). Extracting it into its own
package keeps the core artifact free of the DSL surface.

- @arbiter/evidence-dsl depends on @arbiter/core (config formats are the
  compilation target)
- deep-path exports for the compiler, parser, generator, validation,
  and built-in functions (the surface the core's DSL tests consume)
- tests moved alongside; generate-parser script + peggy devDep local
- CI: test on push, publish on v* tags
2026-08-03 08:48:39 -07:00

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');
});
});