import { describe, expect, it, vi } from "vitest"; import { protectedWriteDeferNote } from "#src/handlers/gates/protected-write"; import type { ProtectedWriteTarget } from "#src/handlers/gates/descriptor"; import type { PermissionCheckResult } from "#src/types"; import { DECIDED_BY_HUMAN } from "#test/helpers/decision-fixtures"; import { makeDescriptor, makeGateRunner } from "#test/helpers/gate-fixtures"; import { makeCheckResult } from "#test/helpers/handler-fixtures"; const MARKER: ProtectedWriteTarget = { path: "/vault/auth.json", pattern: "/vault/auth.json", label: "Pi 凭据", extraCount: 0, }; function protectedDescriptor(check: PermissionCheckResult) { return makeDescriptor({ humanOnly: MARKER, sessionApproval: undefined, preCheck: check, }); } describe("GateRunner — humanOnly protected writes", () => { it("still escalates when the resolved check is a session grant", async () => { const { runner, deps } = makeGateRunner(); const outcome = await runner.run( protectedDescriptor( makeCheckResult({ state: "ask", source: "session", matchedPattern: "/vault/**", }), ), null, ); expect(deps.escalate).toHaveBeenCalledOnce(); expect(outcome).toEqual({ action: "allow" }); expect(deps.reporter.writeReviewLog).not.toHaveBeenCalledWith( "permission_request.session_approved", expect.anything(), ); }); it("still escalates when yolo mode is enabled", async () => { const { runner, deps } = makeGateRunner({ yolo: true }); await runner.run( protectedDescriptor( makeCheckResult({ state: "ask", source: "tool", origin: "builtin" }), ), null, ); expect(deps.escalate).toHaveBeenCalledOnce(); expect(deps.reporter.writeReviewLog).not.toHaveBeenCalledWith( "permission_request.auto_approved", expect.anything(), ); }); it("escalates with humanOnly, the system note, and no session approval", async () => { const { runner, deps } = makeGateRunner(); await runner.run( protectedDescriptor( makeCheckResult({ state: "ask", source: "tool", origin: "builtin" }), ), null, ); expect(deps.escalate).toHaveBeenCalledWith( expect.objectContaining({ humanOnly: true, deferNotes: [protectedWriteDeferNote(MARKER)], }), ); const details = vi.mocked(deps.escalate).mock.calls[0][0]; expect(details.sessionApproval).toBeUndefined(); }); it("records no session grant when the user approves", async () => { const { runner, deps } = makeGateRunner({ escalate: vi.fn().mockResolvedValue({ approved: true, state: "approved_for_session", decidedBy: DECIDED_BY_HUMAN, }), }); const outcome = await runner.run( protectedDescriptor( makeCheckResult({ state: "ask", source: "tool", origin: "builtin" }), ), null, ); // A session-shaped approval cannot be recorded: the descriptor carries no // sessionApproval to grant. expect(deps.recordSessionApproval).not.toHaveBeenCalled(); expect(outcome).toEqual({ action: "allow" }); }); }); describe("protectedWriteDeferNote", () => { it("renders the exact system note", () => { expect(protectedWriteDeferNote(MARKER)).toEqual({ origin: "system", text: "⚠ 该操作将写入受保护文件:/vault/auth.json(Pi 凭据,匹配 /vault/auth.json)。此类写入不经 AutoReview,必须由你人工确认。", }); }); it("adds the extra-target count", () => { const note = protectedWriteDeferNote({ ...MARKER, extraCount: 2 }); expect(note.text).toContain(",另有 2 个受保护目标"); }); });