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,124 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { describe, test } from 'node:test';
|
||||
import { OWAFusion } from '../../src/utils/OWAFusion.js';
|
||||
|
||||
function createRng(seed) {
|
||||
let state = seed >>> 0;
|
||||
return () => {
|
||||
state = (1664525 * state + 1013904223) >>> 0;
|
||||
return state / 0x100000000;
|
||||
};
|
||||
}
|
||||
|
||||
function randInt(rng, max) {
|
||||
return Math.floor(rng() * max);
|
||||
}
|
||||
|
||||
function randFloat(rng, min = 0, max = 1) {
|
||||
return min + (max - min) * rng();
|
||||
}
|
||||
|
||||
function approxEqual(a, b, eps = 1e-6) {
|
||||
return Math.abs(a - b) <= eps;
|
||||
}
|
||||
|
||||
describe('OWA aggregation properties', () => {
|
||||
test('bounded and translation properties across modes', () => {
|
||||
const rng = createRng(42);
|
||||
const modes = ['max', 'min', 'average', 'majority', 'median', 'optimistic', 'pessimistic', 'top2', 'top3'];
|
||||
const iterations = 200;
|
||||
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
const length = randInt(rng, 8) + 1;
|
||||
const values = new Array(length).fill(0).map(() => randFloat(rng, -50, 50));
|
||||
const metas = new Array(length).fill(null);
|
||||
const min = Math.min(...values);
|
||||
const max = Math.max(...values);
|
||||
|
||||
for (const mode of modes) {
|
||||
const weights = OWAFusion.generateOWAWeights(length, mode, null, true);
|
||||
const result = OWAFusion.fuseWithMeta(values, metas, weights, mode, true).value;
|
||||
assert.ok(result >= min - 1e-6 && result <= max + 1e-6, `mode ${mode} bounds`);
|
||||
|
||||
const weightSum = weights.reduce((sum, w) => sum + w, 0);
|
||||
if (approxEqual(weightSum, 1.0)) {
|
||||
const delta = randFloat(rng, -10, 10);
|
||||
const shifted = values.map(v => v + delta);
|
||||
const shiftedResult = OWAFusion.fuseWithMeta(shifted, metas, weights, mode, true).value;
|
||||
assert.ok(approxEqual(shiftedResult - result, delta, 1e-5), `mode ${mode} translation`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('extreme strategies behave as expected', () => {
|
||||
const rng = createRng(7);
|
||||
for (let i = 0; i < 200; i++) {
|
||||
const length = randInt(rng, 8) + 1;
|
||||
const values = new Array(length).fill(0).map(() => randFloat(rng, -20, 20));
|
||||
const metas = new Array(length).fill(null);
|
||||
const max = Math.max(...values);
|
||||
const min = Math.min(...values);
|
||||
|
||||
const maxWeights = OWAFusion.generateOWAWeights(length, 'max', null, true);
|
||||
const minWeights = OWAFusion.generateOWAWeights(length, 'min', null, true);
|
||||
const maxResult = OWAFusion.fuseWithMeta(values, metas, maxWeights, 'max', true).value;
|
||||
const minResult = OWAFusion.fuseWithMeta(values, metas, minWeights, 'min', true).value;
|
||||
assert.ok(approxEqual(maxResult, max, 1e-6), 'max aggregator');
|
||||
assert.ok(approxEqual(minResult, min, 1e-6), 'min aggregator');
|
||||
}
|
||||
});
|
||||
|
||||
test('custom weights respect convex combination', () => {
|
||||
const rng = createRng(13);
|
||||
for (let i = 0; i < 200; i++) {
|
||||
const length = randInt(rng, 8) + 1;
|
||||
const values = new Array(length).fill(0).map(() => randFloat(rng, -100, 100));
|
||||
const metas = new Array(length).fill(null);
|
||||
|
||||
let weights = new Array(length).fill(0).map(() => randFloat(rng, 0, 1));
|
||||
const sum = weights.reduce((a, b) => a + b, 0) || 1;
|
||||
weights = weights.map(w => w / sum);
|
||||
|
||||
const min = Math.min(...values);
|
||||
const max = Math.max(...values);
|
||||
const result = OWAFusion.fuseWithMeta(values, metas, weights, 'custom', true).value;
|
||||
assert.ok(result >= min - 1e-6 && result <= max + 1e-6, 'custom convex bounds');
|
||||
}
|
||||
});
|
||||
|
||||
test('sum weights returns total', () => {
|
||||
const rng = createRng(99);
|
||||
for (let i = 0; i < 200; i++) {
|
||||
const length = randInt(rng, 8) + 1;
|
||||
const values = new Array(length).fill(0).map(() => randFloat(rng, -5, 5));
|
||||
const metas = new Array(length).fill(null);
|
||||
const weights = OWAFusion.generateOWAWeights(length, 'sum', null, false);
|
||||
const result = OWAFusion.fuseWithMeta(values, metas, weights, 'sum', false).value;
|
||||
const expected = values.reduce((a, b) => a + b, 0);
|
||||
assert.ok(approxEqual(result, expected, 1e-6), 'sum matches total');
|
||||
}
|
||||
});
|
||||
|
||||
test('sum_unbounded matches sum', () => {
|
||||
const rng = createRng(101);
|
||||
for (let i = 0; i < 200; i++) {
|
||||
const length = randInt(rng, 8) + 1;
|
||||
const values = new Array(length).fill(0).map(() => randFloat(rng, -5, 5));
|
||||
const metas = new Array(length).fill(null);
|
||||
const sumWeights = OWAFusion.generateOWAWeights(length, 'sum', null, false);
|
||||
const unboundedWeights = OWAFusion.generateOWAWeights(length, 'sum_unbounded', null, false);
|
||||
const sumResult = OWAFusion.fuseWithMeta(values, metas, sumWeights, 'sum', false).value;
|
||||
const unboundedResult = OWAFusion.fuseWithMeta(values, metas, unboundedWeights, 'sum_unbounded', false).value;
|
||||
assert.ok(approxEqual(sumResult, unboundedResult, 1e-6), 'sum_unbounded matches sum');
|
||||
}
|
||||
});
|
||||
|
||||
test('priority weights are normalized proportions', () => {
|
||||
const priorities = [1, 5, 3];
|
||||
const weights = OWAFusion.generateOWAWeights(priorities.length, 'priority', priorities, true);
|
||||
const total = weights.reduce((sum, w) => sum + w, 0);
|
||||
assert.ok(approxEqual(total, 1.0, 1e-6));
|
||||
assert.ok(weights[1] > weights[2] && weights[2] > weights[0], 'weights follow priorities');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user