mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-21 22:21:33 +00:00
Commands: split slow agent runtime tests
This commit is contained in:
231
src/commands/agent.runtime-config.test.ts
Normal file
231
src/commands/agent.runtime-config.test.ts
Normal file
@@ -0,0 +1,231 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { withTempHome as withTempHomeBase } from "../../test/helpers/temp-home.js";
|
||||
import "../cron/isolated-agent.mocks.js";
|
||||
import { __testing as acpManagerTesting } from "../acp/control-plane/manager.js";
|
||||
import { __testing as agentCommandTesting } from "../agents/agent-command.js";
|
||||
import { loadModelCatalog } from "../agents/model-catalog.js";
|
||||
import * as modelSelectionModule from "../agents/model-selection.js";
|
||||
import { runEmbeddedPiAgent } from "../agents/pi-embedded.js";
|
||||
import * as commandConfigResolutionModule from "../cli/command-config-resolution.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import * as configModule from "../config/config.js";
|
||||
import { clearSessionStoreCacheForTest } from "../config/sessions.js";
|
||||
import { resetAgentEventsForTest, resetAgentRunContextForTest } from "../infra/agent-events.js";
|
||||
import { resetPluginRuntimeStateForTest } from "../plugins/runtime.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { agentCommand } from "./agent.js";
|
||||
|
||||
vi.mock("../logging/subsystem.js", () => {
|
||||
const createMockLogger = () => ({
|
||||
subsystem: "test",
|
||||
isEnabled: vi.fn(() => true),
|
||||
trace: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
fatal: vi.fn(),
|
||||
raw: vi.fn(),
|
||||
child: vi.fn(() => createMockLogger()),
|
||||
});
|
||||
return {
|
||||
createSubsystemLogger: vi.fn(() => createMockLogger()),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../agents/auth-profiles.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("../agents/auth-profiles.js")>(
|
||||
"../agents/auth-profiles.js",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
ensureAuthProfileStore: vi.fn(() => ({ version: 1, profiles: {} })),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../agents/workspace.js", () => {
|
||||
const resolveDefaultAgentWorkspaceDir = () => "/tmp/openclaw-workspace";
|
||||
return {
|
||||
DEFAULT_AGENT_WORKSPACE_DIR: "/tmp/openclaw-workspace",
|
||||
DEFAULT_AGENTS_FILENAME: "AGENTS.md",
|
||||
DEFAULT_IDENTITY_FILENAME: "IDENTITY.md",
|
||||
resolveDefaultAgentWorkspaceDir,
|
||||
ensureAgentWorkspace: vi.fn(async ({ dir }: { dir: string }) => ({ dir })),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../agents/command/session-store.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("../agents/command/session-store.js")>(
|
||||
"../agents/command/session-store.js",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
updateSessionStoreAfterAgentRun: vi.fn(async () => undefined),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../agents/skills.js", () => ({
|
||||
buildWorkspaceSkillSnapshot: vi.fn(() => undefined),
|
||||
loadWorkspaceSkillEntries: vi.fn(() => []),
|
||||
}));
|
||||
|
||||
vi.mock("../agents/skills/refresh.js", () => ({
|
||||
getSkillsSnapshotVersion: vi.fn(() => 0),
|
||||
}));
|
||||
|
||||
const runtime: RuntimeEnv = {
|
||||
log: vi.fn(),
|
||||
error: vi.fn(),
|
||||
exit: vi.fn(() => {
|
||||
throw new Error("exit");
|
||||
}),
|
||||
};
|
||||
|
||||
const configSpy = vi.spyOn(configModule, "loadConfig");
|
||||
const readConfigFileSnapshotForWriteSpy = vi.spyOn(configModule, "readConfigFileSnapshotForWrite");
|
||||
|
||||
async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
|
||||
return withTempHomeBase(fn, { prefix: "openclaw-agent-" });
|
||||
}
|
||||
|
||||
function mockConfig(
|
||||
home: string,
|
||||
storePath: string,
|
||||
agentOverrides?: Partial<NonNullable<NonNullable<OpenClawConfig["agents"]>["defaults"]>>,
|
||||
) {
|
||||
const cfg = {
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "anthropic/claude-opus-4-6" },
|
||||
models: { "anthropic/claude-opus-4-6": {} },
|
||||
workspace: path.join(home, "openclaw"),
|
||||
...agentOverrides,
|
||||
},
|
||||
},
|
||||
session: { store: storePath, mainKey: "main" },
|
||||
} as OpenClawConfig;
|
||||
configSpy.mockReturnValue(cfg);
|
||||
return cfg;
|
||||
}
|
||||
|
||||
function createDefaultAgentResult() {
|
||||
return {
|
||||
payloads: [{ text: "ok" }],
|
||||
meta: {
|
||||
durationMs: 5,
|
||||
agentMeta: { sessionId: "s", provider: "p", model: "m" },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
clearSessionStoreCacheForTest();
|
||||
resetAgentEventsForTest();
|
||||
resetAgentRunContextForTest();
|
||||
resetPluginRuntimeStateForTest();
|
||||
acpManagerTesting.resetAcpSessionManagerForTests();
|
||||
configModule.clearRuntimeConfigSnapshot();
|
||||
vi.mocked(runEmbeddedPiAgent).mockResolvedValue(createDefaultAgentResult());
|
||||
vi.mocked(loadModelCatalog).mockResolvedValue([]);
|
||||
vi.mocked(modelSelectionModule.isCliProvider).mockImplementation(() => false);
|
||||
readConfigFileSnapshotForWriteSpy.mockResolvedValue({
|
||||
snapshot: { valid: false, resolved: {} as OpenClawConfig },
|
||||
writeOptions: {},
|
||||
} as Awaited<ReturnType<typeof configModule.readConfigFileSnapshotForWrite>>);
|
||||
});
|
||||
|
||||
describe("agentCommand runtime config", () => {
|
||||
it("sets runtime snapshots from source config before embedded agent run", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const setRuntimeConfigSnapshotSpy = vi.spyOn(configModule, "setRuntimeConfigSnapshot");
|
||||
|
||||
const store = path.join(home, "sessions.json");
|
||||
const loadedConfig = {
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "anthropic/claude-opus-4-6" },
|
||||
models: { "anthropic/claude-opus-4-6": {} },
|
||||
workspace: path.join(home, "openclaw"),
|
||||
},
|
||||
},
|
||||
session: { store, mainKey: "main" },
|
||||
models: {
|
||||
providers: {
|
||||
openai: {
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
apiKey: { source: "env", provider: "default", id: "OPENAI_API_KEY" }, // pragma: allowlist secret
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig;
|
||||
const sourceConfig = {
|
||||
...loadedConfig,
|
||||
models: {
|
||||
providers: {
|
||||
openai: {
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
apiKey: { source: "env", provider: "default", id: "OPENAI_API_KEY" }, // pragma: allowlist secret
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig;
|
||||
const resolvedConfig = {
|
||||
...loadedConfig,
|
||||
models: {
|
||||
providers: {
|
||||
openai: {
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
apiKey: "sk-resolved-runtime", // pragma: allowlist secret
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig;
|
||||
|
||||
configSpy.mockReturnValue(loadedConfig);
|
||||
readConfigFileSnapshotForWriteSpy.mockResolvedValue({
|
||||
snapshot: { valid: true, resolved: sourceConfig },
|
||||
writeOptions: {},
|
||||
} as Awaited<ReturnType<typeof configModule.readConfigFileSnapshotForWrite>>);
|
||||
const resolveConfigWithSecretsSpy = vi
|
||||
.spyOn(commandConfigResolutionModule, "resolveCommandConfigWithSecrets")
|
||||
.mockResolvedValueOnce({
|
||||
resolvedConfig,
|
||||
effectiveConfig: resolvedConfig,
|
||||
diagnostics: [],
|
||||
});
|
||||
|
||||
const prepared = await agentCommandTesting.resolveAgentRuntimeConfig(runtime);
|
||||
|
||||
expect(resolveConfigWithSecretsSpy).toHaveBeenCalledWith({
|
||||
config: loadedConfig,
|
||||
commandName: "agent",
|
||||
targetIds: expect.any(Set),
|
||||
runtime,
|
||||
});
|
||||
expect(setRuntimeConfigSnapshotSpy).toHaveBeenCalledWith(resolvedConfig, sourceConfig);
|
||||
expect(prepared.cfg).toBe(resolvedConfig);
|
||||
});
|
||||
});
|
||||
|
||||
it("creates a session entry when deriving from --to", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const store = path.join(home, "sessions.json");
|
||||
mockConfig(home, store);
|
||||
|
||||
await agentCommand({ message: "hello", to: "+1555" }, runtime);
|
||||
|
||||
const saved = JSON.parse(fs.readFileSync(store, "utf-8")) as Record<
|
||||
string,
|
||||
{ sessionId: string }
|
||||
>;
|
||||
const entry = Object.values(saved)[0];
|
||||
expect(entry.sessionId).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,6 @@ import { beforeEach, describe, expect, it, type MockInstance, vi } from "vitest"
|
||||
import { withTempHome as withTempHomeBase } from "../../test/helpers/temp-home.js";
|
||||
import "../cron/isolated-agent.mocks.js";
|
||||
import { __testing as acpManagerTesting } from "../acp/control-plane/manager.js";
|
||||
import { __testing as agentCommandTesting } from "../agents/agent-command.js";
|
||||
import { resolveAgentDir, resolveSessionAgentId } from "../agents/agent-scope.js";
|
||||
import * as authProfilesModule from "../agents/auth-profiles.js";
|
||||
import * as sessionStoreModule from "../agents/command/session-store.js";
|
||||
@@ -12,7 +11,6 @@ import { resolveSession } from "../agents/command/session.js";
|
||||
import { loadModelCatalog } from "../agents/model-catalog.js";
|
||||
import * as modelSelectionModule from "../agents/model-selection.js";
|
||||
import { runEmbeddedPiAgent } from "../agents/pi-embedded.js";
|
||||
import * as commandConfigResolutionModule from "../cli/command-config-resolution.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import * as configModule from "../config/config.js";
|
||||
import { clearSessionStoreCacheForTest } from "../config/sessions.js";
|
||||
@@ -309,97 +307,6 @@ beforeEach(() => {
|
||||
});
|
||||
|
||||
describe("agentCommand", () => {
|
||||
it("sets runtime snapshots from source config before embedded agent run", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const setRuntimeConfigSnapshotSpy = vi.spyOn(configModule, "setRuntimeConfigSnapshot");
|
||||
|
||||
const store = path.join(home, "sessions.json");
|
||||
const loadedConfig = {
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "anthropic/claude-opus-4-6" },
|
||||
models: { "anthropic/claude-opus-4-6": {} },
|
||||
workspace: path.join(home, "openclaw"),
|
||||
},
|
||||
},
|
||||
session: { store, mainKey: "main" },
|
||||
models: {
|
||||
providers: {
|
||||
openai: {
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
apiKey: { source: "env", provider: "default", id: "OPENAI_API_KEY" }, // pragma: allowlist secret
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig;
|
||||
const sourceConfig = {
|
||||
...loadedConfig,
|
||||
models: {
|
||||
providers: {
|
||||
openai: {
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
apiKey: { source: "env", provider: "default", id: "OPENAI_API_KEY" }, // pragma: allowlist secret
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig;
|
||||
const resolvedConfig = {
|
||||
...loadedConfig,
|
||||
models: {
|
||||
providers: {
|
||||
openai: {
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
apiKey: "sk-resolved-runtime", // pragma: allowlist secret
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig;
|
||||
|
||||
configSpy.mockReturnValue(loadedConfig);
|
||||
readConfigFileSnapshotForWriteSpy.mockResolvedValue({
|
||||
snapshot: { valid: true, resolved: sourceConfig },
|
||||
writeOptions: {},
|
||||
} as Awaited<ReturnType<typeof configModule.readConfigFileSnapshotForWrite>>);
|
||||
const resolveConfigWithSecretsSpy = vi
|
||||
.spyOn(commandConfigResolutionModule, "resolveCommandConfigWithSecrets")
|
||||
.mockResolvedValueOnce({
|
||||
resolvedConfig,
|
||||
effectiveConfig: resolvedConfig,
|
||||
diagnostics: [],
|
||||
});
|
||||
|
||||
const prepared = await agentCommandTesting.resolveAgentRuntimeConfig(runtime);
|
||||
|
||||
expect(resolveConfigWithSecretsSpy).toHaveBeenCalledWith({
|
||||
config: loadedConfig,
|
||||
commandName: "agent",
|
||||
targetIds: expect.any(Set),
|
||||
runtime,
|
||||
});
|
||||
expect(setRuntimeConfigSnapshotSpy).toHaveBeenCalledWith(resolvedConfig, sourceConfig);
|
||||
expect(prepared.cfg).toBe(resolvedConfig);
|
||||
});
|
||||
});
|
||||
|
||||
it("creates a session entry when deriving from --to", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const store = path.join(home, "sessions.json");
|
||||
mockConfig(home, store);
|
||||
|
||||
await agentCommand({ message: "hello", to: "+1555" }, runtime);
|
||||
|
||||
const saved = JSON.parse(fs.readFileSync(store, "utf-8")) as Record<
|
||||
string,
|
||||
{ sessionId: string }
|
||||
>;
|
||||
const entry = Object.values(saved)[0];
|
||||
expect(entry.sessionId).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it("persists thinking and verbose overrides", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const store = path.join(home, "sessions.json");
|
||||
|
||||
Reference in New Issue
Block a user