forked from EduCraft/curriculum-project-hub
72 lines
2.6 KiB
TypeScript
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;
|
|
}
|
|
});
|