openclaw/src/commands/status-json.test.ts
2026-03-19 16:55:13 -07:00

119 lines
3.6 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import type { RuntimeEnv } from "../runtime.js";
const mocks = vi.hoisted(() => ({
scanStatusJsonFast: vi.fn(),
runSecurityAudit: vi.fn(),
loadProviderUsageSummary: vi.fn(),
callGateway: vi.fn(),
getDaemonStatusSummary: vi.fn(),
getNodeDaemonStatusSummary: vi.fn(),
normalizeUpdateChannel: vi.fn((value?: string | null) => value ?? null),
resolveUpdateChannelDisplay: vi.fn(() => ({
channel: "stable",
source: "config",
})),
}));
vi.mock("./status.scan.fast-json.js", () => ({
scanStatusJsonFast: mocks.scanStatusJsonFast,
}));
vi.mock("../security/audit.runtime.js", () => ({
runSecurityAudit: mocks.runSecurityAudit,
}));
vi.mock("../infra/provider-usage.js", () => ({
loadProviderUsageSummary: mocks.loadProviderUsageSummary,
}));
vi.mock("../gateway/call.js", () => ({
callGateway: mocks.callGateway,
}));
vi.mock("./status.daemon.js", () => ({
getDaemonStatusSummary: mocks.getDaemonStatusSummary,
getNodeDaemonStatusSummary: mocks.getNodeDaemonStatusSummary,
}));
vi.mock("../infra/update-channels.js", () => ({
normalizeUpdateChannel: mocks.normalizeUpdateChannel,
resolveUpdateChannelDisplay: mocks.resolveUpdateChannelDisplay,
}));
const { statusJsonCommand } = await import("./status-json.js");
function createRuntimeCapture() {
const logs: string[] = [];
const runtime: RuntimeEnv = {
log: vi.fn((value: unknown) => {
logs.push(String(value));
}),
error: vi.fn(),
exit: vi.fn() as unknown as RuntimeEnv["exit"],
};
return { runtime, logs };
}
function createScanResult() {
return {
cfg: { update: { channel: "stable" } },
sourceConfig: {},
summary: { ok: true, configuredChannels: [] },
osSummary: { platform: "linux" },
update: { installKind: "npm", git: { tag: null, branch: null } },
memory: null,
memoryPlugin: null,
gatewayMode: "local",
gatewayConnection: { url: "ws://127.0.0.1:18789", urlSource: "config" },
remoteUrlMissing: false,
gatewayReachable: false,
gatewayProbe: null,
gatewaySelf: null,
gatewayProbeAuthWarning: null,
agentStatus: [],
secretDiagnostics: [],
};
}
describe("statusJsonCommand", () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.scanStatusJsonFast.mockResolvedValue(createScanResult());
mocks.runSecurityAudit.mockResolvedValue({
summary: { critical: 1, warn: 0, info: 0 },
findings: [],
});
mocks.getDaemonStatusSummary.mockResolvedValue({ installed: false });
mocks.getNodeDaemonStatusSummary.mockResolvedValue({ installed: false });
mocks.loadProviderUsageSummary.mockResolvedValue({ providers: [] });
mocks.callGateway.mockResolvedValue({});
});
it("keeps plain status --json off the security audit fast path", async () => {
const { runtime, logs } = createRuntimeCapture();
await statusJsonCommand({}, runtime);
expect(mocks.runSecurityAudit).not.toHaveBeenCalled();
expect(logs).toHaveLength(1);
expect(JSON.parse(logs[0] ?? "{}")).not.toHaveProperty("securityAudit");
});
it("includes security audit details only when --all is requested", async () => {
const { runtime, logs } = createRuntimeCapture();
await statusJsonCommand({ all: true }, runtime);
expect(mocks.runSecurityAudit).toHaveBeenCalledWith({
config: expect.any(Object),
sourceConfig: expect.any(Object),
deep: false,
includeFilesystem: true,
includeChannelSecurity: true,
});
expect(logs).toHaveLength(1);
expect(JSON.parse(logs[0] ?? "{}")).toHaveProperty("securityAudit.summary.critical", 1);
});
});