/** * @file Tests for skills-install.js: skill-copy path resolution (source: * repoRoot/skills/ not repoRoot/.claude/skills/) and error handling. * @author Nguyễn Ngọc Trí Vĩ */ const os = require("node:os"); const path = require("node:path"); const fs = require("node:fs"); const SUITE_ROOT = fs.mkdtempSync(path.join(os.tmpdir(), "agenthub-skills-")); process.env.DASHBOARD_DB_PATH = path.join(SUITE_ROOT, "dashboard.db"); const { describe, it, after } = require("node:test"); const assert = require("node:assert/strict"); const { installShipFeatureLaneSkill } = require("../lib/skills-install"); after(() => fs.rmSync(SUITE_ROOT, { recursive: true, force: true })); describe("installShipFeatureLaneSkill", () => { it("resolves source from repoRoot/skills/ (new path), not .claude/skills/", () => { // Build a fake repoRoot with the skill only under skills/ship-feature-lane/ const repoRoot = path.join(SUITE_ROOT, "mock-repo"); const src = path.join(repoRoot, "skills", "ship-feature-lane"); fs.mkdirSync(src, { recursive: true }); fs.writeFileSync(path.join(src, "SKILL.md"), "# Fake Ship Feature Lane Skill"); // The old path should NOT exist const oldPath = path.join(repoRoot, ".claude", "skills", "ship-feature-lane", "SKILL.md"); assert.equal(fs.existsSync(oldPath), false); // Override HOME so we don't touch real ~/.claude const origHome = process.env.HOME; const fakeHome = path.join(SUITE_ROOT, "fake-home"); process.env.HOME = fakeHome; try { const result = installShipFeatureLaneSkill({ repoRoot }); assert.equal(result.installed, true); assert.ok(result.path.startsWith(fakeHome)); const installedFile = path.join(result.path, "SKILL.md"); assert.ok(fs.existsSync(installedFile)); assert.equal(fs.readFileSync(installedFile, "utf8"), "# Fake Ship Feature Lane Skill"); } finally { process.env.HOME = origHome; } }); it("throws ENOSKILLSRC when skills/ship-feature-lane/ is missing", () => { const repoRoot = path.join(SUITE_ROOT, "empty-repo"); fs.mkdirSync(repoRoot, { recursive: true }); assert.throws( () => installShipFeatureLaneSkill({ repoRoot }), (err) => err.code === "ENOSKILLSRC" ); }); it("overwrites an existing installed skill (force: true)", () => { const repoRoot = path.join(SUITE_ROOT, "mock-repo-overwrite"); const src = path.join(repoRoot, "skills", "ship-feature-lane"); fs.mkdirSync(src, { recursive: true }); fs.writeFileSync(path.join(src, "SKILL.md"), "# Updated skill"); const origHome = process.env.HOME; const fakeHome = path.join(SUITE_ROOT, "fake-home-overwrite"); process.env.HOME = fakeHome; try { // First install installShipFeatureLaneSkill({ repoRoot }); // Tamper with installed copy const existing = path.join(fakeHome, ".claude", "skills", "ship-feature-lane", "SKILL.md"); fs.writeFileSync(existing, "# Tampered"); // Reinstall — should overwrite installShipFeatureLaneSkill({ repoRoot }); assert.equal(fs.readFileSync(existing, "utf8"), "# Updated skill"); } finally { process.env.HOME = origHome; } }); });