Files
openclaw/src/agents/bash-tools.exec-task-tracking.test.ts
2026-07-18 09:35:14 +01:00

146 lines
4.0 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import type { ExecProcessOutcome } from "./bash-tools.exec-runtime.js";
const taskRuntime = vi.hoisted(() => ({
createRunningTaskRun: vi.fn(),
finalizeTaskRunByRunId: vi.fn(),
}));
vi.mock("../tasks/detached-task-runtime.js", () => taskRuntime);
import {
createBackgroundExecTask,
finalizeBackgroundExecTask,
} from "./bash-tools.exec-task-tracking.js";
describe("background exec task tracking", () => {
beforeEach(() => {
taskRuntime.createRunningTaskRun.mockReset();
taskRuntime.finalizeTaskRunByRunId.mockReset();
});
it("creates a silent CLI ledger row without persisting command text", () => {
taskRuntime.createRunningTaskRun.mockReturnValue({ taskId: "task-1" });
const handle = createBackgroundExecTask({
processSessionId: "amber-reef",
sessionKey: "agent:main:main",
agentId: "main",
startedAt: 100,
});
expect(handle).toEqual({
taskId: "task-1",
runId: "exec:amber-reef",
sessionKey: "agent:main:main",
});
expect(taskRuntime.createRunningTaskRun).toHaveBeenCalledWith({
runtime: "cli",
taskKind: "exec",
sourceId: "amber-reef",
requesterSessionKey: "agent:main:main",
ownerKey: "agent:main:main",
scopeKind: "session",
agentId: "main",
requesterAgentId: "main",
runId: "exec:amber-reef",
label: "CLI command",
task: "Background CLI command",
notifyPolicy: "silent",
deliveryStatus: "not_applicable",
startedAt: 100,
lastEventAt: 100,
progressSummary: "Command running",
});
});
it.each([
{
label: "success",
outcome: {
status: "completed",
exitCode: 0,
exitSignal: null,
durationMs: 25,
aggregated: "secret output",
timedOut: false,
} satisfies ExecProcessOutcome,
status: "succeeded",
error: undefined,
},
{
label: "timeout",
outcome: {
status: "failed",
exitCode: null,
exitSignal: "SIGTERM",
exitReason: "overall-timeout",
durationMs: 25,
aggregated: "secret output",
timedOut: true,
failureKind: "overall-timeout",
reason: "secret output\nCommand timed out",
} satisfies ExecProcessOutcome,
status: "timed_out",
error: "Command timed out",
},
{
label: "nonzero exit",
outcome: {
status: "completed",
exitCode: 17,
exitSignal: null,
durationMs: 25,
aggregated: "secret output",
timedOut: false,
} satisfies ExecProcessOutcome,
status: "failed",
error: "Command failed (exit code 17)",
},
{
label: "operator cancellation",
outcome: {
status: "failed",
exitCode: null,
exitSignal: "SIGTERM",
exitReason: "manual-cancel",
durationMs: 25,
aggregated: "secret output",
timedOut: false,
failureKind: "signal",
reason: "secret output\nCommand aborted",
} satisfies ExecProcessOutcome,
status: "cancelled",
error: "Cancelled by operator",
},
])(
"finalizes $label before wake without persisting process output",
({ outcome, status, error }) => {
finalizeBackgroundExecTask({
handle: {
taskId: "task-1",
runId: "exec:amber-reef",
sessionKey: "agent:main:main",
},
outcome,
});
expect(taskRuntime.finalizeTaskRunByRunId).toHaveBeenCalledWith(
expect.objectContaining({
runId: "exec:amber-reef",
runtime: "cli",
sessionKey: "agent:main:main",
status,
...(error ? { error } : { clearError: true }),
}),
);
expect(JSON.stringify(taskRuntime.finalizeTaskRunByRunId.mock.calls)).not.toContain(
"secret output",
);
expect(JSON.stringify(taskRuntime.finalizeTaskRunByRunId.mock.calls)).not.toContain(
"processSessionId",
);
},
);
});