Skip to content

Commit 9dcf424

Browse files
committed
docs: document embedded attempt spawn tests
1 parent 0f53d00 commit 9dcf424

8 files changed

Lines changed: 40 additions & 0 deletions

src/agents/embedded-agent-runner/run/attempt.spawn-workspace.context-injection.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Coverage for bootstrap context injection and heartbeat filtering in attempts.
12
import type { AgentMessage } from "openclaw/plugin-sdk/agent-core";
23
import { beforeEach, describe, expect, it, vi } from "vitest";
34
import { filterHeartbeatTranscriptArtifacts } from "../../../auto-reply/heartbeat-filter.js";
@@ -19,6 +20,8 @@ async function resolveBootstrapContext(params: {
1920
completed?: boolean;
2021
resolver?: () => Promise<{ bootstrapFiles: unknown[]; contextFiles: unknown[] }>;
2122
}) {
23+
// Helper exposes both resolved context and dependency calls so tests can
24+
// assert when bootstrap probing is skipped.
2225
const hasCompletedBootstrapTurn = vi.fn(async () => params.completed ?? false);
2326
const resolveBootstrapContextForRun =
2427
params.resolver ??
@@ -94,6 +97,8 @@ describe("embedded attempt context injection", () => {
9497
});
9598

9699
it("does not let a stale completed marker suppress pending workspace bootstrap", async () => {
100+
// Pending BOOTSTRAP.md content takes precedence over completed-turn markers
101+
// from earlier sessions.
97102
const resolver = vi.fn(async () => ({
98103
bootstrapFiles: [{ name: "BOOTSTRAP.md" }],
99104
contextFiles: [{ path: "BOOTSTRAP.md" }],
@@ -195,6 +200,8 @@ describe("embedded attempt context injection", () => {
195200
});
196201

197202
it("filters no-op heartbeat pairs before history limiting and context-engine assembly", async () => {
203+
// Heartbeat artifacts should not consume the limited turn budget passed into
204+
// context-engine assembly.
198205
const assemble = vi.fn(async ({ messages }: { messages: AgentMessage[] }) => ({
199206
messages,
200207
estimatedTokens: 1,

src/agents/embedded-agent-runner/run/attempt.spawn-workspace.resource-loader.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
// Coverage for explicit resource-loader wiring during attempt session creation.
12
import { describe, expect, it, vi } from "vitest";
23
import { createEmbeddedAgentSessionWithResourceLoader } from "./attempt-session.js";
34

45
describe("runEmbeddedAttempt resource loader wiring", () => {
56
it("passes an explicit resourceLoader to createAgentSession even without extension factories", async () => {
7+
// The resource loader is an explicit runtime dependency; it must be passed
8+
// even when no inline extension factories are present.
69
const resourceLoader = { reload: vi.fn() };
710
const createAgentSession = vi.fn(async () => ({ session: { id: "session" } }));
811

src/agents/embedded-agent-runner/run/attempt.spawn-workspace.sessions-spawn.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
// Coverage for sessions_spawn workspace inheritance from sandbox context.
12
import { describe, expect, it } from "vitest";
23
import { createAgentToolsSandboxContext } from "../../test-helpers/agent-tools-sandbox-context.js";
34
import { resolveAttemptSpawnWorkspaceDir } from "./attempt.thread-helpers.js";
45

56
describe("runEmbeddedAttempt sessions_spawn workspace inheritance", () => {
67
it("passes the real workspace to sessions_spawn when workspaceAccess is ro", () => {
8+
// Read-only sandbox copies should not become the child session workspace;
9+
// spawned sessions need the canonical real workspace.
710
const realWorkspace = "/tmp/openclaw-real-workspace";
811
const sandboxWorkspace = "/tmp/openclaw-sandbox-workspace";
912
const sandbox = createAgentToolsSandboxContext({

src/agents/embedded-agent-runner/run/attempt.spawn-workspace.test-support.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Shared harness and mocks for embedded attempt spawn-workspace tests.
12
import fs from "node:fs/promises";
23
import os from "node:os";
34
import path from "node:path";
@@ -45,6 +46,8 @@ type BootstrapContext = {
4546
};
4647

4748
function normalizeMockProviderId(providerId?: string): string {
49+
// Provider ids in mocked model routing follow the same lowercase normalization
50+
// as production helpers.
4851
return normalizeLowercaseStringOrEmpty(providerId);
4952
}
5053

@@ -96,6 +99,8 @@ type AttemptSpawnWorkspaceHoisted = {
9699
};
97100

98101
export function createSubscriptionMock(): SubscriptionMock {
102+
// Minimal subscription surface for runEmbeddedAttempt tests; individual tests
103+
// override only the lifecycle method they need.
99104
return {
100105
assistantTexts: [] as string[],
101106
toolMetas: [] as Array<{ toolName: string; meta?: string; asyncStarted?: boolean }>,
@@ -131,6 +136,8 @@ export function createSubscriptionMock(): SubscriptionMock {
131136
}
132137

133138
const hoisted = vi.hoisted((): AttemptSpawnWorkspaceHoisted => {
139+
// Hoisted mocks must exist before the runner module graph is imported, because
140+
// runEmbeddedAttempt captures these dependencies at module load.
134141
const spawnSubagentDirectMock = vi.fn();
135142
const createAgentSessionMock = vi.fn();
136143
const sessionManagerOpenMock = vi.fn();

src/agents/embedded-agent-runner/run/attempt.spawn-workspace.timeout.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
// Coverage for global Undici timeout wiring during embedded attempts.
12
import { beforeEach, describe, expect, it, vi } from "vitest";
23

34
const mocks = vi.hoisted(() => ({
5+
// Hoisted because attempt-http-runtime imports Undici wiring at module load.
46
DEFAULT_UNDICI_STREAM_TIMEOUT_MS: 30 * 60 * 1000,
57
ensureGlobalUndiciDispatcherStreamTimeouts: vi.fn(),
68
ensureGlobalUndiciEnvProxyDispatcher: vi.fn(),
@@ -21,6 +23,8 @@ describe("runEmbeddedAttempt undici timeout wiring", () => {
2123
});
2224

2325
it("does not lower global undici stream tuning below the shared default", () => {
26+
// Attempt timeouts can be shorter than the shared stream timeout, but global
27+
// dispatcher tuning must not be reduced for other in-flight streams.
2428
configureEmbeddedAttemptHttpRuntime({ timeoutMs: 123_456 });
2529

2630
expect(mocks.ensureGlobalUndiciEnvProxyDispatcher).toHaveBeenCalledOnce();

src/agents/embedded-agent-runner/run/attempt.stop-reason-recovery.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Coverage for converting sensitive/unhandled stop reasons into assistant errors.
12
import type { StreamFn } from "openclaw/plugin-sdk/agent-core";
23
import {
34
createAssistantMessageEventStream,
@@ -15,6 +16,8 @@ const anthropicModel = {
1516

1617
describe("wrapStreamFnHandleSensitiveStopReason", () => {
1718
it("rewrites unhandled stop-reason errors into structured assistant errors", async () => {
19+
// Some providers surface unhandled stop reasons as stream errors; convert
20+
// them into a normal assistant error so fallback/retry paths can inspect it.
1821
const baseStreamFn: StreamFn = () => {
1922
const stream = createAssistantMessageEventStream();
2023
queueMicrotask(() => {

src/agents/embedded-agent-runner/run/attempt.subscription-cleanup.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Coverage for ordered cleanup of embedded attempt subscriptions and resources.
12
import { afterEach, describe, expect, it, vi } from "vitest";
23
import { log } from "../logger.js";
34
import {
@@ -6,6 +7,7 @@ import {
67
} from "./attempt.subscription-cleanup.js";
78

89
function createDeferred<T>() {
10+
// Manual deferreds let cleanup tests prove ordering around abort settlement.
911
let resolve!: (value: T | PromiseLike<T>) => void;
1012
let reject!: (reason?: unknown) => void;
1113
const promise = new Promise<T>((resolvePromise, rejectPromise) => {
@@ -22,6 +24,8 @@ describe("cleanupEmbeddedAttemptResources", () => {
2224
});
2325

2426
it("waits for aborted prompt settlement before flushing and releasing the lock", async () => {
27+
// After an abort, pending prompt work gets a short chance to settle before
28+
// session flush/release/dispose run.
2529
const order: string[] = [];
2630
const settle = createDeferred<void>();
2731

@@ -97,6 +101,8 @@ describe("cleanupEmbeddedAttemptResources", () => {
97101
});
98102

99103
it("releases the lock before runtime teardown can hang", async () => {
104+
// Bundle runtime disposal can hang; release transcript locks first so other
105+
// turns are not blocked by diagnostic cleanup.
100106
const order: string[] = [];
101107
let markRuntimeDisposeStarted!: () => void;
102108
const runtimeDisposeStarted = new Promise<void>((resolve) => {

src/agents/embedded-agent-runner/run/attempt.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Broad helper coverage for runEmbeddedAttempt prompt, stream, and tool seams.
12
import { describe, expect, it, vi } from "vitest";
23
import { streamSimple } from "../../../llm/stream.js";
34

@@ -47,6 +48,8 @@ function createFakeStream(params: {
4748
events: unknown[];
4849
resultMessage: unknown;
4950
}): FakeWrappedStream {
51+
// Minimal stream compatible with wrappers that decorate result and iteration
52+
// without needing a real provider stream.
5053
return {
5154
async result() {
5255
return params.resultMessage;
@@ -67,6 +70,7 @@ async function invokeWrappedTestStream(
6770
) => (...args: never[]) => FakeWrappedStream | Promise<FakeWrappedStream>,
6871
baseFn: (...args: never[]) => unknown,
6972
): Promise<FakeWrappedStream> {
73+
// Helper keeps wrapper tests focused on mutated stream behavior.
7074
const wrappedFn = wrap(baseFn);
7175
return await Promise.resolve(wrappedFn({} as never, {} as never, {} as never));
7276
}
@@ -101,6 +105,7 @@ function expectSingleToolCallContent(content: unknown[], name: string) {
101105
}
102106

103107
function firstBaseContext(baseFn: ReturnType<typeof vi.fn>): { messages: unknown[] } {
108+
// Wrapper tests assert the context passed to the underlying stream function.
104109
const call = baseFn.mock.calls.at(0);
105110
if (!call) {
106111
throw new Error("expected base stream call");
@@ -178,6 +183,8 @@ describe("resolvePromptBuildHookResult", () => {
178183
});
179184

180185
it("merges prompt-build and before_agent_start context fields in deterministic order", async () => {
186+
// Prompt-build hook context comes before before_agent_start context so plugin
187+
// injections are replayed in stable order.
181188
const hookRunner = {
182189
hasHooks: vi.fn(() => true),
183190
runBeforePromptBuild: vi.fn(async () => ({

0 commit comments

Comments
 (0)