perf(test): reduce skills + update + memory suite overhead

This commit is contained in:
Peter Steinberger
2026-02-14 15:36:04 +00:00
parent 684c18458a
commit 76e4e9d176
6 changed files with 90 additions and 43 deletions

View File

@@ -4,6 +4,7 @@ import { resolveBundledSkillsDir, type BundledSkillsResolveOptions } from "./bun
const skillsLogger = createSubsystemLogger("skills");
let hasWarnedMissingBundledDir = false;
let cachedBundledContext: { dir: string; names: Set<string> } | null = null;
export type BundledSkillsContext = {
dir?: string;
@@ -24,11 +25,16 @@ export function resolveBundledSkillsContext(
}
return { dir, names };
}
if (cachedBundledContext?.dir === dir) {
return { dir, names: new Set(cachedBundledContext.names) };
}
const result = loadSkillsFromDir({ dir, source: "openclaw-bundled" });
for (const skill of result.skills) {
if (skill.name.trim()) {
names.add(skill.name);
}
}
cachedBundledContext = { dir, names: new Set(names) };
return { dir, names };
}

View File

@@ -1,7 +1,8 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import {
buildWorkspaceSkillStatus,
type SkillStatusEntry,
@@ -214,6 +215,18 @@ describe("skills-cli", () => {
});
describe("integration: loads real skills from bundled directory", () => {
let tempWorkspaceDir = "";
beforeAll(() => {
tempWorkspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-skills-test-"));
});
afterAll(() => {
if (tempWorkspaceDir) {
fs.rmSync(tempWorkspaceDir, { recursive: true, force: true });
}
});
function resolveBundledSkillsDir(): string | undefined {
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
const root = path.resolve(moduleDir, "..", "..");
@@ -231,7 +244,7 @@ describe("skills-cli", () => {
return;
}
const report = buildWorkspaceSkillStatus("/tmp", {
const report = buildWorkspaceSkillStatus(tempWorkspaceDir, {
managedSkillsDir: "/nonexistent",
});
@@ -257,7 +270,7 @@ describe("skills-cli", () => {
return;
}
const report = buildWorkspaceSkillStatus("/tmp", {
const report = buildWorkspaceSkillStatus(tempWorkspaceDir, {
managedSkillsDir: "/nonexistent",
});

View File

@@ -9,6 +9,10 @@ const select = vi.fn();
const spinner = vi.fn(() => ({ start: vi.fn(), stop: vi.fn() }));
const isCancel = (value: unknown) => value === "cancel";
const readPackageName = vi.fn();
const readPackageVersion = vi.fn();
const resolveGlobalManager = vi.fn();
vi.mock("@clack/prompts", () => ({
confirm,
select,
@@ -61,6 +65,16 @@ vi.mock("../process/exec.js", () => ({
runCommandWithTimeout: vi.fn(),
}));
vi.mock("./update-cli/shared.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("./update-cli/shared.js")>();
return {
...actual,
readPackageName,
readPackageVersion,
resolveGlobalManager,
};
});
// Mock doctor (heavy module; should not run in unit tests)
vi.mock("../commands/doctor.js", () => ({
doctorCommand: vi.fn(),
@@ -129,7 +143,23 @@ describe("update-cli", () => {
};
beforeEach(() => {
vi.clearAllMocks();
confirm.mockReset();
select.mockReset();
vi.mocked(runGatewayUpdate).mockReset();
vi.mocked(resolveOpenClawPackageRoot).mockReset();
vi.mocked(readConfigFileSnapshot).mockReset();
vi.mocked(writeConfigFile).mockReset();
vi.mocked(checkUpdateStatus).mockReset();
vi.mocked(fetchNpmTagVersion).mockReset();
vi.mocked(resolveNpmChannelTag).mockReset();
vi.mocked(runCommandWithTimeout).mockReset();
vi.mocked(runDaemonRestart).mockReset();
vi.mocked(defaultRuntime.log).mockReset();
vi.mocked(defaultRuntime.error).mockReset();
vi.mocked(defaultRuntime.exit).mockReset();
readPackageName.mockReset();
readPackageVersion.mockReset();
resolveGlobalManager.mockReset();
vi.mocked(resolveOpenClawPackageRoot).mockResolvedValue(process.cwd());
vi.mocked(readConfigFileSnapshot).mockResolvedValue(baseSnapshot);
vi.mocked(fetchNpmTagVersion).mockResolvedValue({
@@ -172,6 +202,9 @@ describe("update-cli", () => {
signal: null,
killed: false,
});
readPackageName.mockResolvedValue("openclaw");
readPackageVersion.mockResolvedValue("1.0.0");
resolveGlobalManager.mockResolvedValue("npm");
setTty(false);
setStdoutTty(false);
});
@@ -241,11 +274,6 @@ describe("update-cli", () => {
it("defaults to stable channel for package installs when unset", async () => {
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({
@@ -293,11 +321,6 @@ describe("update-cli", () => {
it("falls back to latest when beta tag is older than release", async () => {
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({
@@ -335,11 +358,6 @@ describe("update-cli", () => {
it("honors --tag override", async () => {
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({
@@ -478,11 +496,7 @@ describe("update-cli", () => {
it("requires confirmation on downgrade when non-interactive", async () => {
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",
);
readPackageVersion.mockResolvedValue("2.0.0");
vi.mocked(resolveOpenClawPackageRoot).mockResolvedValue(tempDir);
vi.mocked(checkUpdateStatus).mockResolvedValue({
@@ -520,11 +534,7 @@ describe("update-cli", () => {
it("allows downgrade with --yes in non-interactive mode", async () => {
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",
);
readPackageVersion.mockResolvedValue("2.0.0");
vi.mocked(resolveOpenClawPackageRoot).mockResolvedValue(tempDir);
vi.mocked(checkUpdateStatus).mockResolvedValue({

View File

@@ -75,9 +75,9 @@ describe("memory index", () => {
memorySearch: {
provider: "openai",
model: "mock-embed",
store: { path: indexPath },
store: { path: indexPath, vector: { enabled: false } },
sync: { watch: false, onSessionStart: false, onSearch: true },
query: { minScore: 0 },
query: { minScore: 0, hybrid: { enabled: false } },
},
},
list: [{ id: "main", default: true }],
@@ -114,7 +114,7 @@ describe("memory index", () => {
provider: "openai",
store: { path: indexPath },
sync: { watch: false, onSessionStart: false, onSearch: true },
query: { minScore: 0 },
query: { minScore: 0, hybrid: { enabled: false } },
},
},
list: [{ id: "main", default: true }],
@@ -182,7 +182,7 @@ describe("memory index", () => {
model: "mock-embed",
store: { path: indexPath, vector: { enabled: false } },
sync: { watch: false, onSessionStart: false, onSearch: false },
query: { minScore: 0 },
query: { minScore: 0, hybrid: { enabled: false } },
cache: { enabled: true },
},
},
@@ -276,8 +276,9 @@ describe("memory index", () => {
memorySearch: {
provider: "openai",
model: "mock-embed",
store: { path: indexPath },
store: { path: indexPath, vector: { enabled: false } },
sync: { watch: false, onSessionStart: false, onSearch: true },
query: { minScore: 0, hybrid: { enabled: false } },
},
},
list: [{ id: "main", default: true }],
@@ -304,8 +305,9 @@ describe("memory index", () => {
memorySearch: {
provider: "openai",
model: "mock-embed",
store: { path: indexPath },
store: { path: indexPath, vector: { enabled: false } },
sync: { watch: false, onSessionStart: false, onSearch: true },
query: { minScore: 0, hybrid: { enabled: false } },
extraPaths: [extraDir],
},
},

View File

@@ -67,10 +67,10 @@ describe("memory embedding batches", () => {
memorySearch: {
provider: "openai",
model: "mock-embed",
store: { path: indexPath },
store: { path: indexPath, vector: { enabled: false } },
chunking: { tokens: 1250, overlap: 0 },
sync: { watch: false, onSessionStart: false, onSearch: false },
query: { minScore: 0 },
query: { minScore: 0, hybrid: { enabled: false } },
},
},
list: [{ id: "main", default: true }],
@@ -114,10 +114,10 @@ describe("memory embedding batches", () => {
memorySearch: {
provider: "openai",
model: "mock-embed",
store: { path: indexPath },
store: { path: indexPath, vector: { enabled: false } },
chunking: { tokens: 200, overlap: 0 },
sync: { watch: false, onSessionStart: false, onSearch: false },
query: { minScore: 0 },
query: { minScore: 0, hybrid: { enabled: false } },
},
},
list: [{ id: "main", default: true }],
@@ -174,10 +174,10 @@ describe("memory embedding batches", () => {
memorySearch: {
provider: "openai",
model: "mock-embed",
store: { path: indexPath },
store: { path: indexPath, vector: { enabled: false } },
chunking: { tokens: 200, overlap: 0 },
sync: { watch: false, onSessionStart: false, onSearch: false },
query: { minScore: 0 },
query: { minScore: 0, hybrid: { enabled: false } },
},
},
list: [{ id: "main", default: true }],
@@ -209,9 +209,9 @@ describe("memory embedding batches", () => {
memorySearch: {
provider: "openai",
model: "mock-embed",
store: { path: indexPath },
store: { path: indexPath, vector: { enabled: false } },
sync: { watch: false, onSessionStart: false, onSearch: false },
query: { minScore: 0 },
query: { minScore: 0, hybrid: { enabled: false } },
},
},
list: [{ id: "main", default: true }],

View File

@@ -52,8 +52,22 @@ function windowsPathExtensions(): string[] {
return ["", ...list.filter(Boolean)];
}
let cachedHasBinaryPath: string | undefined;
let cachedHasBinaryPathExt: string | undefined;
const hasBinaryCache = new Map<string, boolean>();
export function hasBinary(bin: string): boolean {
const pathEnv = process.env.PATH ?? "";
const pathExt = process.platform === "win32" ? (process.env.PATHEXT ?? "") : "";
if (cachedHasBinaryPath !== pathEnv || cachedHasBinaryPathExt !== pathExt) {
cachedHasBinaryPath = pathEnv;
cachedHasBinaryPathExt = pathExt;
hasBinaryCache.clear();
}
if (hasBinaryCache.has(bin)) {
return hasBinaryCache.get(bin)!;
}
const parts = pathEnv.split(path.delimiter).filter(Boolean);
const extensions = process.platform === "win32" ? windowsPathExtensions() : [""];
for (const part of parts) {
@@ -61,11 +75,13 @@ export function hasBinary(bin: string): boolean {
const candidate = path.join(part, bin + ext);
try {
fs.accessSync(candidate, fs.constants.X_OK);
hasBinaryCache.set(bin, true);
return true;
} catch {
// keep scanning
}
}
}
hasBinaryCache.set(bin, false);
return false;
}