89 lines
3.2 KiB
JavaScript
89 lines
3.2 KiB
JavaScript
|
|
import { test } from 'node:test';
|
||
|
|
import assert from 'node:assert/strict';
|
||
|
|
import { performance } from 'node:perf_hooks';
|
||
|
|
import { Arbiter } from '../../src/core/Arbiter.js';
|
||
|
|
import { PartialGraphContext } from '../../src/core/PartialGraphContext.js';
|
||
|
|
import { validateClaimsForLayer } from '../../src/core/partial-graph/layer-registry.js';
|
||
|
|
|
||
|
|
const runPerf = process.env.RUN_PERF_TESTS === '1';
|
||
|
|
const perfTest = runPerf ? test : test.skip;
|
||
|
|
|
||
|
|
function percentile(sorted, p) {
|
||
|
|
if (!sorted.length) return 0;
|
||
|
|
const idx = Math.min(sorted.length - 1, Math.max(0, Math.floor(sorted.length * p) - 1));
|
||
|
|
return sorted[idx];
|
||
|
|
}
|
||
|
|
|
||
|
|
perfTest('core perf: layer registry validation stays sub-1ms', () => {
|
||
|
|
const claims = [
|
||
|
|
{ relation: 'delegated_authority', object: 'resource:alpha:item:1', ttl_seconds: 60 },
|
||
|
|
{ relation: 'workflow_step', object: 'workflow:loan:step:2', ttl_seconds: 60 }
|
||
|
|
];
|
||
|
|
const iterations = 10000;
|
||
|
|
const durations = [];
|
||
|
|
|
||
|
|
for (let i = 0; i < iterations; i++) {
|
||
|
|
const start = performance.now();
|
||
|
|
validateClaimsForLayer('workflow_overlay', claims, ['delegated_authority', 'workflow_step']);
|
||
|
|
durations.push(performance.now() - start);
|
||
|
|
}
|
||
|
|
|
||
|
|
const avg = durations.reduce((a, b) => a + b, 0) / iterations;
|
||
|
|
const sorted = [...durations].sort((a, b) => a - b);
|
||
|
|
const p95 = percentile(sorted, 0.95);
|
||
|
|
|
||
|
|
assert.ok(avg < 1.0, `avg ${avg.toFixed(4)}ms exceeds 1ms target`);
|
||
|
|
assert.ok(p95 < 1.0, `p95 ${p95.toFixed(4)}ms exceeds 1ms target`);
|
||
|
|
});
|
||
|
|
|
||
|
|
perfTest('core perf: PartialGraphContext direct lookups stay sub-1ms', () => {
|
||
|
|
const arbiter = new Arbiter();
|
||
|
|
arbiter.addNode('user:1', 'user');
|
||
|
|
arbiter.addNode('doc:1', 'doc');
|
||
|
|
|
||
|
|
const context = new PartialGraphContext(arbiter, {
|
||
|
|
relations: [
|
||
|
|
{ src: 'user:1', relation: 'can_read', dst: 'doc:1', possibility: 1.0 }
|
||
|
|
]
|
||
|
|
});
|
||
|
|
|
||
|
|
const srcId = context.nodeIdByKey.get('user:1');
|
||
|
|
const dstId = context.nodeIdByKey.get('doc:1');
|
||
|
|
|
||
|
|
const iterations = 10000;
|
||
|
|
const durations = [];
|
||
|
|
for (let i = 0; i < iterations; i++) {
|
||
|
|
const start = performance.now();
|
||
|
|
const relation = context.getDirectRelation(srcId, 'can_read', dstId);
|
||
|
|
durations.push(performance.now() - start);
|
||
|
|
assert.ok(relation);
|
||
|
|
}
|
||
|
|
|
||
|
|
const avg = durations.reduce((a, b) => a + b, 0) / iterations;
|
||
|
|
const sorted = [...durations].sort((a, b) => a - b);
|
||
|
|
const p95 = percentile(sorted, 0.95);
|
||
|
|
|
||
|
|
assert.ok(avg < 1.0, `avg ${avg.toFixed(4)}ms exceeds 1ms target`);
|
||
|
|
assert.ok(p95 < 1.0, `p95 ${p95.toFixed(4)}ms exceeds 1ms target`);
|
||
|
|
});
|
||
|
|
|
||
|
|
perfTest('core perf: Arbiter direct checks stay sub-1ms average', () => {
|
||
|
|
const arbiter = new Arbiter();
|
||
|
|
arbiter.addNode('user:1', 'user');
|
||
|
|
arbiter.addNode('doc:1', 'doc');
|
||
|
|
arbiter.setRelationConfig('can_read', { type: 'direct' });
|
||
|
|
arbiter.addRelation('user:1', 'can_read', 'doc:1', 1.0);
|
||
|
|
|
||
|
|
const iterations = 5000;
|
||
|
|
const durations = [];
|
||
|
|
for (let i = 0; i < iterations; i++) {
|
||
|
|
const start = performance.now();
|
||
|
|
const result = arbiter.check('user:1', 'can_read', 'doc:1');
|
||
|
|
durations.push(performance.now() - start);
|
||
|
|
assert.ok(result.possibility > 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
const avg = durations.reduce((a, b) => a + b, 0) / iterations;
|
||
|
|
assert.ok(avg < 1.0, `avg ${avg.toFixed(4)}ms exceeds 1ms target`);
|
||
|
|
});
|