import { describe, it, expect } from "vitest"; import { decideOnPairClose, decideOnPairTimeout, shouldAutoReconnect, } from "./pair-state.js"; describe("decideOnPairClose", () => { it("logged-out close → terminal `logged_out` and wipes QR", () => { const r = decideOnPairClose({ current: "pending", loggedOut: true }); expect(r).toEqual({ next: "logged_out", clearQrPng: true }); }); it("restart-required close → null (it's a SUCCESS — reconnect, don't touch DB)", () => { // Regression we just fixed: after the user scans, Baileys closes // the socket with status 515 ("restart required") so it can // reopen with the new credentials. Treating that close as a // failure produced a spurious "Pairing timed out" right at the // moment the user actually paired successfully. expect( decideOnPairClose({ current: "pending", loggedOut: false, restartRequired: true }), ).toBe(null); }); it("non-loggedOut close from `pending` parks the row as `unpaired`", () => { const r = decideOnPairClose({ current: "pending", loggedOut: false }); expect(r).toEqual({ next: "unpaired", clearQrPng: true }); }); it("non-loggedOut close from any transient state parks as `unpaired`", () => { for (const current of ["disconnected", "unpaired", "connected"] as const) { const r = decideOnPairClose({ current, loggedOut: false }); expect(r).not.toBe(null); expect(r!.next).toBe("unpaired"); expect(r!.clearQrPng).toBe(true); } }); }); describe("decideOnPairTimeout (5-min pair-window expiry)", () => { it("parks a still-`pending` row as `unpaired`", () => { expect(decideOnPairTimeout({ current: "pending" })).toEqual({ next: "unpaired", clearQrPng: true, }); }); it("does nothing if the row already moved on", () => { // Don't clobber a successfully-paired account that just happened // to fire after the timeout for any reason. for (const current of ["connected", "unpaired", "logged_out", "banned"] as const) { expect(decideOnPairTimeout({ current })).toBe(null); } }); }); describe("shouldAutoReconnect", () => { it("never reconnects after a logged-out close", () => { expect(shouldAutoReconnect({ loggedOut: true, hasEverConnected: true })).toBe(false); expect(shouldAutoReconnect({ loggedOut: true, hasEverConnected: false })).toBe(false); // Even if Baileys also flagged restartRequired (it shouldn't, but // be defensive), loggedOut wins. expect( shouldAutoReconnect({ loggedOut: true, restartRequired: true, hasEverConnected: true }), ).toBe(false); }); it("ALWAYS reconnects on restart-required (post-pair-success), even for first-time accounts", () => { // The regression: brand-new pair attempts have hasEverConnected=false, // so the old logic refused to reconnect after status 515 — and the // user got "Pairing timed out" the moment they actually paired. expect( shouldAutoReconnect({ loggedOut: false, restartRequired: true, hasEverConnected: false }), ).toBe(true); expect( shouldAutoReconnect({ loggedOut: false, restartRequired: true, hasEverConnected: true }), ).toBe(true); }); it("reconnects only for accounts that have been linked at least once for non-restartRequired drops", () => { expect(shouldAutoReconnect({ loggedOut: false, hasEverConnected: true })).toBe(true); expect(shouldAutoReconnect({ loggedOut: false, hasEverConnected: false })).toBe(false); }); });