/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { ApprovalMode, PolicyDecision } from './types.js';
import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import * as os from 'node:os';
import { loadPoliciesFromToml } from './toml-loader.js';
describe('policy-toml-loader', () => {
    let tempDir;
    beforeEach(async () => {
        tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'policy-test-'));
    });
    afterEach(async () => {
        if (tempDir) {
            await fs.rm(tempDir, {
                recursive: true,
                force: true,
                maxRetries: 3,
                retryDelay: 10,
            });
        }
    });
    async function runLoadPoliciesFromToml(tomlContent, fileName = 'test.toml') {
        await fs.writeFile(path.join(tempDir, fileName), tomlContent);
        const getPolicyTier = (_dir) => 1;
        return loadPoliciesFromToml(ApprovalMode.DEFAULT, [tempDir], getPolicyTier);
    }
    describe('loadPoliciesFromToml', () => {
        it('should load and parse a simple policy file', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "glob"
decision = "allow"
priority = 100
`);
            expect(result.rules).toHaveLength(1);
            expect(result.rules[0]).toEqual({
                toolName: 'glob',
                decision: PolicyDecision.ALLOW,
                priority: 1.1, // tier 1 + 100/1000
            });
            expect(result.checkers).toHaveLength(0);
            expect(result.errors).toHaveLength(0);
        });
        it('should expand commandPrefix array to multiple rules', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "run_shell_command"
commandPrefix = ["git status", "git log"]
decision = "allow"
priority = 100
`);
            expect(result.rules).toHaveLength(2);
            expect(result.rules[0].toolName).toBe('run_shell_command');
            expect(result.rules[1].toolName).toBe('run_shell_command');
            expect(result.rules[0].argsPattern?.test('{"command":"git status"}')).toBe(true);
            expect(result.rules[1].argsPattern?.test('{"command":"git log"}')).toBe(true);
            expect(result.errors).toHaveLength(0);
        });
        it('should transform commandRegex to argsPattern', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "run_shell_command"
commandRegex = "git (status|log).*"
decision = "allow"
priority = 100
`);
            expect(result.rules).toHaveLength(1);
            expect(result.rules[0].argsPattern?.test('{"command":"git status"}')).toBe(true);
            expect(result.rules[0].argsPattern?.test('{"command":"git log --all"}')).toBe(true);
            expect(result.rules[0].argsPattern?.test('{"command":"git branch"}')).toBe(false);
            expect(result.errors).toHaveLength(0);
        });
        it('should expand toolName array', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = ["glob", "grep", "read"]
decision = "allow"
priority = 100
`);
            expect(result.rules).toHaveLength(3);
            expect(result.rules.map((r) => r.toolName)).toEqual([
                'glob',
                'grep',
                'read',
            ]);
            expect(result.errors).toHaveLength(0);
        });
        it('should transform mcpName to composite toolName', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
mcpName = "google-workspace"
toolName = ["calendar.list", "calendar.get"]
decision = "allow"
priority = 100
`);
            expect(result.rules).toHaveLength(2);
            expect(result.rules[0].toolName).toBe('google-workspace__calendar.list');
            expect(result.rules[1].toolName).toBe('google-workspace__calendar.get');
            expect(result.errors).toHaveLength(0);
        });
        it('should filter rules by mode', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "glob"
decision = "allow"
priority = 100
modes = ["default", "yolo"]

[[rule]]
toolName = "grep"
decision = "allow"
priority = 100
modes = ["yolo"]
`);
            // Only the first rule should be included (modes includes "default")
            expect(result.rules).toHaveLength(1);
            expect(result.rules[0].toolName).toBe('glob');
            expect(result.errors).toHaveLength(0);
        });
        it('should handle TOML parse errors', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]
