import { describe, test } from 'node:test'; import assert from 'node:assert/strict'; import fc from 'fast-check'; import { Arbiter } from '../../src/core/Arbiter.js'; describe('Fast-check: chain rule monotonicity', () => { test('adding edges does not reduce access', () => { fc.assert( fc.property( fc.integer({ min: 1, max: 5 }), fc.integer({ min: 1, max: 5 }), fc.integer({ min: 1, max: 5 }), fc.array( fc.record({ user: fc.integer({ min: 0, max: 4 }), group: fc.integer({ min: 0, max: 4 }) }), { minLength: 1, maxLength: 20 } ), fc.array( fc.record({ group: fc.integer({ min: 0, max: 4 }), doc: fc.integer({ min: 0, max: 4 }) }), { minLength: 1, maxLength: 20 } ), (userCount, groupCount, docCount, memberships, viewers) => { const arbiter = new Arbiter(); arbiter.setRelationConfig('member', { type: 'direct' }); arbiter.setRelationConfig('viewer', { type: 'direct' }); arbiter.setRelationConfig('can_view', { type: 'chain', tuple: 'member', computed: 'viewer' }); for (let u = 0; u < userCount; u++) arbiter.addNode(`user:${u}`, 'user'); for (let g = 0; g < groupCount; g++) arbiter.addNode(`group:${g}`, 'group'); for (let d = 0; d < docCount; d++) arbiter.addNode(`doc:${d}`, 'doc'); for (const edge of memberships) { const u = edge.user % userCount; const g = edge.group % groupCount; arbiter.addRelation(`user:${u}`, 'member', `group:${g}`, 1.0); } for (const edge of viewers) { const g = edge.group % groupCount; const d = edge.doc % docCount; arbiter.addRelation(`group:${g}`, 'viewer', `doc:${d}`, 1.0); } const baseline = []; for (let u = 0; u < userCount; u++) { for (let d = 0; d < docCount; d++) { const result = arbiter.check(`user:${u}`, 'can_view', `doc:${d}`); baseline.push(result.possibility); } } for (let u = 0; u < userCount; u++) { for (let g = 0; g < groupCount; g++) { arbiter.addRelation(`user:${u}`, 'member', `group:${g}`, 1.0); } } let idx = 0; for (let u = 0; u < userCount; u++) { for (let d = 0; d < docCount; d++) { const result = arbiter.check(`user:${u}`, 'can_view', `doc:${d}`); assert.ok(result.possibility >= baseline[idx]); idx++; } } } ), { numRuns: 30 } ); }); });