test: tie script tmp dirs to cleanup hooks

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
masonxhuang
2026-05-27 18:02:06 +08:00
parent 42f3550f7e
commit ef2d8a0bf4
14 changed files with 143 additions and 78 deletions

View File

@@ -7,7 +7,7 @@ import path from "node:path";
import { pathToFileURL } from "node:url";
import { afterEach, describe, expect, it } from "vitest";
const tempDirs: string[] = [];
const tempDirCleanups: Array<() => void> = [];
const probePath = path.resolve("scripts/e2e/lib/bundled-plugin-install-uninstall/probe.mjs");
const runtimeSmokePath = path.resolve(
"scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs",
@@ -22,7 +22,9 @@ type PluginListEntry = {
function makePackageRoot(): string {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-bundled-probe-"));
tempDirs.push(root);
tempDirCleanups.push(() => {
fs.rmSync(root, { force: true, recursive: true });
});
fs.writeFileSync(path.join(root, "package.json"), '{"type":"module"}\n', "utf8");
fs.mkdirSync(path.join(root, "dist"), { recursive: true });
return root;
@@ -127,8 +129,8 @@ async function closeServer(server: HttpServer | NetServer): Promise<void> {
}
afterEach(() => {
for (const dir of tempDirs.splice(0)) {
fs.rmSync(dir, { force: true, recursive: true });
for (const cleanup of tempDirCleanups.splice(0)) {
cleanup();
}
});
@@ -212,7 +214,10 @@ describe("bundled plugin install/uninstall probe", () => {
it("creates runtime smoke state with OPENCLAW_HOME at the test home", async () => {
const runtimeSmoke = await import(pathToFileURL(runtimeSmokePath).href);
const env = runtimeSmoke.createIsolatedStateEnv("runtime-env");
tempDirs.push(path.dirname(env.HOME));
const homeRoot = path.dirname(env.HOME);
tempDirCleanups.push(() => {
fs.rmSync(homeRoot, { force: true, recursive: true });
});
expect(env.USERPROFILE).toBe(env.HOME);
expect(env.OPENCLAW_HOME).toBe(env.HOME);

View File

@@ -5,11 +5,13 @@ import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
const SCRIPT_PATH = path.resolve("scripts/ci-docker-pull-retry.sh");
const tempDirs: string[] = [];
const tempDirCleanups: Array<() => void> = [];
function makeTempBin(prefix: string) {
const dir = mkdtempSync(path.join(tmpdir(), prefix));
tempDirs.push(dir);
tempDirCleanups.push(() => {
rmSync(dir, { force: true, recursive: true });
});
return dir;
}
@@ -38,8 +40,8 @@ function runPullHelperWithEnv(binDir: string, env: Record<string, string>) {
}
afterEach(() => {
while (tempDirs.length > 0) {
rmSync(tempDirs.pop()!, { force: true, recursive: true });
while (tempDirCleanups.length > 0) {
tempDirCleanups.pop()!();
}
});
@@ -128,7 +130,9 @@ describe("scripts/ci-docker-pull-retry.sh", () => {
const result = runPullHelper(binDir);
expect(result.status).toBe(127);
expect(result.stderr).toContain("timeout command not found; cannot bound Docker pull after 42s");
expect(result.stderr).toContain(
"timeout command not found; cannot bound Docker pull after 42s",
);
expect(existsSync(dockerArgsPath)).toBe(false);
});
@@ -162,6 +166,8 @@ describe("scripts/ci-docker-pull-retry.sh", () => {
expect(result.status).toBe(42);
expect(result.stderr).toContain("Docker pull failed or timed out after 42s: status=42");
expect(execFileSync("wc", ["-l", dockerArgsPath], { encoding: "utf8" }).trim()).toMatch(/^2\b/u);
expect(execFileSync("wc", ["-l", dockerArgsPath], { encoding: "utf8" }).trim()).toMatch(
/^2\b/u,
);
});
});

View File

@@ -8,6 +8,7 @@ import { createScriptTestHarness } from "./test-helpers.js";
const scriptPath = path.join(process.cwd(), "scripts", "committer");
const { createTempDir } = createScriptTestHarness();
let templateRepo: string;
let templateRepoCleanup: (() => void) | null = null;
function run(cwd: string, command: string, args: string[]) {
return execFileSync(command, args, {
@@ -28,6 +29,9 @@ function createRepo() {
function createTemplateRepo() {
const repo = mkdtempSync(path.join(tmpdir(), "committer-template-"));
templateRepoCleanup = () => {
rmSync(repo, { recursive: true, force: true });
};
git(repo, "init", "-q");
git(repo, "config", "user.email", "test@example.com");
git(repo, "config", "user.name", "Test User");
@@ -83,7 +87,8 @@ describe("scripts/committer", () => {
});
afterAll(() => {
rmSync(templateRepo, { recursive: true, force: true });
templateRepoCleanup?.();
templateRepoCleanup = null;
});
it("accepts supported path argument shapes", () => {

View File

@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
import path from "node:path";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
const tempDirs: string[] = [];
const tempDirCleanups: Array<() => void> = [];
const repoRoot = process.cwd();
const fakeCrabboxBinDirs = new Map<string, string>();
@@ -14,7 +14,9 @@ function makeFakeCrabbox(helpText: string): string {
return cached;
}
const binDir = mkdtempSync(path.join(tmpdir(), "openclaw-fake-crabbox-"));
tempDirs.push(binDir);
tempDirCleanups.push(() => {
rmSync(binDir, { recursive: true, force: true });
});
writeFakeCrabbox(binDir, helpText);
fakeCrabboxBinDirs.set(helpText, binDir);
return binDir;
@@ -126,7 +128,9 @@ function makeFakeGit(
responses: Record<string, { status?: number; stdout?: string; stderr?: string }>,
): string {
const binDir = mkdtempSync(path.join(tmpdir(), "openclaw-fake-git-"));
tempDirs.push(binDir);
tempDirCleanups.push(() => {
rmSync(binDir, { recursive: true, force: true });
});
const gitPath = path.join(binDir, "git");
if (process.platform !== "win32") {
const script = [
@@ -272,8 +276,8 @@ function expectGroupedShellCommand(remoteCommand: string, command: string): void
}
afterAll(() => {
for (const dir of tempDirs.splice(0)) {
rmSync(dir, { recursive: true, force: true });
for (const cleanup of tempDirCleanups.splice(0)) {
cleanup();
}
});
@@ -386,11 +390,9 @@ describe.concurrent("scripts/crabbox-wrapper", () => {
});
it("keeps explicit provider env overrides for Windows runs", () => {
const result = runWrapper(
azureProviderHelp,
["run", "--target", "windows", "--", "echo ok"],
{ env: { CRABBOX_PROVIDER: "aws" } },
);
const result = runWrapper(azureProviderHelp, ["run", "--target", "windows", "--", "echo ok"], {
env: { CRABBOX_PROVIDER: "aws" },
});
expect(result.status).toBe(0);
expect(parseFakeCrabboxOutput(result).args).toEqual([
@@ -1400,7 +1402,9 @@ describe.concurrent("scripts/crabbox-wrapper", () => {
"finds a Crabbox checkout next to the Git common dir in linked worktrees",
() => {
const fakeWorkspaceParent = mkdtempSync(path.join(tmpdir(), "openclaw-linked-worktree-"));
tempDirs.push(fakeWorkspaceParent);
tempDirCleanups.push(() => {
rmSync(fakeWorkspaceParent, { recursive: true, force: true });
});
const gitCommonDir = path.join(fakeWorkspaceParent, "openclaw", ".git");
const crabboxBinDir = path.join(fakeWorkspaceParent, "crabbox", "bin");
mkdirSync(gitCommonDir, { recursive: true });
@@ -1460,7 +1464,9 @@ describe.concurrent("scripts/crabbox-wrapper", () => {
if (process.platform !== "win32") {
it("keeps POSIX PATH lookup semantics for non-executable entries", () => {
const staleBinDir = mkdtempSync(path.join(tmpdir(), "openclaw-stale-crabbox-"));
tempDirs.push(staleBinDir);
tempDirCleanups.push(() => {
rmSync(staleBinDir, { recursive: true, force: true });
});
writeFileSync(path.join(staleBinDir, "crabbox"), "not executable\n", "utf8");
const result = runWrapper("provider: aws\n", ["run", "--provider", "aws", "--", "echo ok"], {
extraPathEntries: [staleBinDir],

View File

@@ -1,13 +1,19 @@
import { mkdtemp, writeFile } from "node:fs/promises";
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { afterEach, describe, expect, it } from "vitest";
import {
classifyVulnerabilityFindings,
renderDependencyVulnerabilityGateMarkdownReport,
runDependencyVulnerabilityGate,
} from "../../scripts/dependency-vulnerability-gate.mjs";
const tempDirCleanups: Array<() => Promise<void>> = [];
afterEach(async () => {
await Promise.all(tempDirCleanups.splice(0).map((cleanup) => cleanup()));
});
function advisory({
id,
severity,
@@ -97,6 +103,7 @@ describe("dependency-vulnerability-gate", () => {
it("queries full and production lockfile graphs separately", async () => {
const rootDir = await mkdtemp(path.join(tmpdir(), "openclaw-vuln-gate-"));
tempDirCleanups.push(() => rm(rootDir, { recursive: true, force: true }));
await writeLockfile(rootDir);
const payloads: Record<string, string[]>[] = [];

View File

@@ -1,7 +1,7 @@
import { mkdtemp, readFile, writeFile } from "node:fs/promises";
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { afterEach, describe, expect, it } from "vitest";
import {
DEPENDENCY_EVIDENCE_REPORTS,
collectDependencyEvidenceSummaryCounts,
@@ -12,6 +12,12 @@ import {
resolveReleaseTag,
} from "../../scripts/generate-dependency-release-evidence.mjs";
const tempDirCleanups: Array<() => Promise<void>> = [];
afterEach(async () => {
await Promise.all(tempDirCleanups.splice(0).map((cleanup) => cleanup()));
});
async function writeJson(dir: string, fileName: string, value: unknown) {
await writeFile(path.join(dir, fileName), `${JSON.stringify(value, null, 2)}\n`, "utf8");
}
@@ -105,6 +111,7 @@ describe("generate-dependency-release-evidence", () => {
it("collects report counts and renders human summaries", async () => {
const dir = await mkdtemp(path.join(tmpdir(), "openclaw-release-dependency-evidence-test-"));
tempDirCleanups.push(() => rm(dir, { recursive: true, force: true }));
await writeJson(dir, "dependency-vulnerability-gate.json", {
blockers: [{ id: "GHSA-blocker" }],
findings: [{ id: "GHSA-blocker" }, { id: "GHSA-report" }],

View File

@@ -4,11 +4,13 @@ import { tmpdir } from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
const tempDirs: string[] = [];
const tempDirCleanups: Array<() => void> = [];
function makeTempBin(prefix: string) {
const dir = mkdtempSync(path.join(tmpdir(), prefix));
tempDirs.push(dir);
tempDirCleanups.push(() => {
rmSync(dir, { force: true, recursive: true });
});
return dir;
}
@@ -60,8 +62,8 @@ function failDockerRunArgs(pathPrefix: string) {
}
afterEach(() => {
while (tempDirs.length > 0) {
rmSync(tempDirs.pop()!, { force: true, recursive: true });
while (tempDirCleanups.length > 0) {
tempDirCleanups.pop()!();
}
});
@@ -93,14 +95,9 @@ describe("scripts/lib/live-docker-auth.sh", () => {
const binDir = makeTempBin("openclaw-live-docker-auth-plain-");
writeExecutable(
path.join(binDir, "timeout"),
[
"#!/bin/sh",
'if [ "$1" = "--kill-after=1s" ]; then',
" exit 1",
"fi",
"exit 0",
"",
].join("\n"),
["#!/bin/sh", 'if [ "$1" = "--kill-after=1s" ]; then', " exit 1", "fi", "exit 0", ""].join(
"\n",
),
);
expect(resolveDockerRunArgs(binDir)).toEqual(["timeout", "42s", "docker", "run"]);

View File

@@ -8,17 +8,19 @@ import {
renderEvidenceComment,
} from "../../scripts/mantis/publish-pr-evidence.mjs";
const tempDirs: string[] = [];
const tempDirCleanups: Array<() => void> = [];
afterEach(() => {
for (const dir of tempDirs.splice(0)) {
rmSync(dir, { recursive: true, force: true });
for (const cleanup of tempDirCleanups.splice(0)) {
cleanup();
}
});
function makeLane(name: string) {
const repo = mkdtempSync(path.join(tmpdir(), `mantis-telegram-${name}-repo-`));
tempDirs.push(repo);
tempDirCleanups.push(() => {
rmSync(repo, { recursive: true, force: true });
});
const outputDir = path.join(repo, ".artifacts", "qa-e2e", name);
mkdirSync(outputDir, { recursive: true });
const gif = path.join(outputDir, "telegram-user-crabbox-session-motion-telegram-window.gif");
@@ -49,7 +51,9 @@ describe("scripts/mantis/build-telegram-desktop-proof-evidence", () => {
const baseline = makeLane("baseline");
const candidate = makeLane("candidate");
const outputDir = mkdtempSync(path.join(tmpdir(), "mantis-telegram-proof-"));
tempDirs.push(outputDir);
tempDirCleanups.push(() => {
rmSync(outputDir, { recursive: true, force: true });
});
const result = writeTelegramDesktopProofEvidence([
"--output-dir",

View File

@@ -4,12 +4,14 @@ import { tmpdir } from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
const tempDirs: string[] = [];
const tempDirCleanups: Array<() => void> = [];
const scriptPath = "scripts/package-mac-app.sh";
function makePlist(): string {
const dir = mkdtempSync(path.join(tmpdir(), "openclaw-plistbuddy-"));
tempDirs.push(dir);
tempDirCleanups.push(() => {
rmSync(dir, { recursive: true, force: true });
});
const plist = path.join(dir, "Info.plist");
writeFileSync(
plist,
@@ -48,8 +50,8 @@ function getPackageManagerHelperBlock(): string {
}
afterEach(() => {
for (const dir of tempDirs.splice(0)) {
rmSync(dir, { recursive: true, force: true });
for (const cleanup of tempDirCleanups.splice(0)) {
cleanup();
}
});
@@ -71,7 +73,14 @@ describe("package-mac-app plist stamping", () => {
const tempRoot = mkdtempSync(path.join(tmpdir(), "openclaw-package-pnpm-root-"));
const toolsDir = mkdtempSync(path.join(tmpdir(), "openclaw-package-pnpm-tools-"));
const logPath = path.join(tempRoot, "corepack.log");
tempDirs.push(tempRoot, toolsDir);
tempDirCleanups.push(
() => {
rmSync(tempRoot, { recursive: true, force: true });
},
() => {
rmSync(toolsDir, { recursive: true, force: true });
},
);
const corepackPath = path.join(toolsDir, "corepack");
writeFileSync(
@@ -79,8 +88,8 @@ describe("package-mac-app plist stamping", () => {
[
"#!/usr/bin/env bash",
"set -euo pipefail",
"printf '%s|%s\\n' \"$PWD\" \"$*\" >> \"$OPENCLAW_TEST_LOG\"",
"if [[ \"${1:-}\" == \"pnpm\" && \"${2:-}\" == \"--version\" ]]; then",
'printf \'%s|%s\\n\' "$PWD" "$*" >> "$OPENCLAW_TEST_LOG"',
'if [[ "${1:-}" == "pnpm" && "${2:-}" == "--version" ]]; then',
" echo '11.2.2'",
"fi",
"",
@@ -112,7 +121,14 @@ describe("package-mac-app plist stamping", () => {
const helperBlock = getPackageManagerHelperBlock();
const tempRoot = mkdtempSync(path.join(tmpdir(), "openclaw-package-pnpm-root-"));
const toolsDir = mkdtempSync(path.join(tmpdir(), "openclaw-package-pnpm-tools-"));
tempDirs.push(tempRoot, toolsDir);
tempDirCleanups.push(
() => {
rmSync(tempRoot, { recursive: true, force: true });
},
() => {
rmSync(toolsDir, { recursive: true, force: true });
},
);
const result = runHelper(`
set -euo pipefail
@@ -156,11 +172,15 @@ describe("package-mac-app plist stamping", () => {
it("fails closed when required bundled resources are missing", () => {
const script = readFileSync(scriptPath, "utf8");
const modelCatalogBlock = script.slice(
script.indexOf('MODEL_CATALOG_SRC="$ROOT_DIR/node_modules/@earendil-works/pi-ai/dist/models.generated.js"'),
script.indexOf(
'MODEL_CATALOG_SRC="$ROOT_DIR/node_modules/@earendil-works/pi-ai/dist/models.generated.js"',
),
script.indexOf('echo "📦 Copying Control UI assets"'),
);
const openClawKitBlock = script.slice(
script.indexOf('OPENCLAWKIT_BUNDLE="$(build_path_for_arch "$PRIMARY_ARCH")/$BUILD_CONFIG/OpenClawKit_OpenClawKit.bundle"'),
script.indexOf(
'OPENCLAWKIT_BUNDLE="$(build_path_for_arch "$PRIMARY_ARCH")/$BUILD_CONFIG/OpenClawKit_OpenClawKit.bundle"',
),
script.indexOf('echo "📦 Copying Textual resources"'),
);

View File

@@ -23,14 +23,18 @@ import {
describe("plugin gateway gauntlet helpers", () => {
let repoRoot: string;
let repoRootCleanup: (() => Promise<void>) | null = null;
beforeEach(async () => {
repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), "plugin-gauntlet-"));
const tempRepoRoot = await fs.mkdtemp(path.join(os.tmpdir(), "plugin-gauntlet-"));
repoRoot = tempRepoRoot;
repoRootCleanup = () => fs.rm(tempRepoRoot, { recursive: true, force: true });
await fs.mkdir(path.join(repoRoot, "extensions"), { recursive: true });
});
afterEach(async () => {
await fs.rm(repoRoot, { recursive: true, force: true });
await repoRootCleanup?.();
repoRootCleanup = null;
});
async function writeManifest(pluginDir: string, fileName: string, source: string) {
@@ -332,9 +336,7 @@ describe("plugin gateway gauntlet helpers", () => {
it("does not count prebuild setup as gauntlet work", () => {
expect(hasGauntletWorkRows([])).toBe(false);
expect(hasGauntletWorkRows([{ phase: "prebuild" }])).toBe(false);
expect(hasGauntletWorkRows([{ phase: "prebuild" }, { phase: "lifecycle:install" }])).toBe(
true,
);
expect(hasGauntletWorkRows([{ phase: "prebuild" }, { phase: "lifecycle:install" }])).toBe(true);
expect(hasGauntletWorkRows([{ phase: "slash:help" }])).toBe(true);
expect(hasGauntletWorkRows([{ phase: "qa:rpc" }])).toBe(true);
});

View File

@@ -27,10 +27,10 @@ const CREDENTIAL_SCRIPT_PATH = path.resolve(
);
const CONFIG_SCRIPT_PATH = path.resolve(TEST_DIR, "../../scripts/e2e/npm-telegram-rtt-config.mjs");
const execFileAsync = promisify(execFile);
const tempDirs: string[] = [];
const tempDirCleanups: Array<() => Promise<void>> = [];
afterEach(async () => {
await Promise.all(tempDirs.splice(0).map((dir) => fs.rm(dir, { recursive: true, force: true })));
await Promise.all(tempDirCleanups.splice(0).map((cleanup) => cleanup()));
});
describe("RTT harness", () => {
@@ -149,8 +149,8 @@ describe("RTT harness", () => {
expect(script).toContain(
'"$timeout_bin" --kill-after=30s "$npm_install_timeout" npm install -g "$install_source" --no-fund --no-audit',
);
expect(script).toContain('elif command -v gtimeout >/dev/null 2>&1; then');
expect(script).toContain("timeout_bin=\"gtimeout\"");
expect(script).toContain("elif command -v gtimeout >/dev/null 2>&1; then");
expect(script).toContain('timeout_bin="gtimeout"');
expect(script).toContain(
'echo "timeout or gtimeout is required for OPENCLAW_E2E_NPM_INSTALL_TIMEOUT=$npm_install_timeout" >&2',
);
@@ -158,7 +158,9 @@ describe("RTT harness", () => {
expect(script).toContain(
'"$timeout_bin" "$npm_install_timeout" npm install -g "$install_source" --no-fund --no-audit',
);
expect(script).not.toContain("running package install without OPENCLAW_E2E_NPM_INSTALL_TIMEOUT");
expect(script).not.toContain(
"running package install without OPENCLAW_E2E_NPM_INSTALL_TIMEOUT",
);
expect(script).toContain("run_logged docker_e2e_docker_run_cmd run --rm");
expect(script).not.toContain("run_logged docker run --rm");
expect(heartbeatStartIndex).toBeGreaterThan(sourceIndex);
@@ -180,7 +182,7 @@ describe("RTT harness", () => {
it("generates final-only Telegram RTT delivery config for release packages", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-rtt-config-test-"));
tempDirs.push(tempDir);
tempDirCleanups.push(() => fs.rm(tempDir, { recursive: true, force: true }));
const configPath = path.join(tempDir, "config.json");
await execFileAsync(process.execPath, [
@@ -292,7 +294,7 @@ describe("RTT harness", () => {
it("appends JSONL rows", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-rtt-test-"));
tempDirs.push(tempDir);
tempDirCleanups.push(() => fs.rm(tempDir, { recursive: true, force: true }));
const jsonlPath = path.join(tempDir, "data/rtt.jsonl");
await appendJsonl(jsonlPath, { run: 1 });
await appendJsonl(jsonlPath, { run: 2 });

View File

@@ -4,17 +4,17 @@ import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { collectSourceFileContents } from "../../scripts/lib/source-file-scan-cache.mjs";
const tempDirs: string[] = [];
const tempDirCleanups: Array<() => Promise<void>> = [];
async function makeTempRepo() {
const repoRoot = await mkdtemp(path.join(os.tmpdir(), "openclaw-source-scan-"));
tempDirs.push(repoRoot);
tempDirCleanups.push(() => rm(repoRoot, { recursive: true, force: true }));
return repoRoot;
}
describe("source file scan cache", () => {
afterEach(async () => {
await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true })));
await Promise.all(tempDirCleanups.splice(0).map((cleanup) => cleanup()));
});
it("bounds concurrent source file reads while preserving sorted output", async () => {

View File

@@ -5,17 +5,19 @@ import { afterEach, describe, expect, it } from "vitest";
import { findBuiltStatusMessageRuntimePath } from "../../scripts/test-built-status-message-runtime.mjs";
import { expectNoReaddirSyncDuring } from "../../src/test-utils/fs-scan-assertions.js";
const tempDirs: string[] = [];
const tempDirCleanups: Array<() => void> = [];
afterEach(() => {
for (const dir of tempDirs.splice(0)) {
fs.rmSync(dir, { recursive: true, force: true });
for (const cleanup of tempDirCleanups.splice(0)) {
cleanup();
}
});
function makeDistDir(): string {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-status-runtime-"));
tempDirs.push(root);
tempDirCleanups.push(() => {
fs.rmSync(root, { recursive: true, force: true });
});
const distDir = path.join(root, "dist");
fs.mkdirSync(distDir, { recursive: true });
return distDir;

View File

@@ -9,11 +9,11 @@ import {
writeShardTimings,
} from "../../scripts/lib/vitest-shard-timings.mjs";
const tempDirs: string[] = [];
const tempDirCleanups: Array<() => void> = [];
afterEach(() => {
for (const dir of tempDirs.splice(0)) {
fs.rmSync(dir, { recursive: true, force: true });
for (const cleanup of tempDirCleanups.splice(0)) {
cleanup();
}
});
@@ -56,7 +56,9 @@ describe("scripts/lib/vitest-shard-timings.mjs", () => {
it("persists include-pattern timing metadata", () => {
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-shard-timings-"));
tempDirs.push(tempDir);
tempDirCleanups.push(() => {
fs.rmSync(tempDir, { recursive: true, force: true });
});
const env = {
OPENCLAW_TEST_PROJECTS_TIMINGS_PATH: path.join(tempDir, "timings.json"),
OPENCLAW_VITEST_SHARD_NAME: "auto-reply-reply-agent-runner",