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:
John Dvorak
2026-07-31 13:44:06 -07:00
commit 717ae1031e
373 changed files with 654131 additions and 0 deletions
+372
View File
@@ -0,0 +1,372 @@
import { test, describe, it, beforeEach } from 'node:test';
import assert from 'node:assert/strict';
import { Arbiter } from '../../src/index.js';
/**
* Performance Regression Tests
*
* Tests that performance optimizations don't regress and that
* authorization remains fast even with large datasets.
*/
describe.skip('Performance Regression Tests', () => {
let arbiter;
beforeEach(() => {
arbiter = new Arbiter({
embeddingDimensions: 256,
directCheckCacheSize: 1000,
directCheckCacheTTL: 60000
});
});
describe('Large Dataset Performance', () => {
it('maintains performance with large number of nodes', () => {
const nodeCount = 1000;
const startTime = Date.now();
// Create many nodes
for (let i = 0; i < nodeCount; i++) {
arbiter.addNode(`user:user-${i}`, 'user');
arbiter.addNode(`group:group-${i}`, 'group');
arbiter.addNode(`document:doc-${i}`, 'document');
}
const creationTime = Date.now() - startTime;
if (process.env.TEST_DEBUG === '1') console.log(`Created ${nodeCount * 3} nodes in ${creationTime}ms`);
// Set up relations
arbiter.setRelationConfig('member_of', { type: 'direct' });
arbiter.setRelationConfig('owner', { type: 'direct' });
const relationStartTime = Date.now();
for (let i = 0; i < nodeCount; i++) {
arbiter.addRelation(`user:user-${i}`, 'member_of', `group:group-${i}`);
arbiter.addRelation(`document:doc-${i}`, 'owner', `group:group-${i}`);
}
const relationTime = Date.now() - relationStartTime;
if (process.env.TEST_DEBUG === '1') console.log(`Created ${nodeCount * 2} relations in ${relationTime}ms`);
// Test authorization performance
const authStartTime = Date.now();
for (let i = 0; i < 100; i++) {
const result = arbiter.check(`user:user-${i}`, 'member_of', `group:group-${i}`);
assert.equal(result.possibility, 1.0);
}
const authTime = Date.now() - authStartTime;
if (process.env.TEST_DEBUG === '1') console.log(`Performed 100 authorization checks in ${authTime}ms`);
// Performance assertions
assert.ok(creationTime < 5000, `Node creation too slow: ${creationTime}ms`);
assert.ok(relationTime < 3000, `Relation creation too slow: ${relationTime}ms`);
assert.ok(authTime < 1000, `Authorization too slow: ${authTime}ms`);
});
it('maintains performance with complex tuple-to-userset rules', () => {
const groupCount = 100;
const userCount = 50;
// Create groups and users
for (let i = 0; i < groupCount; i++) {
arbiter.addNode(`group:group-${i}`, 'group');
}
for (let i = 0; i < userCount; i++) {
arbiter.addNode(`user:user-${i}`, 'user');
}
arbiter.setRelationConfig('member_of', { type: 'direct' });
arbiter.setRelationConfig('owner', { type: 'direct' });
// Create complex membership patterns
for (let i = 0; i < userCount; i++) {
// Each user is member of multiple groups
for (let j = 0; j < 5; j++) {
const groupIndex = (i + j) % groupCount;
arbiter.addRelation(`user:user-${i}`, 'member_of', `group:group-${groupIndex}`);
}
}
// Create document ownership
for (let i = 0; i < groupCount; i++) {
arbiter.addRelation(`document:doc-${i}`, 'owner', `group:group-${i}`);
}
// Set up tuple-to-userset rule
arbiter.setRelationConfig('group_access', {
type: 'tuple_to_userset',
tuplesetRelation: 'owner',
computedRelation: 'member_of'
});
const startTime = Date.now();
// Test authorization performance
for (let i = 0; i < 50; i++) {
const result = arbiter.check(`user:user-${i}`, 'group_access', `document:doc-${i}`);
assert.ok(result.possibility >= 0);
}
const duration = Date.now() - startTime;
if (process.env.TEST_DEBUG === '1') console.log(`Complex tuple-to-userset authorization: ${duration}ms for 50 checks`);
// Should complete within reasonable time
assert.ok(duration < 2000, `Complex authorization too slow: ${duration}ms`);
});
it('maintains performance with deep chain rules', () => {
const depth = 10;
const nodeCount = depth + 1;
// Create chain: user → group1 → group2 → ... → groupN → document
for (let i = 0; i < nodeCount; i++) {
arbiter.addNode(`entity:entity-${i}`, 'entity');
}
arbiter.setRelationConfig('next', { type: 'direct' });
// Create chain relations
for (let i = 0; i < depth; i++) {
arbiter.addRelation(`entity:entity-${i}`, 'next', `entity:entity-${i + 1}`);
}
// Set up deep chain rule
const steps = [];
for (let i = 0; i < depth; i++) {
steps.push({ relation: 'next', direction: 'out' });
}
arbiter.setRelationConfig('deep_chain', {
type: 'chain',
steps: steps
});
const startTime = Date.now();
// Test deep chain authorization
const result = arbiter.check('entity:entity-0', 'deep_chain', `entity:entity-${depth}`);
const duration = Date.now() - startTime;
if (process.env.TEST_DEBUG === '1') console.log(`Deep chain authorization (depth ${depth}): ${duration}ms`);
assert.equal(result.possibility, 1.0);
assert.ok(duration < 1000, `Deep chain too slow: ${duration}ms`);
});
});
describe('Cache Performance', () => {
it('demonstrates cache effectiveness with repeated queries', () => {
// Set up simple authorization
arbiter.addNode('user:alice', 'user');
arbiter.addNode('document:spec', 'document');
arbiter.addRelation('user:alice', 'can_read', 'document:spec', { possibility: 1.0 });
arbiter.setRelationConfig('can_read', { type: 'direct' });
// First query (cache miss)
const startTime1 = Date.now();
const result1 = arbiter.check('user:alice', 'can_read', 'document:spec');
const time1 = Date.now() - startTime1;
// Second query (cache hit)
const startTime2 = Date.now();
const result2 = arbiter.check('user:alice', 'can_read', 'document:spec');
const time2 = Date.now() - startTime2;
if (process.env.TEST_DEBUG === '1') console.log(`First query: ${time1}ms, Second query: ${time2}ms`);
assert.equal(result1.possibility, 1.0);
assert.equal(result2.possibility, 1.0);
// Cache should make second query faster (though this might be too fast to measure)
assert.ok(time2 <= time1, 'Cache should not make queries slower');
});
it('handles cache eviction under memory pressure', () => {
// Set up cache with small size
const smallArbiter = new Arbiter({
directCheckCacheSize: 10, // Very small cache
directCheckCacheTTL: 60000
});
smallArbiter.setRelationConfig('can_read', { type: 'direct' });
// Create many users and documents
for (let i = 0; i < 50; i++) {
smallArbiter.addNode(`user:user-${i}`, 'user');
smallArbiter.addNode(`document:doc-${i}`, 'document');
smallArbiter.addRelation(`user:user-${i}`, 'can_read', `document:doc-${i}`, { possibility: 1.0 });
}
const startTime = Date.now();
// Perform many authorization checks to trigger cache eviction
for (let i = 0; i < 100; i++) {
const userIndex = i % 50;
const docIndex = (i + 1) % 50;
const result = smallArbiter.check(`user:user-${userIndex}`, 'can_read', `document:doc-${docIndex}`);
assert.ok(result.possibility >= 0);
}
const duration = Date.now() - startTime;
if (process.env.TEST_DEBUG === '1') console.log(`Cache eviction test: ${duration}ms for 100 checks`);
// Should complete within reasonable time even with cache eviction
assert.ok(duration < 3000, `Cache eviction too slow: ${duration}ms`);
});
});
describe('Binary Mode Performance', () => {
it('demonstrates binary mode performance benefits', () => {
// Set up authorization scenario
arbiter.addNode('user:alice', 'user');
arbiter.addNode('document:spec', 'document');
arbiter.addRelation('user:alice', 'can_read', 'document:spec', { possibility: 1.0 });
arbiter.setRelationConfig('can_read', { type: 'direct' });
// Test normal mode
const normalStartTime = Date.now();
for (let i = 0; i < 1000; i++) {
const result = arbiter.check('user:alice', 'can_read', 'document:spec');
assert.equal(result.possibility, 1.0);
}
const normalTime = Date.now() - normalStartTime;
// Test binary mode
const binaryStartTime = Date.now();
for (let i = 0; i < 1000; i++) {
const result = arbiter.check('user:alice', 'can_read', 'document:spec', { binary: true });
assert.equal(result.possibility, 1.0);
}
const binaryTime = Date.now() - binaryStartTime;
if (process.env.TEST_DEBUG === '1') console.log(`Normal mode: ${normalTime}ms, Binary mode: ${binaryTime}ms`);
// Binary mode should be faster for simple cases
assert.ok(binaryTime <= normalTime, 'Binary mode should not be slower than normal mode');
});
it('maintains binary mode performance with complex rules', () => {
// Set up complex tuple-to-userset scenario
for (let i = 0; i < 20; i++) {
arbiter.addNode(`user:user-${i}`, 'user');
arbiter.addNode(`group:group-${i}`, 'group');
arbiter.addNode(`document:doc-${i}`, 'document');
arbiter.addRelation(`user:user-${i}`, 'member_of', `group:group-${i}`);
arbiter.addRelation(`document:doc-${i}`, 'owner', `group:group-${i}`);
}
arbiter.setRelationConfig('member_of', { type: 'direct' });
arbiter.setRelationConfig('owner', { type: 'direct' });
arbiter.setRelationConfig('group_access', {
type: 'tuple_to_userset',
tuplesetRelation: 'owner',
computedRelation: 'member_of'
});
const startTime = Date.now();
// Test binary mode with complex rules
for (let i = 0; i < 100; i++) {
const userIndex = i % 20;
const docIndex = i % 20;
const result = arbiter.check(`user:user-${userIndex}`, 'group_access', `document:doc-${docIndex}`, { binary: true });
assert.ok(result.possibility >= 0);
}
const duration = Date.now() - startTime;
if (process.env.TEST_DEBUG === '1') console.log(`Binary mode complex rules: ${duration}ms for 100 checks`);
// Should complete within reasonable time
assert.ok(duration < 2000, `Binary mode too slow: ${duration}ms`);
});
});
describe('Memory Usage Performance', () => {
it('tracks memory usage during large operations', () => {
const initialMemory = process.memoryUsage();
// Create large dataset
const nodeCount = 500;
for (let i = 0; i < nodeCount; i++) {
arbiter.addNode(`user:user-${i}`, 'user');
arbiter.addNode(`group:group-${i}`, 'group');
arbiter.addNode(`document:doc-${i}`, 'document');
}
const afterNodesMemory = process.memoryUsage();
// Add relations
arbiter.setRelationConfig('member_of', { type: 'direct' });
arbiter.setRelationConfig('owner', { type: 'direct' });
for (let i = 0; i < nodeCount; i++) {
arbiter.addRelation(`user:user-${i}`, 'member_of', `group:group-${i}`);
arbiter.addRelation(`document:doc-${i}`, 'owner', `group:group-${i}`);
}
const afterRelationsMemory = process.memoryUsage();
// Perform authorization checks
for (let i = 0; i < 100; i++) {
arbiter.check(`user:user-${i % nodeCount}`, 'member_of', `group:group-${i % nodeCount}`);
}
const finalMemory = process.memoryUsage();
if (process.env.TEST_DEBUG === '1') console.log('Memory usage:');
if (process.env.TEST_DEBUG === '1') console.log(` Initial: ${Math.round(initialMemory.heapUsed / 1024 / 1024)}MB`);
if (process.env.TEST_DEBUG === '1') console.log(` After nodes: ${Math.round(afterNodesMemory.heapUsed / 1024 / 1024)}MB`);
if (process.env.TEST_DEBUG === '1') console.log(` After relations: ${Math.round(afterRelationsMemory.heapUsed / 1024 / 1024)}MB`);
if (process.env.TEST_DEBUG === '1') console.log(` Final: ${Math.round(finalMemory.heapUsed / 1024 / 1024)}MB`);
// Memory usage should be reasonable
const memoryIncrease = finalMemory.heapUsed - initialMemory.heapUsed;
const memoryIncreaseMB = memoryIncrease / 1024 / 1024;
assert.ok(memoryIncreaseMB < 100, `Memory usage too high: ${memoryIncreaseMB}MB increase`);
});
});
describe('Concurrent Performance', () => {
it('handles concurrent authorization requests efficiently', async () => {
// Set up test data
for (let i = 0; i < 100; i++) {
arbiter.addNode(`user:user-${i}`, 'user');
arbiter.addNode(`document:doc-${i}`, 'document');
arbiter.addRelation(`user:user-${i}`, 'can_read', `document:doc-${i}`, { possibility: 1.0 });
}
arbiter.setRelationConfig('can_read', { type: 'direct' });
const startTime = Date.now();
// Create concurrent authorization requests
const promises = [];
for (let i = 0; i < 50; i++) {
promises.push(
new Promise((resolve) => {
const result = arbiter.check(`user:user-${i}`, 'can_read', `document:doc-${i}`);
resolve(result);
})
);
}
const results = await Promise.all(promises);
const duration = Date.now() - startTime;
if (process.env.TEST_DEBUG === '1') console.log(`Concurrent authorization: ${duration}ms for 50 requests`);
// All results should be successful
for (const result of results) {
assert.equal(result.possibility, 1.0);
}
// Should complete within reasonable time
assert.ok(duration < 1000, `Concurrent authorization too slow: ${duration}ms`);
});
});
});