mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:50:43 +00:00
perf(test): slim entry and chat tests
This commit is contained in:
26
src/entry.ts
26
src/entry.ts
@@ -3,11 +3,12 @@ import { spawn } from "node:child_process";
|
||||
import { enableCompileCache } from "node:module";
|
||||
import process from "node:process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { isRootHelpInvocation, isRootVersionInvocation } from "./cli/argv.js";
|
||||
import { isRootHelpInvocation } from "./cli/argv.js";
|
||||
import { parseCliContainerArgs, resolveCliContainerTarget } from "./cli/container-target.js";
|
||||
import { applyCliProfileEnv, parseCliProfileArgs } from "./cli/profile.js";
|
||||
import { normalizeWindowsArgv } from "./cli/windows-argv.js";
|
||||
import { buildCliRespawnPlan } from "./entry.respawn.js";
|
||||
import { tryHandleRootVersionFastPath } from "./entry.version-fast-path.js";
|
||||
import { isTruthyEnvValue, normalizeEnv } from "./infra/env.js";
|
||||
import { isMainModule } from "./infra/is-main.js";
|
||||
import { ensureOpenClawExecMarkerOnProcess } from "./infra/openclaw-exec-env.js";
|
||||
@@ -99,29 +100,6 @@ if (
|
||||
return true;
|
||||
}
|
||||
|
||||
function tryHandleRootVersionFastPath(argv: string[]): boolean {
|
||||
if (resolveCliContainerTarget(argv)) {
|
||||
return false;
|
||||
}
|
||||
if (!isRootVersionInvocation(argv)) {
|
||||
return false;
|
||||
}
|
||||
Promise.all([import("./version.js"), import("./infra/git-commit.js")])
|
||||
.then(([{ VERSION }, { resolveCommitHash }]) => {
|
||||
const commit = resolveCommitHash({ moduleUrl: import.meta.url });
|
||||
console.log(commit ? `OpenClaw ${VERSION} (${commit})` : `OpenClaw ${VERSION}`);
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
"[openclaw] Failed to resolve version:",
|
||||
error instanceof Error ? (error.stack ?? error.message) : error,
|
||||
);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
process.argv = normalizeWindowsArgv(process.argv);
|
||||
|
||||
if (!ensureCliRespawnReady()) {
|
||||
|
||||
@@ -1,163 +1,80 @@
|
||||
import process from "node:process";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { importFreshModule } from "../test/helpers/import-fresh.js";
|
||||
|
||||
const applyCliProfileEnvMock = vi.hoisted(() => vi.fn());
|
||||
const attachChildProcessBridgeMock = vi.hoisted(() => vi.fn());
|
||||
const installProcessWarningFilterMock = vi.hoisted(() => vi.fn());
|
||||
const isMainModuleMock = vi.hoisted(() => vi.fn(() => true));
|
||||
const isRootHelpInvocationMock = vi.hoisted(() => vi.fn(() => false));
|
||||
const isRootVersionInvocationMock = vi.hoisted(() => vi.fn(() => true));
|
||||
const normalizeEnvMock = vi.hoisted(() => vi.fn());
|
||||
const normalizeWindowsArgvMock = vi.hoisted(() => vi.fn((argv: string[]) => argv));
|
||||
const parseCliProfileArgsMock = vi.hoisted(() => vi.fn((argv: string[]) => ({ ok: true, argv })));
|
||||
const resolveCliContainerTargetMock = vi.hoisted(() => vi.fn<() => string | null>(() => null));
|
||||
const resolveCommitHashMock = vi.hoisted(() => vi.fn<() => string | null>(() => "abc1234"));
|
||||
const runCliMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
const shouldSkipRespawnForArgvMock = vi.hoisted(() => vi.fn(() => true));
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { tryHandleRootVersionFastPath } from "./entry.version-fast-path.js";
|
||||
|
||||
vi.mock("./cli/argv.js", () => ({
|
||||
isRootHelpInvocation: isRootHelpInvocationMock,
|
||||
isRootVersionInvocation: isRootVersionInvocationMock,
|
||||
isRootHelpInvocation: () => false,
|
||||
isRootVersionInvocation: (argv: string[]) => argv.includes("--version"),
|
||||
}));
|
||||
|
||||
vi.mock("./cli/container-target.js", () => ({
|
||||
parseCliContainerArgs: (argv: string[]) => ({ ok: true, container: null, argv }),
|
||||
resolveCliContainerTarget: resolveCliContainerTargetMock,
|
||||
resolveCliContainerTarget: (argv: string[], env: NodeJS.ProcessEnv = process.env) =>
|
||||
argv.includes("--container") ? "demo" : (env.OPENCLAW_CONTAINER ?? null),
|
||||
}));
|
||||
|
||||
vi.mock("./cli/profile.js", () => ({
|
||||
applyCliProfileEnv: applyCliProfileEnvMock,
|
||||
parseCliProfileArgs: parseCliProfileArgsMock,
|
||||
}));
|
||||
|
||||
vi.mock("./cli/run-main.js", () => ({
|
||||
runCli: runCliMock,
|
||||
}));
|
||||
|
||||
vi.mock("./cli/respawn-policy.js", () => ({
|
||||
shouldSkipRespawnForArgv: shouldSkipRespawnForArgvMock,
|
||||
}));
|
||||
|
||||
vi.mock("./cli/windows-argv.js", () => ({
|
||||
normalizeWindowsArgv: normalizeWindowsArgvMock,
|
||||
}));
|
||||
|
||||
vi.mock("./infra/env.js", () => ({
|
||||
isTruthyEnvValue: () => false,
|
||||
normalizeEnv: normalizeEnvMock,
|
||||
}));
|
||||
|
||||
vi.mock("./infra/git-commit.js", () => ({
|
||||
resolveCommitHash: resolveCommitHashMock,
|
||||
}));
|
||||
|
||||
vi.mock("./infra/gaxios-fetch-compat.js", () => ({
|
||||
installGaxiosFetchCompat: vi.fn(async () => {}),
|
||||
}));
|
||||
|
||||
vi.mock("./infra/is-main.js", () => ({
|
||||
isMainModule: isMainModuleMock,
|
||||
}));
|
||||
|
||||
vi.mock("./infra/warning-filter.js", () => ({
|
||||
installProcessWarningFilter: installProcessWarningFilterMock,
|
||||
}));
|
||||
|
||||
vi.mock("./process/child-process-bridge.js", () => ({
|
||||
attachChildProcessBridge: attachChildProcessBridgeMock,
|
||||
}));
|
||||
|
||||
vi.mock("./version.js", () => ({
|
||||
VERSION: "9.9.9-test",
|
||||
}));
|
||||
|
||||
async function importEntry(scope: string) {
|
||||
return await importFreshModule<typeof import("./entry.js")>(
|
||||
import.meta.url,
|
||||
`./entry.js?scope=${scope}`,
|
||||
);
|
||||
}
|
||||
|
||||
async function flushEntrySideEffects() {
|
||||
async function flushVersionFastPath() {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
}
|
||||
|
||||
describe("entry root version fast path", () => {
|
||||
let originalArgv: string[];
|
||||
let originalGatewayToken: string | undefined;
|
||||
let exitSpy: ReturnType<typeof vi.spyOn>;
|
||||
it("prints version output and skips host handling when container-targeted", async () => {
|
||||
const output = vi.fn();
|
||||
const exit = vi.fn();
|
||||
const resolveVersion = vi.fn(async () => ({
|
||||
VERSION: "9.9.9-test",
|
||||
resolveCommitHash: vi.fn(() => "abc1234"),
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
originalArgv = [...process.argv];
|
||||
originalGatewayToken = process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||
delete process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||
process.argv = ["node", "openclaw", "--version"];
|
||||
exitSpy = vi
|
||||
.spyOn(process, "exit")
|
||||
.mockImplementation(((_code?: number) => undefined) as typeof process.exit);
|
||||
});
|
||||
expect(
|
||||
tryHandleRootVersionFastPath(["node", "openclaw", "--version"], {
|
||||
output,
|
||||
exit,
|
||||
resolveVersion,
|
||||
}),
|
||||
).toBe(true);
|
||||
await flushVersionFastPath();
|
||||
expect(output).toHaveBeenCalledWith("OpenClaw 9.9.9-test (abc1234)");
|
||||
expect(exit).toHaveBeenCalledWith(0);
|
||||
|
||||
afterEach(() => {
|
||||
process.argv = originalArgv;
|
||||
if (originalGatewayToken === undefined) {
|
||||
delete process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||
} else {
|
||||
process.env.OPENCLAW_GATEWAY_TOKEN = originalGatewayToken;
|
||||
}
|
||||
exitSpy.mockRestore();
|
||||
});
|
||||
output.mockClear();
|
||||
exit.mockClear();
|
||||
resolveVersion.mockResolvedValueOnce({
|
||||
VERSION: "9.9.9-test",
|
||||
resolveCommitHash: vi.fn(() => null),
|
||||
});
|
||||
|
||||
it("prints commit-tagged version output when commit metadata is available", async () => {
|
||||
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
expect(
|
||||
tryHandleRootVersionFastPath(["node", "openclaw", "--version"], {
|
||||
output,
|
||||
exit,
|
||||
resolveVersion,
|
||||
}),
|
||||
).toBe(true);
|
||||
await flushVersionFastPath();
|
||||
expect(output).toHaveBeenCalledWith("OpenClaw 9.9.9-test");
|
||||
expect(exit).toHaveBeenCalledWith(0);
|
||||
|
||||
await importEntry("commit-tagged");
|
||||
await flushEntrySideEffects();
|
||||
expect(logSpy).toHaveBeenCalledWith("OpenClaw 9.9.9-test (abc1234)");
|
||||
expect(exitSpy).toHaveBeenCalledWith(0);
|
||||
output.mockClear();
|
||||
exit.mockClear();
|
||||
expect(
|
||||
tryHandleRootVersionFastPath(["node", "openclaw", "--container", "demo", "--version"], {
|
||||
output,
|
||||
exit,
|
||||
resolveVersion,
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(resolveVersion).toHaveBeenCalledTimes(2);
|
||||
expect(output).not.toHaveBeenCalled();
|
||||
expect(exit).not.toHaveBeenCalled();
|
||||
|
||||
logSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("falls back to plain version output when commit metadata is unavailable", async () => {
|
||||
resolveCommitHashMock.mockReturnValueOnce(null);
|
||||
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
|
||||
await importEntry("plain-version");
|
||||
await flushEntrySideEffects();
|
||||
expect(logSpy).toHaveBeenCalledWith("OpenClaw 9.9.9-test");
|
||||
expect(exitSpy).toHaveBeenCalledWith(0);
|
||||
|
||||
logSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("skips the host version fast path when a container target is active", async () => {
|
||||
resolveCliContainerTargetMock.mockReturnValue("demo");
|
||||
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
|
||||
await importEntry("container-target");
|
||||
await flushEntrySideEffects();
|
||||
expect(runCliMock).toHaveBeenCalledWith(["node", "openclaw", "--version"]);
|
||||
expect(logSpy).not.toHaveBeenCalled();
|
||||
expect(exitSpy).not.toHaveBeenCalled();
|
||||
|
||||
logSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("allows root version container mode when gateway override env vars are set", async () => {
|
||||
resolveCliContainerTargetMock.mockReturnValue("demo");
|
||||
process.env.OPENCLAW_GATEWAY_TOKEN = "demo-token";
|
||||
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
|
||||
await importEntry("gateway-override");
|
||||
await flushEntrySideEffects();
|
||||
expect(runCliMock).toHaveBeenCalledWith(["node", "openclaw", "--version"]);
|
||||
expect(errorSpy).not.toHaveBeenCalled();
|
||||
expect(exitSpy).not.toHaveBeenCalled();
|
||||
|
||||
errorSpy.mockRestore();
|
||||
expect(
|
||||
tryHandleRootVersionFastPath(["node", "openclaw", "--version"], {
|
||||
env: { OPENCLAW_CONTAINER: "demo" },
|
||||
output,
|
||||
exit,
|
||||
resolveVersion,
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
53
src/entry.version-fast-path.ts
Normal file
53
src/entry.version-fast-path.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { isRootVersionInvocation } from "./cli/argv.js";
|
||||
import { resolveCliContainerTarget } from "./cli/container-target.js";
|
||||
|
||||
export function tryHandleRootVersionFastPath(
|
||||
argv: string[],
|
||||
deps: {
|
||||
env?: NodeJS.ProcessEnv;
|
||||
moduleUrl?: string;
|
||||
output?: (message: string) => void;
|
||||
exit?: (code?: number) => void;
|
||||
onError?: (error: unknown) => void;
|
||||
resolveVersion?: () => Promise<{
|
||||
VERSION: string;
|
||||
resolveCommitHash: (params: { moduleUrl: string }) => string | null;
|
||||
}>;
|
||||
} = {},
|
||||
): boolean {
|
||||
if (resolveCliContainerTarget(argv, deps.env)) {
|
||||
return false;
|
||||
}
|
||||
if (!isRootVersionInvocation(argv)) {
|
||||
return false;
|
||||
}
|
||||
const output = deps.output ?? ((message: string) => console.log(message));
|
||||
const exit = deps.exit ?? ((code?: number) => process.exit(code));
|
||||
const onError =
|
||||
deps.onError ??
|
||||
((error: unknown) => {
|
||||
console.error(
|
||||
"[openclaw] Failed to resolve version:",
|
||||
error instanceof Error ? (error.stack ?? error.message) : error,
|
||||
);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
const resolveVersion =
|
||||
deps.resolveVersion ??
|
||||
(async () => {
|
||||
const [{ VERSION }, { resolveCommitHash }] = await Promise.all([
|
||||
import("./version.js"),
|
||||
import("./infra/git-commit.js"),
|
||||
]);
|
||||
return { VERSION, resolveCommitHash };
|
||||
});
|
||||
|
||||
resolveVersion()
|
||||
.then(({ VERSION, resolveCommitHash }) => {
|
||||
const commit = resolveCommitHash({ moduleUrl: deps.moduleUrl ?? import.meta.url });
|
||||
output(commit ? `OpenClaw ${VERSION} (${commit})` : `OpenClaw ${VERSION}`);
|
||||
exit(0);
|
||||
})
|
||||
.catch(onError);
|
||||
return true;
|
||||
}
|
||||
@@ -16,6 +16,23 @@ vi.mock("../markdown.ts", () => ({
|
||||
toSanitizedMarkdownHtml: (value: string) => value,
|
||||
}));
|
||||
|
||||
vi.mock("../chat/export.ts", () => ({
|
||||
exportChatMarkdown: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../chat/speech.ts", () => ({
|
||||
isSttActive: () => false,
|
||||
isSttSupported: () => false,
|
||||
isTtsSpeaking: () => false,
|
||||
isTtsSupported: () => false,
|
||||
speakText: () => false,
|
||||
startStt: () => false,
|
||||
stopStt: () => undefined,
|
||||
stopTts: () => undefined,
|
||||
}));
|
||||
|
||||
vi.mock("../components/resizable-divider.ts", () => ({}));
|
||||
|
||||
vi.mock("./markdown-sidebar.ts", async () => {
|
||||
const { html } = await import("lit");
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user