From e81442ac8023d1ce8142e0db3be922baaeb0d29a Mon Sep 17 00:00:00 2001 From: Tak Hoffman <781889+Takhoffman@users.noreply.github.com> Date: Sat, 14 Mar 2026 15:51:21 -0500 Subject: [PATCH] Fix full local gate on main --- scripts/bundle-a2ui.sh | 3 +++ src/agents/model-compat.test.ts | 2 -- src/canvas-host/server.test.ts | 18 +++++++++--------- .../config.nix-integration-u3-u5-u9.test.ts | 9 ++++++--- src/config/config.plugin-validation.test.ts | 2 -- src/config/plugin-auto-enable.test.ts | 7 +------ src/infra/dotenv.test.ts | 11 +++++------ src/infra/git-commit.test.ts | 4 +--- src/plugins/discovery.test.ts | 7 +------ src/plugins/loader.test.ts | 2 -- src/plugins/manifest-registry.test.ts | 7 +------ src/utils.test.ts | 6 ++++-- 12 files changed, 31 insertions(+), 47 deletions(-) diff --git a/scripts/bundle-a2ui.sh b/scripts/bundle-a2ui.sh index 3888e4cf5cb..4d53c40ca4c 100755 --- a/scripts/bundle-a2ui.sh +++ b/scripts/bundle-a2ui.sh @@ -88,6 +88,9 @@ fi pnpm -s exec tsc -p "$A2UI_RENDERER_DIR/tsconfig.json" if command -v rolldown >/dev/null 2>&1 && rolldown --version >/dev/null 2>&1; then rolldown -c "$A2UI_APP_DIR/rolldown.config.mjs" +elif [[ -f "$ROOT_DIR/node_modules/.pnpm/rolldown@1.0.0-rc.9/node_modules/rolldown/bin/cli.mjs" ]]; then + node "$ROOT_DIR/node_modules/.pnpm/rolldown@1.0.0-rc.9/node_modules/rolldown/bin/cli.mjs" \ + -c "$A2UI_APP_DIR/rolldown.config.mjs" else pnpm -s dlx rolldown -c "$A2UI_APP_DIR/rolldown.config.mjs" fi diff --git a/src/agents/model-compat.test.ts b/src/agents/model-compat.test.ts index 9c751975a09..3ae2e1b99fe 100644 --- a/src/agents/model-compat.test.ts +++ b/src/agents/model-compat.test.ts @@ -86,8 +86,6 @@ function expectSupportsDeveloperRoleForcedOff(overrides?: Partial>): const normalized = normalizeModelCompat(model as Model); expect(supportsDeveloperRole(normalized)).toBe(false); } - - function expectResolvedForwardCompat( model: Model | undefined, expected: { provider: string; id: string }, diff --git a/src/canvas-host/server.test.ts b/src/canvas-host/server.test.ts index fe888f7e54b..05fdb47528e 100644 --- a/src/canvas-host/server.test.ts +++ b/src/canvas-host/server.test.ts @@ -313,6 +313,7 @@ describe("canvas host", () => { const linkPath = path.join(a2uiRoot, linkName); let createdBundle = false; let createdLink = false; + let server: Awaited> | undefined; try { await fs.stat(bundlePath); @@ -324,17 +325,16 @@ describe("canvas host", () => { await fs.symlink(path.join(process.cwd(), "package.json"), linkPath); createdLink = true; - let server: Awaited>; try { - server = await startFixtureCanvasHost(dir); - } catch (error) { - if (isLoopbackBindDenied(error)) { - return; + try { + server = await startFixtureCanvasHost(dir); + } catch (error) { + if (isLoopbackBindDenied(error)) { + return; + } + throw error; } - throw error; - } - try { const res = await fetch(`http://127.0.0.1:${server.port}/__openclaw__/a2ui/`); const html = await res.text(); expect(res.status).toBe(200); @@ -356,7 +356,7 @@ describe("canvas host", () => { expect(symlinkRes.status).toBe(404); expect(await symlinkRes.text()).toBe("not found"); } finally { - await server.close(); + await server?.close(); if (createdLink) { await fs.rm(linkPath, { force: true }); } diff --git a/src/config/config.nix-integration-u3-u5-u9.test.ts b/src/config/config.nix-integration-u3-u5-u9.test.ts index 5e843607ddb..70ff90e5138 100644 --- a/src/config/config.nix-integration-u3-u5-u9.test.ts +++ b/src/config/config.nix-integration-u3-u5-u9.test.ts @@ -111,9 +111,12 @@ describe("Nix integration (U3, U5, U9)", () => { }); it("CONFIG_PATH uses STATE_DIR when only state dir is overridden", () => { - expect(resolveConfigPathCandidate(envWith({ OPENCLAW_STATE_DIR: "/custom/state" }))).toBe( - path.join(path.resolve("/custom/state"), "openclaw.json"), - ); + expect( + resolveConfigPathCandidate( + envWith({ OPENCLAW_STATE_DIR: "/custom/state", OPENCLAW_TEST_FAST: "1" }), + () => path.join(path.sep, "tmp", "openclaw-config-home"), + ), + ).toBe(path.join(path.resolve("/custom/state"), "openclaw.json")); }); }); diff --git a/src/config/config.plugin-validation.test.ts b/src/config/config.plugin-validation.test.ts index d7e6ae46aca..51d38b1a9af 100644 --- a/src/config/config.plugin-validation.test.ts +++ b/src/config/config.plugin-validation.test.ts @@ -44,7 +44,6 @@ async function writePluginFixture(params: { } describe("config plugin validation", () => { - const previousUmask = process.umask(0o022); let fixtureRoot = ""; let suiteHome = ""; let badPluginDir = ""; @@ -136,7 +135,6 @@ describe("config plugin validation", () => { afterAll(async () => { await fs.rm(fixtureRoot, { recursive: true, force: true }); clearPluginManifestRegistryCache(); - process.umask(previousUmask); }); it("reports missing plugin refs across load paths, entries, and allowlist surfaces", async () => { diff --git a/src/config/plugin-auto-enable.test.ts b/src/config/plugin-auto-enable.test.ts index c44a600a23f..1de11be4a1e 100644 --- a/src/config/plugin-auto-enable.test.ts +++ b/src/config/plugin-auto-enable.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; -import { afterAll, afterEach, describe, expect, it } from "vitest"; +import { afterEach, describe, expect, it } from "vitest"; import { clearPluginDiscoveryCache } from "../plugins/discovery.js"; import { clearPluginManifestRegistryCache, @@ -11,7 +11,6 @@ import { validateConfigObject } from "./config.js"; import { applyPluginAutoEnable } from "./plugin-auto-enable.js"; const tempDirs: string[] = []; -const previousUmask = process.umask(0o022); function chmodSafeDir(dir: string) { if (process.platform === "win32") { @@ -126,10 +125,6 @@ afterEach(() => { } }); -afterAll(() => { - process.umask(previousUmask); -}); - describe("applyPluginAutoEnable", () => { it("auto-enables built-in channels and appends to existing allowlist", () => { const result = applyWithSlackConfig({ plugins: { allow: ["telegram"] } }); diff --git a/src/infra/dotenv.test.ts b/src/infra/dotenv.test.ts index 0b77866a23b..326041a7584 100644 --- a/src/infra/dotenv.test.ts +++ b/src/infra/dotenv.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import { describe, expect, it } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { loadDotEnv } from "./dotenv.js"; async function writeEnvFile(filePath: string, contents: string) { @@ -11,11 +11,10 @@ async function writeEnvFile(filePath: string, contents: string) { async function withIsolatedEnvAndCwd(run: () => Promise) { const prevEnv = { ...process.env }; - const prevCwd = process.cwd(); try { await run(); } finally { - process.chdir(prevCwd); + vi.restoreAllMocks(); for (const key of Object.keys(process.env)) { if (!(key in prevEnv)) { delete process.env[key]; @@ -54,7 +53,7 @@ describe("loadDotEnv", () => { await writeEnvFile(path.join(stateDir, ".env"), "FOO=from-global\nBAR=1\n"); await writeEnvFile(path.join(cwdDir, ".env"), "FOO=from-cwd\n"); - process.chdir(cwdDir); + vi.spyOn(process, "cwd").mockReturnValue(cwdDir); delete process.env.FOO; delete process.env.BAR; @@ -74,7 +73,7 @@ describe("loadDotEnv", () => { await writeEnvFile(path.join(stateDir, ".env"), "FOO=from-global\n"); await writeEnvFile(path.join(cwdDir, ".env"), "FOO=from-cwd\n"); - process.chdir(cwdDir); + vi.spyOn(process, "cwd").mockReturnValue(cwdDir); loadDotEnv({ quiet: true }); @@ -87,7 +86,7 @@ describe("loadDotEnv", () => { await withIsolatedEnvAndCwd(async () => { await withDotEnvFixture(async ({ cwdDir, stateDir }) => { await writeEnvFile(path.join(stateDir, ".env"), "FOO=from-global\n"); - process.chdir(cwdDir); + vi.spyOn(process, "cwd").mockReturnValue(cwdDir); delete process.env.FOO; loadDotEnv({ quiet: true }); diff --git a/src/infra/git-commit.test.ts b/src/infra/git-commit.test.ts index c0ddb136e85..cffd27162b0 100644 --- a/src/infra/git-commit.test.ts +++ b/src/infra/git-commit.test.ts @@ -42,7 +42,6 @@ describe("git commit resolution", () => { const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); beforeEach(async () => { - process.chdir(repoRoot); vi.restoreAllMocks(); vi.doUnmock("node:fs"); vi.doUnmock("node:module"); @@ -52,7 +51,6 @@ describe("git commit resolution", () => { }); afterEach(async () => { - process.chdir(repoRoot); vi.restoreAllMocks(); vi.doUnmock("node:fs"); vi.doUnmock("node:module"); @@ -87,9 +85,9 @@ describe("git commit resolution", () => { .trim() .slice(0, 7); - process.chdir(otherRepo); const { resolveCommitHash } = await import("./git-commit.js"); const entryModuleUrl = pathToFileURL(path.join(repoRoot, "src", "entry.ts")).href; + vi.spyOn(process, "cwd").mockReturnValue(otherRepo); expect(resolveCommitHash({ moduleUrl: entryModuleUrl })).toBe(repoHead); expect(resolveCommitHash({ moduleUrl: entryModuleUrl })).not.toBe(otherHead); diff --git a/src/plugins/discovery.test.ts b/src/plugins/discovery.test.ts index 3b10146d28f..1069c223b1e 100644 --- a/src/plugins/discovery.test.ts +++ b/src/plugins/discovery.test.ts @@ -1,6 +1,6 @@ import fs from "node:fs"; import path from "node:path"; -import { afterAll, afterEach, describe, expect, it } from "vitest"; +import { afterEach, describe, expect, it } from "vitest"; import { clearPluginDiscoveryCache, discoverOpenClawPlugins } from "./discovery.js"; import { cleanupTrackedTempDirs, @@ -9,7 +9,6 @@ import { } from "./test-helpers/fs-fixtures.js"; const tempDirs: string[] = []; -const previousUmask = process.umask(0o022); function makeTempDir() { return makeTrackedTempDir("openclaw-plugins", tempDirs); @@ -59,10 +58,6 @@ afterEach(() => { cleanupTrackedTempDirs(tempDirs); }); -afterAll(() => { - process.umask(previousUmask); -}); - describe("discoverOpenClawPlugins", () => { it("discovers global and workspace extensions", async () => { const stateDir = makeTempDir(); diff --git a/src/plugins/loader.test.ts b/src/plugins/loader.test.ts index 588def450af..6be4992821c 100644 --- a/src/plugins/loader.test.ts +++ b/src/plugins/loader.test.ts @@ -34,7 +34,6 @@ const { loadOpenClawPlugins, resetGlobalHookRunner, } = await importFreshPluginTestModules(); -const previousUmask = process.umask(0o022); type TempPlugin = { dir: string; file: string; id: string }; @@ -300,7 +299,6 @@ afterAll(() => { } catch { // ignore cleanup failures } finally { - process.umask(previousUmask); cachedBundledTelegramDir = ""; cachedBundledMemoryDir = ""; } diff --git a/src/plugins/manifest-registry.test.ts b/src/plugins/manifest-registry.test.ts index a948344cba8..3675dd56f5c 100644 --- a/src/plugins/manifest-registry.test.ts +++ b/src/plugins/manifest-registry.test.ts @@ -1,6 +1,6 @@ import fs from "node:fs"; import path from "node:path"; -import { afterAll, afterEach, describe, expect, it } from "vitest"; +import { afterEach, describe, expect, it } from "vitest"; import type { PluginCandidate } from "./discovery.js"; import { clearPluginManifestRegistryCache, @@ -9,7 +9,6 @@ import { import { cleanupTrackedTempDirs, makeTrackedTempDir } from "./test-helpers/fs-fixtures.js"; const tempDirs: string[] = []; -const previousUmask = process.umask(0o022); function chmodSafeDir(dir: string) { if (process.platform === "win32") { @@ -132,10 +131,6 @@ afterEach(() => { cleanupTrackedTempDirs(tempDirs); }); -afterAll(() => { - process.umask(previousUmask); -}); - describe("loadPluginManifestRegistry", () => { it("emits duplicate warning for truly distinct plugins with same id", () => { const dirA = makeTempDir(); diff --git a/src/utils.test.ts b/src/utils.test.ts index d958e0a26ec..8880f41f6b1 100644 --- a/src/utils.test.ts +++ b/src/utils.test.ts @@ -206,11 +206,13 @@ describe("resolveJidToE164", () => { describe("resolveUserPath", () => { it("expands ~ to home dir", () => { - expect(resolveUserPath("~")).toBe(path.resolve(os.homedir())); + expect(resolveUserPath("~", {}, () => "/Users/thoffman")).toBe(path.resolve("/Users/thoffman")); }); it("expands ~/ to home dir", () => { - expect(resolveUserPath("~/openclaw")).toBe(path.resolve(os.homedir(), "openclaw")); + expect(resolveUserPath("~/openclaw", {}, () => "/Users/thoffman")).toBe( + path.resolve("/Users/thoffman", "openclaw"), + ); }); it("resolves relative paths", () => {