diff --git a/src/acp/translator.event-ledger.test.ts b/src/acp/translator.event-ledger.test.ts index 1a163ec0dad3..589aa1dcc15d 100644 --- a/src/acp/translator.event-ledger.test.ts +++ b/src/acp/translator.event-ledger.test.ts @@ -169,21 +169,22 @@ describe("ACP translator event ledger replay", () => { await secondAgent.loadSession(createLoadSessionRequest(created.sessionId)); - expect(secondRequestMock).not.toHaveBeenCalledWith("sessions.get", expect.anything()); + expect(secondRequestMock.mock.calls.map((call) => call[0])).not.toContain("sessions.get"); const replayedUpdates = secondConnection.__sessionUpdateMock.mock.calls.map( (call) => call[0]?.update, ); const replayedUpdateTypes = replayedUpdates.map((update) => update?.sessionUpdate); - expect(replayedUpdateTypes).toEqual( - expect.arrayContaining([ - "session_info_update", - "available_commands_update", - "user_message_chunk", - "tool_call", - "tool_call_update", - "agent_message_chunk", - ]), - ); + expect(replayedUpdateTypes).toEqual([ + "session_info_update", + "available_commands_update", + "user_message_chunk", + "tool_call", + "tool_call_update", + "agent_message_chunk", + "session_info_update", + "session_info_update", + "available_commands_update", + ]); expect(replayedUpdates).toContainEqual({ sessionUpdate: "user_message_chunk", content: { type: "text", text: "Question" }, @@ -223,13 +224,21 @@ describe("ACP translator event ledger replay", () => { await listedAgent.loadSession(createLoadSessionRequest(firstSession.sessionKey)); - expect(listedRequestMock).not.toHaveBeenCalledWith("sessions.get", expect.anything()); + expect(listedRequestMock.mock.calls.map((call) => call[0])).not.toContain("sessions.get"); const listedReplayTypes = listedConnection.__sessionUpdateMock.mock.calls.map( (call) => call[0]?.update?.sessionUpdate, ); - expect(listedReplayTypes).toEqual( - expect.arrayContaining(["user_message_chunk", "tool_call", "agent_message_chunk"]), - ); + expect(listedReplayTypes).toEqual([ + "session_info_update", + "available_commands_update", + "user_message_chunk", + "tool_call", + "tool_call_update", + "agent_message_chunk", + "session_info_update", + "session_info_update", + "available_commands_update", + ]); const listedPrompt = listedAgent.prompt( createPromptRequest(firstSession.sessionKey, "Follow-up"), @@ -265,7 +274,7 @@ describe("ACP translator event ledger replay", () => { ).toHaveLength(2); await expect( eventLedger.readReplayBySessionId({ sessionId: firstSession.sessionKey }), - ).resolves.toMatchObject({ complete: false }); + ).resolves.toEqual({ complete: false, events: [] }); firstSessionStore.clearAllSessionsForTest(); }); diff --git a/src/auto-reply/reply/queue.collect.test.ts b/src/auto-reply/reply/queue.collect.test.ts index 6be6bdd5b919..82c85e0e91b8 100644 --- a/src/auto-reply/reply/queue.collect.test.ts +++ b/src/auto-reply/reply/queue.collect.test.ts @@ -574,9 +574,9 @@ describe("followup queue collect routing", () => { await done.promise; expect(calls.map((call) => call.prompt)).toEqual([ - expect.stringContaining("first"), - expect.stringContaining("second"), - expect.stringContaining("third"), + "[Queued messages while agent was busy]\n\n---\nQueued #1 (from A)\nfirst", + "[Queued messages while agent was busy]\n\n---\nQueued #1 (from Owner)\nsecond", + "[Queued messages while agent was busy]\n\n---\nQueued #1 (from A)\nthird", ]); }); @@ -814,8 +814,8 @@ describe("followup queue collect routing", () => { expect(guestAttempts).toHaveLength(1); expect(ownerAttempts).toHaveLength(2); expect(successfulCalls.map((call) => call.prompt)).toEqual([ - expect.stringContaining("guest message"), - expect.stringContaining("owner message"), + "[Queued messages while agent was busy]\n\n---\nQueued #1 (from Guest)\nguest message", + "[Queued messages while agent was busy]\n\n---\nQueued #1 (from Owner)\nowner message", ]); }); diff --git a/src/cli/completion-cli.write-state.test.ts b/src/cli/completion-cli.write-state.test.ts index c50ccce7ada8..5f999a1cbf17 100644 --- a/src/cli/completion-cli.write-state.test.ts +++ b/src/cli/completion-cli.write-state.test.ts @@ -94,7 +94,7 @@ describe("completion-cli write-state", () => { await program.parseAsync(["completion", "--write-state"], { from: "user" }); const cacheDir = path.join(stateDir, "completions"); - expect((await fs.readdir(cacheDir)).sort()).toEqual([ + expect((await fs.readdir(cacheDir)).toSorted()).toEqual([ "openclaw.bash", "openclaw.fish", "openclaw.ps1", @@ -137,7 +137,7 @@ describe("completion-cli write-state", () => { [program, "qa", process.argv, { purpose: "completion" }], ]); expect(registerPluginCliCommandsFromValidatedConfigMock).not.toHaveBeenCalled(); - expect((await fs.readdir(path.join(stateDir, "completions"))).sort()).toEqual([ + expect((await fs.readdir(path.join(stateDir, "completions"))).toSorted()).toEqual([ "openclaw.bash", "openclaw.fish", "openclaw.ps1", diff --git a/src/cli/run-main.exit.test.ts b/src/cli/run-main.exit.test.ts index 0a1391791dc4..f14fb078e3cf 100644 --- a/src/cli/run-main.exit.test.ts +++ b/src/cli/run-main.exit.test.ts @@ -570,15 +570,16 @@ describe("runCli exit behavior", () => { ["tools", ["node", "openclaw", "tools", "effective"]], ])("keeps reserved %s command roots out of plugin command discovery", async (_name, argv) => { const parseAsync = vi.fn().mockResolvedValueOnce(undefined); - buildProgramMock.mockReturnValueOnce({ + const program = { commands: [], parseAsync, - }); + }; + buildProgramMock.mockReturnValueOnce(program); await runCli(argv); expect(startProxyMock).not.toHaveBeenCalled(); - expect(registerSubCliByNameMock).toHaveBeenCalledWith(expect.anything(), argv[2], argv); + expect(registerSubCliByNameMock.mock.calls).toEqual([[program, argv[2], argv]]); expect(registerPluginCliCommandsFromValidatedConfigMock).not.toHaveBeenCalled(); expect(parseAsync).toHaveBeenCalledWith(argv); }); @@ -801,47 +802,41 @@ describe("runCli exit behavior", () => { it("swallows Commander parse exits after recording the exit code", async () => { const exitCode = process.exitCode; - buildProgramMock.mockReturnValueOnce({ + const program = { commands: [{ name: () => "status" }], parseAsync: vi .fn() .mockRejectedValueOnce( new CommanderError(1, "commander.excessArguments", "too many arguments for 'status'"), ), - }); + }; + buildProgramMock.mockReturnValueOnce(program); await expect(runCli(["node", "openclaw", "status"])).resolves.toBeUndefined(); - expect(registerSubCliByNameMock).toHaveBeenCalledWith(expect.anything(), "status", [ - "node", - "openclaw", - "status", + expect(registerSubCliByNameMock.mock.calls).toEqual([ + [program, "status", ["node", "openclaw", "status"]], ]); expect(process.exitCode).toBe(1); process.exitCode = exitCode; }); it("loads the real primary command before rendering command help", async () => { - buildProgramMock.mockReturnValueOnce({ + const program = { commands: [{ name: () => "doctor" }], parseAsync: vi.fn().mockResolvedValueOnce(undefined), - }); + }; + buildProgramMock.mockReturnValueOnce(program); const ctx = { programVersion: "0.0.0-test" }; getProgramContextMock.mockReturnValueOnce(ctx as never); await runCli(["node", "openclaw", "doctor", "--help"]); - expect(registerCoreCliByNameMock).toHaveBeenCalledWith(expect.anything(), ctx, "doctor", [ - "node", - "openclaw", - "doctor", - "--help", + expect(registerCoreCliByNameMock.mock.calls).toEqual([ + [program, ctx, "doctor", ["node", "openclaw", "doctor", "--help"]], ]); - expect(registerSubCliByNameMock).toHaveBeenCalledWith(expect.anything(), "doctor", [ - "node", - "openclaw", - "doctor", - "--help", + expect(registerSubCliByNameMock.mock.calls).toEqual([ + [program, "doctor", ["node", "openclaw", "doctor", "--help"]], ]); }); @@ -907,10 +902,9 @@ describe("runCli exit behavior", () => { code: "EHOSTUNREACH", }); expect(handler(hostUnreachable)).toBeUndefined(); - expect(consoleWarnSpy).toHaveBeenCalledWith( - "[openclaw] Non-fatal uncaught exception (continuing):", - expect.stringContaining("EHOSTUNREACH"), - ); + expect(consoleWarnSpy.mock.calls).toEqual([ + ["[openclaw] Non-fatal uncaught exception (continuing):", hostUnreachable.stack], + ]); expect(restoreTerminalStateMock).not.toHaveBeenCalled(); expect(exitSpy).not.toHaveBeenCalled(); } finally { diff --git a/src/gateway/gateway-stability.test.ts b/src/gateway/gateway-stability.test.ts index 375d3caa660b..41e05cf3a6bd 100644 --- a/src/gateway/gateway-stability.test.ts +++ b/src/gateway/gateway-stability.test.ts @@ -124,10 +124,8 @@ describe("gateway stability lane", () => { expect(lastSeq).toBeGreaterThan(firstSeq); expect(snapshot.summary.byType["diagnostic.memory.sample"]).toBeGreaterThan(0); expect(snapshot.summary.byType["message.queued"]).toBeGreaterThan(0); - expect(snapshot.summary.memory).toMatchObject({ - maxRssBytes: maxSyntheticRssBytes, - pressureCount: 0, - }); + expect(snapshot.summary.memory?.maxRssBytes).toBe(maxSyntheticRssBytes); + expect(snapshot.summary.memory?.pressureCount).toBe(0); expect(snapshot.summary.memory?.maxHeapUsedBytes).toBeLessThan(96 * MB); expect(snapshot.summary.payloadLarge?.chunked).toBeGreaterThan(0); expect(snapshot.summary.payloadLarge?.bySurface["gateway.stability.probe"]).toBeGreaterThan(0); diff --git a/src/gateway/openresponses-phase.test.ts b/src/gateway/openresponses-phase.test.ts index 69aa7886b885..1b7c70a4eed9 100644 --- a/src/gateway/openresponses-phase.test.ts +++ b/src/gateway/openresponses-phase.test.ts @@ -58,10 +58,11 @@ describe("openresponses phase support", () => { phase: "commentary", status: "completed", }), - ).toMatchObject({ + ).toEqual({ type: "message", id: "msg_commentary", role: "assistant", + content: [{ type: "output_text", text: "Checking logs." }], phase: "commentary", status: "completed", }); @@ -73,10 +74,11 @@ describe("openresponses phase support", () => { phase: "final_answer", status: "completed", }), - ).toMatchObject({ + ).toEqual({ type: "message", id: "msg_final", role: "assistant", + content: [{ type: "output_text", text: "Root cause found." }], phase: "final_answer", status: "completed", }); diff --git a/src/gateway/server-startup-early.test.ts b/src/gateway/server-startup-early.test.ts index 44dff5751ff1..a3507bad5faa 100644 --- a/src/gateway/server-startup-early.test.ts +++ b/src/gateway/server-startup-early.test.ts @@ -1,8 +1,10 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; +type StartGatewayDiscovery = typeof import("./server-discovery-runtime.js").startGatewayDiscovery; + const mocks = vi.hoisted(() => ({ getMachineDisplayName: vi.fn(async () => "Test Machine"), - startGatewayDiscovery: vi.fn(async () => ({ bonjourStop: null })), + startGatewayDiscovery: vi.fn(async () => ({ bonjourStop: null })), })); vi.mock("../infra/machine-name.js", () => ({ @@ -87,15 +89,15 @@ describe("startGatewayEarlyRuntime", () => { }), ).resolves.toBe(stop); - expect(mocks.startGatewayDiscovery).toHaveBeenCalledWith( - expect.objectContaining({ - machineDisplayName: "Test Machine", - port: 19_001, - gatewayTls: { enabled: true, fingerprintSha256: "abc123" }, - tailscaleMode: "serve", - mdnsMode: "full", - gatewayDiscoveryServices: [service], - }), - ); + const [discoveryParams] = mocks.startGatewayDiscovery.mock.calls.at(-1) ?? []; + if (discoveryParams === undefined) { + throw new Error("Expected gateway discovery to start"); + } + expect(discoveryParams.machineDisplayName).toBe("Test Machine"); + expect(discoveryParams.port).toBe(19_001); + expect(discoveryParams.gatewayTls).toEqual({ enabled: true, fingerprintSha256: "abc123" }); + expect(discoveryParams.tailscaleMode).toBe("serve"); + expect(discoveryParams.mdnsMode).toBe("full"); + expect(discoveryParams.gatewayDiscoveryServices).toEqual([service]); }); }); diff --git a/src/gateway/server-startup.test.ts b/src/gateway/server-startup.test.ts index dfa361b98594..9c822379b673 100644 --- a/src/gateway/server-startup.test.ts +++ b/src/gateway/server-startup.test.ts @@ -36,6 +36,19 @@ vi.mock("../agents/pi-embedded-runner/runtime.js", () => ({ let prewarmConfiguredPrimaryModel: typeof import("./server-startup-post-attach.js").__testing.prewarmConfiguredPrimaryModel; let shouldSkipStartupModelPrewarm: typeof import("./server-startup-post-attach.js").__testing.shouldSkipStartupModelPrewarm; +function expectModelsJsonPrewarmCall(cfg: OpenClawConfig) { + expect(ensureOpenClawModelsJsonMock).toHaveBeenCalledTimes(1); + const [calledConfig, agentDir, options] = ensureOpenClawModelsJsonMock.mock.calls[0] ?? []; + expect(calledConfig).toBe(cfg); + expect(agentDir).toBe("/tmp/agent"); + expect(options).toEqual({ + workspaceDir: "/tmp/workspace", + providerDiscoveryProviderIds: ["openai-codex"], + providerDiscoveryTimeoutMs: 5000, + providerDiscoveryEntriesOnly: true, + }); +} + describe("gateway startup primary model warmup", () => { beforeAll(async () => { ({ @@ -66,16 +79,7 @@ describe("gateway startup primary model warmup", () => { log: { warn: vi.fn() }, }); - expect(ensureOpenClawModelsJsonMock).toHaveBeenCalledWith( - cfg, - "/tmp/agent", - expect.objectContaining({ - workspaceDir: "/tmp/workspace", - providerDiscoveryProviderIds: ["openai-codex"], - providerDiscoveryTimeoutMs: 5000, - providerDiscoveryEntriesOnly: true, - }), - ); + expectModelsJsonPrewarmCall(cfg); expect(piModelModuleLoadedMock).not.toHaveBeenCalled(); }); @@ -163,16 +167,7 @@ describe("gateway startup primary model warmup", () => { log: { warn: vi.fn() }, }); - expect(ensureOpenClawModelsJsonMock).toHaveBeenCalledWith( - cfg, - "/tmp/agent", - expect.objectContaining({ - workspaceDir: "/tmp/workspace", - providerDiscoveryProviderIds: ["openai-codex"], - providerDiscoveryTimeoutMs: 5000, - providerDiscoveryEntriesOnly: true, - }), - ); + expectModelsJsonPrewarmCall(cfg); expect(piModelModuleLoadedMock).not.toHaveBeenCalled(); }); diff --git a/src/gateway/server.sessions.create.test.ts b/src/gateway/server.sessions.create.test.ts index a26dbff0bb20..05be3319dd40 100644 --- a/src/gateway/server.sessions.create.test.ts +++ b/src/gateway/server.sessions.create.test.ts @@ -69,22 +69,19 @@ test("sessions.create stores dashboard session model and parent linkage, and cre } >; const key = created.payload?.key as string; - expect(rawStore[key]).toMatchObject({ - sessionId: created.payload?.sessionId, - label: "Dashboard Chat", - providerOverride: "openai", - modelOverride: "gpt-test-a", - parentSessionKey: "agent:main:main", - }); + expect(rawStore[key]?.sessionId).toBe(created.payload?.sessionId); + expect(rawStore[key]?.label).toBe("Dashboard Chat"); + expect(rawStore[key]?.providerOverride).toBe("openai"); + expect(rawStore[key]?.modelOverride).toBe("gpt-test-a"); + expect(rawStore[key]?.parentSessionKey).toBe("agent:main:main"); expect(sessionFile).toBe(rawStore[key]?.sessionFile); const transcriptPath = path.join(dir, `${created.payload?.sessionId}.jsonl`); const transcript = await fs.readFile(transcriptPath, "utf-8"); const [headerLine] = transcript.trim().split(/\r?\n/, 1); - expect(JSON.parse(headerLine) as { type?: string; id?: string }).toMatchObject({ - type: "session", - id: created.payload?.sessionId, - }); + const header = JSON.parse(headerLine) as { type?: string; id?: string }; + expect(header.type).toBe("session"); + expect(header.id).toBe(created.payload?.sessionId); }); test("sessions.create accepts an explicit key for persistent dashboard sessions", async () => {