From fb18f953486bf30a8d1832770cfbcd78be83d1d7 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 29 May 2026 20:45:43 +0200 Subject: [PATCH] test: stabilize slow assertion timings --- .../channel-import-guardrails.test.ts | 67 ++++++++++++++++--- src/cli/gateway-cli.coverage.test.ts | 9 +-- 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/src/channels/plugins/contracts/channel-import-guardrails.test.ts b/src/channels/plugins/contracts/channel-import-guardrails.test.ts index 105787390e8..a2cdac6498b 100644 --- a/src/channels/plugins/contracts/channel-import-guardrails.test.ts +++ b/src/channels/plugins/contracts/channel-import-guardrails.test.ts @@ -1,3 +1,4 @@ +import { spawnSync } from "node:child_process"; import fs from "node:fs"; import { basename, dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; @@ -416,6 +417,55 @@ function collectExtensionSourceFiles(): string[] { return extensionSourceFilesCache; } +function isGuardedExtensionSourceFile(relativePath: string): boolean { + if (!/\.(?:[cm]?ts|[cm]?js|tsx|jsx)$/u.test(relativePath) || relativePath.endsWith(".d.ts")) { + return false; + } + if (relativePath.split("/").some((part) => part === "node_modules" || part === "dist")) { + return false; + } + const entryName = basename(relativePath); + return !( + classifyBundledExtensionSourcePath(resolve(REPO_ROOT, relativePath)).isTestLike || + entryName === "api.ts" || + entryName === "runtime-api.ts" + ); +} + +function collectExtensionForbiddenImportMatches(literals: readonly string[]): string[] { + const result = spawnSync( + "git", + [ + "grep", + "-n", + "-F", + ...literals.flatMap((literal) => ["-e", literal]), + "--", + BUNDLED_PLUGIN_ROOT_DIR, + ], + { + cwd: REPO_ROOT, + encoding: "utf8", + maxBuffer: 8 * 1024 * 1024, + stdio: ["ignore", "pipe", "ignore"], + }, + ); + if (result.status === 1) { + return []; + } + if (result.status !== 0) { + throw new Error("git grep failed while checking extension import guardrails"); + } + return result.stdout + .split("\n") + .map((line) => line.trim()) + .filter((line) => { + const file = line.split(":", 1)[0]; + return file ? isGuardedExtensionSourceFile(file) : false; + }) + .toSorted(); +} + function collectCoreSourceFiles(): string[] { const srcDir = resolve(ROOT_DIR, "..", "src"); const normalizedPluginSdkDir = normalizePath(resolve(ROOT_DIR, "plugin-sdk")); @@ -626,15 +676,14 @@ describe("channel import guardrails", () => { }); it("keeps bundled extension source files off root and compat plugin-sdk imports", () => { - for (const file of collectExtensionSourceFiles()) { - const text = readSource(file); - expect(text, `${file} should not import openclaw/plugin-sdk root`).not.toMatch( - /["']openclaw\/plugin-sdk["']/, - ); - expect(text, `${file} should not import openclaw/plugin-sdk/compat`).not.toMatch( - /["']openclaw\/plugin-sdk\/compat["']/, - ); - } + expect( + collectExtensionForbiddenImportMatches([ + `"openclaw/plugin-sdk"`, + `'openclaw/plugin-sdk'`, + `"openclaw/plugin-sdk/compat"`, + `'openclaw/plugin-sdk/compat'`, + ]), + ).toEqual([]); }); it("keeps bundled extension source files off legacy core send-deps src imports", () => { diff --git a/src/cli/gateway-cli.coverage.test.ts b/src/cli/gateway-cli.coverage.test.ts index 0863cde0039..9813f0b40b4 100644 --- a/src/cli/gateway-cli.coverage.test.ts +++ b/src/cli/gateway-cli.coverage.test.ts @@ -413,11 +413,11 @@ describe("gateway-cli coverage", () => { expect(runtimeErrors.join("\n")).toContain("Invalid --timeout"); }); - it("validates gateway ports and handles force/start errors", async () => { - // Invalid port + it("validates gateway ports before starting", async () => { await expectGatewayExit(["gateway", "--port", "0", "--token", "test-token"]); + }); - // Force free failure + it("reports force-free port failures", async () => { forceFreePortAndWait.mockImplementationOnce(async () => { throw new Error("boom"); }); @@ -430,8 +430,9 @@ describe("gateway-cli coverage", () => { "--force", "--allow-unconfigured", ]); + }); - // Start failure (generic) + it("reports gateway start failures without leaking signal listeners", async () => { startGatewayServer.mockRejectedValueOnce(new Error("nope")); const beforeSigterm = new Set(process.listeners("SIGTERM")); const beforeSigint = new Set(process.listeners("SIGINT"));