import { test } from 'node:test'; import assert from 'node:assert/strict'; import { buildReplyContent, pickReplySession, replyFailureText, replySendUrl, sendNotificationReply, } from './swReply'; test('thread replies carry the m.thread relation, room replies do not', () => { assert.deepEqual(buildReplyContent('hi'), { msgtype: 'm.text', body: 'hi', 'm.mentions': {} }); assert.deepEqual(buildReplyContent('hi', '$root')['m.relates_to'], { rel_type: 'm.thread', event_id: '$root', is_falling_back: true, 'm.in_reply_to': { event_id: '$root' }, }); }); test('send URL encodes the room id against the session base URL', () => { assert.equal( replySendUrl('https://matrix.example.org/', '!r:example.org', 'tx1'), 'https://matrix.example.org/_matrix/client/v3/rooms/!r%3Aexample.org/send/m.room.message/tx1', ); }); test('sendNotificationReply checks encryption, then PUTs with the bearer token (#203, #299)', async () => { const calls: { url: string; init: RequestInit }[] = []; const fetchFor = (encStatus: number, sendOk = true) => (async (url: string, init: RequestInit = {}) => { calls.push({ url, init }); if (url.includes('/state/m.room.encryption/')) { return { ok: encStatus === 200, status: encStatus } as Response; } return { ok: sendOk, status: sendOk ? 200 : 500 } as Response; }) as unknown as typeof fetch; const session = { accessToken: 'tok', baseUrl: 'https://hs' }; assert.equal(await sendNotificationReply(session, '!r:hs', '$t', 'yo', fetchFor(404)), 'sent'); assert.match(calls[0].url, /\/rooms\/!r%3Ahs\/state\/m\.room\.encryption\/$/); const put = calls[1]; assert.equal(put.init.method, 'PUT'); assert.equal((put.init.headers as Record).Authorization, 'Bearer tok'); assert.match( put.url, /^https:\/\/hs\/_matrix\/client\/v3\/rooms\/!r%3Ahs\/send\/m\.room\.message\/swreply-/, ); assert.equal(JSON.parse(put.init.body as string)['m.relates_to'].event_id, '$t'); // Encryption turned on since the notification was shown: nothing is sent. calls.length = 0; assert.equal( await sendNotificationReply(session, '!r:hs', undefined, 'yo', fetchFor(200)), 'encrypted', ); assert.equal(calls.length, 1); // Can't tell (server error): fail safe, nothing is sent. calls.length = 0; assert.equal( await sendNotificationReply(session, '!r:hs', undefined, 'yo', fetchFor(500)), 'failed', ); assert.equal(calls.length, 1); assert.equal( await sendNotificationReply(session, '!r:hs', undefined, 'yo', fetchFor(404, false)), 'failed', ); assert.equal( await sendNotificationReply(undefined, '!r:hs', undefined, 'yo', fetchFor(404)), 'no-session', ); }); test('pickReplySession: the notification account only (#299)', () => { const a = { accessToken: 'a', baseUrl: 'https://hs', userId: '@a:hs' }; const b = { accessToken: 'b', baseUrl: 'https://hs', userId: '@b:hs' }; assert.equal(pickReplySession([a, b], '@a:hs'), a); assert.equal(pickReplySession([b], '@a:hs'), undefined, 'logged out of @a here'); assert.equal(pickReplySession([a], undefined), a, 'old notification, one session'); assert.equal(pickReplySession([a, b], undefined), undefined, 'old notification, ambiguous'); }); test('replyFailureText says why', () => { assert.match(replyFailureText('encrypted', 'hi'), /now encrypted/); assert.match(replyFailureText('no-session', 'hi'), /no longer signed in/); assert.match(replyFailureText('failed', 'hi'), /retry/); });