/** * Tests that handleToolCall emits permissions:decision events at every * gate resolution and fast-path site. */ import { describe, expect, it, vi } from "vitest"; import type { AskEscalator } from "#src/authority/authorizer-selection"; import { DECIDED_BY_HUMAN } from "#test/helpers/decision-fixtures"; import { getDecisionEvents, makeCheckResult, makeCtx, makeHandler, makeToolCallEvent, } from "#test/helpers/handler-fixtures"; // ── policy_allow path ────────────────────────────────────────────────────── describe("handleToolCall decision events — policy_allow", () => { it("emits allow with policy_allow when checkPermission returns allow", async () => { const { handler, events } = makeHandler({ session: { checkPermission: vi.fn().mockReturnValue( makeCheckResult({ state: "allow", origin: "global", matchedPattern: "*", }), ), }, }); await handler.handleToolCall(makeToolCallEvent("read"), makeCtx()); const decisions = getDecisionEvents(events); expect(decisions).toHaveLength(1); expect(decisions[0]).toMatchObject({ surface: "read", result: "allow", resolution: "policy_allow", origin: "global", matchedPattern: "*", }); }); }); // ── policy_deny path ─────────────────────────────────────────────────────── describe("handleToolCall decision events — policy_deny", () => { it("emits deny with policy_deny when checkPermission returns deny", async () => { const { handler, events } = makeHandler({ session: { checkPermission: vi.fn().mockReturnValue( makeCheckResult({ state: "deny", origin: "project", matchedPattern: "read", }), ), }, }); await handler.handleToolCall(makeToolCallEvent("read"), makeCtx()); const decisions = getDecisionEvents(events); expect(decisions).toHaveLength(1); expect(decisions[0]).toMatchObject({ surface: "read", result: "deny", resolution: "policy_deny", }); }); }); // ── session_approved fast path ───────────────────────────────────────────── describe("handleToolCall decision events — session_approved", () => { it("emits allow with session_approved when checkPermission returns source:session", async () => { const { handler, events } = makeHandler({ session: { checkPermission: vi.fn().mockReturnValue( makeCheckResult({ state: "allow", source: "session", matchedPattern: "git *", }), ), }, }); await handler.handleToolCall( makeToolCallEvent("bash", { input: { command: "git status" } }), makeCtx(), ); const decisions = getDecisionEvents(events); expect(decisions).toHaveLength(1); expect(decisions[0]).toMatchObject({ surface: "bash", result: "allow", resolution: "session_approved", }); }); });