Files
nomadamas__k-skill/scripts/test_generate_plugin_manifest.js
Jeffrey (Dongkyu) Kim 98b7ee80ba refactor(rail): consolidate timetable lookup on Korail (#653)
* refactor(rail): unify KTX and SRT timetable skills

Plan: 578 unified railway timetable integration

* docs(rail): update consolidated skill count

Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>

* fix(rail): use Korail integrated timetable only

Plan: 578 unified railway timetable integration

* docs(rail): remove retired SRT references

Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>

---------

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
2026-09-05 15:37:16 +09:00

106 lines
3.9 KiB
JavaScript

"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const {
discoverSkillPaths,
buildManifest,
serialize,
run,
manifestPathFor,
} = require("./generate-plugin-manifest.js");
/** Create a throwaway repo-like tree and return its root path. */
function makeFixtureRoot(layout) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "k-skill-manifest-"));
for (const [relPath, contents] of Object.entries(layout)) {
const full = path.join(root, relPath);
fs.mkdirSync(path.dirname(full), { recursive: true });
fs.writeFileSync(full, contents);
}
return root;
}
const SKILL_FM = "---\nname: x\ndescription: y\n---\n";
test("discoverSkillPaths returns sorted ./-prefixed dirs that contain SKILL.md", () => {
const root = makeFixtureRoot({
"lotto-results/SKILL.md": SKILL_FM,
"railway-timetable/SKILL.md": SKILL_FM,
"not-a-skill/README.md": "no skill here",
"top-level-file.md": "ignored",
});
assert.deepEqual(discoverSkillPaths(root), ["./lotto-results", "./railway-timetable"]);
});
test("discoverSkillPaths excludes infrastructure dirs and nested fixtures", () => {
const root = makeFixtureRoot({
"lotto-results/SKILL.md": SKILL_FM,
// Excluded root dirs that happen to contain a SKILL.md somewhere.
"packages/k-lotto/SKILL.md": SKILL_FM,
"scripts/SKILL.md": SKILL_FM,
"tools/example-fixtures/skills/kbo-results/SKILL.md": SKILL_FM,
"docs/SKILL.md": SKILL_FM,
// Dot-directory must be skipped regardless of contents.
".github/SKILL.md": SKILL_FM,
});
assert.deepEqual(discoverSkillPaths(root), ["./lotto-results"]);
});
test("buildManifest backfills identity fields and preserves author overrides", () => {
const root = makeFixtureRoot({ "lotto-results/SKILL.md": SKILL_FM });
// Pre-seed a manifest with a custom description that must survive.
fs.mkdirSync(path.join(root, ".claude-plugin"), { recursive: true });
fs.writeFileSync(
manifestPathFor(root),
serialize({ name: "k-skill", description: "custom desc", skills: [] }),
);
const manifest = buildManifest(root);
assert.equal(manifest.description, "custom desc"); // not clobbered
assert.equal(manifest.license, "MIT"); // backfilled from default
assert.deepEqual(manifest.skills, ["./lotto-results"]); // always refreshed
});
test("run --check passes when manifest matches, fails after drift", () => {
const root = makeFixtureRoot({ "lotto-results/SKILL.md": SKILL_FM });
// First write, then a check should agree.
const written = run({ root });
assert.equal(written.written, true);
assert.equal(run({ root, check: true }).ok, true);
// Add a new skill on disk -> check must now report drift.
fs.mkdirSync(path.join(root, "railway-timetable"));
fs.writeFileSync(path.join(root, "railway-timetable", "SKILL.md"), SKILL_FM);
assert.equal(run({ root, check: true }).ok, false);
});
test("run --check treats CRLF working-tree checkouts as up to date", () => {
const root = makeFixtureRoot({ "lotto-results/SKILL.md": SKILL_FM });
run({ root });
const manifestPath = manifestPathFor(root);
const lf = fs.readFileSync(manifestPath, "utf8");
fs.writeFileSync(manifestPath, lf.replace(/\n/g, "\r\n"));
assert.equal(run({ root, check: true }).ok, true);
});
test("run --check passes when local manifest is absent", () => {
const root = makeFixtureRoot({ "lotto-results/SKILL.md": SKILL_FM });
const result = run({ root, check: true });
assert.equal(result.ok, true);
assert.equal(result.missing, true);
});
test("run writes deterministic, trailing-newline JSON", () => {
const root = makeFixtureRoot({ "lotto-results/SKILL.md": SKILL_FM });
run({ root });
const raw = fs.readFileSync(manifestPathFor(root), "utf8");
assert.ok(raw.endsWith("\n"));
assert.equal(raw, serialize(buildManifest(root)));
});