test(cli): remove private test seams (#122046)

This commit is contained in:
Peter Steinberger
2026-08-11 06:21:28 -07:00
committed by GitHub
parent 2c8ed54ddb
commit 241e1accde
7 changed files with 29 additions and 77 deletions
+16 -18
View File
@@ -128,32 +128,33 @@ describe("emitCliBanner", () => {
expect(written).toContain("( o.o )");
});
it("keeps lobster day out of plain terminals and pinned tagline modes", async () => {
const { emitCliBanner, testing } = await importFreshBannerModule();
it.each([
{ label: "plain terminals", mode: "random" as const, richTty: false },
{ label: "pinned tagline modes", mode: "off" as const, richTty: true },
])("keeps lobster day out of $label", async ({ mode, richTty }) => {
const { emitCliBanner } = await importFreshBannerModule();
setStdoutIsTty(true);
const writeSpy = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
const base = {
emitCliBanner("2026.3.7", {
argv: ["node", "openclaw"],
commit: "abc1234",
env: { LANG: "en_US.UTF-8" },
isTty: true,
mode,
now: () => new Date(2026, 1, 26),
platform: "darwin" as const,
};
emitCliBanner("2026.3.7", { ...base, mode: "random", richTty: false });
testing.resetBannerEmittedForTests();
emitCliBanner("2026.3.7", { ...base, mode: "off", richTty: true });
platform: "darwin",
richTty,
});
const written = writeSpy.mock.calls.map(([chunk]) => String(chunk)).join("");
expect(written).not.toContain("( o.o )");
});
it("can reset banner emission state for same-module tests", async () => {
const { emitCliBanner, hasEmittedCliBanner, testing } = await importFreshBannerModule();
it("emits only once per module instance", async () => {
const { emitCliBanner, hasEmittedCliBanner } = await importFreshBannerModule();
setStdoutIsTty(true);
const writeSpy = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
const options = {
argv: ["node", "openclaw"],
commit: "abc1234",
@@ -165,12 +166,9 @@ describe("emitCliBanner", () => {
};
emitCliBanner("2026.3.7", options);
expect(hasEmittedCliBanner()).toBe(true);
testing.resetBannerEmittedForTests();
expect(hasEmittedCliBanner()).toBe(false);
emitCliBanner("2026.3.7", options);
expect(writeSpy).toHaveBeenCalledTimes(2);
expect(hasEmittedCliBanner()).toBe(true);
expect(writeSpy).toHaveBeenCalledTimes(1);
});
});
-6
View File
@@ -134,9 +134,3 @@ export function emitCliBanner(version: string, options: BannerOptions = {}) {
export function hasEmittedCliBanner(): boolean {
return bannerEmitted;
}
export const testing = {
resetBannerEmittedForTests(): void {
bannerEmitted = false;
},
};
-22
View File
@@ -1,22 +0,0 @@
// Update CLI test-helper tests cover helper fixtures used by update command tests.
import path from "node:path";
import { describe, expect, it } from "vitest";
import { isOwningNpmCommand } from "./update-cli.test-helpers.js";
describe("isOwningNpmCommand", () => {
it("accepts absolute npm binaries under the owning prefix", () => {
const prefix = path.join(path.sep, "opt", "homebrew");
expect(isOwningNpmCommand(path.join(prefix, "bin", "npm"), prefix)).toBe(true);
expect(isOwningNpmCommand(path.join(prefix, "npm.cmd"), prefix)).toBe(true);
});
it("rejects plain npm and paths outside the owning prefix", () => {
const prefix = path.join(path.sep, "opt", "homebrew");
expect(isOwningNpmCommand("npm", prefix)).toBe(false);
expect(isOwningNpmCommand(path.join(path.sep, "usr", "local", "bin", "npm"), prefix)).toBe(
false,
);
});
});
-19
View File
@@ -1,19 +0,0 @@
// Update CLI test helpers provide shared fixtures and path helpers for update tests.
import path from "node:path";
function isPathInsideRoot(candidate: string, root: string): boolean {
const relative = path.relative(path.resolve(root), path.resolve(candidate));
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
}
export function isOwningNpmCommand(value: unknown, owningPrefix: string): boolean {
if (typeof value !== "string" || !path.isAbsolute(value)) {
return false;
}
const normalized = path.normalize(value);
return (
normalized !== path.normalize("npm") &&
isPathInsideRoot(normalized, owningPrefix) &&
/npm(?:\.cmd)?$/i.test(normalized)
);
}
+5 -3
View File
@@ -27,7 +27,6 @@ import { CLAWHUB_INSTALL_ERROR_CODE } from "../plugins/clawhub-error-codes.js";
import { captureEnv, withEnvAsync } from "../test-utils/env.js";
import { VERSION } from "../version.js";
import { createCliRuntimeCapture, getMockCallOutput } from "./test-runtime-capture.js";
import { isOwningNpmCommand } from "./update-cli.test-helpers.js";
const confirm = vi.fn();
const select = vi.fn();
@@ -5365,6 +5364,9 @@ describe("update-cli", () => {
const pkgRoot = path.join(brewRoot, "openclaw");
const brewNpm = path.join(brewPrefix, "bin", "npm");
const win32PrefixNpm = path.join(brewPrefix, "npm.cmd");
const owningNpmCommands = new Set([brewNpm, win32PrefixNpm].map(path.normalize));
const isOwningNpmCommand = (value: unknown) =>
typeof value === "string" && owningNpmCommands.has(path.normalize(value));
const pathNpmRoot = createCaseDir("nvm-root");
mockPackageInstallStatus(pkgRoot);
pathExists.mockResolvedValue(false);
@@ -5376,7 +5378,7 @@ describe("update-cli", () => {
if (argv[0] === "npm" && argv[1] === "root" && argv[2] === "-g") {
return commandResult({ stdout: `${pathNpmRoot}\n` });
}
if (isOwningNpmCommand(argv[0], brewPrefix) && argv[1] === "root" && argv[2] === "-g") {
if (isOwningNpmCommand(argv[0]) && argv[1] === "root" && argv[2] === "-g") {
return commandResult({ stdout: `${brewRoot}\n` });
}
return commandResult();
@@ -5395,7 +5397,7 @@ describe("update-cli", () => {
.mock.calls.find(
([argv]) =>
Array.isArray(argv) &&
isOwningNpmCommand(argv[0], brewPrefix) &&
isOwningNpmCommand(argv[0]) &&
argv[1] === "i" &&
argv[2] === "-g" &&
argv.includes("openclaw@9999.0.0"),
+8 -7
View File
@@ -25,19 +25,12 @@ import {
resolveControlUiLinks,
resolveLocalControlUiProbeLinks,
summarizeExistingConfig,
testing,
validateGatewayPasswordInput,
waitForGatewayReachable,
} from "./onboard-helpers.js";
const tempDirs = useAutoCleanupTempDirTracker(afterEach);
describe("onboard error summaries", () => {
it("keeps the bounded first line UTF-16 well-formed", () => {
expect(testing.summarizeError(`${"x".repeat(118)}🚀tail\nignored`)).toBe(`${"x".repeat(118)}`);
});
});
describe("printWizardHeader", () => {
const withColumns = async (columns: number | undefined, run: () => Promise<void>) => {
const previous = Object.getOwnPropertyDescriptor(process.stdout, "columns");
@@ -699,6 +692,14 @@ describe("probeGatewayReachable", () => {
});
});
it("bounds thrown probe errors without splitting UTF-16", async () => {
const detail = `${"x".repeat(118)}`;
const params = { url: "ws://127.0.0.1:18789" };
mocks.probeGateway.mockRejectedValue(new Error(`${"x".repeat(118)}🚀tail\nignored`));
expect(await probeGatewayReachable(params)).toEqual({ ok: false, detail });
expect(await probeGatewayConfiguredModel(params)).toEqual({ kind: "unreachable", detail });
});
it("forwards a configured TLS fingerprint to the gateway probe", async () => {
mocks.probeGateway.mockResolvedValueOnce({
ok: true,
-2
View File
@@ -537,7 +537,5 @@ function summarizeError(err: unknown): string {
return line.length > 120 ? `${truncateUtf16Safe(line, 119)}` : line;
}
export const testing = { summarizeError };
/** Default workspace path shown by onboarding prompts. */
export const DEFAULT_WORKSPACE = DEFAULT_AGENT_WORKSPACE_DIR;