import { describe, expect, it } from "vitest"; import type { HealthSummary } from "./health.js"; import { buildStatusFooterLines, buildStatusHealthRows, buildStatusPairingRecoveryLines, buildStatusPluginCompatibilityLines, buildStatusSecurityAuditLines, buildStatusSessionsRows, buildStatusSystemEventsRows, buildStatusSystemEventsTrailer, statusHealthColumns, } from "./status.command-sections.ts"; describe("status.command-sections", () => { it("formats security audit lines with finding caps and follow-up commands", () => { const lines = buildStatusSecurityAuditLines({ securityAudit: { summary: { critical: 1, warn: 6, info: 2 }, findings: [ { severity: "warn", title: "Warn first", detail: "warn detail", }, { severity: "critical", title: "Critical first", detail: "critical\ndetail", remediation: "fix it", }, ...Array.from({ length: 5 }, (_, index) => ({ severity: "warn" as const, title: `Warn ${index + 2}`, detail: `detail ${index + 2}`, })), ], }, theme: { error: (value) => `error(${value})`, warn: (value) => `warn(${value})`, muted: (value) => `muted(${value})`, }, shortenText: (value) => value, formatCliCommand: (value) => `cmd:${value}`, }); expect(lines[0]).toBe("muted(Summary: error(1 critical) · warn(6 warn) · muted(2 info))"); expect(lines).toContain(" error(CRITICAL) Critical first"); expect(lines).toContain(" critical detail"); expect(lines).toContain(" muted(Fix: fix it)"); expect(lines).toContain("muted(… +1 more)"); expect(lines.at(-2)).toBe("muted(Full report: cmd:openclaw security audit)"); expect(lines.at(-1)).toBe("muted(Deep probe: cmd:openclaw security audit --deep)"); }); it("builds verbose sessions rows and empty fallback rows", () => { const verboseRows = buildStatusSessionsRows({ recent: [ { key: "session-key-1234567890", kind: "direct", updatedAt: 1, age: 5_000, model: "gpt-5.4", totalTokens: null, totalTokensFresh: false, remainingTokens: null, percentUsed: null, contextTokens: null, flags: [], }, ], verbose: true, shortenText: (value) => value.slice(0, 8), formatTimeAgo: (value) => `${value}ms`, formatTokensCompact: () => "12k", formatPromptCacheCompact: () => "cache ok", muted: (value) => `muted(${value})`, }); expect(verboseRows).toEqual([ { Key: "session-", Kind: "direct", Age: "5000ms", Model: "gpt-5.4", Tokens: "12k", Cache: "cache ok", }, ]); const emptyRows = buildStatusSessionsRows({ recent: [], verbose: true, shortenText: (value) => value, formatTimeAgo: () => "", formatTokensCompact: () => "", formatPromptCacheCompact: () => null, muted: (value) => `muted(${value})`, }); expect(emptyRows).toEqual([ { Key: "muted(no sessions yet)", Kind: "", Age: "", Model: "", Tokens: "", Cache: "", }, ]); }); it("maps health channel detail lines into status rows", () => { const rows = buildStatusHealthRows({ health: { durationMs: 42 } as HealthSummary, formatHealthChannelLines: () => [ "Telegram: OK · ready", "Slack: failed · auth", "Discord: not configured", "Matrix: linked", "Signal: not linked", ], ok: (value) => `ok(${value})`, warn: (value) => `warn(${value})`, muted: (value) => `muted(${value})`, }); expect(rows).toEqual([ { Item: "Gateway", Status: "ok(reachable)", Detail: "42ms" }, { Item: "Telegram", Status: "ok(OK)", Detail: "OK · ready" }, { Item: "Slack", Status: "warn(WARN)", Detail: "failed · auth" }, { Item: "Discord", Status: "muted(OFF)", Detail: "not configured" }, { Item: "Matrix", Status: "ok(LINKED)", Detail: "linked" }, { Item: "Signal", Status: "warn(UNLINKED)", Detail: "not linked" }, ]); }); it("builds footer lines from update and reachability state", () => { expect( buildStatusFooterLines({ updateHint: "upgrade ready", warn: (value) => `warn(${value})`, formatCliCommand: (value) => `cmd:${value}`, nodeOnlyGateway: null, gatewayReachable: false, }), ).toEqual([ "FAQ: https://docs.openclaw.ai/faq", "Troubleshooting: https://docs.openclaw.ai/troubleshooting", "", "warn(upgrade ready)", "Next steps:", " Need to share? cmd:openclaw status --all", " Need to debug live? cmd:openclaw logs --follow", " Fix reachability first: cmd:openclaw gateway probe", ]); }); it("builds plugin compatibility lines and pairing recovery guidance", () => { expect( buildStatusPluginCompatibilityLines({ notices: [ { severity: "warn" as const, message: "legacy" }, { severity: "info" as const, message: "heads-up" }, { severity: "warn" as const, message: "extra" }, ], limit: 2, formatNotice: (notice) => String(notice.message), warn: (value) => `warn(${value})`, muted: (value) => `muted(${value})`, }), ).toEqual([" warn(WARN) legacy", " muted(INFO) heads-up", "muted( … +1 more)"]); expect( buildStatusPairingRecoveryLines({ pairingRecovery: { requestId: "req-123" }, warn: (value) => `warn(${value})`, muted: (value) => `muted(${value})`, formatCliCommand: (value) => `cmd:${value}`, }), ).toEqual([ "warn(Gateway pairing approval required.)", "muted(Recovery: cmd:openclaw devices approve req-123)", "muted(Fallback: cmd:openclaw devices approve --latest)", "muted(Inspect: cmd:openclaw devices list)", ]); }); it("builds system event rows and health columns", () => { expect( buildStatusSystemEventsRows({ queuedSystemEvents: ["one", "two", "three"], limit: 2, }), ).toEqual([{ Event: "one" }, { Event: "two" }]); expect( buildStatusSystemEventsTrailer({ queuedSystemEvents: ["one", "two", "three"], limit: 2, muted: (value) => `muted(${value})`, }), ).toBe("muted(… +1 more)"); expect(statusHealthColumns).toEqual([ { key: "Item", header: "Item", minWidth: 10 }, { key: "Status", header: "Status", minWidth: 8 }, { key: "Detail", header: "Detail", flex: true, minWidth: 28 }, ]); }); });