import { strict as assert } from 'node:assert'; import { describe, it } from 'node:test'; import { TEAM_ROLES, TEAMS, type Team, type TeamRole } from '../src/ontology'; import { CAPABILITIES, permissionGranted, resolvePermissionGrants, resolveReadPermissionGrants, resolveWritePermissionGrants, roleMeets, type Capability, type PermissionSubject, } from '../src/permissions'; describe('role permissions', () => { it('keeps deal writes on the side where the person is a member', () => { const grants = resolvePermissionGrants({ isPlatformAdmin: false, teams: [{ team: 'demand', role: 'member' }], }); assert.equal(permissionGranted(grants, 'deal:write', 'demand'), true); assert.equal(permissionGranted(grants, 'deal:write', 'supply'), false); assert.equal(permissionGranted(grants, 'commitment:write', 'demand'), false); }); it('allows supply leads to commit capacity without letting them sign contracts', () => { const grants = resolvePermissionGrants({ isPlatformAdmin: false, teams: [{ team: 'supply', role: 'lead' }], }); assert.equal(permissionGranted(grants, 'commitment:write', 'supply'), true); assert.equal(permissionGranted(grants, 'contract:sign', 'supply'), false); }); it('keeps signing and bulk import at team-admin level', () => { const grants = resolvePermissionGrants({ isPlatformAdmin: false, teams: [{ team: 'demand', role: 'admin' }], }); assert.equal(permissionGranted(grants, 'contract:sign', 'demand'), true); assert.equal(permissionGranted(grants, 'contract:sign', 'supply'), false); assert.equal(permissionGranted(grants, 'data:import', 'demand'), true); assert.equal(permissionGranted(grants, 'data:import', 'research'), false); assert.equal(permissionGranted(grants, 'settings:admin'), false); }); it('gives platform admins global grants without synthetic team memberships', () => { const grants = resolvePermissionGrants({ isPlatformAdmin: true, teams: [] }); assert.equal(permissionGranted(grants, 'deal:write', 'demand'), true); assert.equal(permissionGranted(grants, 'commitment:write', 'supply'), true); assert.equal(permissionGranted(grants, 'data:import', 'research'), true); assert.equal(permissionGranted(grants, 'settings:admin'), true); }); }); describe('the three authorities that used to be data:import', () => { it('does not let a research admin rewrite a commercial book', () => { const grants = resolvePermissionGrants({ isPlatformAdmin: false, teams: [{ team: 'research', role: 'admin' }], }); assert.equal(permissionGranted(grants, 'fact:review', 'research'), true); assert.equal(permissionGranted(grants, 'data:import', 'demand'), false); assert.equal(permissionGranted(grants, 'data:import', 'supply'), false); }); it('does not let a commercial admin approve a claim about a person', () => { const grants = resolvePermissionGrants({ isPlatformAdmin: false, teams: [{ team: 'supply', role: 'admin' }], }); assert.equal(permissionGranted(grants, 'data:import', 'supply'), true); assert.equal(permissionGranted(grants, 'integration:connect', 'supply'), true); // Fact review lives on research alone; being a supply admin buys nothing. assert.equal(permissionGranted(grants, 'fact:review', 'research'), false); }); }); describe('reads', () => { it('resolves read grants platform-wide, never per team', () => { const grants = resolveReadPermissionGrants({ isPlatformAdmin: false, teams: [{ team: 'demand', role: 'member' }], }); // A team-scoped read grant would be a promise the query layer does not // keep: `/api/contracts` returns supply paper to a demand reader either // way. See READ_CAPABILITIES. assert.deepEqual( grants, [ { capability: 'book:read', team: null }, { capability: 'economics:read', team: null }, { capability: 'team:read', team: null }, ], ); }); it('shows a research contractor the book but not what we pay for capacity', () => { const grants = resolveReadPermissionGrants({ isPlatformAdmin: false, teams: [{ team: 'research', role: 'lead' }], }); assert.equal(permissionGranted(grants, 'book:read'), true); assert.equal(permissionGranted(grants, 'team:read'), true); assert.equal(permissionGranted(grants, 'economics:read'), false); }); it('gives a viewer reads and no writes at all', () => { const subject: PermissionSubject = { isPlatformAdmin: false, teams: [{ team: 'demand', role: 'viewer' }], }; assert.deepEqual(resolveWritePermissionGrants(subject), []); assert.equal(permissionGranted(resolveReadPermissionGrants(subject), 'book:read'), true); // Cost economics are a commercial member's tool, not a reader's. assert.equal(permissionGranted(resolveReadPermissionGrants(subject), 'economics:read'), false); }); }); /** * The matrix is pure data, so pinning every cell is cheap — and it is the only * way a role added later cannot quietly inherit an authority nobody chose to * give it. Change a rule and this table tells you exactly which cells moved. */ describe('the whole role × capability matrix', () => { const EXPECTED: Readonly> = { viewer: ['book:read', 'team:read'], member: ['book:read', 'economics:read', 'team:read', 'deal:write', 'activity:write'], lead: [ 'book:read', 'economics:read', 'team:read', 'deal:write', 'activity:write', 'commitment:write', ], admin: [ 'book:read', 'economics:read', 'team:read', 'deal:write', 'activity:write', 'commitment:write', 'contract:sign', 'data:import', 'integration:connect', ], }; /** Held on the supply team, whose rules exercise every rank threshold. */ for (const role of TEAM_ROLES) { it(`grants a supply ${role} exactly the expected set`, () => { const grants = resolvePermissionGrants({ isPlatformAdmin: false, teams: [{ team: 'supply', role }], }); const held = CAPABILITIES.filter((capability) => grants.some((grant) => grant.capability === capability), ); assert.deepEqual(new Set(held), new Set(EXPECTED[role])); }); } it('gives research its own shape — evidence review, no commercial reach', () => { const grants = resolvePermissionGrants({ isPlatformAdmin: false, teams: [{ team: 'research', role: 'admin' }], }); const held = CAPABILITIES.filter((capability) => grants.some((grant) => grant.capability === capability), ); assert.deepEqual( new Set(held), new Set([ 'book:read', 'team:read', 'activity:write', 'data:import', 'fact:review', 'integration:connect', ]), ); }); it('never grants a lower rank something a higher rank on the same team lacks', () => { for (const team of TEAMS as readonly Team[]) { let previous = new Set(); for (const role of TEAM_ROLES) { const held = new Set( resolvePermissionGrants({ isPlatformAdmin: false, teams: [{ team, role }] }).map( (grant) => grant.capability, ), ); for (const capability of previous) { assert.ok(held.has(capability), `${team}/${role} lost ${capability} by promotion`); } previous = held; } } }); it('ranks viewer below member, which is what makes it safe to add', () => { assert.equal(roleMeets('viewer', 'member'), false); assert.equal(roleMeets('member', 'viewer'), true); assert.equal(roleMeets('admin', 'admin'), true); assert.equal(TEAM_ROLES[0], 'viewer'); }); });