perf(test): cut setup/import overhead in hot suites

This commit is contained in:
Peter Steinberger
2026-02-13 21:23:44 +00:00
parent 93dd51bce0
commit caebe70e9a
16 changed files with 428 additions and 426 deletions

View File

@@ -1,7 +1,7 @@
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { UpdateRunResult } from "../infra/update-runner.js";
const confirm = vi.fn();
@@ -91,6 +91,23 @@ const { updateCommand, registerUpdateCli, updateStatusCommand, updateWizardComma
await import("./update-cli.js");
describe("update-cli", () => {
let fixtureRoot = "";
let fixtureCount = 0;
const createCaseDir = async (prefix: string) => {
const dir = path.join(fixtureRoot, `${prefix}-${fixtureCount++}`);
await fs.mkdir(dir, { recursive: true });
return dir;
};
beforeAll(async () => {
fixtureRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-update-tests-"));
});
afterAll(async () => {
await fs.rm(fixtureRoot, { recursive: true, force: true });
});
const baseSnapshot = {
valid: true,
config: {},
@@ -223,41 +240,37 @@ describe("update-cli", () => {
});
it("defaults to stable channel for package installs when unset", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-update-"));
try {
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "openclaw", version: "1.0.0" }),
"utf-8",
);
const tempDir = await createCaseDir("openclaw-update");
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "openclaw", version: "1.0.0" }),
"utf-8",
);
vi.mocked(resolveOpenClawPackageRoot).mockResolvedValue(tempDir);
vi.mocked(checkUpdateStatus).mockResolvedValue({
root: tempDir,
installKind: "package",
packageManager: "npm",
deps: {
manager: "npm",
status: "ok",
lockfilePath: null,
markerPath: null,
},
});
vi.mocked(runGatewayUpdate).mockResolvedValue({
vi.mocked(resolveOpenClawPackageRoot).mockResolvedValue(tempDir);
vi.mocked(checkUpdateStatus).mockResolvedValue({
root: tempDir,
installKind: "package",
packageManager: "npm",
deps: {
manager: "npm",
status: "ok",
mode: "npm",
steps: [],
durationMs: 100,
});
lockfilePath: null,
markerPath: null,
},
});
vi.mocked(runGatewayUpdate).mockResolvedValue({
status: "ok",
mode: "npm",
steps: [],
durationMs: 100,
});
await updateCommand({ yes: true });
await updateCommand({ yes: true });
const call = vi.mocked(runGatewayUpdate).mock.calls[0]?.[0];
expect(call?.channel).toBe("stable");
expect(call?.tag).toBe("latest");
} finally {
await fs.rm(tempDir, { recursive: true, force: true });
}
const call = vi.mocked(runGatewayUpdate).mock.calls[0]?.[0];
expect(call?.channel).toBe("stable");
expect(call?.tag).toBe("latest");
});
it("uses stored beta channel when configured", async () => {
@@ -279,75 +292,67 @@ describe("update-cli", () => {
});
it("falls back to latest when beta tag is older than release", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-update-"));
try {
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "openclaw", version: "1.0.0" }),
"utf-8",
);
const tempDir = await createCaseDir("openclaw-update");
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "openclaw", version: "1.0.0" }),
"utf-8",
);
vi.mocked(resolveOpenClawPackageRoot).mockResolvedValue(tempDir);
vi.mocked(readConfigFileSnapshot).mockResolvedValue({
...baseSnapshot,
config: { update: { channel: "beta" } },
});
vi.mocked(checkUpdateStatus).mockResolvedValue({
root: tempDir,
installKind: "package",
packageManager: "npm",
deps: {
manager: "npm",
status: "ok",
lockfilePath: null,
markerPath: null,
},
});
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "1.2.3-1",
});
vi.mocked(runGatewayUpdate).mockResolvedValue({
vi.mocked(resolveOpenClawPackageRoot).mockResolvedValue(tempDir);
vi.mocked(readConfigFileSnapshot).mockResolvedValue({
...baseSnapshot,
config: { update: { channel: "beta" } },
});
vi.mocked(checkUpdateStatus).mockResolvedValue({
root: tempDir,
installKind: "package",
packageManager: "npm",
deps: {
manager: "npm",
status: "ok",
mode: "npm",
steps: [],
durationMs: 100,
});
lockfilePath: null,
markerPath: null,
},
});
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "1.2.3-1",
});
vi.mocked(runGatewayUpdate).mockResolvedValue({
status: "ok",
mode: "npm",
steps: [],
durationMs: 100,
});
await updateCommand({});
await updateCommand({});
const call = vi.mocked(runGatewayUpdate).mock.calls[0]?.[0];
expect(call?.channel).toBe("beta");
expect(call?.tag).toBe("latest");
} finally {
await fs.rm(tempDir, { recursive: true, force: true });
}
const call = vi.mocked(runGatewayUpdate).mock.calls[0]?.[0];
expect(call?.channel).toBe("beta");
expect(call?.tag).toBe("latest");
});
it("honors --tag override", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-update-"));
try {
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "openclaw", version: "1.0.0" }),
"utf-8",
);
const tempDir = await createCaseDir("openclaw-update");
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "openclaw", version: "1.0.0" }),
"utf-8",
);
vi.mocked(resolveOpenClawPackageRoot).mockResolvedValue(tempDir);
vi.mocked(runGatewayUpdate).mockResolvedValue({
status: "ok",
mode: "npm",
steps: [],
durationMs: 100,
});
vi.mocked(resolveOpenClawPackageRoot).mockResolvedValue(tempDir);
vi.mocked(runGatewayUpdate).mockResolvedValue({
status: "ok",
mode: "npm",
steps: [],
durationMs: 100,
});
await updateCommand({ tag: "next" });
await updateCommand({ tag: "next" });
const call = vi.mocked(runGatewayUpdate).mock.calls[0]?.[0];
expect(call?.tag).toBe("next");
} finally {
await fs.rm(tempDir, { recursive: true, force: true });
}
const call = vi.mocked(runGatewayUpdate).mock.calls[0]?.[0];
expect(call?.tag).toBe("next");
});
it("updateCommand outputs JSON when --json is set", async () => {
@@ -471,95 +476,87 @@ describe("update-cli", () => {
});
it("requires confirmation on downgrade when non-interactive", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-update-"));
try {
setTty(false);
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "openclaw", version: "2.0.0" }),
"utf-8",
);
const tempDir = await createCaseDir("openclaw-update");
setTty(false);
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "openclaw", version: "2.0.0" }),
"utf-8",
);
vi.mocked(resolveOpenClawPackageRoot).mockResolvedValue(tempDir);
vi.mocked(checkUpdateStatus).mockResolvedValue({
root: tempDir,
installKind: "package",
packageManager: "npm",
deps: {
manager: "npm",
status: "ok",
lockfilePath: null,
markerPath: null,
},
});
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "0.0.1",
});
vi.mocked(runGatewayUpdate).mockResolvedValue({
vi.mocked(resolveOpenClawPackageRoot).mockResolvedValue(tempDir);
vi.mocked(checkUpdateStatus).mockResolvedValue({
root: tempDir,
installKind: "package",
packageManager: "npm",
deps: {
manager: "npm",
status: "ok",
mode: "npm",
steps: [],
durationMs: 100,
});
vi.mocked(defaultRuntime.error).mockClear();
vi.mocked(defaultRuntime.exit).mockClear();
lockfilePath: null,
markerPath: null,
},
});
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "0.0.1",
});
vi.mocked(runGatewayUpdate).mockResolvedValue({
status: "ok",
mode: "npm",
steps: [],
durationMs: 100,
});
vi.mocked(defaultRuntime.error).mockClear();
vi.mocked(defaultRuntime.exit).mockClear();
await updateCommand({});
await updateCommand({});
expect(defaultRuntime.error).toHaveBeenCalledWith(
expect.stringContaining("Downgrade confirmation required."),
);
expect(defaultRuntime.exit).toHaveBeenCalledWith(1);
} finally {
await fs.rm(tempDir, { recursive: true, force: true });
}
expect(defaultRuntime.error).toHaveBeenCalledWith(
expect.stringContaining("Downgrade confirmation required."),
);
expect(defaultRuntime.exit).toHaveBeenCalledWith(1);
});
it("allows downgrade with --yes in non-interactive mode", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-update-"));
try {
setTty(false);
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "openclaw", version: "2.0.0" }),
"utf-8",
);
const tempDir = await createCaseDir("openclaw-update");
setTty(false);
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "openclaw", version: "2.0.0" }),
"utf-8",
);
vi.mocked(resolveOpenClawPackageRoot).mockResolvedValue(tempDir);
vi.mocked(checkUpdateStatus).mockResolvedValue({
root: tempDir,
installKind: "package",
packageManager: "npm",
deps: {
manager: "npm",
status: "ok",
lockfilePath: null,
markerPath: null,
},
});
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "0.0.1",
});
vi.mocked(runGatewayUpdate).mockResolvedValue({
vi.mocked(resolveOpenClawPackageRoot).mockResolvedValue(tempDir);
vi.mocked(checkUpdateStatus).mockResolvedValue({
root: tempDir,
installKind: "package",
packageManager: "npm",
deps: {
manager: "npm",
status: "ok",
mode: "npm",
steps: [],
durationMs: 100,
});
vi.mocked(defaultRuntime.error).mockClear();
vi.mocked(defaultRuntime.exit).mockClear();
lockfilePath: null,
markerPath: null,
},
});
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "0.0.1",
});
vi.mocked(runGatewayUpdate).mockResolvedValue({
status: "ok",
mode: "npm",
steps: [],
durationMs: 100,
});
vi.mocked(defaultRuntime.error).mockClear();
vi.mocked(defaultRuntime.exit).mockClear();
await updateCommand({ yes: true });
await updateCommand({ yes: true });
expect(defaultRuntime.error).not.toHaveBeenCalledWith(
expect.stringContaining("Downgrade confirmation required."),
);
expect(runGatewayUpdate).toHaveBeenCalled();
} finally {
await fs.rm(tempDir, { recursive: true, force: true });
}
expect(defaultRuntime.error).not.toHaveBeenCalledWith(
expect.stringContaining("Downgrade confirmation required."),
);
expect(runGatewayUpdate).toHaveBeenCalled();
});
it("updateWizardCommand requires a TTY", async () => {
@@ -576,7 +573,7 @@ describe("update-cli", () => {
});
it("updateWizardCommand offers dev checkout and forwards selections", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-update-wizard-"));
const tempDir = await createCaseDir("openclaw-update-wizard");
const previousGitDir = process.env.OPENCLAW_GIT_DIR;
try {
setTty(true);
@@ -608,7 +605,6 @@ describe("update-cli", () => {
expect(call?.channel).toBe("dev");
} finally {
process.env.OPENCLAW_GIT_DIR = previousGitDir;
await fs.rm(tempDir, { recursive: true, force: true });
}
});
});