mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-19 18:39:20 -05:00
perf(test): remove fs skill scanning from skill-commands tests
This commit is contained in:
@@ -1,48 +1,65 @@
|
||||
import fsSync from "node:fs";
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { beforeAll, describe, expect, it, vi } from "vitest";
|
||||
|
||||
// Avoid importing/parsing the full skills loader + user home skills during unit tests.
|
||||
vi.mock("@mariozechner/pi-coding-agent", () => ({
|
||||
formatSkillsForPrompt: () => "",
|
||||
loadSkillsFromDir: ({ dir, source }: { dir: string; source: string }) => {
|
||||
try {
|
||||
const entries = fsSync.readdirSync(dir, { withFileTypes: true });
|
||||
const skills = entries
|
||||
.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."))
|
||||
.map((entry) => {
|
||||
const baseDir = path.join(dir, entry.name);
|
||||
const filePath = path.join(baseDir, "SKILL.md");
|
||||
if (!fsSync.existsSync(filePath)) {
|
||||
return null;
|
||||
}
|
||||
let raw = "";
|
||||
try {
|
||||
raw = fsSync.readFileSync(filePath, "utf-8");
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
const nameMatch = raw.match(/^\s*name:\s*(.+)\s*$/m);
|
||||
const descriptionMatch = raw.match(/^\s*description:\s*(.+)\s*$/m);
|
||||
const name = (nameMatch?.[1] ?? entry.name).trim();
|
||||
const description = (descriptionMatch?.[1] ?? "").trim();
|
||||
return { name, description, source, filePath, baseDir };
|
||||
})
|
||||
.filter(Boolean);
|
||||
return { skills };
|
||||
} catch {
|
||||
return { skills: [] };
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
// Avoid importing the full chat command registry for reserved-name calculation.
|
||||
vi.mock("./commands-registry.js", () => ({
|
||||
listChatCommands: () => [],
|
||||
}));
|
||||
|
||||
vi.mock("../infra/skills-remote.js", () => ({
|
||||
getRemoteSkillEligibility: () => ({}),
|
||||
}));
|
||||
|
||||
// Avoid filesystem-driven skill scanning for these unit tests; we only need command naming semantics.
|
||||
vi.mock("../agents/skills.js", () => {
|
||||
function resolveUniqueName(base: string, used: Set<string>): string {
|
||||
let name = base;
|
||||
let suffix = 2;
|
||||
while (used.has(name.toLowerCase())) {
|
||||
name = `${base}_${suffix}`;
|
||||
suffix += 1;
|
||||
}
|
||||
used.add(name.toLowerCase());
|
||||
return name;
|
||||
}
|
||||
|
||||
function resolveWorkspaceSkills(
|
||||
workspaceDir: string,
|
||||
): Array<{ skillName: string; description: string }> {
|
||||
const dirName = path.basename(workspaceDir);
|
||||
if (dirName === "main") {
|
||||
return [{ skillName: "demo-skill", description: "Demo skill" }];
|
||||
}
|
||||
if (dirName === "research") {
|
||||
return [
|
||||
{ skillName: "demo-skill", description: "Demo skill 2" },
|
||||
{ skillName: "extra-skill", description: "Extra skill" },
|
||||
];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
return {
|
||||
buildWorkspaceSkillCommandSpecs: (
|
||||
workspaceDir: string,
|
||||
opts?: { reservedNames?: Set<string> },
|
||||
) => {
|
||||
const used = new Set<string>();
|
||||
for (const reserved of opts?.reservedNames ?? []) {
|
||||
used.add(String(reserved).toLowerCase());
|
||||
}
|
||||
|
||||
return resolveWorkspaceSkills(workspaceDir).map((entry) => {
|
||||
const base = entry.skillName.replace(/-/g, "_");
|
||||
const name = resolveUniqueName(base, used);
|
||||
return { name, skillName: entry.skillName, description: entry.description };
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
let listSkillCommandsForAgents: typeof import("./skill-commands.js").listSkillCommandsForAgents;
|
||||
let resolveSkillCommandInvocation: typeof import("./skill-commands.js").resolveSkillCommandInvocation;
|
||||
|
||||
@@ -51,22 +68,6 @@ beforeAll(async () => {
|
||||
await import("./skill-commands.js"));
|
||||
});
|
||||
|
||||
async function writeSkill(params: {
|
||||
workspaceDir: string;
|
||||
dirName: string;
|
||||
name: string;
|
||||
description: string;
|
||||
}) {
|
||||
const { workspaceDir, dirName, name, description } = params;
|
||||
const skillDir = path.join(workspaceDir, "skills", dirName);
|
||||
await fs.mkdir(skillDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(skillDir, "SKILL.md"),
|
||||
`---\nname: ${name}\ndescription: ${description}\n---\n\n# ${name}\n`,
|
||||
"utf-8",
|
||||
);
|
||||
}
|
||||
|
||||
describe("resolveSkillCommandInvocation", () => {
|
||||
it("matches skill commands and parses args", () => {
|
||||
const invocation = resolveSkillCommandInvocation({
|
||||
@@ -109,24 +110,8 @@ describe("listSkillCommandsForAgents", () => {
|
||||
const baseDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-skills-"));
|
||||
const mainWorkspace = path.join(baseDir, "main");
|
||||
const researchWorkspace = path.join(baseDir, "research");
|
||||
await writeSkill({
|
||||
workspaceDir: mainWorkspace,
|
||||
dirName: "demo",
|
||||
name: "demo-skill",
|
||||
description: "Demo skill",
|
||||
});
|
||||
await writeSkill({
|
||||
workspaceDir: researchWorkspace,
|
||||
dirName: "demo2",
|
||||
name: "demo-skill",
|
||||
description: "Demo skill 2",
|
||||
});
|
||||
await writeSkill({
|
||||
workspaceDir: researchWorkspace,
|
||||
dirName: "extra",
|
||||
name: "extra-skill",
|
||||
description: "Extra skill",
|
||||
});
|
||||
await fs.mkdir(mainWorkspace, { recursive: true });
|
||||
await fs.mkdir(researchWorkspace, { recursive: true });
|
||||
|
||||
const commands = listSkillCommandsForAgents({
|
||||
cfg: {
|
||||
|
||||
Reference in New Issue
Block a user