import { describe, expect, it } from "vitest"; import { createAsyncComputedAccountStatusAdapter, buildBaseAccountStatusSnapshot, buildBaseChannelStatusSummary, buildComputedAccountStatusSnapshot, buildRuntimeAccountStatusSnapshot, createComputedAccountStatusAdapter, buildWebhookChannelStatusSummary, buildTokenChannelStatusSummary, collectStatusIssuesFromLastError, createDependentCredentialStatusIssueCollector, createDefaultChannelRuntimeState, } from "./status-helpers.js"; const defaultRuntimeState = { running: false, lastStartAt: null, lastStopAt: null, lastError: null, }; type ExpectedAccountSnapshot = { accountId: string; name?: string; enabled?: boolean; configured?: boolean; running: boolean; lastStartAt: number | null; lastStopAt: number | null; lastError: string | null; probe?: unknown; lastInboundAt: number | null; lastOutboundAt: number | null; } & Record; const defaultChannelSummary = { configured: false, ...defaultRuntimeState, }; const defaultTokenChannelSummary = { ...defaultChannelSummary, tokenSource: "none", mode: null, probe: undefined, lastProbeAt: null, }; const defaultAccountSnapshot: ExpectedAccountSnapshot = { accountId: "default", name: undefined, enabled: undefined, configured: false, ...defaultRuntimeState, probe: undefined, lastInboundAt: null, lastOutboundAt: null, }; function expectedAccountSnapshot( overrides: Partial = {}, ): ExpectedAccountSnapshot { return { ...defaultAccountSnapshot, ...overrides, }; } const adapterAccount = { accountId: "default", enabled: true, profileUrl: "https://example.test", }; const adapterRuntime = { accountId: "default", running: true, }; const adapterProbe = { ok: true }; function expectedAdapterAccountSnapshot() { return { ...expectedAccountSnapshot({ enabled: true, configured: true, running: true, probe: adapterProbe, }), profileUrl: adapterAccount.profileUrl, connected: true, }; } function createComputedStatusAdapter() { return createComputedAccountStatusAdapter< { accountId: string; enabled: boolean; profileUrl: string }, { ok: boolean } >({ defaultRuntime: createDefaultChannelRuntimeState("default"), resolveAccountSnapshot: ({ account, runtime, probe }) => ({ accountId: account.accountId, enabled: account.enabled, configured: true, extra: { profileUrl: account.profileUrl, connected: runtime?.running ?? false, probe, }, }), }); } function createAsyncStatusAdapter() { return createAsyncComputedAccountStatusAdapter< { accountId: string; enabled: boolean; profileUrl: string }, { ok: boolean } >({ defaultRuntime: createDefaultChannelRuntimeState("default"), resolveAccountSnapshot: async ({ account, runtime, probe }) => ({ accountId: account.accountId, enabled: account.enabled, configured: true, extra: { profileUrl: account.profileUrl, connected: runtime?.running ?? false, probe, }, }), }); } describe("createDefaultChannelRuntimeState", () => { it.each([ { name: "builds default runtime state without extra fields", accountId: "default", extra: undefined, expected: { accountId: "default", ...defaultRuntimeState, }, }, { name: "merges extra fields into the default runtime state", accountId: "alerts", extra: { probeAt: 123, healthy: true, }, expected: { accountId: "alerts", ...defaultRuntimeState, probeAt: 123, healthy: true, }, }, ])("$name", ({ accountId, extra, expected }) => { expect(createDefaultChannelRuntimeState(accountId, extra)).toEqual(expected); }); }); describe("buildBaseChannelStatusSummary", () => { it("defaults missing values", () => { expect(buildBaseChannelStatusSummary({})).toEqual(defaultChannelSummary); }); it("keeps explicit values", () => { expect( buildBaseChannelStatusSummary({ configured: true, running: true, lastStartAt: 1, lastStopAt: 2, lastError: "boom", }), ).toEqual({ ...defaultChannelSummary, configured: true, running: true, lastStartAt: 1, lastStopAt: 2, lastError: "boom", }); }); it("merges extra fields into the normalized channel summary", () => { expect( buildBaseChannelStatusSummary( { configured: true, }, { mode: "webhook", secretSource: "env", }, ), ).toEqual({ ...defaultChannelSummary, configured: true, mode: "webhook", secretSource: "env", }); }); }); describe("buildBaseAccountStatusSnapshot", () => { it("builds account status with runtime defaults", () => { expect( buildBaseAccountStatusSnapshot({ account: { accountId: "default", enabled: true, configured: true }, }), ).toEqual(expectedAccountSnapshot({ enabled: true, configured: true })); }); it("merges extra snapshot fields after the shared account shape", () => { expect( buildBaseAccountStatusSnapshot( { account: { accountId: "default", configured: true }, }, { connected: true, mode: "polling", }, ), ).toEqual({ ...expectedAccountSnapshot({ configured: true }), connected: true, mode: "polling", }); }); }); describe("buildComputedAccountStatusSnapshot", () => { it("builds account status when configured is computed outside resolver", () => { expect( buildComputedAccountStatusSnapshot({ accountId: "default", enabled: true, configured: false, }), ).toEqual(expectedAccountSnapshot({ enabled: true })); }); it("merges computed extras after the shared fields", () => { expect( buildComputedAccountStatusSnapshot( { accountId: "default", configured: true, }, { connected: true, }, ), ).toEqual({ ...expectedAccountSnapshot({ configured: true }), connected: true, }); }); }); describe("computed account status adapters", () => { it.each([ { name: "sync", createStatus: createComputedStatusAdapter, }, { name: "async", createStatus: createAsyncStatusAdapter, }, ])( "builds account snapshots from $name computed account metadata and extras", async ({ createStatus }) => { const status = createStatus(); await expect( Promise.resolve( status.buildAccountSnapshot?.({ account: adapterAccount, cfg: {} as never, runtime: adapterRuntime, probe: adapterProbe, }), ), ).resolves.toEqual(expectedAdapterAccountSnapshot()); }, ); }); describe("buildRuntimeAccountStatusSnapshot", () => { it.each([ { name: "builds runtime lifecycle fields with defaults", input: {}, extra: undefined, expected: { ...defaultRuntimeState, probe: undefined, }, }, { name: "merges extra fields into runtime snapshots", input: {}, extra: { port: 3978 }, expected: { ...defaultRuntimeState, probe: undefined, port: 3978, }, }, ])("$name", ({ input, extra, expected }) => { expect(buildRuntimeAccountStatusSnapshot(input, extra)).toEqual(expected); }); }); describe("buildTokenChannelStatusSummary", () => { it.each([ { name: "includes token/probe fields with mode by default", input: {}, options: undefined, expected: defaultTokenChannelSummary, }, { name: "can omit mode for channels without a mode state", input: { configured: true, tokenSource: "env", running: true, lastStartAt: 1, lastStopAt: 2, lastError: "boom", probe: { ok: true }, lastProbeAt: 3, }, options: { includeMode: false }, expected: { configured: true, tokenSource: "env", running: true, lastStartAt: 1, lastStopAt: 2, lastError: "boom", probe: { ok: true }, lastProbeAt: 3, }, }, ])("$name", ({ input, options, expected }) => { expect(buildTokenChannelStatusSummary(input, options)).toEqual(expected); }); }); describe("buildWebhookChannelStatusSummary", () => { it("defaults mode to webhook and keeps supplied extras", () => { expect( buildWebhookChannelStatusSummary( { configured: true, running: true, }, { secretSource: "env", }, ), ).toEqual({ configured: true, running: true, lastStartAt: null, lastStopAt: null, lastError: null, mode: "webhook", secretSource: "env", }); }); }); describe("createDependentCredentialStatusIssueCollector", () => { it("uses source metadata from sanitized snapshots to pick the missing field", () => { const collect = createDependentCredentialStatusIssueCollector({ channel: "line", dependencySourceKey: "tokenSource", missingPrimaryMessage: "LINE channel access token not configured", missingDependentMessage: "LINE channel secret not configured", }); expect( collect([ { accountId: "default", configured: false, tokenSource: "none" }, { accountId: "work", configured: false, tokenSource: "env" }, { accountId: "ok", configured: true, tokenSource: "env" }, ]), ).toEqual([ { channel: "line", accountId: "default", kind: "config", message: "LINE channel access token not configured", }, { channel: "line", accountId: "work", kind: "config", message: "LINE channel secret not configured", }, ]); }); }); describe("collectStatusIssuesFromLastError", () => { it("returns runtime issues only for non-empty string lastError values", () => { expect( collectStatusIssuesFromLastError("demo-channel", [ { accountId: "default", lastError: " timeout " }, { accountId: "silent", lastError: " " }, { accountId: "typed", lastError: { message: "boom" } }, ]), ).toEqual([ { channel: "demo-channel", accountId: "default", kind: "runtime", message: "Channel error: timeout", }, ]); }); });