diff --git a/src/infra/channel-summary.test.ts b/src/infra/channel-summary.test.ts index 91079168602..afb5ac8886c 100644 --- a/src/infra/channel-summary.test.ts +++ b/src/infra/channel-summary.test.ts @@ -124,6 +124,51 @@ function makeTelegramSummaryPlugin(params: { }; } +function makeSignalSummaryPlugin(params: { enabled: boolean; configured: boolean }): ChannelPlugin { + return { + id: "signal", + meta: { + id: "signal", + label: "Signal", + selectionLabel: "Signal", + docsPath: "/channels/signal", + blurb: "test", + }, + capabilities: { chatTypes: ["direct"] }, + config: { + listAccountIds: () => ["desktop"], + defaultAccountId: () => "desktop", + inspectAccount: () => ({ + accountId: "desktop", + name: "Desktop", + enabled: params.enabled, + configured: params.configured, + appTokenSource: "env", + baseUrl: "https://signal.example.test", + port: 31337, + cliPath: "/usr/local/bin/signal-cli", + dbPath: "/tmp/signal.db", + }), + resolveAccount: () => ({ + accountId: "desktop", + name: "Desktop", + enabled: params.enabled, + configured: params.configured, + appTokenSource: "env", + baseUrl: "https://signal.example.test", + port: 31337, + cliPath: "/usr/local/bin/signal-cli", + dbPath: "/tmp/signal.db", + }), + isConfigured: (account) => Boolean((account as { configured?: boolean }).configured), + isEnabled: (account) => Boolean((account as { enabled?: boolean }).enabled), + }, + actions: { + listActions: () => ["send"], + }, + }; +} + describe("buildChannelSummary", () => { it("preserves Slack HTTP signing-secret unavailable state from source config", async () => { vi.mocked(listChannelPlugins).mockReturnValue([makeSlackHttpSummaryPlugin()]); @@ -172,4 +217,38 @@ describe("buildChannelSummary", () => { expect(lines).toContain("Telegram: linked +15551234567 auth 5m ago"); expect(lines).toContain(" - primary (Main Bot) (dm:mutuals, token:env, allow:alice,bob)"); }); + + it("shows not-linked status when linked metadata is explicitly false", async () => { + vi.mocked(listChannelPlugins).mockReturnValue([ + makeTelegramSummaryPlugin({ + enabled: true, + configured: true, + linked: false, + }), + ]); + + const lines = await buildChannelSummary({ channels: {} } as never, { + colorize: false, + includeAllowFrom: false, + }); + + expect(lines).toContain("Telegram: not linked +15551234567"); + expect(lines).toContain(" - primary (Main Bot) (dm:mutuals, token:env)"); + }); + + it("renders non-slack account detail fields for configured accounts", async () => { + vi.mocked(listChannelPlugins).mockReturnValue([ + makeSignalSummaryPlugin({ enabled: false, configured: true }), + ]); + + const lines = await buildChannelSummary({ channels: {} } as never, { + colorize: false, + includeAllowFrom: false, + }); + + expect(lines).toEqual([ + "Signal: disabled", + " - desktop (Desktop) (disabled, app:env, https://signal.example.test, port:31337, cli:/usr/local/bin/signal-cli, db:/tmp/signal.db)", + ]); + }); }); diff --git a/src/infra/fetch.test.ts b/src/infra/fetch.test.ts index 1ee31b6182b..deef81f551f 100644 --- a/src/infra/fetch.test.ts +++ b/src/infra/fetch.test.ts @@ -223,6 +223,48 @@ describe("wrapFetchWithAbortSignal", () => { expect(seenSignal).toBe(fakeSignal); }); + it("passes through native AbortSignal instances unchanged", async () => { + let seenSignal: AbortSignal | undefined; + const fetchImpl = withFetchPreconnect( + vi.fn(async (_input: RequestInfo | URL, init?: RequestInit) => { + seenSignal = init?.signal as AbortSignal | undefined; + return {} as Response; + }), + ); + const wrapped = wrapFetchWithAbortSignal(fetchImpl); + const controller = new AbortController(); + + await wrapped("https://example.com", { signal: controller.signal }); + + expect(seenSignal).toBe(controller.signal); + }); + + it("passes through foreign signals unchanged when AbortController is unavailable", async () => { + let seenSignal: AbortSignal | undefined; + const fetchImpl = withFetchPreconnect( + vi.fn(async (_input: RequestInfo | URL, init?: RequestInit) => { + seenSignal = init?.signal as AbortSignal | undefined; + return {} as Response; + }), + ); + const wrapped = wrapFetchWithAbortSignal(fetchImpl); + const fakeSignal = { + aborted: false, + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + } as unknown as AbortSignal; + const previousAbortController = globalThis.AbortController; + vi.stubGlobal("AbortController", undefined); + + try { + await wrapped("https://example.com", { signal: fakeSignal }); + } finally { + vi.stubGlobal("AbortController", previousAbortController); + } + + expect(seenSignal).toBe(fakeSignal); + }); + it("returns the same function when called with an already wrapped fetch", () => { const fetchImpl = withFetchPreconnect(vi.fn(async () => ({ ok: true }) as Response)); const wrapped = wrapFetchWithAbortSignal(fetchImpl); @@ -249,6 +291,15 @@ describe("wrapFetchWithAbortSignal", () => { expect(preconnectSpy).toHaveBeenCalledOnce(); expect(seenThis).toBe(fetchImpl); }); + + it("exposes a no-op preconnect when the source fetch has none", () => { + const fetchImpl = vi.fn(async () => ({ ok: true }) as Response) as typeof fetch; + const wrapped = wrapFetchWithAbortSignal(fetchImpl) as typeof fetch & { + preconnect: (url: string, init?: { credentials?: RequestCredentials }) => unknown; + }; + + expect(() => wrapped.preconnect("https://example.com")).not.toThrow(); + }); }); describe("resolveFetch", () => {