initial commit: @arbiter/core authorization engine with js-rigor hardening
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.
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { describe, test } from 'node:test';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { CondensedGraph } from '../../src/core/CondensedGraph.js';
|
||||
import { ShardedSnapshotBuilder } from '../../src/core/shards/ShardedSnapshotBuilder.js';
|
||||
import { ShardedSnapshot } from '../../src/core/shards/ShardedSnapshot.js';
|
||||
import { FileShardStorage } from '../../src/core/shards/FileShardStorage.js';
|
||||
|
||||
function buildShardedSnapshot(bucketSize = 2) {
|
||||
const graph = new CondensedGraph();
|
||||
|
||||
const user0 = graph._ensureNode('user:0');
|
||||
const user1 = graph._ensureNode('user:1');
|
||||
const user2 = graph._ensureNode('user:2');
|
||||
const user3 = graph._ensureNode('user:3');
|
||||
const group0 = graph._ensureNode('group:0');
|
||||
const group1 = graph._ensureNode('group:1');
|
||||
const doc0 = graph._ensureNode('doc:0');
|
||||
|
||||
graph.addEdge(user0, 'member', group0);
|
||||
graph.addEdge(user3, 'member', group0);
|
||||
graph.addEdge(user2, 'member', group1);
|
||||
|
||||
graph.addEdge(group0, 'viewer', doc0);
|
||||
graph.addEdge(group1, 'viewer', doc0);
|
||||
|
||||
graph.addEdge(user0, 'risk', doc0, { value: 0.2, possibility: 1, reliability: 1 });
|
||||
graph.addEdge(user3, 'risk', doc0, { value: 0.9, possibility: 1, reliability: 1 });
|
||||
graph.addEdge(user2, 'risk', doc0, { value: 0.6, possibility: 1, reliability: 1 });
|
||||
|
||||
graph.finalizePerfectHash();
|
||||
graph.finalizeWaveletAdjacency({ dropAdjacencyList: true });
|
||||
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'sharded-cross-chain-'));
|
||||
const builder = new ShardedSnapshotBuilder({ bucketSize, includeDirections: ['out', 'in'] });
|
||||
const manifest = builder.build(graph, dir);
|
||||
const storage = new FileShardStorage(dir);
|
||||
const snapshot = new ShardedSnapshot(manifest, storage, { cacheLimit: 8 });
|
||||
snapshot.initializeSync();
|
||||
|
||||
return { graph, snapshot, dir, ids: { user0, user1, user2, user3, group0, group1, doc0 } };
|
||||
}
|
||||
|
||||
function collectRiskPaths(snapshot, relationIds, docId) {
|
||||
const viewerRel = relationIds.viewer;
|
||||
const memberRel = relationIds.member;
|
||||
const riskRel = relationIds.risk;
|
||||
|
||||
const paths = [];
|
||||
const viewerEdges = snapshot.executeGetInEdgesSync(docId, viewerRel, new Set()) || [];
|
||||
for (const viewer of viewerEdges) {
|
||||
const groupId = viewer.src;
|
||||
const memberEdges = snapshot.executeGetInEdgesSync(groupId, memberRel, new Set()) || [];
|
||||
for (const member of memberEdges) {
|
||||
const userId = member.src;
|
||||
const riskEdge = snapshot.executeFindEdgeSync(userId, riskRel, docId, new Set());
|
||||
if (riskEdge) {
|
||||
paths.push({ userId, docId });
|
||||
}
|
||||
}
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
// ADR-003: sharded snapshots are stubs in src/core/shards/ — tests are the spec for when the subsystem is implemented.
|
||||
describe.skip('Sharded snapshot cross-chain aggregation', () => {
|
||||
test('aggregates risk values across userset paths', () => {
|
||||
const { graph, snapshot, dir, ids } = buildShardedSnapshot(2);
|
||||
const relationIds = {
|
||||
member: graph.getRelationId('member'),
|
||||
viewer: graph.getRelationId('viewer'),
|
||||
risk: graph.getRelationId('risk')
|
||||
};
|
||||
|
||||
const plan = new Set();
|
||||
snapshot.planInEdges(relationIds.viewer, ids.doc0, plan);
|
||||
snapshot.planInEdges(relationIds.member, ids.group0, plan);
|
||||
snapshot.planInEdges(relationIds.member, ids.group1, plan);
|
||||
snapshot.planOutEdges(relationIds.risk, ids.user0, plan);
|
||||
snapshot.planOutEdges(relationIds.risk, ids.user2, plan);
|
||||
snapshot.planOutEdges(relationIds.risk, ids.user3, plan);
|
||||
snapshot.prefetchPlanSync(plan);
|
||||
|
||||
const paths = collectRiskPaths(snapshot, relationIds, ids.doc0);
|
||||
assert.strictEqual(paths.length, 3);
|
||||
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
test('cross-bucket paths resolve with inbound and outbound shards', () => {
|
||||
const { graph, snapshot, dir, ids } = buildShardedSnapshot(2);
|
||||
const relationIds = {
|
||||
member: graph.getRelationId('member'),
|
||||
viewer: graph.getRelationId('viewer'),
|
||||
risk: graph.getRelationId('risk')
|
||||
};
|
||||
|
||||
const plan = new Set();
|
||||
snapshot.planInEdges(relationIds.viewer, ids.doc0, plan);
|
||||
snapshot.planInEdges(relationIds.member, ids.group0, plan);
|
||||
snapshot.planInEdges(relationIds.member, ids.group1, plan);
|
||||
snapshot.planOutEdges(relationIds.risk, ids.user0, plan);
|
||||
snapshot.planOutEdges(relationIds.risk, ids.user2, plan);
|
||||
snapshot.planOutEdges(relationIds.risk, ids.user3, plan);
|
||||
snapshot.prefetchPlanSync(plan);
|
||||
|
||||
const paths = collectRiskPaths(snapshot, relationIds, ids.doc0);
|
||||
assert.strictEqual(paths.length, 3);
|
||||
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user