test(system-agent): extract setup apply harness

This commit is contained in:
Amp
2026-08-21 05:06:04 +00:00
parent 291f90d2b4
commit 4e7dae4222
2 changed files with 231 additions and 208 deletions
@@ -0,0 +1,217 @@
import { vi } from "vitest";
import { resolveAgentEntry } from "../agents/agent-scope-config.js";
import * as configModule from "../config/config.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import type { RuntimeEnv } from "../runtime.js";
type ConfigSnapshot = {
exists: boolean;
valid: boolean;
path: string;
hash: string | null;
parsed: unknown;
sourceConfigBeforeMigrations?: OpenClawConfig;
config: OpenClawConfig;
sourceConfig: OpenClawConfig;
runtimeConfig?: OpenClawConfig;
issues: Array<{ path?: string; message: string }>;
};
export type CommitTransform = (
currentConfig: OpenClawConfig,
context: {
previousHash: string | null;
snapshot: ConfigSnapshot;
attempt: number;
},
) =>
| { nextConfig: OpenClawConfig; result?: unknown }
| Promise<{ nextConfig: OpenClawConfig; result?: unknown }>;
const mocks = vi.hoisted(() => ({
state: {
initialSnapshot: {} as ConfigSnapshot,
commitConfig: {} as OpenClawConfig,
commitSnapshot: {} as ConfigSnapshot,
commitPreviousHash: "probe" as string | null,
persistedConfig: undefined as OpenClawConfig | undefined,
},
events: [] as string[],
readSnapshot: vi.fn<() => Promise<ConfigSnapshot>>(),
readVerifiedSnapshot: vi.fn<() => Promise<ConfigSnapshot>>(),
readVerifiedSnapshotWithPluginMetadata: vi.fn(),
commit: vi.fn(),
configureGateway: vi.fn(),
ensureWorkspace: vi.fn(),
ensureGatewayService: vi.fn(),
waitForGatewayReachable: vi.fn<() => Promise<{ ok: boolean; detail?: string }>>(),
refreshPluginRegistry: vi.fn(),
updateExecApprovals: vi.fn(),
ensureOnboardingAgent: vi.fn(),
verifySetupInferenceConfig: vi.fn(),
}));
vi.mock("../commands/onboard-agent.js", async (importOriginal) => ({
...(await importOriginal<typeof import("../commands/onboard-agent.js")>()),
ensureOnboardingAgent: mocks.ensureOnboardingAgent,
}));
vi.mock("./setup-inference.js", () => ({
verifySetupInferenceConfig: mocks.verifySetupInferenceConfig,
}));
vi.mock("../config/config.js", async (importOriginal) => ({
...(await importOriginal<typeof import("../config/config.js")>()),
readConfigFileSnapshot: mocks.readVerifiedSnapshot,
readConfigFileSnapshotWithPluginMetadata: mocks.readVerifiedSnapshotWithPluginMetadata,
}));
vi.mock("../wizard/setup.shared.js", async (importOriginal) => ({
...(await importOriginal<typeof import("../wizard/setup.shared.js")>()),
readSetupConfigFileSnapshot: mocks.readSnapshot,
}));
vi.mock("../commands/onboard-helpers.js", () => ({
applyWizardMetadata: (config: OpenClawConfig) => ({
...config,
wizard: {
...config.wizard,
lastRunAt: "2026-07-10T00:00:00.000Z",
lastRunVersion: "test",
lastRunCommand: "onboard",
lastRunMode: "local",
},
}),
ensureWorkspaceAndSessions: mocks.ensureWorkspace,
resolveLocalControlUiProbeLinks: ({ port }: { port: number }) => ({
wsUrl: `ws://127.0.0.1:${port}`,
}),
waitForGatewayReachable: mocks.waitForGatewayReachable,
}));
vi.mock("../plugins/install-record-commit.js", async (importOriginal) => ({
...(await importOriginal<typeof import("../plugins/install-record-commit.js")>()),
transformConfigWithPendingPluginInstalls: mocks.commit,
}));
vi.mock("../wizard/setup.gateway-config.js", () => ({
configureGatewayForSetup: mocks.configureGateway,
}));
vi.mock("../wizard/setup.finalize.js", () => ({
ensureGatewayServiceForOnboarding: mocks.ensureGatewayService,
}));
vi.mock("../plugins/registry-refresh.js", () => ({
refreshPluginRegistryAfterConfigMutation: mocks.refreshPluginRegistry,
}));
vi.mock("../infra/exec-approvals.js", () => ({
updateExecApprovals: mocks.updateExecApprovals,
}));
vi.mock("../agents/agent-scope.js", async (importOriginal) => ({
...(await importOriginal<typeof import("../agents/agent-scope.js")>()),
resolveAgentDir: (config: OpenClawConfig, agentId: string) =>
resolveAgentEntry(config, agentId)?.agentDir ?? `/agents/${agentId}`,
}));
export function getSetupApplyMocks() {
return mocks;
}
export const runtime: RuntimeEnv = {
log: vi.fn(),
error: vi.fn(),
exit: vi.fn(),
};
export function snapshot(
hash: string | null,
sourceConfig: OpenClawConfig,
runtimeConfig: OpenClawConfig = sourceConfig,
): ConfigSnapshot {
return {
exists: hash !== null,
valid: true,
path: "/tmp/openclaw.json",
hash,
parsed: structuredClone(sourceConfig),
sourceConfigBeforeMigrations: structuredClone(sourceConfig),
config: runtimeConfig,
sourceConfig: runtimeConfig,
runtimeConfig,
issues: [],
};
}
export function codexPluginMetadataSnapshot(homeScope: "agent" | "user") {
return {
manifestRegistry: {
diagnostics: [],
plugins: [
{
id: "codex",
origin: "global",
channels: [],
providers: [],
cliBackends: [],
skills: [],
settingsFiles: [],
hooks: [],
rootDir: "/tmp/codex",
source: "/tmp/codex/index.js",
manifestPath: "/tmp/codex/openclaw.plugin.json",
configSchema: {
type: "object",
additionalProperties: false,
properties: {
codexDynamicToolsLoading: { type: "string", default: "searchable" },
appServer: {
type: "object",
additionalProperties: false,
properties: {
transport: { type: "string", default: "stdio" },
homeScope: { type: "string", default: homeScope },
requestTimeoutMs: { type: "number", default: 60_000 },
},
},
},
},
},
],
},
} as never;
}
export function materializePluginDefaults(
config: OpenClawConfig,
pluginMetadataSnapshot: ReturnType<typeof codexPluginMetadataSnapshot>,
): OpenClawConfig {
const result = configModule.validateConfigObjectWithPlugins(config, { pluginMetadataSnapshot });
if (!result.ok) {
throw new Error(result.issues[0]?.message ?? "test config failed validation");
}
return result.config;
}
export function baseParams(
overrides: Partial<Parameters<typeof import("./setup-apply.js").applySystemAgentSetup>[0]> = {},
) {
return {
workspace: "/tmp/openclaw-workspace",
surface: "gateway" as const,
runtime,
...overrides,
};
}
export function mainAgentModelConfig(model = "openai/gpt-5.5"): OpenClawConfig {
return { agents: { defaults: { model }, entries: { main: { default: true } } } };
}
export function setSetupCommitState(config: OpenClawConfig, initialSnapshot: ConfigSnapshot): void {
mocks.state.initialSnapshot = initialSnapshot;
mocks.state.commitConfig = config;
mocks.state.commitSnapshot = initialSnapshot;
}
+14 -208
View File
@@ -1,223 +1,29 @@
// Preserve module setup before modules that consume it.
// oxfmt-ignore
import {
baseParams,
type CommitTransform,
codexPluginMetadataSnapshot,
getSetupApplyMocks,
mainAgentModelConfig,
materializePluginDefaults,
runtime,
setSetupCommitState,
snapshot,
} from "./setup-apply.test-harness.js";
import fs from "node:fs/promises";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js";
import { resolveAgentEntry } from "../agents/agent-scope-config.js";
import * as configModule from "../config/config.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import type { RuntimeEnv } from "../runtime.js";
import { withEnvAsync } from "../test-utils/env.js";
import { projectDefaultInferenceRoute } from "./inference-route.js";
type ConfigSnapshot = {
exists: boolean;
valid: boolean;
path: string;
hash: string | null;
parsed: unknown;
sourceConfigBeforeMigrations?: OpenClawConfig;
config: OpenClawConfig;
sourceConfig: OpenClawConfig;
runtimeConfig?: OpenClawConfig;
issues: Array<{ path?: string; message: string }>;
};
type CommitTransform = (
currentConfig: OpenClawConfig,
context: {
previousHash: string | null;
snapshot: ConfigSnapshot;
attempt: number;
},
) =>
| { nextConfig: OpenClawConfig; result?: unknown }
| Promise<{ nextConfig: OpenClawConfig; result?: unknown }>;
const mocks = vi.hoisted(() => ({
state: {
initialSnapshot: {} as ConfigSnapshot,
commitConfig: {} as OpenClawConfig,
commitSnapshot: {} as ConfigSnapshot,
commitPreviousHash: "probe" as string | null,
persistedConfig: undefined as OpenClawConfig | undefined,
},
events: [] as string[],
readSnapshot: vi.fn<() => Promise<ConfigSnapshot>>(),
readVerifiedSnapshot: vi.fn<() => Promise<ConfigSnapshot>>(),
readVerifiedSnapshotWithPluginMetadata: vi.fn(),
commit: vi.fn(),
configureGateway: vi.fn(),
ensureWorkspace: vi.fn(),
ensureGatewayService: vi.fn(),
waitForGatewayReachable: vi.fn<() => Promise<{ ok: boolean; detail?: string }>>(),
refreshPluginRegistry: vi.fn(),
updateExecApprovals: vi.fn(),
ensureOnboardingAgent: vi.fn(),
verifySetupInferenceConfig: vi.fn(),
}));
vi.mock("../commands/onboard-agent.js", async (importOriginal) => ({
...(await importOriginal<typeof import("../commands/onboard-agent.js")>()),
ensureOnboardingAgent: mocks.ensureOnboardingAgent,
}));
vi.mock("./setup-inference.js", () => ({
verifySetupInferenceConfig: mocks.verifySetupInferenceConfig,
}));
vi.mock("../config/config.js", async (importOriginal) => ({
...(await importOriginal<typeof import("../config/config.js")>()),
readConfigFileSnapshot: mocks.readVerifiedSnapshot,
readConfigFileSnapshotWithPluginMetadata: mocks.readVerifiedSnapshotWithPluginMetadata,
}));
vi.mock("../wizard/setup.shared.js", async (importOriginal) => ({
...(await importOriginal<typeof import("../wizard/setup.shared.js")>()),
readSetupConfigFileSnapshot: mocks.readSnapshot,
}));
vi.mock("../commands/onboard-helpers.js", () => ({
applyWizardMetadata: (config: OpenClawConfig) => ({
...config,
wizard: {
...config.wizard,
lastRunAt: "2026-07-10T00:00:00.000Z",
lastRunVersion: "test",
lastRunCommand: "onboard",
lastRunMode: "local",
},
}),
ensureWorkspaceAndSessions: mocks.ensureWorkspace,
resolveLocalControlUiProbeLinks: ({ port }: { port: number }) => ({
wsUrl: `ws://127.0.0.1:${port}`,
}),
waitForGatewayReachable: mocks.waitForGatewayReachable,
}));
vi.mock("../plugins/install-record-commit.js", async (importOriginal) => ({
...(await importOriginal<typeof import("../plugins/install-record-commit.js")>()),
transformConfigWithPendingPluginInstalls: mocks.commit,
}));
vi.mock("../wizard/setup.gateway-config.js", () => ({
configureGatewayForSetup: mocks.configureGateway,
}));
vi.mock("../wizard/setup.finalize.js", () => ({
ensureGatewayServiceForOnboarding: mocks.ensureGatewayService,
}));
vi.mock("../plugins/registry-refresh.js", () => ({
refreshPluginRegistryAfterConfigMutation: mocks.refreshPluginRegistry,
}));
vi.mock("../infra/exec-approvals.js", () => ({
updateExecApprovals: mocks.updateExecApprovals,
}));
vi.mock("../agents/agent-scope.js", async (importOriginal) => ({
...(await importOriginal<typeof import("../agents/agent-scope.js")>()),
resolveAgentDir: (config: OpenClawConfig, agentId: string) =>
resolveAgentEntry(config, agentId)?.agentDir ?? `/agents/${agentId}`,
}));
import { applySystemAgentSetup } from "./setup-apply.js";
const runtime: RuntimeEnv = {
log: vi.fn(),
error: vi.fn(),
exit: vi.fn(),
};
const mocks = getSetupApplyMocks();
const testTempDirs = useAutoCleanupTempDirTracker(afterEach);
function snapshot(
hash: string | null,
sourceConfig: OpenClawConfig,
runtimeConfig: OpenClawConfig = sourceConfig,
): ConfigSnapshot {
return {
exists: hash !== null,
valid: true,
path: "/tmp/openclaw.json",
hash,
parsed: structuredClone(sourceConfig),
sourceConfigBeforeMigrations: structuredClone(sourceConfig),
config: runtimeConfig,
sourceConfig: runtimeConfig,
runtimeConfig,
issues: [],
};
}
function codexPluginMetadataSnapshot(homeScope: "agent" | "user") {
return {
manifestRegistry: {
diagnostics: [],
plugins: [
{
id: "codex",
origin: "global",
channels: [],
providers: [],
cliBackends: [],
skills: [],
settingsFiles: [],
hooks: [],
rootDir: "/tmp/codex",
source: "/tmp/codex/index.js",
manifestPath: "/tmp/codex/openclaw.plugin.json",
configSchema: {
type: "object",
additionalProperties: false,
properties: {
codexDynamicToolsLoading: { type: "string", default: "searchable" },
appServer: {
type: "object",
additionalProperties: false,
properties: {
transport: { type: "string", default: "stdio" },
homeScope: { type: "string", default: homeScope },
requestTimeoutMs: { type: "number", default: 60_000 },
},
},
},
},
},
],
},
} as never;
}
function materializePluginDefaults(
config: OpenClawConfig,
pluginMetadataSnapshot: ReturnType<typeof codexPluginMetadataSnapshot>,
): OpenClawConfig {
const result = configModule.validateConfigObjectWithPlugins(config, { pluginMetadataSnapshot });
if (!result.ok) {
throw new Error(result.issues[0]?.message ?? "test config failed validation");
}
return result.config;
}
function baseParams(overrides: Partial<Parameters<typeof applySystemAgentSetup>[0]> = {}) {
return {
workspace: "/tmp/openclaw-workspace",
surface: "gateway" as const,
runtime,
...overrides,
};
}
function mainAgentModelConfig(model = "openai/gpt-5.5"): OpenClawConfig {
return { agents: { defaults: { model }, entries: { main: { default: true } } } };
}
function setSetupCommitState(config: OpenClawConfig, initialSnapshot: ConfigSnapshot): void {
mocks.state.initialSnapshot = initialSnapshot;
mocks.state.commitConfig = config;
mocks.state.commitSnapshot = initialSnapshot;
}
describe("applySystemAgentSetup transaction boundaries", () => {
beforeEach(() => {
vi.resetAllMocks();