import { test } from 'node:test'; import assert from 'node:assert/strict'; import { declineInvite } from './matrix'; // declineInvite must only leave when we're still in the room (invite/join/knock) // — re-leaving an already-left remote room is what Synapse 500s on — and must // always forget afterwards (best-effort) so the ghost invite can't linger. const makeMx = ( membership: string | undefined, opts: { leaveRejects?: boolean; forgetRejects?: boolean } = {}, ) => { const calls: string[] = []; const mx = { getRoom: () => (membership === undefined ? null : { getMyMembership: () => membership }), leave: async () => { calls.push('leave'); if (opts.leaveRejects) throw new Error('leave failed'); return {}; }, forget: async () => { calls.push('forget'); if (opts.forgetRejects) throw new Error('forget failed'); return {}; }, // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any; return { mx, calls }; }; test('declineInvite: invite → leave then forget', async () => { const { mx, calls } = makeMx('invite'); await declineInvite(mx, '!r:s'); assert.deepEqual(calls, ['leave', 'forget']); }); test('declineInvite: already left → skips leave, still forgets (no double-leave 500)', async () => { const { mx, calls } = makeMx('leave'); await declineInvite(mx, '!r:s'); assert.deepEqual(calls, ['forget']); }); test('declineInvite: join → leave then forget', async () => { const { mx, calls } = makeMx('join'); await declineInvite(mx, '!r:s'); assert.deepEqual(calls, ['leave', 'forget']); }); test('declineInvite: no room object → only forget', async () => { const { mx, calls } = makeMx(undefined); await declineInvite(mx, '!r:s'); assert.deepEqual(calls, ['forget']); }); test('declineInvite: forget failure is swallowed (best-effort)', async () => { const { mx, calls } = makeMx('invite', { forgetRejects: true }); await declineInvite(mx, '!r:s'); // resolves despite forget throwing assert.deepEqual(calls, ['leave', 'forget']); }); test('declineInvite: genuine leave failure rejects', async () => { const { mx } = makeMx('invite', { leaveRejects: true }); await assert.rejects(() => declineInvite(mx, '!r:s'), /leave failed/); });