mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 16:30:57 +00:00
test: mock status command scan seam
This commit is contained in:
@@ -175,6 +175,207 @@ function createDefaultSecurityAuditResult() {
|
||||
};
|
||||
}
|
||||
|
||||
async function createStatusServiceSummary(
|
||||
service: ReturnType<(typeof mocks)["resolveGatewayService"]>,
|
||||
) {
|
||||
const [loaded, runtime, command] = await Promise.all([
|
||||
service.isLoaded(),
|
||||
service.readRuntime(),
|
||||
service.readCommand(),
|
||||
]);
|
||||
return {
|
||||
label: service.label,
|
||||
installed: Boolean(command) || runtime?.status === "running",
|
||||
loaded,
|
||||
managedByOpenClaw: Boolean(command),
|
||||
externallyManaged: !command && runtime?.status === "running",
|
||||
loadedText: service.loadedText,
|
||||
runtime,
|
||||
runtimeShort: runtime?.pid ? `pid ${runtime.pid}` : null,
|
||||
};
|
||||
}
|
||||
|
||||
function createSessionStatusRows() {
|
||||
const agents = mocks.listGatewayAgentsBasic().agents ?? [{ id: "main", name: "Main" }];
|
||||
const byAgent = agents.map((agent: { id: string }) => {
|
||||
const path = mocks.resolveStorePath("sessions", { agentId: agent.id });
|
||||
const store = mocks.loadSessionStore(path) as Record<
|
||||
string,
|
||||
ReturnType<typeof createDefaultSessionStoreEntry>
|
||||
>;
|
||||
const recent = Object.entries(store).map(([key, entry]) => {
|
||||
const contextTokens = typeof entry.contextTokens === "number" ? entry.contextTokens : null;
|
||||
const freshTotal =
|
||||
typeof entry.totalTokens === "number" && entry.totalTokensFresh !== false
|
||||
? entry.totalTokens
|
||||
: null;
|
||||
return {
|
||||
agentId: agent.id,
|
||||
key,
|
||||
kind: key.startsWith("+") ? ("direct" as const) : ("unknown" as const),
|
||||
sessionId: entry.sessionId,
|
||||
updatedAt: entry.updatedAt ?? null,
|
||||
age: typeof entry.updatedAt === "number" ? Math.max(0, Date.now() - entry.updatedAt) : null,
|
||||
thinkingLevel: entry.thinkingLevel,
|
||||
verboseLevel: entry.verboseLevel,
|
||||
inputTokens: entry.inputTokens,
|
||||
outputTokens: entry.outputTokens,
|
||||
totalTokens: freshTotal,
|
||||
totalTokensFresh: freshTotal !== null,
|
||||
cacheRead: entry.cacheRead,
|
||||
cacheWrite: entry.cacheWrite,
|
||||
remainingTokens:
|
||||
freshTotal !== null && contextTokens !== null
|
||||
? Math.max(0, contextTokens - freshTotal)
|
||||
: null,
|
||||
percentUsed:
|
||||
freshTotal !== null && contextTokens
|
||||
? Math.round((freshTotal / contextTokens) * 100)
|
||||
: null,
|
||||
model: typeof entry.model === "string" ? entry.model : null,
|
||||
contextTokens,
|
||||
flags: [
|
||||
...(entry.verboseLevel ? [`verbose:${entry.verboseLevel}`] : []),
|
||||
...(entry.thinkingLevel ? [`think:${entry.thinkingLevel}`] : []),
|
||||
],
|
||||
};
|
||||
});
|
||||
return { agentId: agent.id, path, count: recent.length, recent };
|
||||
});
|
||||
const recent = byAgent.flatMap((entry) => entry.recent);
|
||||
return {
|
||||
paths: byAgent.map((entry) => entry.path),
|
||||
count: recent.length,
|
||||
defaults: {
|
||||
model: recent[0]?.model ?? "pi:opus",
|
||||
contextTokens: recent[0]?.contextTokens ?? 10_000,
|
||||
},
|
||||
recent,
|
||||
byAgent,
|
||||
};
|
||||
}
|
||||
|
||||
async function createMockStatusScanResult(params: { includePluginCompatibility?: boolean } = {}) {
|
||||
const cfg = mocks.loadConfig();
|
||||
const gatewayProbe = await mocks.probeGateway();
|
||||
const gatewayReachable = gatewayProbe.ok === true;
|
||||
const gatewayAuthWarning =
|
||||
cfg.gateway?.auth?.token && typeof cfg.gateway.auth.token === "object"
|
||||
? "gateway.auth.token unavailable"
|
||||
: undefined;
|
||||
const agentStatus = {
|
||||
...mocks.listGatewayAgentsBasic(),
|
||||
bootstrapPendingCount: 0,
|
||||
totalSessions: 1,
|
||||
agents: mocks.listGatewayAgentsBasic().agents.map((agent: { id: string; name?: string }) => ({
|
||||
...agent,
|
||||
bootstrapPending: false,
|
||||
activeSessions: 1,
|
||||
})),
|
||||
};
|
||||
const sessions = createSessionStatusRows();
|
||||
const channelIssues = gatewayReachable
|
||||
? [
|
||||
{
|
||||
channel: "signal",
|
||||
accountId: "default",
|
||||
message: "gateway: signal-cli unreachable",
|
||||
},
|
||||
{
|
||||
channel: "imessage",
|
||||
accountId: "default",
|
||||
message: "gateway: imessage permission denied",
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
channel: "signal",
|
||||
accountId: "default",
|
||||
message: "Channel error: signal-cli unreachable",
|
||||
},
|
||||
{
|
||||
channel: "imessage",
|
||||
accountId: "default",
|
||||
message: "Channel error: imessage permission denied",
|
||||
},
|
||||
];
|
||||
const pluginCompatibility =
|
||||
params.includePluginCompatibility === false ? [] : mocks.buildPluginCompatibilityNotices();
|
||||
return {
|
||||
cfg,
|
||||
sourceConfig: cfg,
|
||||
secretDiagnostics: gatewayAuthWarning ? ["gateway.auth.token unavailable"] : [],
|
||||
osSummary: {
|
||||
platform: "darwin",
|
||||
arch: "arm64",
|
||||
release: "23.0.0",
|
||||
label: "macos 14.0 (arm64)",
|
||||
},
|
||||
tailscaleMode: "off",
|
||||
tailscaleDns: null,
|
||||
tailscaleHttpsUrl: null,
|
||||
update: {
|
||||
root: "/tmp/openclaw",
|
||||
installKind: "git",
|
||||
packageManager: "pnpm",
|
||||
git: {
|
||||
root: "/tmp/openclaw",
|
||||
branch: "main",
|
||||
upstream: "origin/main",
|
||||
dirty: false,
|
||||
ahead: 0,
|
||||
behind: 0,
|
||||
fetchOk: true,
|
||||
},
|
||||
deps: {
|
||||
manager: "pnpm",
|
||||
status: "ok",
|
||||
lockfilePath: "/tmp/openclaw/pnpm-lock.yaml",
|
||||
markerPath: "/tmp/openclaw/node_modules/.modules.yaml",
|
||||
},
|
||||
registry: { latestVersion: "0.0.0" },
|
||||
},
|
||||
gatewayConnection: { url: "ws://127.0.0.1:18789" },
|
||||
remoteUrlMissing: false,
|
||||
gatewayMode: "local" as const,
|
||||
gatewayProbeAuth: process.env.OPENCLAW_GATEWAY_TOKEN
|
||||
? { token: process.env.OPENCLAW_GATEWAY_TOKEN }
|
||||
: {},
|
||||
gatewayProbeAuthWarning: gatewayAuthWarning,
|
||||
gatewayProbe,
|
||||
gatewayReachable,
|
||||
gatewaySelf: gatewayProbe.presence ? { host: "gateway", ip: "127.0.0.1" } : null,
|
||||
channelIssues,
|
||||
agentStatus,
|
||||
channels: {
|
||||
rows: [
|
||||
{ id: "whatsapp", label: "WhatsApp", enabled: true, state: "ok", detail: "linked" },
|
||||
{ id: "signal", label: "Signal", enabled: true, state: "warn", detail: "gateway warning" },
|
||||
{
|
||||
id: "imessage",
|
||||
label: "iMessage",
|
||||
enabled: true,
|
||||
state: "warn",
|
||||
detail: "gateway warning",
|
||||
},
|
||||
],
|
||||
details: [],
|
||||
},
|
||||
summary: {
|
||||
runtimeVersion: null,
|
||||
heartbeat: { defaultAgentId: "main", agents: [] },
|
||||
channelSummary: [],
|
||||
queuedSystemEvents: [],
|
||||
tasks: mocks.getInspectableTaskRegistrySummary(),
|
||||
taskAudit: mocks.getInspectableTaskAuditSummary(),
|
||||
sessions,
|
||||
},
|
||||
memory: null,
|
||||
memoryPlugin: { enabled: true, slot: "memory-core" },
|
||||
pluginCompatibility,
|
||||
};
|
||||
}
|
||||
|
||||
async function withEnvVar<T>(key: string, value: string, run: () => Promise<T>): Promise<T> {
|
||||
const prevValue = process.env[key];
|
||||
process.env[key] = value;
|
||||
@@ -517,7 +718,65 @@ vi.mock("../plugins/status.js", () => ({
|
||||
`${notice.pluginId} ${notice.message}`,
|
||||
}));
|
||||
|
||||
import { statusCommand } from "./status.js";
|
||||
vi.mock("./status.scan.fast-json.js", () => ({
|
||||
scanStatusJsonFast: vi.fn(async () =>
|
||||
createMockStatusScanResult({ includePluginCompatibility: false }),
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("./status.scan.js", () => ({
|
||||
scanStatus: vi.fn(async () => createMockStatusScanResult()),
|
||||
}));
|
||||
|
||||
vi.mock("./status-runtime-shared.ts", () => ({
|
||||
loadStatusProviderUsageModule: vi.fn(async () => ({
|
||||
formatUsageReportLines: vi.fn(() => []),
|
||||
})),
|
||||
resolveStatusGatewayHealth: vi.fn(async () => ({})),
|
||||
resolveStatusSecurityAudit: vi.fn(async (input: unknown) =>
|
||||
mocks.runSecurityAudit({
|
||||
...(typeof input === "object" && input ? input : {}),
|
||||
deep: false,
|
||||
includeFilesystem: true,
|
||||
includeChannelSecurity: true,
|
||||
}),
|
||||
),
|
||||
resolveStatusUsageSummary: vi.fn(async () => undefined),
|
||||
resolveStatusRuntimeSnapshot: vi.fn(
|
||||
async (params: {
|
||||
includeSecurityAudit?: boolean;
|
||||
resolveSecurityAudit?: (input: unknown) => Promise<unknown>;
|
||||
config: unknown;
|
||||
sourceConfig: unknown;
|
||||
}) => {
|
||||
const securityAudit = params.includeSecurityAudit
|
||||
? await (
|
||||
params.resolveSecurityAudit ??
|
||||
(async (input) =>
|
||||
await mocks.runSecurityAudit({
|
||||
...(typeof input === "object" && input ? input : {}),
|
||||
deep: false,
|
||||
includeFilesystem: true,
|
||||
includeChannelSecurity: true,
|
||||
}))
|
||||
)({
|
||||
config: params.config,
|
||||
sourceConfig: params.sourceConfig,
|
||||
})
|
||||
: undefined;
|
||||
return {
|
||||
securityAudit,
|
||||
usage: undefined,
|
||||
health: undefined,
|
||||
lastHeartbeat: null,
|
||||
gatewayService: await createStatusServiceSummary(mocks.resolveGatewayService()),
|
||||
nodeService: await createStatusServiceSummary(mocks.resolveNodeService()),
|
||||
};
|
||||
},
|
||||
),
|
||||
}));
|
||||
|
||||
import { statusCommand } from "./status.command.js";
|
||||
|
||||
const runtime = {
|
||||
log: vi.fn(),
|
||||
|
||||
Reference in New Issue
Block a user