From 70489cbed0d6dce89083882834ae93893ef17a4c Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 14 Mar 2026 00:23:50 +0000 Subject: [PATCH] fix: tighten package tag and channel summary coverage --- src/infra/channel-summary.test.ts | 72 +++++++++++++++++++++++++++++++ src/infra/package-tag.test.ts | 5 +++ src/infra/package-tag.ts | 3 ++ 3 files changed, 80 insertions(+) diff --git a/src/infra/channel-summary.test.ts b/src/infra/channel-summary.test.ts index afb5ac8886c..e2db1dffc5a 100644 --- a/src/infra/channel-summary.test.ts +++ b/src/infra/channel-summary.test.ts @@ -169,6 +169,43 @@ function makeSignalSummaryPlugin(params: { enabled: boolean; configured: boolean }; } +function makeFallbackSummaryPlugin(params: { + configured: boolean; + enabled: boolean; + accountIds?: string[]; + defaultAccountId?: string; +}): ChannelPlugin { + return { + id: "fallback-plugin", + meta: { + id: "fallback-plugin", + selectionLabel: "Fallback", + docsPath: "/channels/fallback", + blurb: "test", + }, + capabilities: { chatTypes: ["direct"] }, + config: { + listAccountIds: () => params.accountIds ?? [], + defaultAccountId: () => params.defaultAccountId ?? "default", + inspectAccount: (_cfg, accountId) => ({ + accountId, + enabled: params.enabled, + configured: params.configured, + }), + resolveAccount: (_cfg, accountId) => ({ + accountId, + enabled: params.enabled, + configured: params.configured, + }), + 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()]); @@ -251,4 +288,39 @@ describe("buildChannelSummary", () => { " - desktop (Desktop) (disabled, app:env, https://signal.example.test, port:31337, cli:/usr/local/bin/signal-cli, db:/tmp/signal.db)", ]); }); + + it("falls back to plugin id and default account id when no label or accounts exist", async () => { + vi.mocked(listChannelPlugins).mockReturnValue([ + makeFallbackSummaryPlugin({ + enabled: true, + configured: true, + accountIds: [], + defaultAccountId: "fallback-account", + }), + ]); + + const lines = await buildChannelSummary({ channels: {} } as never, { + colorize: false, + includeAllowFrom: false, + }); + + expect(lines).toEqual(["fallback-plugin: configured", " - fallback-account"]); + }); + + it("shows not-configured status when enabled accounts exist without configured ones", async () => { + vi.mocked(listChannelPlugins).mockReturnValue([ + makeFallbackSummaryPlugin({ + enabled: true, + configured: false, + accountIds: ["fallback-account"], + }), + ]); + + const lines = await buildChannelSummary({ channels: {} } as never, { + colorize: false, + includeAllowFrom: false, + }); + + expect(lines).toEqual(["fallback-plugin: not configured"]); + }); }); diff --git a/src/infra/package-tag.test.ts b/src/infra/package-tag.test.ts index c50237e5fd3..9e49f9fcdcc 100644 --- a/src/infra/package-tag.test.ts +++ b/src/infra/package-tag.test.ts @@ -15,6 +15,11 @@ describe("normalizePackageTagInput", () => { expect(normalizePackageTagInput("openclaw@ ", packageNames)).toBeNull(); }); + it("treats exact known package names as an empty tag", () => { + expect(normalizePackageTagInput("openclaw", packageNames)).toBeNull(); + expect(normalizePackageTagInput(" @openclaw/plugin ", packageNames)).toBeNull(); + }); + it("returns trimmed raw values when no package prefix matches", () => { expect(normalizePackageTagInput(" latest ", packageNames)).toBe("latest"); expect(normalizePackageTagInput("@other/plugin@beta", packageNames)).toBe("@other/plugin@beta"); diff --git a/src/infra/package-tag.ts b/src/infra/package-tag.ts index c98bac5bbd5..db10d372c46 100644 --- a/src/infra/package-tag.ts +++ b/src/infra/package-tag.ts @@ -8,6 +8,9 @@ export function normalizePackageTagInput( } for (const packageName of packageNames) { + if (trimmed === packageName) { + return null; + } const prefix = `${packageName}@`; if (trimmed.startsWith(prefix)) { const tag = trimmed.slice(prefix.length).trim();