d095131eec0e | atagen | 2026-10-24 08:39 UTC | 1 | import assert from 'node:assert/strict'; |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 2 | import { test } from 'node:test'; |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 3 | import { clonePackDestinations, cloneRequest, editableChoices, findSticker, freePackStateKey, packsForRoom, packSlug, reactionShortcode, shortcodeFor, stickersOf } from '../src/lib/packs.ts'; |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 4 | |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 5 | const image = (shortcode: string, usage: ('Emoticon' | 'Sticker')[]) => ({ |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 6 | shortcode, |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 7 | media_id: `mxc://x/${shortcode}`, |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 8 | reaction_key: `mxc://x/${shortcode}`, |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 9 | body: null, |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 10 | mimetype: null, |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 11 | width: null, |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 12 | height: null, |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 13 | usage |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 14 | }); |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 15 | |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 16 | const roomPack = (state_key: string, editable: boolean) => ({ |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 17 | id: `room:!r:x:${state_key}`, |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 18 | display_name: state_key, |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 19 | avatar: null, |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 20 | attribution: null, |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 21 | source: { Room: { room_id: '!r:x', state_key } }, |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 22 | images: [image('a', ['Sticker']), image('b', ['Emoticon']), image('c', [])], |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 23 | editable, |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 24 | enabled_everywhere: false |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 25 | }); |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 26 | |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 27 | test('shortcodes come from file names and never clash', () => { |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 28 | const taken = new Set(['cat']); |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 29 | assert.equal(shortcodeFor('Cat.PNG', taken), 'cat_2'); |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 30 | assert.equal(shortcodeFor('my cool sticker!.webp', taken), 'my_cool_sticker'); |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 31 | assert.equal(shortcodeFor('.png', taken), 'image'); |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 32 | assert.equal(packSlug('Cats & Dogs'), 'cats-dogs'); |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 33 | }); |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 34 | |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 35 | test('stickers include images with no usage of their own', () => { |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 36 | assert.deepEqual(stickersOf(roomPack('s', true)).map((i) => i.shortcode), ['a', 'c']); |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 37 | }); |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 38 | |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 39 | test('custom reaction nicknames fall back to known packs without changing the reaction key', () => { |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 40 | const packs = [roomPack('mine', true)]; |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 41 | assert.equal(reactionShortcode('mxc://x/a', undefined, packs), 'a'); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 42 | assert.equal(reactionShortcode('mxc://x/a', ' preferred ', packs), 'preferred'); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 43 | assert.equal(reactionShortcode('mxc://x/unknown', undefined, packs), undefined); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 44 | assert.equal(reactionShortcode('👍', 'thumbsup', packs), undefined); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 45 | }); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 46 | |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 47 | test('sticker commands resolve names without matching emoticons or choosing ambiguous stickers', () => { |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 48 | const pack = roomPack('s', true); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 49 | assert.equal(findSticker([pack], 'A').sticker.shortcode, 'a'); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 50 | assert.equal(findSticker([pack], 'c').pack, pack); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 51 | assert.throws(() => findSticker([pack], 'b'), /No sticker named b/); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 52 | assert.throws(() => findSticker([pack], 'missing'), /No sticker named missing/); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 53 | assert.throws(() => findSticker([pack, roomPack('other', false)], 'a'), /More than one sticker/); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 54 | }); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 55 | |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 56 | test('your pack is always a destination, then editable room packs only', () => { |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 57 | const packs = { global: [], rooms: { '!r:x': [roomPack('mine', true), roomPack('theirs', false)] } }; |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 58 | const choices = editableChoices(packs, () => 'Room'); |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 59 | assert.deepEqual(choices.map((c) => c.target), [{ kind: 'user' }, { kind: 'room', room_id: '!r:x', state_key: 'mine' }]); |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 60 | const dests = clonePackDestinations(roomPack('Fancy Pack', false), [{ room_id: '!d:x', name: 'Dest' }], () => 'Room'); |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 61 | assert.deepEqual(dests[1].target, { kind: 'room', room_id: '!d:x', state_key: 'fancy-pack' }); |
d095131eec0e | atagen | 2026-10-24 08:39 UTC | 62 | }); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 63 | |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 64 | test('new pack names avoid existing keys without changing merge destinations', () => { |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 65 | const packs = { global: [], rooms: { '!r:x': [roomPack('cats', true), roomPack('cats-2', true)] } }; |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 66 | assert.equal(freePackStateKey(packs, '!r:x', 'Cats'), 'cats-3'); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 67 | assert.equal(freePackStateKey(packs, '!other:x', 'Cats'), 'cats'); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 68 | const destination = editableChoices(packs, () => 'Room')[1]; |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 69 | const request = cloneRequest({ kind: 'pack', pack: roomPack('source', false) }, destination.target, ['Sticker', 'Emoticon'], null, destination.pack); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 70 | assert.deepEqual(request, { op: 'clone_pack', from_pack: 'room:!r:x:source', to: destination.target, display_name: null, usage: ['Sticker', 'Emoticon'] }); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 71 | }); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 72 | |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 73 | test('image and event clones keep explicit usage and event names avoid collisions', () => { |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 74 | const pack = roomPack('source', false); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 75 | const to = { kind: 'user' as const }; |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 76 | assert.deepEqual(cloneRequest({ kind: 'image', pack, image: pack.images[1] }, to, ['Sticker'], null, null), { |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 77 | op: 'clone_pack_image', from_pack: pack.id, shortcode: 'b', to, usage: ['Sticker'] |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 78 | }); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 79 | const source = { kind: 'sticker' as const, room_id: '!r:x', event_id: '$event', body: 'a' }; |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 80 | assert.deepEqual(cloneRequest(source, to, ['Sticker', 'Emoticon'], null, pack), { |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 81 | op: 'clone_sticker', room_id: '!r:x', event_id: '$event', shortcode: 'a_2', to, usage: ['Sticker', 'Emoticon'] |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 82 | }); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 83 | assert.throws(() => cloneRequest(source, to, [], null, pack), /at least one use/); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 84 | }); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 85 | |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 86 | test('channel packs include all ancestor spaces once and do not cross to unrelated spaces', () => { |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 87 | const local = roomPack('local', false); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 88 | const parent = { ...roomPack('parent', false), id: 'space-pack' }; |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 89 | const ancestor = { ...roomPack('ancestor', false), id: 'ancestor-pack' }; |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 90 | const unrelated = { ...roomPack('unrelated', false), id: 'unrelated-pack' }; |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 91 | const global = { ...roomPack('global', false), id: 'global-pack' }; |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 92 | const packs = { global: [global, parent], rooms: { '!r:x': [local], '!s:x': [parent], '!a:x': [ancestor], '!u:x': [unrelated] } }; |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 93 | const rooms = [ |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 94 | { room_id: '!r:x', parent_spaces: ['!s:x'], children: [], space: false }, |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 95 | { room_id: '!s:x', parent_spaces: ['!a:x'], children: ['!r:x'], space: true }, |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 96 | { room_id: '!a:x', parent_spaces: ['!s:x'], children: ['!s:x'], space: true }, |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 97 | { room_id: '!u:x', parent_spaces: [], children: [], space: true } |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 98 | ]; |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 99 | assert.deepEqual(packsForRoom(packs, '!r:x', rooms).map((pack) => pack.id), [local.id, parent.id, ancestor.id, global.id]); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 100 | assert.deepEqual(packsForRoom(packs, '!r:x', [{ ...rooms[0], parent_spaces: [] }, ...rooms.slice(1)]).map((pack) => pack.id), [local.id, parent.id, ancestor.id, global.id]); |
132eeceeb362 | atagen | 2026-10-25 03:34 UTC | 101 | }); |