Files
core/tests/rules/rule-reachability-integration-verification.test.js
T

213 lines
8.4 KiB
JavaScript
Raw Normal View History

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