/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { describe, it, expect } from 'vitest';
import { createPolicyEngineConfig } from './policy.js';
import { ApprovalMode, PolicyDecision } from '@google/gemini-cli-core';
describe('createPolicyEngineConfig', () => {
    it('should return ASK_USER for all tools by default', () => {
        const settings = {};
        const config = createPolicyEngineConfig(settings, ApprovalMode.DEFAULT);
        expect(config.defaultDecision).toBe(PolicyDecision.ASK_USER);
        expect(config.rules).toEqual([
            { toolName: 'replace', decision: 'ask_user', priority: 10 },
            { toolName: 'save_memory', decision: 'ask_user', priority: 10 },
            { toolName: 'run_shell_command', decision: 'ask_user', priority: 10 },
            { toolName: 'write_file', decision: 'ask_user', priority: 10 },
            { toolName: 'web_fetch', decision: 'ask_user', priority: 10 },
        ]);
    });
    it('should allow tools in tools.allowed', () => {
        const settings = {
            tools: { allowed: ['run_shell_command'] },
        };
        const config = createPolicyEngineConfig(settings, ApprovalMode.DEFAULT);
        const rule = config.rules?.find((r) => r.toolName === 'run_shell_command' &&
            r.decision === PolicyDecision.ALLOW);
        expect(rule).toBeDefined();
        expect(rule?.priority).toBe(100);
    });
    it('should deny tools in tools.exclude', () => {
        const settings = {
            tools: { exclude: ['run_shell_command'] },
        };
        const config = createPolicyEngineConfig(settings, ApprovalMode.DEFAULT);
        const rule = config.rules?.find((r) => r.toolName === 'run_shell_command' &&
            r.decision === PolicyDecision.DENY);
        expect(rule).toBeDefined();
        expect(rule?.priority).toBe(200);
    });
    it('should allow tools from allowed MCP servers', () => {
        const settings = {
            mcp: { allowed: ['my-server'] },
        };
        const config = createPolicyEngineConfig(settings, ApprovalMode.DEFAULT);
        const rule = config.rules?.find((r) => r.toolName === 'my-server__*' && r.decision === PolicyDecision.ALLOW);
        expect(rule).toBeDefined();
        expect(rule?.priority).toBe(85);
    });
    it('should deny tools from excluded MCP servers', () => {
        const settings = {
            mcp: { excluded: ['my-server'] },
        };
        const config = createPolicyEngineConfig(settings, ApprovalMode.DEFAULT);
        const rule = config.rules?.find((r) => r.toolName === 'my-server__*' && r.decision === PolicyDecision.DENY);
        expect(rule).toBeDefined();
        expect(rule?.priority).toBe(195);
    });
    it('should allow tools from trusted MCP servers', () => {
        const settings = {
            mcpServers: {
                'trusted-server': {
                    command: 'node',
                    args: ['server.js'],
                    trust: true,
                },
                'untrusted-server': {
                    command: 'node',
                    args: ['server.js'],
                    trust: false,
                },
            },
        };
        const config = createPolicyEngineConfig(settings, ApprovalMode.DEFAULT);
        const trustedRule = config.rules?.find((r) => r.toolName === 'trusted-server__*' &&
            r.decision === PolicyDecision.ALLOW);
        expect(trustedRule).toBeDefined();
        expect(trustedRule?.priority).toBe(90);
        // Untrusted server should not have an allow rule
        const untrustedRule = config.rules?.find((r) => r.toolName === 'untrusted-server__*' &&
            r.decision === PolicyDecision.ALLOW);
        expect(untrustedRule).toBeUndefined();
    });
    it('should handle multiple MCP server configurations together', () => {
        const settings = {
            mcp: {
                allowed: ['allowed-server'],
                excluded: ['excluded-server'],
            },
            mcpServers: {
                'trusted-server': {
                    command: 'node',
                    args: ['server.js'],
                    trust: true,
                },
            },
        };
        const config = createPolicyEngineConfig(settings, ApprovalMode.DEFAULT);
        // Check allowed server
        const allowedRule = config.rules?.find((r) => r.toolName === 'allowed-server__*' &&
            r.decision === PolicyDecision.ALLOW);
        expect(allowedRule).toBeDefined();
        expect(allowedRule?.priority).toBe(85);
        // Check trusted server
        const trustedRule = config.rules?.find((r) => r.toolName === 'trusted-server__*' &&
            r.decision === PolicyDecision.ALLOW);
        expect(trustedRule).toBeDefined();
        expect(trustedRule?.priority).toBe(90);
        // Check excluded server
        const excludedRule = config.rules?.find((r) => r.toolName === 'excluded-server__*' &&
            r.decision === PolicyDecision.DENY);
        expect(excludedRule).toBeDefined();
        expect(excludedRule?.priority).toBe(195);
    });
    it('should allow read-only tools if autoAccept is true', () => {
        const settings = {
            tools: { autoAccept: true },
        };
        const config = createPolicyEngineConfig(settings, ApprovalMode.DEFAULT);
        const rule = config.rules?.find((r) => r.toolName === 'glob' && r.decision === PolicyDecision.ALLOW);
        expect(rule).toBeDefined();
        expect(rule?.priority).toBe(50);
    });
    it('should allow all tools in YOLO mode', () => {
        const settings = {};
        const config = createPolicyEngineConfig(settings, ApprovalMode.YOLO);
        const rule = config.rules?.find((r) => r.decision === PolicyDecision.ALLOW && r.priority === 0);
        expect(rule).toBeDefined();
    });
    it('should allow edit tool in AUTO_EDIT mode', () => {
        const settings = {};
        const config = createPolicyEngineConfig(settings, ApprovalMode.AUTO_EDIT);
        const rule = config.rules?.find((r) => r.toolName === 'replace' && r.decision === PolicyDecision.ALLOW);
        expect(rule).toBeDefined();
        expect(rule?.priority).toBe(15);
    });
    it('should prioritize exclude over allow', () => {
        const settings = {
            tools: { allowed: ['run_shell_command'], exclude: ['run_shell_command'] },
        };
        const config = createPolicyEngineConfig(settings, ApprovalMode.DEFAULT);
        const denyRule = config.rules?.find((r) => r.toolName === 'run_shell_command' &&
            r.decision === PolicyDecision.DENY);
        const allowRule = config.rules?.find((r) => r.toolName === 'run_shell_command' &&
            r.decision === PolicyDecision.ALLOW);
        expect(denyRule).toBeDefined();
        expect(allowRule).toBeDefined();
        expect(denyRule.priority).toBeGreaterThan(allowRule.priority);
    });
    it('should prioritize specific tool allows over MCP server excludes', () => {
        const settings = {
            mcp: { excluded: ['my-server'] },
            tools: { allowed: ['my-server__specific-tool'] },
        };
        const config = createPolicyEngineConfig(settings, ApprovalMode.DEFAULT);
        const serverDenyRule = config.rules?.find((r) => r.toolName === 'my-server__*' && r.decision === PolicyDecision.DENY);
        const toolAllowRule = config.rules?.find((r) => r.toolName === 'my-server__specific-tool' &&
            r.decision === PolicyDecision.ALLOW);
        expect(serverDenyRule).toBeDefined();
        expect(serverDenyRule?.priority).toBe(195);
        expect(toolAllowRule).toBeDefined();
        expect(toolAllowRule?.priority).toBe(100);
        // Tool allow (100) has lower priority than server deny (195),
        // so server deny wins - this might be counterintuitive
    });
    it('should prioritize specific tool excludes over MCP server allows', () => {
        const settings = {
            mcp: { allowed: ['my-server'] },
            mcpServers: {
                'my-server': {
                    command: 'node',
                    args: ['server.js'],
                    trust: true,
                },
            },
            tools: { exclude: ['my-server__dangerous-tool'] },
        };
        const config = createPolicyEngineConfig(settings, ApprovalMode.DEFAULT);
        const serverAllowRule = config.rules?.find((r) => r.toolName === 'my-server__*' && r.decision === PolicyDecision.ALLOW);
        const toolDenyRule = config.rules?.find((r) => r.toolName === 'my-server__dangerous-tool' &&
            r.decision === PolicyDecision.DENY);
        expect(serverAllowRule).toBeDefined();
        expect(toolDenyRule).toBeDefined();
        expect(toolDenyRule.priority).toBeGreaterThan(serverAllowRule.priority);
    });
    it('should handle complex priority scenarios correctly', () => {
        const settings = {
            tools: {
                autoAccept: true, // Priority 50 for read-only tools
                allowed: ['my-server__tool1', 'other-tool'], // Priority 100
                exclude: ['my-server__tool2', 'glob'], // Priority 200
            },
            mcp: {
                allowed: ['allowed-server'], // Priority 85
                excluded: ['excluded-server'], // Priority 195
            },
            mcpServers: {
                'trusted-server': {
                    command: 'node',
                    args: ['server.js'],
                    trust: true, // Priority 90
                },
            },
        };
        const config = createPolicyEngineConfig(settings, ApprovalMode.DEFAULT);
        // Verify glob is denied even though autoAccept would allow it
        const globDenyRule = config.rules?.find((r) => r.toolName === 'glob' && r.decision === PolicyDecision.DENY);
        const globAllowRule = config.rules?.find((r) => r.toolName === 'glob' && r.decision === PolicyDecision.ALLOW);
        expect(globDenyRule).toBeDefined();
        expect(globAllowRule).toBeDefined();
        expect(globDenyRule.priority).toBe(200);
        expect(globAllowRule.priority).toBe(50);
        // Verify all priority levels are correct
        const priorities = config.rules
            ?.map((r) => ({
            tool: r.toolName,
            decision: r.decision,
            priority: r.priority,
        }))
            .sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
        // Check that the highest priority items are the excludes
        const highestPriorityExcludes = priorities?.filter((p) => p.priority === 200);
        expect(highestPriorityExcludes?.every((p) => p.decision === PolicyDecision.DENY)).toBe(true);
    });
    it('should handle MCP servers with undefined trust property', () => {
        const settings = {
            mcpServers: {
                'no-trust-property': {
                    command: 'node',
                    args: ['server.js'],
                    // trust property is undefined/missing
                },
                'explicit-false': {
                    command: 'node',
                    args: ['server.js'],
                    trust: false,
                },
            },
        };
        const config = createPolicyEngineConfig(settings, ApprovalMode.DEFAULT);
        // Neither server should have an allow rule
        const noTrustRule = config.rules?.find((r) => r.toolName === 'no-trust-property__*' &&
            r.decision === PolicyDecision.ALLOW);
        const explicitFalseRule = config.rules?.find((r) => r.toolName === 'explicit-false__*' &&
            r.decision === PolicyDecision.ALLOW);
        expect(noTrustRule).toBeUndefined();
        expect(explicitFalseRule).toBeUndefined();
    });
    it('should not add write tool rules in YOLO mode', () => {
        const settings = {
            tools: { exclude: ['dangerous-tool'] },
        };
        const config = createPolicyEngineConfig(settings, ApprovalMode.YOLO);
        // Should have the wildcard allow rule with priority 0
        const wildcardRule = config.rules?.find((r) => !r.toolName && r.decision === PolicyDecision.ALLOW && r.priority === 0);
        expect(wildcardRule).toBeDefined();
        // Should NOT have any write tool rules (which would have priority 10)
        const writeToolRules = config.rules?.filter((r) => [
            'replace',
            'save_memory',
            'run_shell_command',
            'write_file',
            'web_fetch',
        ].includes(r.toolName || '') && r.decision === PolicyDecision.ASK_USER);
        expect(writeToolRules).toHaveLength(0);
        // Should still have the exclude rule
        const excludeRule = config.rules?.find((r) => r.toolName === 'dangerous-tool' && r.decision === PolicyDecision.DENY);
        expect(excludeRule).toBeDefined();
        expect(excludeRule?.priority).toBe(200);
    });
    it('should handle combination of trusted server and excluded server for same name', () => {
        const settings = {
            mcpServers: {
                'conflicted-server': {
                    command: 'node',
                    args: ['server.js'],
                    trust: true, // Priority 90
                },
            },
            mcp: {
                excluded: ['conflicted-server'], // Priority 195
            },
        };
        const config = createPolicyEngineConfig(settings, ApprovalMode.DEFAULT);
        // Both rules should exist
        const trustRule = config.rules?.find((r) => r.toolName === 'conflicted-server__*' &&
            r.decision === PolicyDecision.ALLOW);
        const excludeRule = config.rules?.find((r) => r.toolName === 'conflicted-server__*' &&
            r.decision === PolicyDecision.DENY);
        expect(trustRule).toBeDefined();
        expect(trustRule?.priority).toBe(90);
        expect(excludeRule).toBeDefined();
        expect(excludeRule?.priority).toBe(195);
        // Exclude (195) should win over trust (90) when evaluated
    });
    it('should create all read-only tool rules when autoAccept is enabled', () => {
        const settings = {
            tools: { autoAccept: true },
        };
        const config = createPolicyEngineConfig(settings, ApprovalMode.DEFAULT);
        // All read-only tools should have allow rules
        const readOnlyTools = [
            'glob',
            'search_file_content',
            'list_directory',
            'read_file',
            'read_many_files',
        ];
        for (const tool of readOnlyTools) {
            const rule = config.rules?.find((r) => r.toolName === tool && r.decision === PolicyDecision.ALLOW);
            expect(rule).toBeDefined();
            expect(rule?.priority).toBe(50);
        }
    });
    it('should handle all approval modes correctly', () => {
        const settings = {};
        // Test DEFAULT mode
        const defaultConfig = createPolicyEngineConfig(settings, ApprovalMode.DEFAULT);
        expect(defaultConfig.defaultDecision).toBe(PolicyDecision.ASK_USER);
        expect(defaultConfig.rules?.find((r) => !r.toolName && r.decision === PolicyDecision.ALLOW)).toBeUndefined();
        // Test YOLO mode
        const yoloConfig = createPolicyEngineConfig(settings, ApprovalMode.YOLO);
        expect(yoloConfig.defaultDecision).toBe(PolicyDecision.ASK_USER);
        const yoloWildcard = yoloConfig.rules?.find((r) => !r.toolName && r.decision === PolicyDecision.ALLOW);
        expect(yoloWildcard).toBeDefined();
        expect(yoloWildcard?.priority).toBe(0);
        // Test AUTO_EDIT mode
        const autoEditConfig = createPolicyEngineConfig(settings, ApprovalMode.AUTO_EDIT);
        expect(autoEditConfig.defaultDecision).toBe(PolicyDecision.ASK_USER);
        const editRule = autoEditConfig.rules?.find((r) => r.toolName === 'replace' && r.decision === PolicyDecision.ALLOW);
        expect(editRule).toBeDefined();
        expect(editRule?.priority).toBe(15);
    });
});
//# sourceMappingURL=policy.test.js.map