import { test } from 'node:test'; import assert from 'node:assert/strict'; import { IPresenceOpts, MatrixError } from 'matrix-js-sdk'; import { PresenceDeduper, describePresenceError, isPresenceWritePending, queuePresenceWrite, setPresenceWithRetry, } from './presenceWrite'; const limited = (retryMs = 3000) => new MatrixError( { errcode: 'M_LIMIT_EXCEEDED', error: 'Too Many Requests', retry_after_ms: retryMs }, 429, ); test('429 is retried after Retry-After, then succeeds', async () => { let n = 0; const waits: number[] = []; const mx = { setPresence: async () => { n += 1; if (n < 3) throw limited(3000); return {}; }, }; await setPresenceWithRetry( mx, { presence: 'online', status_msg: 'hi' }, { sleepFn: async (ms) => { waits.push(ms); }, }, ); assert.equal(n, 3); assert.deepEqual(waits, [3000, 3000]); }); test('gives up once the total wait would exceed the cap; other errors are not retried', async () => { const mx = { setPresence: async () => { throw limited(20_000); }, }; await assert.rejects( () => setPresenceWithRetry( mx, { presence: 'online' }, { maxWaitMs: 25_000, sleepFn: async () => undefined }, ), /Too Many/, ); let calls = 0; const forbidden = { setPresence: async () => { calls += 1; throw new MatrixError({ errcode: 'M_FORBIDDEN', error: 'nope' }, 403); }, }; await assert.rejects(() => setPresenceWithRetry(forbidden, { presence: 'online' })); assert.equal(calls, 1); }); test('deduper skips identical writes and notices changes', () => { const d = new PresenceDeduper(); assert.equal(d.shouldSend({ presence: 'online', status_msg: 'a' }), true); d.sent({ presence: 'online', status_msg: 'a' }); assert.equal(d.shouldSend({ presence: 'online', status_msg: 'a' }), false); assert.equal(d.shouldSend({ presence: 'unavailable', status_msg: 'a' }), true); assert.equal(d.shouldSend({ presence: 'online', status_msg: 'b' }), true); d.reset(); assert.equal(d.shouldSend({ presence: 'online', status_msg: 'a' }), true); }); test('error wording', () => { assert.match(describePresenceError(limited()), /once every 10 seconds/); assert.equal( describePresenceError( new MatrixError({ errcode: 'M_FORBIDDEN', error: 'Presence is disabled' }, 403), ), 'Failed to save status: Presence is disabled', ); assert.match(describePresenceError(new TypeError('Failed to fetch')), /couldn't reach/); }); test('[status save] queuePresenceWrite: newest wins, waits out 429 once, all callers settle', async () => { const sent: string[] = []; let calls = 0; const sleeps: number[] = []; const mx = { setPresence: async (w: IPresenceOpts) => { calls += 1; if (calls === 1) { sent.push(`429:${w.status_msg}`); throw new MatrixError({ errcode: 'M_LIMIT_EXCEEDED', retry_after_ms: 7000 }, 429); } sent.push(`ok:${w.status_msg}`); }, }; const sleepFn = async (ms: number) => { sleeps.push(ms); }; // 'a' goes out at once; 'b' and 'c' arrive while it waits out the limit. const first = queuePresenceWrite(mx, { presence: 'online', status_msg: 'a' }, { sleepFn }); const second = queuePresenceWrite(mx, { presence: 'online', status_msg: 'b' }, { sleepFn }); const third = queuePresenceWrite(mx, { presence: 'online', status_msg: 'c' }, { sleepFn }); assert.equal(isPresenceWritePending(mx), true); await Promise.all([first, second, third]); // 'a' was 429'd; after the wait only the newest ('c') goes out — 'a' isn't // retried and 'b' is never sent. assert.deepEqual(sent, ['429:a', 'ok:c']); assert.deepEqual(sleeps, [7000]); assert.equal(isPresenceWritePending(mx), false); }); test('[status save] queuePresenceWrite: a failure rejects the callers of that write', async () => { const mx = { setPresence: async () => { throw new MatrixError({ errcode: 'M_FORBIDDEN' }, 403); }, }; await assert.rejects(queuePresenceWrite(mx, { presence: 'online', status_msg: 'x' })); assert.equal(isPresenceWritePending(mx), false); });