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: tuple-to-userset invariants', () => { test('tuple_to_userset matches model', () => { 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({ doc: fc.integer({ min: 0, max: 4 }), group: fc.integer({ min: 0, max: 4 }) }), { minLength: 0, maxLength: 20 } ), fc.array( fc.record({ user: fc.integer({ min: 0, max: 4 }), group: fc.integer({ min: 0, max: 4 }) }), { minLength: 0, maxLength: 20 } ), (userCount, groupCount, docCount, owners, members) => { const arbiter = new Arbiter(); 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'); arbiter.setRelationConfig('owner', { type: 'direct' }); arbiter.setRelationConfig('member', { type: 'direct' }); arbiter.setRelationConfig('access', { type: 'tuple_to_userset', tuplesetRelation: 'owner', computedRelation: 'member' }); const ownersByDoc = new Map(); const membersByGroup = new Map(); for (const edge of owners) { const doc = edge.doc % docCount; const group = edge.group % groupCount; arbiter.addRelation(`doc:${doc}`, 'owner', `group:${group}`, 1.0); const set = ownersByDoc.get(doc) || new Set(); set.add(group); ownersByDoc.set(doc, set); } for (const edge of members) { const user = edge.user % userCount; const group = edge.group % groupCount; arbiter.addRelation(`user:${user}`, 'member', `group:${group}`, 1.0); const set = membersByGroup.get(group) || new Set(); set.add(user); membersByGroup.set(group, set); } for (let u = 0; u < userCount; u++) { for (let d = 0; d < docCount; d++) { const result = arbiter.check(`user:${u}`, 'access', `doc:${d}`); const groups = ownersByDoc.get(d) || new Set(); let expected = false; for (const group of groups) { const membersSet = membersByGroup.get(group) || new Set(); if (membersSet.has(u)) { expected = true; break; } } assert.strictEqual(result.possibility > 0, expected); } } } ), { numRuns: 30 } ); }); });