mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
162 lines
5.4 KiB
TypeScript
162 lines
5.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
formatSkillDenyNotice,
|
|
SkillInputGatePipeline,
|
|
} from "#src/handlers/gates/skill-input-gate-pipeline";
|
|
|
|
import {
|
|
makeGateRunner,
|
|
makeNotifier,
|
|
makeSkillInputInputs,
|
|
} from "#test/helpers/gate-fixtures";
|
|
import { makeCheckResult } from "#test/helpers/handler-fixtures";
|
|
|
|
// ── formatSkillDenyNotice ─────────────────────────────────────────────────
|
|
|
|
describe("formatSkillDenyNotice", () => {
|
|
it("includes the skill name in the message (no agent)", () => {
|
|
const msg = formatSkillDenyNotice("librarian", null);
|
|
expect(msg).toContain("librarian");
|
|
});
|
|
|
|
it("includes the skill name and agent name when agent is present", () => {
|
|
const msg = formatSkillDenyNotice("librarian", "code-agent");
|
|
expect(msg).toContain("librarian");
|
|
expect(msg).toContain("code-agent");
|
|
});
|
|
});
|
|
|
|
// ── SkillInputGatePipeline.evaluate ───────────────────────────────────────
|
|
|
|
describe("SkillInputGatePipeline.evaluate", () => {
|
|
// ── notifier behaviour ──────────────────────────────────────────────────
|
|
|
|
it("calls notifier.warn when the skill is denied", async () => {
|
|
const inputs = makeSkillInputInputs({
|
|
checkPermission: () => makeCheckResult({ state: "deny" }),
|
|
});
|
|
const notifier = makeNotifier();
|
|
const { runner } = makeGateRunner();
|
|
const pipeline = new SkillInputGatePipeline(inputs);
|
|
|
|
await pipeline.evaluate("librarian", null, notifier, runner);
|
|
|
|
expect(notifier.warn).toHaveBeenCalledOnce();
|
|
expect(notifier.warn).toHaveBeenCalledWith(
|
|
expect.stringContaining("librarian"),
|
|
);
|
|
});
|
|
|
|
it("does not call notifier.warn when the skill is allowed", async () => {
|
|
const inputs = makeSkillInputInputs({
|
|
checkPermission: () => makeCheckResult({ state: "allow" }),
|
|
});
|
|
const notifier = makeNotifier();
|
|
const { runner } = makeGateRunner();
|
|
const pipeline = new SkillInputGatePipeline(inputs);
|
|
|
|
await pipeline.evaluate("librarian", null, notifier, runner);
|
|
|
|
expect(notifier.warn).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("does not call notifier.warn when the skill requires approval (ask)", async () => {
|
|
const inputs = makeSkillInputInputs({
|
|
checkPermission: () => makeCheckResult({ state: "ask" }),
|
|
});
|
|
const notifier = makeNotifier();
|
|
const { runner } = makeGateRunner();
|
|
const pipeline = new SkillInputGatePipeline(inputs);
|
|
|
|
await pipeline.evaluate("librarian", null, notifier, runner);
|
|
|
|
expect(notifier.warn).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("includes agent name in the deny notice when agent is present", async () => {
|
|
const inputs = makeSkillInputInputs({
|
|
checkPermission: () => makeCheckResult({ state: "deny" }),
|
|
});
|
|
const notifier = makeNotifier();
|
|
const { runner } = makeGateRunner();
|
|
const pipeline = new SkillInputGatePipeline(inputs);
|
|
|
|
await pipeline.evaluate("librarian", "code-agent", notifier, runner);
|
|
|
|
expect(notifier.warn).toHaveBeenCalledWith(
|
|
expect.stringContaining("code-agent"),
|
|
);
|
|
});
|
|
|
|
// ── outcome mapping ─────────────────────────────────────────────────────
|
|
|
|
it("returns allow when the gate passes", async () => {
|
|
const inputs = makeSkillInputInputs({
|
|
checkPermission: () => makeCheckResult({ state: "allow" }),
|
|
});
|
|
const { runner } = makeGateRunner();
|
|
const pipeline = new SkillInputGatePipeline(inputs);
|
|
|
|
const result = await pipeline.evaluate(
|
|
"librarian",
|
|
null,
|
|
makeNotifier(),
|
|
runner,
|
|
);
|
|
|
|
expect(result).toEqual({ action: "allow" });
|
|
});
|
|
|
|
it("returns block when the gate denies", async () => {
|
|
const inputs = makeSkillInputInputs({
|
|
checkPermission: () =>
|
|
makeCheckResult({ state: "deny", matchedPattern: "*" }),
|
|
});
|
|
const { runner } = makeGateRunner();
|
|
const pipeline = new SkillInputGatePipeline(inputs);
|
|
|
|
const result = await pipeline.evaluate(
|
|
"librarian",
|
|
null,
|
|
makeNotifier(),
|
|
runner,
|
|
);
|
|
|
|
expect(result).toEqual({
|
|
action: "block",
|
|
reason: expect.stringContaining("librarian"),
|
|
});
|
|
});
|
|
|
|
// ── checkPermission call ────────────────────────────────────────────────
|
|
|
|
it("calls checkPermission with the skill surface, skill name, and agent name", async () => {
|
|
const inputs = makeSkillInputInputs();
|
|
const { runner } = makeGateRunner();
|
|
const pipeline = new SkillInputGatePipeline(inputs);
|
|
|
|
await pipeline.evaluate("explorer", "code-agent", makeNotifier(), runner);
|
|
|
|
expect(inputs.checkPermission).toHaveBeenCalledWith(
|
|
"skill",
|
|
{ name: "explorer" },
|
|
"code-agent",
|
|
);
|
|
});
|
|
|
|
it("calls checkPermission with undefined agentName when agentName is null", async () => {
|
|
const inputs = makeSkillInputInputs();
|
|
const { runner } = makeGateRunner();
|
|
const pipeline = new SkillInputGatePipeline(inputs);
|
|
|
|
await pipeline.evaluate("explorer", null, makeNotifier(), runner);
|
|
|
|
expect(inputs.checkPermission).toHaveBeenCalledWith(
|
|
"skill",
|
|
{ name: "explorer" },
|
|
undefined,
|
|
);
|
|
});
|
|
});
|