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.
213 lines
8.4 KiB
JavaScript
213 lines
8.4 KiB
JavaScript
/**
|
|
* Test reachability integration across all rule types
|
|
*/
|
|
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { BigGraphGenerator } from '../helpers/big-graph-generator.js';
|
|
import { ChainRule } from '../../src/authorization/rules/ChainRule.js';
|
|
import { MultiHopRule } from '../../src/authorization/rules/MultiHopRule.js';
|
|
import { ParentRule } from '../../src/authorization/rules/ParentRule.js';
|
|
import { DirectRule } from '../../src/authorization/rules/DirectRule.js';
|
|
|
|
test('verifies reachability integration across all rule types', async () => {
|
|
if (process.env.TEST_DEBUG === '1') console.log('🔍 Testing reachability integration across all rule types...');
|
|
|
|
const generator = new BigGraphGenerator();
|
|
const graphData = generator.generateGraph('enterprise');
|
|
const arbiter = generator.loadIntoArbiter(graphData);
|
|
|
|
// Initialize reachability checker
|
|
await arbiter.initializeReachabilityChecker({
|
|
strategy: 'auto',
|
|
twoHopOptions: { remainderBits: 8 },
|
|
treeCoverOptions: { remainderBits: 8 }
|
|
});
|
|
|
|
if (process.env.TEST_DEBUG === '1') console.log(' ✅ Reachability checker initialized');
|
|
|
|
// Test with different rule types
|
|
const rules = [
|
|
{ name: 'ChainRule', rule: new ChainRule(arbiter) },
|
|
{ name: 'MultiHopRule', rule: new MultiHopRule(arbiter) },
|
|
{ name: 'ParentRule', rule: new ParentRule(arbiter) },
|
|
{ name: 'DirectRule', rule: new DirectRule(arbiter) }
|
|
];
|
|
|
|
const testRelations = graphData.relations.filter(r =>
|
|
r.src.startsWith('user:') && r.dst.startsWith('doc:')
|
|
).slice(0, 3);
|
|
|
|
for (const { name, rule } of rules) {
|
|
if (process.env.TEST_DEBUG === '1') console.log(` Testing ${name} with reachability integration...`);
|
|
|
|
for (const relation of testRelations) {
|
|
if (process.env.TEST_DEBUG === '1') console.log(` Testing: ${relation.src} -> ${relation.dst}`);
|
|
|
|
// Test rule evaluation with reachability
|
|
const result = rule.evaluate(
|
|
arbiter.nodeIdByKey.get(relation.src),
|
|
relation.src,
|
|
arbiter.nodeIdByKey.get(relation.dst),
|
|
relation.dst,
|
|
{ type: name.toLowerCase().replace('rule', ''), relation: 'can_read_via_role' },
|
|
new Set(),
|
|
'can_read_via_role',
|
|
{}
|
|
);
|
|
|
|
if (process.env.TEST_DEBUG === '1') console.log(` Result: ${result.possibility} (${result.reason})`);
|
|
if (process.env.TEST_DEBUG === '1') console.log(` Meta method: ${result.meta?.method || 'unknown'}`);
|
|
|
|
// Verify result structure
|
|
assert.ok(typeof result.possibility === 'number', 'Should return numeric possibility');
|
|
// Note: Some rules might not have a reason field, which is acceptable
|
|
|
|
// Check if reachability was used (quick failure)
|
|
if (result.reason && result.reason.includes('reachability index')) {
|
|
if (process.env.TEST_DEBUG === '1') console.log(` ✅ ${name} used reachability quick failure`);
|
|
assert.ok(result.possibility === 0, 'Quick failure should return 0 possibility');
|
|
} else {
|
|
if (process.env.TEST_DEBUG === '1') console.log(` 📝 ${name} proceeded with normal evaluation`);
|
|
}
|
|
}
|
|
|
|
if (process.env.TEST_DEBUG === '1') console.log(` ✅ ${name} reachability integration working`);
|
|
}
|
|
|
|
if (process.env.TEST_DEBUG === '1') console.log(' ✅ All rule types integrated with reachability');
|
|
});
|
|
|
|
test('verifies reachability integration performance benefits', async () => {
|
|
if (process.env.TEST_DEBUG === '1') console.log('⚡ Testing reachability integration performance benefits...');
|
|
|
|
const generator = new BigGraphGenerator();
|
|
const graphData = generator.generateGraph('enterprise');
|
|
const arbiter = generator.loadIntoArbiter(graphData);
|
|
|
|
// Initialize reachability checker
|
|
await arbiter.initializeReachabilityChecker({
|
|
strategy: 'auto',
|
|
twoHopOptions: { remainderBits: 8 },
|
|
treeCoverOptions: { remainderBits: 8 }
|
|
});
|
|
|
|
const rule = new ChainRule(arbiter);
|
|
|
|
// Test with reachable and unreachable pairs
|
|
const testPairs = [
|
|
{ src: 'user:Grace Miller-15', dst: 'doc:report-54', expected: 'reachable' },
|
|
{ src: 'user:Alice Smith-0', dst: 'doc:secret-999', expected: 'unreachable' },
|
|
{ src: 'user:Ivy Brown-13', dst: 'doc:contract-480', expected: 'unreachable' }
|
|
];
|
|
|
|
if (process.env.TEST_DEBUG === '1') console.log(' Testing performance with reachability integration...');
|
|
|
|
for (const { src, dst, expected } of testPairs) {
|
|
if (process.env.TEST_DEBUG === '1') console.log(` Testing: ${src} -> ${dst} (expected: ${expected})`);
|
|
|
|
const startTime = Date.now();
|
|
|
|
const result = rule.evaluate(
|
|
arbiter.nodeIdByKey.get(src),
|
|
src,
|
|
arbiter.nodeIdByKey.get(dst),
|
|
dst,
|
|
{
|
|
type: 'chain',
|
|
steps: [
|
|
{ relation: 'role_membership', direction: 'out' },
|
|
{ relation: 'role_permission', direction: 'out' }
|
|
]
|
|
},
|
|
new Set(),
|
|
'can_read_via_role',
|
|
{}
|
|
);
|
|
|
|
const endTime = Date.now();
|
|
const duration = endTime - startTime;
|
|
|
|
if (process.env.TEST_DEBUG === '1') console.log(` Result: ${result.possibility} (${result.reason}) in ${duration}ms`);
|
|
if (process.env.TEST_DEBUG === '1') console.log(` Meta method: ${result.meta?.method || 'unknown'}`);
|
|
|
|
// Verify performance
|
|
if (expected === 'unreachable' && result.reason.includes('reachability index')) {
|
|
if (process.env.TEST_DEBUG === '1') console.log(` ✅ Quick failure detected - performance optimized`);
|
|
assert.ok(duration < 100, 'Quick failure should be very fast');
|
|
} else {
|
|
if (process.env.TEST_DEBUG === '1') console.log(` 📝 Normal evaluation - ${duration}ms`);
|
|
}
|
|
}
|
|
|
|
if (process.env.TEST_DEBUG === '1') console.log(' ✅ Performance benefits verified');
|
|
});
|
|
|
|
test('verifies reachability integration with batch operations', async () => {
|
|
if (process.env.TEST_DEBUG === '1') console.log('📦 Testing reachability integration with batch operations...');
|
|
|
|
const generator = new BigGraphGenerator();
|
|
const graphData = generator.generateGraph('enterprise');
|
|
const arbiter = generator.loadIntoArbiter(graphData);
|
|
|
|
// Initialize reachability checker
|
|
await arbiter.initializeReachabilityChecker({
|
|
strategy: 'auto',
|
|
twoHopOptions: { remainderBits: 8 },
|
|
treeCoverOptions: { remainderBits: 8 }
|
|
});
|
|
|
|
const rule = new ChainRule(arbiter);
|
|
|
|
// Test batch reachability checking
|
|
const testPairs = graphData.relations.filter(r =>
|
|
r.src.startsWith('user:') && r.dst.startsWith('doc:')
|
|
).slice(0, 5).map(r => ({ sourceKey: r.src, targetKey: r.dst }));
|
|
|
|
if (process.env.TEST_DEBUG === '1') console.log(` Testing batch reachability check for ${testPairs.length} pairs...`);
|
|
|
|
const batchResults = rule._batchReachabilityCheck(testPairs);
|
|
|
|
if (batchResults) {
|
|
if (process.env.TEST_DEBUG === '1') console.log(' Batch results:');
|
|
for (const [pair, result] of Object.entries(batchResults)) {
|
|
if (process.env.TEST_DEBUG === '1') console.log(` ${pair}: ${result}`);
|
|
}
|
|
|
|
// Verify batch results
|
|
assert.ok(Object.keys(batchResults).length === testPairs.length, 'Should have results for all pairs');
|
|
|
|
if (process.env.TEST_DEBUG === '1') console.log(' ✅ Batch reachability checking working');
|
|
} else {
|
|
if (process.env.TEST_DEBUG === '1') console.log(' 📝 No reachability checker available for batch checking');
|
|
}
|
|
|
|
// Test individual rule evaluation with batch context
|
|
if (process.env.TEST_DEBUG === '1') console.log(' Testing individual rule evaluation with batch context...');
|
|
|
|
const testRelation = testPairs[0];
|
|
const result = rule.evaluate(
|
|
arbiter.nodeIdByKey.get(testRelation.sourceKey),
|
|
testRelation.sourceKey,
|
|
arbiter.nodeIdByKey.get(testRelation.targetKey),
|
|
testRelation.targetKey,
|
|
{
|
|
type: 'chain',
|
|
steps: [
|
|
{ relation: 'role_membership', direction: 'out' },
|
|
{ relation: 'role_permission', direction: 'out' }
|
|
]
|
|
},
|
|
new Set(),
|
|
'can_read_via_role',
|
|
{}
|
|
);
|
|
|
|
if (process.env.TEST_DEBUG === '1') console.log(` Individual result: ${result.possibility} (${result.reason})`);
|
|
if (process.env.TEST_DEBUG === '1') console.log(` Meta method: ${result.meta?.method || 'unknown'}`);
|
|
|
|
assert.ok(typeof result.possibility === 'number', 'Should return numeric possibility');
|
|
|
|
if (process.env.TEST_DEBUG === '1') console.log(' ✅ Batch operations with reachability integration working');
|
|
});
|