From 91d0e77e2e9695eb24f0040642adbec92895e66d Mon Sep 17 00:00:00 2001
From: Peter Lee
Date: Tue, 30 Jun 2026 19:43:16 -0500
Subject: [PATCH] test: migrate src/commands tests to shared temp dir helpers
(#96359)
* test: migrate src/commands tests to shared temp dir helpers
* fix(test): remove unused path import in migrate apply test
---
src/commands/agents.add.test.ts | 26 +++++++-----
src/commands/doctor-plugin-manifests.test.ts | 44 +++++++++++---------
src/commands/doctor-security.test.ts | 19 +++++----
src/commands/migrate/apply.test.ts | 17 +++++---
4 files changed, 62 insertions(+), 44 deletions(-)
diff --git a/src/commands/agents.add.test.ts b/src/commands/agents.add.test.ts
index 2ae5d61ec0fc..d8d3d2e1b92b 100644
--- a/src/commands/agents.add.test.ts
+++ b/src/commands/agents.add.test.ts
@@ -1,14 +1,14 @@
// Agents add tests cover agent creation, workspace setup, channel binding, and onboarding integration.
import fs from "node:fs/promises";
-import os from "node:os";
import path from "node:path";
-import { beforeEach, describe, expect, it, vi } from "vitest";
+import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { AUTH_STORE_VERSION } from "../agents/auth-profiles/constants.js";
import { loadPersistedAuthProfileStore } from "../agents/auth-profiles/persisted.js";
import { saveAuthProfileStore } from "../agents/auth-profiles/store.js";
import { formatCliCommand } from "../cli/command-format.js";
import { closeOpenClawAgentDatabasesForTest } from "../state/openclaw-agent-db.js";
import { closeOpenClawStateDatabaseForTest } from "../state/openclaw-state-db.js";
+import { createSuiteTempRootTracker } from "../test-helpers/temp-dir.js";
import { withEnvAsync } from "../test-utils/env.js";
import { baseConfigSnapshot, createTestRuntime } from "./test-runtime-config-helpers.js";
@@ -116,6 +116,18 @@ import { agentsAddCommand, testing } from "./agents.commands.add.js";
const runtime = createTestRuntime();
describe("agents add command", () => {
+ const suiteTempDirs = createSuiteTempRootTracker({ prefix: "openclaw-agents-add-" });
+
+ beforeAll(async () => {
+ await suiteTempDirs.setup();
+ });
+
+ afterAll(async () => {
+ closeOpenClawAgentDatabasesForTest();
+ closeOpenClawStateDatabaseForTest();
+ await suiteTempDirs.cleanup();
+ });
+
beforeEach(() => {
readConfigFileSnapshotMock.mockClear();
writeConfigFileMock.mockClear();
@@ -136,14 +148,8 @@ describe("agents add command", () => {
prefix: string,
run: (root: string) => Promise,
): Promise {
- const root = await fs.mkdtemp(path.join(os.tmpdir(), prefix));
- try {
- await withEnvAsync({ OPENCLAW_STATE_DIR: root }, async () => await run(root));
- } finally {
- closeOpenClawAgentDatabasesForTest();
- closeOpenClawStateDatabaseForTest();
- await fs.rm(root, { recursive: true, force: true });
- }
+ const root = await suiteTempDirs.make(prefix);
+ await withEnvAsync({ OPENCLAW_STATE_DIR: root }, async () => await run(root));
}
it("requires --workspace when flags are present", async () => {
diff --git a/src/commands/doctor-plugin-manifests.test.ts b/src/commands/doctor-plugin-manifests.test.ts
index 2cefe9354284..bfa3de4dea4e 100644
--- a/src/commands/doctor-plugin-manifests.test.ts
+++ b/src/commands/doctor-plugin-manifests.test.ts
@@ -1,25 +1,21 @@
// Doctor plugin manifest tests cover manifest validation, missing installs, and repair guidance.
import fs from "node:fs";
import path from "node:path";
-import { afterEach, describe, expect, it, vi } from "vitest";
+import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/types.openclaw.js";
-import { cleanupTrackedTempDirs } from "../plugins/test-helpers/fs-fixtures.js";
import type { RuntimeEnv } from "../runtime.js";
+import { createSuiteTempRootTracker } from "../test-helpers/temp-dir.js";
import {
collectLegacyPluginManifestContractMigrations,
maybeRepairLegacyPluginManifestContracts,
} from "./doctor-plugin-manifests.js";
import type { DoctorPrompter } from "./doctor-prompter.js";
-const tempDirs: string[] = [];
-
-function makeTrustedBundledPluginsDir() {
- const fixturesRoot = path.join(process.cwd(), "dist", "extensions");
- fs.mkdirSync(fixturesRoot, { recursive: true });
- const dir = fs.mkdtempSync(path.join(fixturesRoot, "openclaw-doctor-plugin-manifests-"));
- tempDirs.push(dir);
- return dir;
-}
+const fixturesRoot = path.join(process.cwd(), "dist", "extensions");
+const suiteTempDirs = createSuiteTempRootTracker({
+ prefix: "openclaw-doctor-plugin-manifests-",
+ parentDir: fixturesRoot,
+});
function configWithPluginLoadPath(pluginRoot: string): OpenClawConfig {
return {
@@ -86,13 +82,21 @@ function createPrompter(overrides: Partial = {}): DoctorPrompter
}
describe("doctor plugin manifest legacy contract repair", () => {
+ beforeAll(async () => {
+ fs.mkdirSync(fixturesRoot, { recursive: true });
+ await suiteTempDirs.setup();
+ });
+
+ afterAll(async () => {
+ await suiteTempDirs.cleanup();
+ });
+
afterEach(() => {
- cleanupTrackedTempDirs(tempDirs);
vi.restoreAllMocks();
});
- it("collects legacy top-level capability keys for migration", () => {
- const pluginsRoot = makeTrustedBundledPluginsDir();
+ it("collects legacy top-level capability keys for migration", async () => {
+ const pluginsRoot = await suiteTempDirs.make("legacy-capability");
const root = path.join(pluginsRoot, "openai");
fs.mkdirSync(root, { recursive: true });
writePackageJson(root);
@@ -129,8 +133,8 @@ describe("doctor plugin manifest legacy contract repair", () => {
]);
});
- it("collects legacy top-level plugin tool keys for migration", () => {
- const pluginsRoot = makeTrustedBundledPluginsDir();
+ it("collects legacy top-level plugin tool keys for migration", async () => {
+ const pluginsRoot = await suiteTempDirs.make("legacy-tool");
const root = path.join(pluginsRoot, "cortex");
fs.mkdirSync(root, { recursive: true });
writePackageJson(root);
@@ -166,7 +170,7 @@ describe("doctor plugin manifest legacy contract repair", () => {
});
it("rewrites legacy top-level capability keys into contracts", async () => {
- const pluginsRoot = makeTrustedBundledPluginsDir();
+ const pluginsRoot = await suiteTempDirs.make("rewrite-capability");
const root = path.join(pluginsRoot, "openai");
fs.mkdirSync(root, { recursive: true });
writePackageJson(root);
@@ -207,7 +211,7 @@ describe("doctor plugin manifest legacy contract repair", () => {
});
it("removes duplicate legacy top-level plugin tools while keeping contracts.tools", async () => {
- const pluginsRoot = makeTrustedBundledPluginsDir();
+ const pluginsRoot = await suiteTempDirs.make("dedupe-tool");
const root = path.join(pluginsRoot, "cortex");
fs.mkdirSync(root, { recursive: true });
writePackageJson(root);
@@ -241,8 +245,8 @@ describe("doctor plugin manifest legacy contract repair", () => {
});
});
- it("ignores non-object contracts payloads when collecting migrations", () => {
- const pluginsRoot = makeTrustedBundledPluginsDir();
+ it("ignores non-object contracts payloads when collecting migrations", async () => {
+ const pluginsRoot = await suiteTempDirs.make("non-object-contracts");
const root = path.join(pluginsRoot, "openai");
fs.mkdirSync(root, { recursive: true });
writePackageJson(root);
diff --git a/src/commands/doctor-security.test.ts b/src/commands/doctor-security.test.ts
index 4cf437013ee8..5b141707d66a 100644
--- a/src/commands/doctor-security.test.ts
+++ b/src/commands/doctor-security.test.ts
@@ -1,9 +1,9 @@
// Doctor security tests cover security audit checks, config findings, and repair output.
import fs from "node:fs/promises";
-import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
+import { withTempDir } from "../test-helpers/temp-dir.js";
const note = vi.hoisted(() => vi.fn());
const pluginRegistry = vi.hoisted(() => ({ list: [] as unknown[] }));
@@ -72,14 +72,15 @@ describe("noteSecurityWarnings gateway exposure", () => {
file: Record,
run: () => Promise,
): Promise {
- const home = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-doctor-security-"));
- process.env.HOME = home;
- await fs.mkdir(path.join(home, ".openclaw"), { recursive: true });
- await fs.writeFile(
- path.join(home, ".openclaw", "exec-approvals.json"),
- JSON.stringify(file, null, 2),
- );
- await run();
+ await withTempDir({ prefix: "openclaw-doctor-security-" }, async (home) => {
+ process.env.HOME = home;
+ await fs.mkdir(path.join(home, ".openclaw"), { recursive: true });
+ await fs.writeFile(
+ path.join(home, ".openclaw", "exec-approvals.json"),
+ JSON.stringify(file, null, 2),
+ );
+ await run();
+ });
}
async function expectAgentExecHostPolicyWarning(agentKey: "*" | "runner") {
diff --git a/src/commands/migrate/apply.test.ts b/src/commands/migrate/apply.test.ts
index 2572e39d909c..122b6bc7ac9b 100644
--- a/src/commands/migrate/apply.test.ts
+++ b/src/commands/migrate/apply.test.ts
@@ -1,13 +1,12 @@
// Migration apply tests cover backups, filtering, provider apply calls, and report output.
-import { mkdtempSync } from "node:fs";
-import { tmpdir } from "node:os";
-import path from "node:path";
-import { describe, expect, it, vi } from "vitest";
+import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
import type { MigrationPlan, MigrationProviderPlugin } from "../../plugins/types.js";
import { createNonExitingRuntime } from "../../runtime.js";
+import { createSuiteTempRootTracker } from "../../test-helpers/temp-dir.js";
import { runMigrationApply } from "./apply.js";
-const stateDir = mkdtempSync(path.join(tmpdir(), "openclaw-migrate-apply-"));
+let stateDir = "";
+const suiteTempDirs = createSuiteTempRootTracker({ prefix: "openclaw-migrate-apply-" });
vi.mock("../../config/paths.js", async (importActual) => {
const actual = await importActual();
@@ -36,6 +35,14 @@ function buildEmptyPlan(): MigrationPlan {
}
describe("runMigrationApply", () => {
+ beforeAll(async () => {
+ stateDir = await suiteTempDirs.setup();
+ });
+
+ afterAll(async () => {
+ await suiteTempDirs.cleanup();
+ });
+
it("uses the resolved provider id when forwarding Codex options", async () => {
const plan = vi.fn(async () => buildEmptyPlan());
const apply = vi.fn(async () => buildEmptyPlan());