/**
 * @license
 * Copyright 2026 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { describe, it, expect, vi } from 'vitest';
import { checkPolicy, updatePolicy } from './policy.js';
import { MessageBusType } from '../confirmation-bus/types.js';
import { ApprovalMode, PolicyDecision } from '../policy/types.js';
import { ToolConfirmationOutcome, } from '../tools/tools.js';
import { DiscoveredMCPTool } from '../tools/mcp-tool.js';
describe('policy.ts', () => {
    describe('checkPolicy', () => {
        it('should return the decision from the policy engine', async () => {
            const mockPolicyEngine = {
                check: vi.fn().mockResolvedValue({ decision: PolicyDecision.ALLOW }),
            };
            const mockConfig = {
                getPolicyEngine: vi.fn().mockReturnValue(mockPolicyEngine),
            };
            const toolCall = {
                request: { name: 'test-tool', args: {} },
                tool: { name: 'test-tool' },
            };
            const decision = await checkPolicy(toolCall, mockConfig);
            expect(decision).toBe(PolicyDecision.ALLOW);
            expect(mockPolicyEngine.check).toHaveBeenCalledWith({ name: 'test-tool', args: {} }, undefined);
        });
        it('should pass serverName for MCP tools', async () => {
            const mockPolicyEngine = {
                check: vi.fn().mockResolvedValue({ decision: PolicyDecision.ALLOW }),
            };
            const mockConfig = {
                getPolicyEngine: vi.fn().mockReturnValue(mockPolicyEngine),
            };
            const mcpTool = Object.create(DiscoveredMCPTool.prototype);
            mcpTool.serverName = 'my-server';
            const toolCall = {
                request: { name: 'mcp-tool', args: {} },
                tool: mcpTool,
            };
            await checkPolicy(toolCall, mockConfig);
            expect(mockPolicyEngine.check).toHaveBeenCalledWith({ name: 'mcp-tool', args: {} }, 'my-server');
        });
        it('should throw if ASK_USER is returned in non-interactive mode', async () => {
            const mockPolicyEngine = {
                check: vi.fn().mockResolvedValue({ decision: PolicyDecision.ASK_USER }),
            };
            const mockConfig = {
                getPolicyEngine: vi.fn().mockReturnValue(mockPolicyEngine),
                isInteractive: vi.fn().mockReturnValue(false),
            };
            const toolCall = {
                request: { name: 'test-tool', args: {} },
                tool: { name: 'test-tool' },
            };
            await expect(checkPolicy(toolCall, mockConfig)).rejects.toThrow(/not supported in non-interactive mode/);
        });
        it('should return DENY without throwing', async () => {
            const mockPolicyEngine = {
                check: vi.fn().mockResolvedValue({ decision: PolicyDecision.DENY }),
            };
            const mockConfig = {
                getPolicyEngine: vi.fn().mockReturnValue(mockPolicyEngine),
            };
            const toolCall = {
                request: { name: 'test-tool', args: {} },
                tool: { name: 'test-tool' },
            };
            const decision = await checkPolicy(toolCall, mockConfig);
            expect(decision).toBe(PolicyDecision.DENY);
        });
        it('should return ASK_USER without throwing in interactive mode', async () => {
            const mockPolicyEngine = {
                check: vi.fn().mockResolvedValue({ decision: PolicyDecision.ASK_USER }),
            };
            const mockConfig = {
                getPolicyEngine: vi.fn().mockReturnValue(mockPolicyEngine),
                isInteractive: vi.fn().mockReturnValue(true),
            };
            const toolCall = {
                request: { name: 'test-tool', args: {} },
                tool: { name: 'test-tool' },
            };
            const decision = await checkPolicy(toolCall, mockConfig);
            expect(decision).toBe(PolicyDecision.ASK_USER);
        });
    });
    describe('updatePolicy', () => {
        it('should set AUTO_EDIT mode for auto-edit transition tools', async () => {
            const mockConfig = {
                setApprovalMode: vi.fn(),
            };
            const mockMessageBus = {
                publish: vi.fn(),
            };
            const tool = { name: 'replace' }; // 'replace' is in EDIT_TOOL_NAMES
            await updatePolicy(tool, ToolConfirmationOutcome.ProceedAlways, undefined, { config: mockConfig, messageBus: mockMessageBus });
            expect(mockConfig.setApprovalMode).toHaveBeenCalledWith(ApprovalMode.AUTO_EDIT);
            expect(mockMessageBus.publish).not.toHaveBeenCalled();
        });
        it('should handle standard policy updates (persist=false)', async () => {
            const mockConfig = {
                setApprovalMode: vi.fn(),
            };
            const mockMessageBus = {
                publish: vi.fn(),
            };
            const tool = { name: 'test-tool' };
            await updatePolicy(tool, ToolConfirmationOutcome.ProceedAlways, undefined, { config: mockConfig, messageBus: mockMessageBus });
            expect(mockMessageBus.publish).toHaveBeenCalledWith(expect.objectContaining({
                type: MessageBusType.UPDATE_POLICY,
                toolName: 'test-tool',
                persist: false,
            }));
        });
        it('should handle standard policy updates with persistence', async () => {
            const mockConfig = {
                setApprovalMode: vi.fn(),
            };
            const mockMessageBus = {
                publish: vi.fn(),
            };
            const tool = { name: 'test-tool' };
            await updatePolicy(tool, ToolConfirmationOutcome.ProceedAlwaysAndSave, undefined, { config: mockConfig, messageBus: mockMessageBus });
            expect(mockMessageBus.publish).toHaveBeenCalledWith(expect.objectContaining({
                type: MessageBusType.UPDATE_POLICY,
                toolName: 'test-tool',
                persist: true,
            }));
        });
        it('should handle shell command prefixes', async () => {
            const mockConfig = {
                setApprovalMode: vi.fn(),
            };
            const mockMessageBus = {
                publish: vi.fn(),
            };
            const tool = { name: 'run_shell_command' };
            const details = {
                type: 'exec',
                command: 'ls -la',
                rootCommand: 'ls',
                rootCommands: ['ls'],
                title: 'Shell',
                onConfirm: vi.fn(),
            };
            await updatePolicy(tool, ToolConfirmationOutcome.ProceedAlways, details, {
                config: mockConfig,
                messageBus: mockMessageBus,
            });
            expect(mockMessageBus.publish).toHaveBeenCalledWith(expect.objectContaining({
                type: MessageBusType.UPDATE_POLICY,
                toolName: 'run_shell_command',
                commandPrefix: ['ls'],
            }));
        });
        it('should handle MCP policy updates (server scope)', async () => {
            const mockConfig = {
                setApprovalMode: vi.fn(),
            };
            const mockMessageBus = {
                publish: vi.fn(),
            };
            const tool = { name: 'mcp-tool' };
            const details = {
                type: 'mcp',
                serverName: 'my-server',
                toolName: 'mcp-tool',
                toolDisplayName: 'My Tool',
                title: 'MCP',
                onConfirm: vi.fn(),
            };
            await updatePolicy(tool, ToolConfirmationOutcome.ProceedAlwaysServer, details, { config: mockConfig, messageBus: mockMessageBus });
            expect(mockMessageBus.publish).toHaveBeenCalledWith(expect.objectContaining({
                type: MessageBusType.UPDATE_POLICY,
                toolName: 'my-server__*',
                mcpName: 'my-server',
                persist: false,
            }));
        });
        it('should NOT publish update for ProceedOnce', async () => {
            const mockConfig = {
                setApprovalMode: vi.fn(),
            };
            const mockMessageBus = {
                publish: vi.fn(),
            };
            const tool = { name: 'test-tool' };
            await updatePolicy(tool, ToolConfirmationOutcome.ProceedOnce, undefined, {
                config: mockConfig,
                messageBus: mockMessageBus,
            });
            expect(mockMessageBus.publish).not.toHaveBeenCalled();
            expect(mockConfig.setApprovalMode).not.toHaveBeenCalled();
        });
        it('should NOT publish update for Cancel', async () => {
            const mockConfig = {
                setApprovalMode: vi.fn(),
            };
            const mockMessageBus = {
                publish: vi.fn(),
            };
            const tool = { name: 'test-tool' };
            await updatePolicy(tool, ToolConfirmationOutcome.Cancel, undefined, {
                config: mockConfig,
                messageBus: mockMessageBus,
            });
            expect(mockMessageBus.publish).not.toHaveBeenCalled();
        });
        it('should NOT publish update for ModifyWithEditor', async () => {
            const mockConfig = {
                setApprovalMode: vi.fn(),
            };
            const mockMessageBus = {
                publish: vi.fn(),
            };
            const tool = { name: 'test-tool' };
            await updatePolicy(tool, ToolConfirmationOutcome.ModifyWithEditor, undefined, { config: mockConfig, messageBus: mockMessageBus });
            expect(mockMessageBus.publish).not.toHaveBeenCalled();
        });
        it('should handle MCP ProceedAlwaysTool (specific tool name)', async () => {
            const mockConfig = {
                setApprovalMode: vi.fn(),
            };
            const mockMessageBus = {
                publish: vi.fn(),
            };
            const tool = { name: 'mcp-tool' };
            const details = {
                type: 'mcp',
                serverName: 'my-server',
                toolName: 'mcp-tool',
                toolDisplayName: 'My Tool',
                title: 'MCP',
                onConfirm: vi.fn(),
            };
            await updatePolicy(tool, ToolConfirmationOutcome.ProceedAlwaysTool, details, { config: mockConfig, messageBus: mockMessageBus });
            expect(mockMessageBus.publish).toHaveBeenCalledWith(expect.objectContaining({
                type: MessageBusType.UPDATE_POLICY,
                toolName: 'mcp-tool', // Specific name, not wildcard
                mcpName: 'my-server',
                persist: false,
            }));
        });
        it('should handle MCP ProceedAlways (persist: false)', async () => {
            const mockConfig = {
                setApprovalMode: vi.fn(),
            };
            const mockMessageBus = {
                publish: vi.fn(),
            };
            const tool = { name: 'mcp-tool' };
            const details = {
                type: 'mcp',
                serverName: 'my-server',
                toolName: 'mcp-tool',
                toolDisplayName: 'My Tool',
                title: 'MCP',
                onConfirm: vi.fn(),
            };
            await updatePolicy(tool, ToolConfirmationOutcome.ProceedAlways, details, {
                config: mockConfig,
                messageBus: mockMessageBus,
            });
            expect(mockMessageBus.publish).toHaveBeenCalledWith(expect.objectContaining({
                type: MessageBusType.UPDATE_POLICY,
                toolName: 'mcp-tool',
                mcpName: 'my-server',
                persist: false,
            }));
        });
        it('should handle MCP ProceedAlwaysAndSave (persist: true)', async () => {
            const mockConfig = {
                setApprovalMode: vi.fn(),
            };
            const mockMessageBus = {
                publish: vi.fn(),
            };
            const tool = { name: 'mcp-tool' };
            const details = {
                type: 'mcp',
                serverName: 'my-server',
                toolName: 'mcp-tool',
                toolDisplayName: 'My Tool',
                title: 'MCP',
                onConfirm: vi.fn(),
            };
            await updatePolicy(tool, ToolConfirmationOutcome.ProceedAlwaysAndSave, details, { config: mockConfig, messageBus: mockMessageBus });
            expect(mockMessageBus.publish).toHaveBeenCalledWith(expect.objectContaining({
                type: MessageBusType.UPDATE_POLICY,
                toolName: 'mcp-tool',
                mcpName: 'my-server',
                persist: true,
            }));
        });
    });
});
//# sourceMappingURL=policy.test.js.map