90 lines
3.2 KiB
JavaScript
90 lines
3.2 KiB
JavaScript
|
|
import assert from 'node:assert/strict';
|
||
|
|
import { describe, test } from 'node:test';
|
||
|
|
import { Arbiter } from '../../src/core/Arbiter.js';
|
||
|
|
|
||
|
|
describe('Graph Structure and Queries', () => {
|
||
|
|
test('basic node and relation creation', () => {
|
||
|
|
const arbiter = new Arbiter();
|
||
|
|
|
||
|
|
arbiter.addNode('user:1', 'user');
|
||
|
|
arbiter.addNode('resource:1', 'resource');
|
||
|
|
|
||
|
|
const nodeId1 = arbiter.resolveNodeId('user:1');
|
||
|
|
const nodeId2 = arbiter.resolveNodeId('resource:1');
|
||
|
|
|
||
|
|
assert.ok(nodeId1 !== undefined, 'user node ID resolved');
|
||
|
|
assert.ok(nodeId2 !== undefined, 'resource node ID resolved');
|
||
|
|
assert.strictEqual(arbiter.resolveKey(nodeId1), 'user:1', 'reverse key lookup works');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('direct relation authorization', () => {
|
||
|
|
const arbiter = new Arbiter();
|
||
|
|
|
||
|
|
arbiter.addNode('user:1', 'user');
|
||
|
|
arbiter.addNode('resource:1', 'resource');
|
||
|
|
arbiter.setRelationConfig('owner', { type: 'direct' });
|
||
|
|
|
||
|
|
arbiter.addRelation('user:1', 'owner', 'resource:1', 1.0);
|
||
|
|
|
||
|
|
const result = arbiter.check('user:1', 'owner', 'resource:1');
|
||
|
|
assert.strictEqual(result.possibility, 1.0, 'owner relation grants access');
|
||
|
|
assert.strictEqual(result.reason, 'direct_match', 'reason is direct_match');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('absence of relation denies access', () => {
|
||
|
|
const arbiter = new Arbiter();
|
||
|
|
|
||
|
|
arbiter.addNode('user:1', 'user');
|
||
|
|
arbiter.addNode('resource:1', 'resource');
|
||
|
|
arbiter.addNode('user:2', 'user');
|
||
|
|
arbiter.setRelationConfig('owner', { type: 'direct' });
|
||
|
|
|
||
|
|
arbiter.addRelation('user:1', 'owner', 'resource:1', 1.0);
|
||
|
|
|
||
|
|
const result = arbiter.check('user:2', 'owner', 'resource:1');
|
||
|
|
assert.strictEqual(result.possibility, 0.0, 'non-owner denied');
|
||
|
|
assert.strictEqual(result.reason, 'no_relation', 'reason is no_relation');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('node data storage and retrieval', () => {
|
||
|
|
const arbiter = new Arbiter();
|
||
|
|
|
||
|
|
arbiter.addNode('user:1', 'user', { tier: 'premium', level: 5 });
|
||
|
|
const data = arbiter.getNodeData('user:1');
|
||
|
|
|
||
|
|
assert.strictEqual(data.tier, 'premium');
|
||
|
|
assert.strictEqual(data.level, 5);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('node data update', () => {
|
||
|
|
const arbiter = new Arbiter();
|
||
|
|
|
||
|
|
arbiter.addNode('user:1', 'user', { tier: 'premium' });
|
||
|
|
arbiter.updateNodeData('user:1', { tier: 'enterprise', level: 10 });
|
||
|
|
|
||
|
|
const data = arbiter.getNodeData('user:1');
|
||
|
|
assert.strictEqual(data.tier, 'enterprise');
|
||
|
|
assert.strictEqual(data.level, 10);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('multiple relations between different node types', () => {
|
||
|
|
const arbiter = new Arbiter();
|
||
|
|
|
||
|
|
arbiter.addNode('user:1', 'user');
|
||
|
|
arbiter.addNode('group:1', 'group');
|
||
|
|
arbiter.addNode('resource:1', 'resource');
|
||
|
|
|
||
|
|
arbiter.setRelationConfig('member', { type: 'direct' });
|
||
|
|
arbiter.setRelationConfig('can_view', { type: 'direct' });
|
||
|
|
|
||
|
|
arbiter.addRelation('user:1', 'member', 'group:1', 1.0);
|
||
|
|
arbiter.addRelation('group:1', 'can_view', 'resource:1', 1.0);
|
||
|
|
|
||
|
|
const memberResult = arbiter.check('user:1', 'member', 'group:1');
|
||
|
|
const viewResult = arbiter.check('group:1', 'can_view', 'resource:1');
|
||
|
|
|
||
|
|
assert.strictEqual(memberResult.possibility, 1.0);
|
||
|
|
assert.strictEqual(viewResult.possibility, 1.0);
|
||
|
|
});
|
||
|
|
});
|