toolName = "glob"
decision = "allow"
priority = 100
`);
            expect(result.rules).toHaveLength(0);
            expect(result.errors).toHaveLength(1);
            expect(result.errors[0].errorType).toBe('toml_parse');
            expect(result.errors[0].fileName).toBe('test.toml');
        });
        it('should handle schema validation errors', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "glob"
priority = 100
`);
            expect(result.rules).toHaveLength(0);
            expect(result.errors).toHaveLength(1);
            expect(result.errors[0].errorType).toBe('schema_validation');
            expect(result.errors[0].details).toContain('decision');
        });
        it('should reject commandPrefix without run_shell_command', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "glob"
commandPrefix = "git status"
decision = "allow"
priority = 100
`);
            expect(result.errors).toHaveLength(1);
            expect(result.errors[0].errorType).toBe('rule_validation');
            expect(result.errors[0].details).toContain('run_shell_command');
        });
        it('should reject commandPrefix + argsPattern combination', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "run_shell_command"
commandPrefix = "git status"
argsPattern = "test"
decision = "allow"
priority = 100
`);
            expect(result.errors).toHaveLength(1);
            expect(result.errors[0].errorType).toBe('rule_validation');
            expect(result.errors[0].details).toContain('mutually exclusive');
        });
        it('should handle invalid regex patterns', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "run_shell_command"
commandRegex = "git (status|branch"
decision = "allow"
priority = 100
`);
            expect(result.rules).toHaveLength(0);
            expect(result.errors).toHaveLength(1);
            expect(result.errors[0].errorType).toBe('regex_compilation');
            expect(result.errors[0].details).toContain('git (status|branch');
        });
        it('should escape regex special characters in commandPrefix', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "run_shell_command"
commandPrefix = "git log *.txt"
decision = "allow"
priority = 100
`);
            expect(result.rules).toHaveLength(1);
            // The regex should have escaped the * and .
            expect(result.rules[0].argsPattern?.test('{"command":"git log file.txt"}')).toBe(false);
            expect(result.rules[0].argsPattern?.test('{"command":"git log *.txt"}')).toBe(true);
            expect(result.errors).toHaveLength(0);
        });
        it('should handle a mix of valid and invalid policy files', async () => {
            await fs.writeFile(path.join(tempDir, 'valid.toml'), `
[[rule]]
toolName = "glob"
decision = "allow"
priority = 100
`);
            await fs.writeFile(path.join(tempDir, 'invalid.toml'), `
[[rule]]
toolName = "grep"
decision = "allow"
priority = -1
`);
            const getPolicyTier = (_dir) => 1;
            const result = await loadPoliciesFromToml(ApprovalMode.DEFAULT, [tempDir], getPolicyTier);
            expect(result.rules).toHaveLength(1);
            expect(result.rules[0].toolName).toBe('glob');
            expect(result.errors).toHaveLength(1);
            expect(result.errors[0].fileName).toBe('invalid.toml');
            expect(result.errors[0].errorType).toBe('schema_validation');
        });
    });
    describe('Negative Tests', () => {
        it('should return a schema_validation error if priority is missing', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "test"
decision = "allow"
`);
            expect(result.errors).toHaveLength(1);
            const error = result.errors[0];
            expect(error.errorType).toBe('schema_validation');
            expect(error.details).toContain('priority');
        });
        it('should return a schema_validation error if priority is a float', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "test"
decision = "allow"
priority = 1.5
`);
            expect(result.errors).toHaveLength(1);
            const error = result.errors[0];
            expect(error.errorType).toBe('schema_validation');
            expect(error.details).toContain('priority');
            expect(error.details).toContain('integer');
        });
        it('should return a schema_validation error if priority is negative', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "test"
decision = "allow"
priority = -1
`);
            expect(result.errors).toHaveLength(1);
            const error = result.errors[0];
            expect(error.errorType).toBe('schema_validation');
            expect(error.details).toContain('priority');
            expect(error.details).toContain('>= 0');
        });
        it('should return a schema_validation error if priority is much lower than 0', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "test"
decision = "allow"
priority = -9999
`);
            expect(result.errors).toHaveLength(1);
            const error = result.errors[0];
            expect(error.errorType).toBe('schema_validation');
            expect(error.details).toContain('priority');
            expect(error.details).toContain('>= 0');
        });
        it('should return a schema_validation error if priority is >= 1000', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "test"
decision = "allow"
priority = 1000
`);
            expect(result.errors).toHaveLength(1);
            const error = result.errors[0];
            expect(error.errorType).toBe('schema_validation');
            expect(error.details).toContain('priority');
            expect(error.details).toContain('<= 999');
        });
        it('should return a schema_validation error if priority is much higher than 1000', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "test"
decision = "allow"
priority = 9999
`);
            expect(result.errors).toHaveLength(1);
            const error = result.errors[0];
            expect(error.errorType).toBe('schema_validation');
            expect(error.details).toContain('priority');
            expect(error.details).toContain('<= 999');
        });
        it('should return a schema_validation error if decision is invalid', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "test"
decision = "maybe"
priority = 100
`);
            expect(result.errors).toHaveLength(1);
            const error = result.errors[0];
            expect(error.errorType).toBe('schema_validation');
            expect(error.details).toContain('decision');
        });
        it('should return a schema_validation error if toolName is not a string or array', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = 123
decision = "allow"
priority = 100
`);
            expect(result.errors).toHaveLength(1);
            const error = result.errors[0];
            expect(error.errorType).toBe('schema_validation');
            expect(error.details).toContain('toolName');
        });
        it('should return a rule_validation error if commandRegex is used with wrong toolName', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "not_shell"
commandRegex = ".*"
decision = "allow"
priority = 100
`);
            expect(result.errors).toHaveLength(1);
            const error = result.errors[0];
            expect(error.errorType).toBe('rule_validation');
            expect(error.details).toContain('run_shell_command');
        });
        it('should return a rule_validation error if commandPrefix and commandRegex are combined', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "run_shell_command"
commandPrefix = "git"
commandRegex = ".*"
decision = "allow"
priority = 100
`);
            expect(result.errors).toHaveLength(1);
            const error = result.errors[0];
            expect(error.errorType).toBe('rule_validation');
            expect(error.details).toContain('mutually exclusive');
        });
        it('should return a regex_compilation error for invalid argsPattern', async () => {
            const result = await runLoadPoliciesFromToml(`
[[rule]]
toolName = "test"
argsPattern = "([a-z)"
decision = "allow"
priority = 100
`);
            expect(result.errors).toHaveLength(1);
            const error = result.errors[0];
            expect(error.errorType).toBe('regex_compilation');
            expect(error.message).toBe('Invalid regex pattern');
        });
        it('should return a file_read error if readdir fails', async () => {
            // Create a file and pass it as a directory to trigger ENOTDIR
            const filePath = path.join(tempDir, 'not-a-dir');
            await fs.writeFile(filePath, 'content');
            const getPolicyTier = (_dir) => 1;
            const result = await loadPoliciesFromToml(ApprovalMode.DEFAULT, [filePath], getPolicyTier);
            expect(result.errors).toHaveLength(1);
            const error = result.errors[0];
            expect(error.errorType).toBe('file_read');
            expect(error.message).toContain('Failed to read policy directory');
        });
    });
});
//# sourceMappingURL=toml-loader.test.js.map