d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 1 | import assert from 'node:assert/strict'; |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 2 | import { test } from 'node:test'; |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 3 | import { allowed, groupMembers, isMuted, levelOf, parsePowerLevels } from '../src/lib/permissions.ts'; |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 4 | |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 5 | const levels = parsePowerLevels({ |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 6 | ban: 50, |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 7 | kick: 50, |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 8 | events_default: 0, |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 9 | state_default: 50, |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 10 | users_default: 0, |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 11 | users: [['@mod:x', 50], ['@admin:x', 100], ['@muted:x', -1]], |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 12 | events: [['m.room.power_levels', 100]], |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 13 | creators: ['@creator:x'] |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 14 | })!; |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 15 | |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 16 | test('levels come from users, the default, or creator rank', () => { |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 17 | assert.equal(levelOf(levels, '@admin:x'), 100); |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 18 | assert.equal(levelOf(levels, '@someone:x'), 0); |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 19 | assert.equal(levelOf(levels, '@creator:x'), Infinity); |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 20 | assert.equal(isMuted(levels, '@muted:x'), true); |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 21 | assert.equal(isMuted(levels, '@someone:x'), false); |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 22 | }); |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 23 | |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 24 | test('you can only act on people below you', () => { |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 25 | assert.deepEqual(allowed(levels, '@mod:x', '@someone:x'), { kick: true, ban: true, setLevel: false, maxLevel: 50 }); |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 26 | assert.equal(allowed(levels, '@mod:x', '@admin:x').kick, false); |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 27 | assert.equal(allowed(levels, '@mod:x', '@mod:x').kick, false); |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 28 | assert.equal(allowed(levels, '@admin:x', '@mod:x').setLevel, true); |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 29 | assert.equal(allowed(levels, '@admin:x', '@creator:x').ban, false); |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 30 | assert.equal(allowed(levels, '@creator:x', '@admin:x').setLevel, true); |
d8624c214c04 | atagen | 2026-10-24 08:24 UTC | 31 | }); |
0380cd8c765c | atagen | 2026-10-24 09:08 UTC | 32 | |
0380cd8c765c | atagen | 2026-10-24 09:08 UTC | 33 | test('anyone can delete their own messages but others need the redact level', async () => { |
0380cd8c765c | atagen | 2026-10-24 09:08 UTC | 34 | const { canRedact } = await import('../src/lib/permissions.ts'); |
0380cd8c765c | atagen | 2026-10-24 09:08 UTC | 35 | assert.equal(canRedact(levels, '@someone:x', '@someone:x'), true); |
0380cd8c765c | atagen | 2026-10-24 09:08 UTC | 36 | assert.equal(canRedact(levels, '@someone:x', '@mod:x'), false); |
0380cd8c765c | atagen | 2026-10-24 09:08 UTC | 37 | assert.equal(canRedact(levels, '@mod:x', '@someone:x'), true); |
0380cd8c765c | atagen | 2026-10-24 09:08 UTC | 38 | assert.equal(canRedact(levels, '@muted:x', '@muted:x'), false); |
0380cd8c765c | atagen | 2026-10-24 09:08 UTC | 39 | }); |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 40 | |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 41 | test('members group by role, highest first, invites last', () => { |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 42 | const m = (user_id: string, name: string, membership = 'Joined') => ({ user_id, name, display_name: null, membership }); |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 43 | const groups = groupMembers( |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 44 | [m('@someone:x', 'zed'), m('@invitee:x', 'ivy', 'Invited'), m('@mod:x', 'mod'), m('@creator:x', 'cr'), m('@admin:x', 'ad'), m('@b:x', 'Abe'), m('@gone:x', 'gone', 'Left')], |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 45 | levels |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 46 | ); |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 47 | assert.deepEqual( |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 48 | groups.map((g) => [g.label, g.members.map((x) => x.user_id)]), |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 49 | [ |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 50 | ['Creator', ['@creator:x']], |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 51 | ['Admin', ['@admin:x']], |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 52 | ['Moderator', ['@mod:x']], |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 53 | ['Members', ['@b:x', '@someone:x']], |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 54 | ['Invited', ['@invitee:x']] |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 55 | ] |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 56 | ); |
dfd1feaae855 | atagen | 2026-10-24 10:54 UTC | 57 | }); |