test: trim workspace skill loading cases

This commit is contained in:
Peter Steinberger
2026-04-18 23:02:22 +01:00
parent 3ea27c63e2
commit 0001551143
2 changed files with 25 additions and 50 deletions

View File

@@ -4,7 +4,6 @@ import path from "node:path";
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import { resetLogger, setLoggerOverride } from "../logging/logger.js";
import { loggingState } from "../logging/state.js";
import { withPathResolutionEnv } from "../test-utils/env.js";
import { writeSkill, writeWorkspaceSkills } from "./skills.e2e-test-helpers.js";
import {
restoreMockSkillsHomeEnv,
@@ -26,10 +25,6 @@ async function createTempWorkspaceDir() {
return workspaceDir;
}
function withWorkspaceHome<T>(workspaceDir: string, cb: () => T): T {
return withPathResolutionEnv(workspaceDir, { PATH: "" }, () => cb());
}
function captureWarningLogger() {
setLoggerOverride({ level: "silent", consoleLevel: "warn" });
const warn = vi.fn();
@@ -42,6 +37,17 @@ function captureWarningLogger() {
return warn;
}
function loadTestWorkspaceSkillEntries(
workspaceDir: string,
opts?: Parameters<typeof loadWorkspaceSkillEntries>[1],
) {
return loadWorkspaceSkillEntries(workspaceDir, {
managedSkillsDir: path.join(workspaceDir, ".managed"),
bundledSkillsDir: "",
...opts,
});
}
beforeAll(async () => {
tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-skills-workspace-"));
fakeHome = path.join(tempRoot, "home");
@@ -80,44 +86,27 @@ async function setupWorkspaceWithProsePlugin() {
}
describe("loadWorkspaceSkillEntries", () => {
it("handles an empty managed skills dir without throwing", async () => {
const workspaceDir = await createTempWorkspaceDir();
const managedDir = path.join(workspaceDir, ".managed");
await fs.mkdir(managedDir, { recursive: true });
const entries = withWorkspaceHome(workspaceDir, () =>
loadWorkspaceSkillEntries(workspaceDir, {
managedSkillsDir: managedDir,
bundledSkillsDir: path.join(workspaceDir, ".bundled"),
}),
);
expect(entries).toEqual([]);
});
it("filters plugin-shipped skills through plugin config", async () => {
const { workspaceDir, managedDir, bundledDir } = await setupWorkspaceWithProsePlugin();
const { workspaceDir, managedDir } = await setupWorkspaceWithProsePlugin();
const enabledEntries = loadWorkspaceSkillEntries(workspaceDir, {
const enabledEntries = loadTestWorkspaceSkillEntries(workspaceDir, {
config: {
plugins: {
entries: { "open-prose": { enabled: true } },
},
},
managedSkillsDir: managedDir,
bundledSkillsDir: bundledDir,
});
expect(enabledEntries.map((entry) => entry.skill.name)).toContain("prose");
const blockedEntries = loadWorkspaceSkillEntries(workspaceDir, {
const blockedEntries = loadTestWorkspaceSkillEntries(workspaceDir, {
config: {
plugins: {
allow: ["something-else"],
},
},
managedSkillsDir: managedDir,
bundledSkillsDir: bundledDir,
});
expect(blockedEntries.map((entry) => entry.skill.name)).not.toContain("prose");
@@ -139,10 +128,7 @@ describe("loadWorkspaceSkillEntries", () => {
frontmatterExtra: "disable-model-invocation: true",
});
const entries = loadWorkspaceSkillEntries(workspaceDir, {
managedSkillsDir: path.join(workspaceDir, ".managed"),
bundledSkillsDir: path.join(workspaceDir, ".bundled"),
});
const entries = loadTestWorkspaceSkillEntries(workspaceDir);
expect(entries.map((entry) => entry.skill.name)).toContain("fallback-name");
const hiddenEntry = entries.find((entry) => entry.skill.name === "hidden-skill");
@@ -159,7 +145,7 @@ describe("loadWorkspaceSkillEntries", () => {
{ name: "docs-search", description: "Docs" },
]);
const defaultEntries = loadWorkspaceSkillEntries(workspaceDir, {
const defaultEntries = loadTestWorkspaceSkillEntries(workspaceDir, {
config: {
agents: {
defaults: {
@@ -169,13 +155,11 @@ describe("loadWorkspaceSkillEntries", () => {
},
},
agentId: "writer",
managedSkillsDir: path.join(workspaceDir, ".managed"),
bundledSkillsDir: path.join(workspaceDir, ".bundled"),
});
expect(defaultEntries.map((entry) => entry.skill.name)).toEqual(["github"]);
const replacementEntries = loadWorkspaceSkillEntries(workspaceDir, {
const replacementEntries = loadTestWorkspaceSkillEntries(workspaceDir, {
config: {
agents: {
defaults: {
@@ -185,8 +169,6 @@ describe("loadWorkspaceSkillEntries", () => {
},
},
agentId: "writer",
managedSkillsDir: path.join(workspaceDir, ".managed"),
bundledSkillsDir: path.join(workspaceDir, ".bundled"),
});
expect(replacementEntries.map((entry) => entry.skill.name)).toEqual(["docs-search"]);
@@ -201,7 +183,7 @@ describe("loadWorkspaceSkillEntries", () => {
metadata: '{"openclaw":{"requires":{"anyBins":["missingbin","sandboxbin"]}}}',
});
const entries = loadWorkspaceSkillEntries(workspaceDir, {
const entries = loadTestWorkspaceSkillEntries(workspaceDir, {
config: {
agents: {
defaults: {
@@ -219,8 +201,6 @@ describe("loadWorkspaceSkillEntries", () => {
note: "sandbox",
},
},
managedSkillsDir: path.join(workspaceDir, ".managed"),
bundledSkillsDir: path.join(workspaceDir, ".bundled"),
});
expect(entries.map((entry) => entry.skill.name)).toEqual(["remote-only"]);
@@ -242,10 +222,7 @@ describe("loadWorkspaceSkillEntries", () => {
await fs.symlink(escapedSkillDir, requestedPath, "dir");
const warn = captureWarningLogger();
const entries = loadWorkspaceSkillEntries(workspaceDir, {
managedSkillsDir: path.join(workspaceDir, ".managed"),
bundledSkillsDir: path.join(workspaceDir, ".bundled"),
});
const entries = loadTestWorkspaceSkillEntries(workspaceDir);
expect(entries.map((entry) => entry.skill.name)).not.toContain("outside-skill");
const [line] = warn.mock.calls[0] ?? [];
@@ -276,8 +253,7 @@ describe("loadWorkspaceSkillEntries", () => {
await fs.symlink(escapedSkillDir, requestedPath, "dir");
const warn = captureWarningLogger();
const entries = loadWorkspaceSkillEntries(workspaceDir, {
managedSkillsDir: path.join(workspaceDir, ".managed"),
const entries = loadTestWorkspaceSkillEntries(workspaceDir, {
bundledSkillsDir: bundledDir,
});
@@ -310,8 +286,7 @@ describe("loadWorkspaceSkillEntries", () => {
await fs.symlink(escapedSkillDir, requestedPath, "dir");
const warn = captureWarningLogger();
loadWorkspaceSkillEntries(workspaceDir, {
managedSkillsDir: path.join(workspaceDir, ".managed"),
loadTestWorkspaceSkillEntries(workspaceDir, {
bundledSkillsDir: bundledDir,
});
@@ -347,10 +322,7 @@ describe("loadWorkspaceSkillEntries", () => {
await fs.mkdir(symlinkedSkillDir, { recursive: true });
await fs.symlink(path.join(targetDir, "SKILL.md"), path.join(symlinkedSkillDir, "SKILL.md"));
const entries = loadWorkspaceSkillEntries(workspaceDir, {
managedSkillsDir: path.join(workspaceDir, ".managed"),
bundledSkillsDir: path.join(workspaceDir, ".bundled"),
});
const entries = loadTestWorkspaceSkillEntries(workspaceDir);
expect(entries.map((entry) => entry.skill.name)).not.toContain("outside-file-skill");
expect(entries.map((entry) => entry.skill.name)).not.toContain("symlink-target");

View File

@@ -365,6 +365,9 @@ function loadSkillEntries(
const loadSkills = (params: { dir: string; source: string }): Skill[] => {
const rootDir = path.resolve(params.dir);
if (!fs.existsSync(rootDir)) {
return [];
}
const rootRealPath = tryRealpath(rootDir) ?? rootDir;
const resolved = resolveNestedSkillsRoot(params.dir, {
maxEntriesToScan: limits.maxCandidatesPerRoot,