Skip to content

Commit a767c6d

Browse files
committed
docs: document agent command tests
1 parent 8fb70a9 commit a767c6d

7 files changed

Lines changed: 59 additions & 13 deletions

src/agents/codex-native-web-search.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Covers Codex-native web search activation and payload projection.
12
import { describe, expect, it } from "vitest";
23
import {
34
buildCodexNativeWebSearchTool,
@@ -47,6 +48,8 @@ describe("resolveCodexNativeSearchActivation", () => {
4748
});
4849

4950
it("activates for direct openai when auth exists", () => {
51+
// Direct OpenAI needs bridgeable auth before OpenClaw can suppress the
52+
// managed web-search tool in favor of Codex native search.
5053
const result = resolveCodexNativeSearchActivation({
5154
config: {
5255
...baseConfig,
@@ -79,6 +82,8 @@ describe("resolveCodexNativeSearchActivation", () => {
7982
});
8083

8184
it("activates for api-compatible openai-chatgpt-responses providers without separate Codex auth", () => {
85+
// Gateway-style providers already execute through a compatible Responses
86+
// API, so native search can be enabled without a separate OpenAI profile.
8287
const result = resolveCodexNativeSearchActivation({
8388
config: baseConfig,
8489
modelProvider: "gateway",
@@ -188,6 +193,8 @@ describe("Codex native web-search payload helpers", () => {
188193

189194
it("injects native web_search into provider payloads", () => {
190195
const payload: Record<string, unknown> = { tools: [{ type: "function", name: "read" }] };
196+
// Payload patching mutates the provider request in place because callers
197+
// already hold the request object that will be sent to the model runtime.
191198
const result = patchCodexNativeWebSearchPayload({ payload, config: baseConfig });
192199

193200
expect(result.status).toBe("injected");
@@ -228,6 +235,8 @@ describe("shouldSuppressManagedWebSearchTool", () => {
228235

229236
describe("isCodexNativeWebSearchRelevant", () => {
230237
it("treats a default model with model-level openai-chatgpt-responses api as relevant", () => {
238+
// Provider-level APIs can be generic while individual models opt into the
239+
// ChatGPT Responses shape that supports native web_search.
231240
expect(
232241
isCodexNativeWebSearchRelevant({
233242
config: {

src/agents/command-poll-backoff.test.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Verifies command polling backoff state used by diagnostic/session commands.
12
import { describe, expect, it } from "vitest";
23
import type { SessionState } from "../logging/diagnostic-session-state.js";
34
import {
@@ -39,7 +40,8 @@ describe("command-poll-backoff", () => {
3940
};
4041
const retryMs = recordCommandPoll(state, "cmd-123", false);
4142
expect(retryMs).toBe(5000);
42-
expect(state.commandPollCounts?.get("cmd-123")?.count).toBe(0); // First poll = index 0
43+
// Poll counts are zero-based indexes into the backoff schedule.
44+
expect(state.commandPollCounts?.get("cmd-123")?.count).toBe(0);
4345
});
4446

4547
it("increments count and increases backoff on consecutive no-output polls", () => {
@@ -49,13 +51,13 @@ describe("command-poll-backoff", () => {
4951
queueDepth: 0,
5052
};
5153

52-
expect(recordCommandPoll(state, "cmd-123", false)).toBe(5000); // count=0 -> 5s
53-
expect(recordCommandPoll(state, "cmd-123", false)).toBe(10000); // count=1 -> 10s
54-
expect(recordCommandPoll(state, "cmd-123", false)).toBe(30000); // count=2 -> 30s
55-
expect(recordCommandPoll(state, "cmd-123", false)).toBe(60000); // count=3 -> 60s
56-
expect(recordCommandPoll(state, "cmd-123", false)).toBe(60000); // count=4 -> 60s (capped)
54+
expect(recordCommandPoll(state, "cmd-123", false)).toBe(5000);
55+
expect(recordCommandPoll(state, "cmd-123", false)).toBe(10000);
56+
expect(recordCommandPoll(state, "cmd-123", false)).toBe(30000);
57+
expect(recordCommandPoll(state, "cmd-123", false)).toBe(60000);
58+
expect(recordCommandPoll(state, "cmd-123", false)).toBe(60000);
5759

58-
expect(state.commandPollCounts?.get("cmd-123")?.count).toBe(4); // 5 polls = index 4
60+
expect(state.commandPollCounts?.get("cmd-123")?.count).toBe(4);
5961
});
6062

6163
it("resets count when poll returns new output", () => {
@@ -70,9 +72,9 @@ describe("command-poll-backoff", () => {
7072
recordCommandPoll(state, "cmd-123", false);
7173
expect(state.commandPollCounts?.get("cmd-123")?.count).toBe(2); // 3 polls = index 2
7274

73-
// New output resets count
75+
// New output resets count so the next quiet poll starts at the fast lane.
7476
const retryMs = recordCommandPoll(state, "cmd-123", true);
75-
expect(retryMs).toBe(5000); // Back to first poll delay
77+
expect(retryMs).toBe(5000);
7678
expect(state.commandPollCounts?.get("cmd-123")?.count).toBe(0);
7779
});
7880

@@ -150,12 +152,12 @@ describe("command-poll-backoff", () => {
150152
state: "processing",
151153
queueDepth: 0,
152154
commandPollCounts: new Map([
153-
["cmd-old", { count: 5, lastPollAt: Date.now() - 7200000 }], // 2 hours ago
154-
["cmd-new", { count: 3, lastPollAt: Date.now() - 1000 }], // 1 second ago
155+
["cmd-old", { count: 5, lastPollAt: Date.now() - 7200000 }],
156+
["cmd-new", { count: 3, lastPollAt: Date.now() - 1000 }],
155157
]),
156158
};
157159

158-
pruneStaleCommandPolls(state, 3600000); // 1 hour max age
160+
pruneStaleCommandPolls(state, 3600000);
159161

160162
expect(state.commandPollCounts?.has("cmd-old")).toBe(false);
161163
expect(state.commandPollCounts?.has("cmd-new")).toBe(true);

src/agents/command/attempt-callbacks.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Verifies the small lifecycle callback adapter used during agent attempts.
12
import { describe, expect, it } from "vitest";
23
import { createAgentAttemptLifecycleCallbacks } from "./attempt-callbacks.js";
34

@@ -10,6 +11,8 @@ describe("createAgentAttemptLifecycleCallbacks", () => {
1011
};
1112
const callbacks = createAgentAttemptLifecycleCallbacks(state);
1213

14+
// The callback mutates only the shared lifecycle state object; it should not
15+
// need access to the wider runAgentAttempt closure.
1316
callbacks.onUserMessagePersisted?.({
1417
role: "user",
1518
content: "hello",

src/agents/command/attempt-execution.cli.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Covers CLI-backed attempt execution and session-binding persistence.
12
import fs from "node:fs/promises";
23
import os from "node:os";
34
import path from "node:path";
@@ -49,6 +50,8 @@ vi.mock("../model-runtime-aliases.js", async () => {
4950
modelId?: string;
5051
}) => {
5152
const key = provider && modelId ? `${provider}/${modelId}` : undefined;
53+
// Runtime alias tests only need the model-level runtime override path;
54+
// keeping the mock narrow avoids loading provider catalogs here.
5255
const runtime = key
5356
? cfg?.agents?.defaults?.models?.[key]?.agentRuntime?.id?.trim()
5457
: undefined;
@@ -109,6 +112,8 @@ async function readSessionFileEntries(sessionFile: string) {
109112
}
110113

111114
async function readSessionFileJsonLines<T>(sessionFile: string): Promise<T[]> {
115+
// Session transcripts are JSONL; tests preserve that format so parent/child
116+
// id ordering and append behavior are covered end-to-end.
112117
const raw = await fs.readFile(sessionFile, "utf-8");
113118
const entries: T[] = [];
114119
for (const line of raw.split(/\r?\n/)) {
@@ -218,6 +223,8 @@ describe("CLI attempt execution", () => {
218223
}
219224

220225
async function writeClaudeCliAssistantTranscript(cliSessionId: string) {
226+
// Claude stores resumable sessions under a workspace-derived project dir,
227+
// so stale-session tests must create the same on-disk shape.
221228
const homeDir = path.join(tmpDir, `home-${cliSessionId}`);
222229
const projectsDir = resolveClaudeCliProjectDirForWorkspace({
223230
workspaceDir: tmpDir,
@@ -279,6 +286,8 @@ describe("CLI attempt execution", () => {
279286
const sessionStore: Record<string, SessionEntry> = { [sessionKey]: sessionEntry };
280287
await fs.writeFile(storePath, JSON.stringify(sessionStore, null, 2), "utf-8");
281288

289+
// The retry hook must clear poisoned bindings before the fresh CLI attempt
290+
// runs, otherwise the runner would resume the same expired Claude session.
282291
runCliAgentMock.mockImplementationOnce(async (args: unknown) => {
283292
const retry = requireRecord(args, "run CLI agent argument").onBeforeFreshCliSessionRetry;
284293
expect(retry).toBeTypeOf("function");
@@ -425,6 +434,8 @@ describe("CLI attempt execution", () => {
425434
const sessionEntry = makeClaudeCliSessionEntry("session-storeless", cliSessionId);
426435
runCliAgentMock.mockResolvedValueOnce(makeCliResult("storeless ok"));
427436

437+
// Storeless attempts cannot persist binding cleanup, so installing the hook
438+
// would only give callers a false sense that stale state was repaired.
428439
await runAgentAttempt({
429440
providerOverride: "claude-cli",
430441
originalProvider: "claude-cli",

src/agents/command/attempt-execution.error-propagation.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Covers ACP diagnostic event propagation and sanitized error formatting.
12
import { afterEach, beforeEach, describe, expect, it } from "vitest";
23
import { AcpRuntimeError } from "../../acp/runtime/errors.js";
34
import {
@@ -18,6 +19,8 @@ let unsubscribe: (() => void) | undefined;
1819
beforeEach(() => {
1920
resetAgentEventsForTest();
2021
captured = [];
22+
// Subscribe to the process-level event bus so tests observe exactly what
23+
// parent relay diagnostics would receive.
2124
unsubscribe = onAgentEvent((evt) => {
2225
captured.push(evt);
2326
});
@@ -95,6 +98,8 @@ describe("emitAcpLifecycleError preserves AcpRuntimeError detail (regression: op
9598
});
9699

97100
it("flattens the cause chain into the error string so the underlying RequestError is not lost", () => {
101+
// ACP callers historically surface a single string; flattening preserves
102+
// the useful nested RequestError without exposing structured internals.
98103
const rootCause = new Error('RequestError: "Method not found": nes/close (-32601)');
99104
const wrapped = new Error("Agent does not support session/close (oneshot:abc)", {
100105
cause: rootCause,

src/agents/command/attempt-execution.shared.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// Covers shared attempt-execution helpers for prompt materialization and
2+
// guarded session-store persistence.
13
import fs from "node:fs";
24
import os from "node:os";
35
import path from "node:path";
@@ -14,6 +16,8 @@ import {
1416
import type { AgentCommandOpts } from "./types.js";
1517

1618
function makeTaskCompletionEvents(): NonNullable<AgentCommandOpts["internalEvents"]> {
19+
// The result deliberately contains internal markers to prove child output
20+
// cannot spoof OpenClaw runtime-context envelopes.
1721
return [
1822
{
1923
type: "task_completion",
@@ -50,6 +54,8 @@ describe("attempt execution prompt materialization", () => {
5054

5155
const prompt = resolveAcpPromptBody(body, events);
5256

57+
// ACP receives visible event text, while private runtime envelopes stay out
58+
// of the model-facing prompt.
5359
expect(prompt).toContain("A background task completed.");
5460
expect(prompt).toContain("inspect ACP delivery");
5561
expect(prompt).toContain("child result");
@@ -92,6 +98,8 @@ describe("persistSessionEntry", () => {
9298
},
9399
};
94100

101+
// A guarded write can decline persistence after rereading disk; local
102+
// memory must be cleared too so later turns do not reuse stale entries.
95103
const persisted = await persistSessionEntry({
96104
sessionStore,
97105
sessionKey: "main",

src/agents/command/attempt-execution.test.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// Covers attempt-execution helper behavior around retries, Claude CLI
2+
// transcripts, and ACP visible text accumulation.
13
import fs from "node:fs/promises";
24
import os from "node:os";
35
import path from "node:path";
@@ -86,6 +88,8 @@ describe("resolveFallbackRetryPrompt", () => {
8688

8789
it("prepends priorContextPrelude before the retry marker on fallback retry", () => {
8890
const prelude = "## Prior session context (from claude-cli)\nuser: prior question";
91+
// Claude fallback prelude must come before the retry marker so the model
92+
// receives prior CLI context before the instruction about failure recovery.
8993
const result = resolveFallbackRetryPrompt({
9094
body: originalBody,
9195
isFallbackRetry: true,
@@ -150,6 +154,8 @@ describe("formatClaudeCliFallbackPrelude", () => {
150154
});
151155

152156
it("formats user/assistant turns and tags tool blocks with compact hints", () => {
157+
// Tool-use blocks are represented as compact hints because fallback prompts
158+
// should preserve intent without replaying full tool schemas or outputs.
153159
const out = formatClaudeCliFallbackPrelude({
154160
recentTurns: [
155161
{
@@ -200,7 +206,7 @@ describe("formatClaudeCliFallbackPrelude", () => {
200206
content: `turn ${i + 1} ${"x".repeat(80)}`,
201207
}));
202208
const out = formatClaudeCliFallbackPrelude({ recentTurns: turns }, { charBudget: 350 });
203-
// Newest turn (turn 10) must be present; oldest (turn 1) must not be.
209+
// Newest turn must be present; oldest turns are the first budget casualty.
204210
expect(out).toContain("turn 10");
205211
expect(out).not.toContain("turn 1 ");
206212
});
@@ -248,6 +254,8 @@ describe("buildClaudeCliFallbackContextPrelude", () => {
248254
const sessionId = "e2e-session";
249255
const projectsDir = path.join(tmpHome, ".claude", "projects", "demo");
250256
try {
257+
// Use Claude's JSONL shape directly so parser and formatter behavior stay
258+
// aligned with real CLI transcripts rather than synthetic message arrays.
251259
await fs.mkdir(projectsDir, { recursive: true });
252260
const lines = [
253261
{

0 commit comments

Comments
 (0)