// A space's policy goes out as a lexicon union and comes back as one. Both // directions are here because the editor reads a policy into a form and writes // the form back, and a mismatch between the two silently resets a field // nobody meant to touch. import { describe, expect, it } from 'vitest'; import { accessKind, allowedOf, isPublic, managingAppOf, policyKind, policyUnion, publishes, } from '#/lib/policy.js'; describe('policyKind', () => { it('reads each baseline policy', () => { for (const kind of ['personalspace', 'memberspace', 'managedspace']) { expect( policyKind({ policy: { $type: `com.atproto.${kind}.policy` } }), ).toBe(kind); } }); it('is null for a policy it does not know', () => { expect(policyKind({ policy: { $type: 'com.example.policy' } })).toBeNull(); expect(policyKind({})).toBeNull(); }); }); describe('reading a memberspace', () => { const config = { policy: { $type: 'com.atproto.memberspace.policy', publicRead: true, publicWrite: false, allowedApps: ['https://a.example/c.json', 7], publishMembers: true, }, }; it('reads its flags', () => { expect(isPublic(config, 'read')).toBe(true); expect(isPublic(config, 'write')).toBe(false); expect(publishes(config, 'publishMembers')).toBe(true); expect(publishes(config, 'publishPolicy')).toBe(false); }); it('reads its allow list, keeping only strings', () => { expect(accessKind(config)).toBe('allowList'); expect(allowedOf(config)).toEqual(['https://a.example/c.json']); }); it('is open with no allow list', () => { expect( accessKind({ policy: { $type: 'com.atproto.memberspace.policy' } }), ).toBe('open'); }); it('reads no public access off another policy', () => { expect( isPublic( { policy: { $type: 'com.atproto.managedspace.policy', publicRead: true, }, }, 'read', ), ).toBe(false); }); }); describe('managingAppOf', () => { it('reads the managing app of a managedspace', () => { expect( managingAppOf({ policy: { $type: 'com.atproto.managedspace.policy', managingApp: 'did:web:app.example#forum', }, }), ).toBe('did:web:app.example#forum'); expect(managingAppOf({})).toBeNull(); }); }); describe('policyUnion', () => { it('writes a memberspace with every field', () => { expect( policyUnion('memberspace', { publicRead: true, allowedApps: ['https://a.example/c.json'], managingApp: 'did:web:ignored.example', }), ).toEqual({ $type: 'com.atproto.memberspace.policy', publicRead: true, publicWrite: false, allowedApps: ['https://a.example/c.json'], publishPolicy: false, publishMembers: false, }); }); it('leaves the allow list out to let any app read', () => { expect( policyUnion('memberspace', { allowedApps: null }), ).not.toHaveProperty('allowedApps'); }); it('writes a managedspace and a personalspace', () => { expect( policyUnion('managedspace', { managingApp: 'did:web:app.example#forum', publishPolicy: true, }), ).toEqual({ $type: 'com.atproto.managedspace.policy', managingApp: 'did:web:app.example#forum', publishPolicy: true, }); expect(policyUnion('personalspace', { publicRead: true })).toEqual({ $type: 'com.atproto.personalspace.policy', }); }); it('round-trips through the readers', () => { const config = { policy: policyUnion('memberspace', { publicWrite: true, allowedApps: ['https://a.example/c.json'], publishPolicy: true, }), }; expect(policyKind(config)).toBe('memberspace'); expect(isPublic(config, 'write')).toBe(true); expect(allowedOf(config)).toEqual(['https://a.example/c.json']); expect(publishes(config, 'publishPolicy')).toBe(true); }); });