diff --git a/desktop/src/features/presence/lib/presenceSubscriptionReconciler.test.mjs b/desktop/src/features/presence/lib/presenceSubscriptionReconciler.test.mjs index f86000dfbf1..19b02498394 100644 --- a/desktop/src/features/presence/lib/presenceSubscriptionReconciler.test.mjs +++ b/desktop/src/features/presence/lib/presenceSubscriptionReconciler.test.mjs @@ -75,6 +75,41 @@ test("a stale async open is closed and never installed", async () => { reconciler.dispose(); }); +test("default timers preserve their global receiver when scheduling retries", async () => { + const originalSetTimeout = globalThis.setTimeout; + const originalClearTimeout = globalThis.clearTimeout; + const timers = []; + const cleared = []; + + globalThis.setTimeout = function (callback) { + assert.equal(this, globalThis); + timers.push(callback); + return timers.length; + }; + globalThis.clearTimeout = function (timer) { + assert.equal(this, globalThis); + cleared.push(timer); + }; + + try { + const reconciler = new PresenceSubscriptionReconciler({ + open: async () => { + throw new Error("relay unavailable"); + }, + }); + + reconciler.setAuthors([A]); + await Promise.resolve(); + assert.equal(timers.length, 1); + + reconciler.dispose(); + assert.deepEqual(cleared, [1]); + } finally { + globalThis.setTimeout = originalSetTimeout; + globalThis.clearTimeout = originalClearTimeout; + } +}); + test("failed replacement preserves the previous subscription and retries", async () => { const timers = []; const actions = []; diff --git a/desktop/src/features/presence/lib/presenceSubscriptionReconciler.ts b/desktop/src/features/presence/lib/presenceSubscriptionReconciler.ts index 2831785ccfb..dccadf85444 100644 --- a/desktop/src/features/presence/lib/presenceSubscriptionReconciler.ts +++ b/desktop/src/features/presence/lib/presenceSubscriptionReconciler.ts @@ -41,8 +41,11 @@ export class PresenceSubscriptionReconciler { this.retryDelay = options.retryDelay ?? ((attempt) => Math.min(1000 * 2 ** attempt, 30_000)); - this.setTimer = options.setTimer ?? setTimeout; - this.clearTimer = options.clearTimer ?? clearTimeout; + this.setTimer = + options.setTimer ?? + ((callback, delayMs) => globalThis.setTimeout(callback, delayMs)); + this.clearTimer = + options.clearTimer ?? ((timer) => globalThis.clearTimeout(timer)); } setAuthors(authors: string[]) {