import { EDR_FREIGHT_PERMISSIONS } from './edr-freight.seed'; import { NOTIFICATION_PERMISSIONS, NOTIFICATION_PERMISSION_ANCHORS, readTwinOf, } from './freight-permissions.registry'; describe('EDR_FREIGHT_PERMISSIONS', () => { // The seeder inserts the whole catalog in one ON CONFLICT (key) DO UPDATE // statement — a duplicated key there is a Postgres 21000 at boot, not a // silent no-op. it('has no duplicate keys', () => { const keys = EDR_FREIGHT_PERMISSIONS.map((permission) => permission.key); const duplicates = [...new Set(keys.filter((key, i) => keys.indexOf(key) !== i))]; expect(duplicates).toEqual([]); }); // The `:read` ids are derived rather than hand-written, so a collision // would surface here instead of as a primary-key violation on a fresh // database. it('has no duplicate ids', () => { const ids = EDR_FREIGHT_PERMISSIONS.map((permission) => permission.id); const duplicates = [...new Set(ids.filter((id, i) => ids.indexOf(id) !== i))]; expect(duplicates).toEqual([]); }); // `:read` grants a module's GET routes without putting it in the backoffice // sidebar. Every `:view` needs its twin or that module has no way to be // granted API-only access. it('gives every :view key a :read twin', () => { const keys = new Set(EDR_FREIGHT_PERMISSIONS.map((p) => p.key)); const missing = [...keys] .filter((key) => key.endsWith(':view')) .map((key) => readTwinOf(key)) .filter((twin): twin is string => twin !== null && !keys.has(twin)); expect(missing).toEqual([]); }); it('mints every :read id in the v5 block', () => { const reads = EDR_FREIGHT_PERMISSIONS.filter((p) => p.key.endsWith(':read')); expect(reads.length).toBeGreaterThan(0); // Version nibble 5 — the source `:view` ids are all v4, so the two sets // cannot overlap however many keys are added. for (const read of reads) { expect(read.id).toMatch( /^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/, ); } }); }); describe('NOTIFICATION_PERMISSION_ANCHORS', () => { const catalog = new Set(EDR_FREIGHT_PERMISSIONS.map((p) => p.key)); // FreightNotificationPermissionsSeeder backfills each get_notification key // onto whoever holds its anchor. A typo'd anchor matches no permission row, // so the key is granted to nobody and every notification on that desk // silently resolves to zero recipients — notify() logs that at debug level. it('anchors every notification key on keys that exist', () => { const missing = Object.values(NOTIFICATION_PERMISSION_ANCHORS) .flat() .filter((key) => !catalog.has(key)); expect(missing).toEqual([]); }); it('seeds every notification key into the catalog', () => { const missing = NOTIFICATION_PERMISSIONS.map((p) => p.key).filter( (key) => !catalog.has(key), ); expect(missing).toEqual([]); }); it('gives every notification key an anchor to backfill from', () => { const anchored = new Set(Object.keys(NOTIFICATION_PERMISSION_ANCHORS)); const unanchored = NOTIFICATION_PERMISSIONS.map((p) => p.key).filter( (key) => !anchored.has(key), ); expect(unanchored).toEqual([]); }); });