diff --git a/test/reinject.test.ts b/test/reinject.test.ts new file mode 100644 index 0000000..8e2e7fc --- /dev/null +++ b/test/reinject.test.ts @@ -0,0 +1,81 @@ +import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "fs"; +import { tmpdir } from "os"; +import { join } from "path"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { buildReinjectBlocks, tryConsumeDeferredReinject } from "../src/reinject"; +import { createDefaultSettings } from "../src/settings"; +import { createInitialState, trackSkill } from "../src/state"; + +const tempDirs: string[] = []; + +afterEach(() => { + for (const dir of tempDirs.splice(0)) { + rmSync(dir, { recursive: true, force: true }); + } +}); + +function tempSkillDir(name: string): { filePath: string; baseDir: string } { + const root = mkdtempSync(join(tmpdir(), "pi-skill-reinject-deferred-")); + tempDirs.push(root); + const baseDir = join(root, name); + mkdirSync(baseDir, { recursive: true }); + const filePath = join(baseDir, "SKILL.md"); + writeFileSync(filePath, "# skill\n", "utf8"); + return { filePath, baseDir }; +} + +function ctxWithNotify(): { ctx: never; notify: ReturnType } { + const notify = vi.fn(); + return { notify, ctx: { hasUI: true, ui: { notify } } as never }; +} + +describe("deferred reinject loose fallback", () => { + it("(a) unregistered on disk with requireRegistered=false → block + info notify", () => { + const { filePath, baseDir } = tempSkillDir("loose"); + const state = createInitialState(); + trackSkill(state, { name: "loose", filePath, baseDir, source: "slash" }); + state.pendingReinject = ["loose"]; + const { ctx, notify } = ctxWithNotify(); + + const result = tryConsumeDeferredReinject(state, createDefaultSettings(), [], ctx); + + expect(result?.message?.content).toContain(' { + const { filePath, baseDir } = tempSkillDir("strict"); + const state = createInitialState(); + trackSkill(state, { name: "strict", filePath, baseDir, source: "slash" }); + const settings = createDefaultSettings(); + settings.requireRegistered = true; + const { ctx, notify } = ctxWithNotify(); + + const blocks = buildReinjectBlocks(["strict"], state, settings, [], ctx); + + expect(blocks).toEqual([]); + expect(notify).toHaveBeenCalledWith( + 'skill-reinject: skipped "strict" — no longer registered', + "warning", + ); + }); + + it("(c) missing filePath on disk → skip + warn", () => { + const state = createInitialState(); + trackSkill(state, { + name: "missing", + filePath: "/no/such/SKILL.md", + baseDir: "/no/such", + source: "slash", + }); + const { ctx, notify } = ctxWithNotify(); + + const blocks = buildReinjectBlocks(["missing"], state, createDefaultSettings(), [], ctx); + + expect(blocks).toEqual([]); + expect(notify).toHaveBeenCalledWith( + 'skill-reinject: skipped "missing" — SKILL.md not found on disk', + "warning", + ); + }); +});