717ae1031e
Zanzibar-style authorization graph engine (direct/chain/TTU/defeasible/ binary modes, condensed snapshots, value relations) with 39 rigor test campaigns. Includes fixes for snapshot binary writer/reader format mismatch (snapshot-of-snapshot corruption), possibility write-boundary validation, empty-graph snapshot serialization, relation lookup cache direction collision, config-redefinition cache invalidation, binary threshold semantics, defeasible compiled routing, and comparator reason whitelisting.
108 lines
3.5 KiB
JavaScript
108 lines
3.5 KiB
JavaScript
import { test, describe, it } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { setupChainRuleTestGraph } from './helpers.js';
|
|
|
|
describe('ChainRule Comprehensive Tests', () => {
|
|
|
|
describe('Edge Cases and Error Handling', () => {
|
|
it('handles empty chains gracefully', () => {
|
|
const arbiter = setupChainRuleTestGraph();
|
|
|
|
arbiter.setRelationConfig('empty_chain', {
|
|
type: 'chain',
|
|
steps: [] // Empty steps
|
|
});
|
|
|
|
const result = arbiter.check('user:alice', 'empty_chain', 'project:web-app');
|
|
assert.equal(result.possibility, 0);
|
|
assert.equal(result.reason, 'no_chain_steps_defined');
|
|
});
|
|
|
|
it('handles broken chains correctly', () => {
|
|
const arbiter = setupChainRuleTestGraph();
|
|
|
|
arbiter.setRelationConfig('broken_chain', {
|
|
type: 'chain',
|
|
steps: [
|
|
{ relation: 'member_of', direction: 'out' },
|
|
{ relation: 'nonexistent_relation', direction: 'out' } // Broken link
|
|
]
|
|
});
|
|
|
|
const result = arbiter.check('user:alice', 'broken_chain', 'project:web-app');
|
|
assert.equal(result.possibility, 0);
|
|
});
|
|
|
|
it('handles invalid extractFrom indices', () => {
|
|
const arbiter = setupChainRuleTestGraph();
|
|
|
|
arbiter.setRelationConfig('invalid_extract', {
|
|
type: 'chain',
|
|
steps: [
|
|
{ relation: 'member_of', direction: 'out' },
|
|
{ relation: 'manages', direction: 'out' }
|
|
],
|
|
extractValues: true,
|
|
extractFrom: 10, // Invalid index (only 2 steps)
|
|
extractRelation: 'has_budget'
|
|
});
|
|
|
|
const result = arbiter.check('user:alice', 'invalid_extract', 'budget:tech-2024');
|
|
|
|
// Should handle gracefully without crashing
|
|
assert.ok(typeof result.possibility === 'number');
|
|
});
|
|
|
|
it('handles circular chain references', () => {
|
|
const arbiter = setupChainRuleTestGraph();
|
|
|
|
// Create a circular reference
|
|
arbiter.addNode('node:a', 'node');
|
|
arbiter.addNode('node:b', 'node');
|
|
arbiter.addNode('node:c', 'node');
|
|
|
|
arbiter.addRelation('node:a', 'connects_to', 'node:b');
|
|
arbiter.addRelation('node:b', 'connects_to', 'node:c');
|
|
arbiter.addRelation('node:c', 'connects_to', 'node:a'); // Circular
|
|
|
|
arbiter.setRelationConfig('connects_to', { type: 'direct' });
|
|
arbiter.setRelationConfig('circular_chain', {
|
|
type: 'chain',
|
|
steps: [
|
|
{ relation: 'connects_to', direction: 'out' },
|
|
{ relation: 'connects_to', direction: 'out' },
|
|
{ relation: 'connects_to', direction: 'out' }
|
|
]
|
|
});
|
|
|
|
const result = arbiter.check('node:a', 'circular_chain', 'node:a');
|
|
|
|
// Should complete the circle
|
|
assert.equal(result.possibility, 1);
|
|
});
|
|
|
|
it('validates step configuration correctness', () => {
|
|
const arbiter = setupChainRuleTestGraph();
|
|
|
|
// Test with malformed step configuration
|
|
try {
|
|
arbiter.setRelationConfig('malformed_chain', {
|
|
type: 'chain',
|
|
steps: [
|
|
{ relation: 'member_of' }, // Missing direction
|
|
{ direction: 'out' } // Missing relation
|
|
]
|
|
});
|
|
|
|
const result = arbiter.check('user:alice', 'malformed_chain', 'project:web-app');
|
|
|
|
// Should handle gracefully
|
|
assert.equal(result.possibility, 0);
|
|
} catch (error) {
|
|
// Or might throw validation error - both acceptable
|
|
assert.ok(error.message.includes('relation') || error.message.includes('direction'));
|
|
}
|
|
});
|
|
});
|
|
});
|