Files
ai-coding-skills/scripts/tests/skill-manager-core-remove.test.mjs
T
stefano 251148c3ff
check / check (ubuntu-latest) (push) Successful in 2m5s
check / check (macos-latest) (push) Has been cancelled
check-online / check-online (ubuntu-latest) (push) Successful in 1m53s
Perform code optimization and document cleanup (#1)
## Summary
- add repository-wide quality tooling and verification scaffolding, including CI workflows, pnpm workspace setup, ESLint/Prettier/markdown checks, and generated-output verification helpers
- reorganize skill sources and generation flow by introducing canonical `_source` variants, generator/manifests, reusable helper abstractions, and shared web-automation/browser utilities
- clean up and expand documentation so the root README flows into docs and skill docs, with clearer development, reviewer, installer, and workflow guidance

## Notable changes
- docs flow and consistency cleanup across `README.md`, `docs/README.md`, and related docs
- new scripts for `check`, docs verification, generated-file verification, shell portability, and safe directory replacement
- refactors in Atlassian and web-automation skill runtimes to reduce duplication and centralize reusable code
- changelog, development documentation, and CI surface updates

## Test Plan
- [ ] `pnpm run check`
- [ ] review generated/manifests and skill sync outputs
- [ ] smoke-check docs flow from `README.md` to `docs/README.md` to skill docs

## Notes
- this branch currently includes tracked `skills/web-automation/shared/node_modules` content that should be reviewed carefully as potentially noisy/accidental committed artifacts

Co-authored-by: Stefano Fiorini <stefano.fiorini@firsthorizon.com>
Reviewed-on: #1
2026-05-04 04:41:34 +00:00

138 lines
4.7 KiB
JavaScript

import assert from "node:assert/strict";
import { mkdtemp, mkdir, writeFile, rm, lstat, symlink } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import test from "node:test";
import { removeTarget } from "../lib/skill-manager-core.mjs";
// ── Happy path: remove existing directory ─────────────────────────────────
test("removeTarget removes an installed skill directory", async () => {
const dir = await mkdtemp(path.join(tmpdir(), "smc-remove-dir-"));
try {
const skillsRoot = path.join(dir, "skills");
const target = path.join(skillsRoot, "create-plan");
await mkdir(target, { recursive: true });
await writeFile(path.join(target, "SKILL.md"), "---\nname: create-plan\n---\n");
const op = { kind: "skill", action: "remove", target, skillsRoot };
const result = await removeTarget(op);
assert.equal(result.status, "ok");
let exists = true;
try {
await lstat(target);
} catch {
exists = false;
}
assert.equal(exists, false, "target directory should be gone");
} finally {
await rm(dir, { recursive: true, force: true });
}
});
// ── Happy path: remove symbolic link ─────────────────────────────────────
test("removeTarget removes a symlink without following it", async () => {
const dir = await mkdtemp(path.join(tmpdir(), "smc-remove-sym-"));
try {
const skillsRoot = path.join(dir, "skills");
const realDir = path.join(dir, "real-skill");
const target = path.join(skillsRoot, "create-plan");
await mkdir(skillsRoot, { recursive: true });
await mkdir(realDir, { recursive: true });
await writeFile(path.join(realDir, "SKILL.md"), "---\nname: create-plan\n---\n");
await symlink(realDir, target, "dir");
const op = { kind: "skill", action: "remove", target, skillsRoot };
const result = await removeTarget(op);
assert.equal(result.status, "ok");
// symlink itself should be gone
let symlinkExists = true;
try {
await lstat(target);
} catch {
symlinkExists = false;
}
assert.equal(symlinkExists, false, "symlink should be removed");
// real directory should still exist
const realStat = await lstat(realDir);
assert.ok(realStat.isDirectory(), "real directory must not be touched");
} finally {
await rm(dir, { recursive: true, force: true });
}
});
// ── Missing skill (partial state): target does not exist ─────────────────
test("removeTarget succeeds when target does not exist (idempotent)", async () => {
const dir = await mkdtemp(path.join(tmpdir(), "smc-remove-missing-"));
try {
const skillsRoot = path.join(dir, "skills");
const target = path.join(skillsRoot, "create-plan");
await mkdir(skillsRoot, { recursive: true });
// target intentionally NOT created
const op = { kind: "skill", action: "remove", target, skillsRoot };
const result = await removeTarget(op);
assert.equal(result.status, "ok");
} finally {
await rm(dir, { recursive: true, force: true });
}
});
// ── Partial state: directory exists but is empty ─────────────────────────
test("removeTarget removes an empty skill directory", async () => {
const dir = await mkdtemp(path.join(tmpdir(), "smc-remove-empty-"));
try {
const skillsRoot = path.join(dir, "skills");
const target = path.join(skillsRoot, "create-plan");
await mkdir(target, { recursive: true });
// directory exists but has no SKILL.md (partial install state)
const op = { kind: "skill", action: "remove", target, skillsRoot };
const result = await removeTarget(op);
assert.equal(result.status, "ok");
let exists = true;
try {
await lstat(target);
} catch {
exists = false;
}
assert.equal(exists, false);
} finally {
await rm(dir, { recursive: true, force: true });
}
});
// ── Safety: refuses to remove path outside skills root ────────────────────
test("removeTarget refuses to remove a path outside the skills root", async () => {
const dir = await mkdtemp(path.join(tmpdir(), "smc-remove-outside-"));
try {
const skillsRoot = path.join(dir, "skills");
const outsideTarget = path.join(dir, "outside");
await mkdir(skillsRoot, { recursive: true });
await mkdir(outsideTarget, { recursive: true });
const op = {
kind: "skill",
action: "remove",
target: outsideTarget,
skillsRoot,
};
await assert.rejects(() => removeTarget(op), /outside skills root/);
} finally {
await rm(dir, { recursive: true, force: true });
}
});