Files
curriculum-project-hub/hub/test/unit/curated-skills.test.ts
T

72 lines
2.6 KiB
TypeScript

import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import {
CURATED_SKILL_IDS,
CURATED_SKILL_NAMES,
CURATED_SKILL_PLUGIN_NAME,
validateCuratedSkillPlugin,
} from "../../src/agent/curatedSkills.js";
describe("validateCuratedSkillPlugin", () => {
const roots: string[] = [];
afterEach(async () => {
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
});
it("accepts exactly the release-owned plugin and returns qualified skill ids", async () => {
const root = await skillPluginFixture();
await expect(validateCuratedSkillPlugin(root)).resolves.toEqual({
root,
skillIds: CURATED_SKILL_IDS,
});
});
it("fails closed when a curated skill is absent from the release", async () => {
const root = await skillPluginFixture();
await rm(join(root, "skills", "outline"), { recursive: true });
await expect(validateCuratedSkillPlugin(root)).rejects.toThrow(/curated plugin entry missing: skills\/outline/);
});
it("fails closed when a skill manifest name does not match the allowlist", async () => {
const root = await skillPluginFixture();
await writeFile(join(root, "skills", "outline", "SKILL.md"), "---\nname: other\n---\n");
await expect(validateCuratedSkillPlugin(root)).rejects.toThrow(/curated skill manifest name mismatch/);
});
it("rejects an extra skill directory", async () => {
const root = await skillPluginFixture();
await mkdir(join(root, "skills", "extra"));
await expect(validateCuratedSkillPlugin(root)).rejects.toThrow(/unexpected curated plugin entry: skills\/extra/);
});
it("rejects plugin capabilities outside the reviewed skill catalog", async () => {
const root = await skillPluginFixture();
await mkdir(join(root, "hooks"));
await expect(validateCuratedSkillPlugin(root)).rejects.toThrow(/unexpected curated plugin entry: hooks/);
});
async function skillPluginFixture(): Promise<string> {
const root = await mkdtemp(join(tmpdir(), "cph-skills-"));
roots.push(root);
await mkdir(join(root, ".claude-plugin"), { recursive: true });
await writeFile(
join(root, ".claude-plugin", "plugin.json"),
JSON.stringify({ name: CURATED_SKILL_PLUGIN_NAME }),
);
for (const name of CURATED_SKILL_NAMES) {
const source = join(root, "skills", name);
await mkdir(source, { recursive: true });
await writeFile(join(source, "SKILL.md"), `---\nname: ${name}\n---\n# ${name}\n`);
}
return root;
}
});