/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { executeToolCall, ToolErrorType, shutdownTelemetry, GeminiEventType, OutputFormat, uiTelemetryService, FatalInputError, } from '@google/gemini-cli-core';
import { runNonInteractive } from './nonInteractiveCli.js';
import { vi } from 'vitest';
// Mock core modules
vi.mock('./ui/hooks/atCommandProcessor.js');
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
    const original = await importOriginal();
    class MockChatRecordingService {
        initialize = vi.fn();
        recordMessage = vi.fn();
        recordMessageTokens = vi.fn();
        recordToolCalls = vi.fn();
    }
    return {
        ...original,
        executeToolCall: vi.fn(),
        shutdownTelemetry: vi.fn(),
        isTelemetrySdkInitialized: vi.fn().mockReturnValue(true),
        ChatRecordingService: MockChatRecordingService,
        uiTelemetryService: {
            getMetrics: vi.fn(),
        },
    };
});
const mockGetCommands = vi.hoisted(() => vi.fn());
const mockCommandServiceCreate = vi.hoisted(() => vi.fn());
vi.mock('./services/CommandService.js', () => ({
    CommandService: {
        create: mockCommandServiceCreate,
    },
}));
vi.mock('./services/FileCommandLoader.js');
vi.mock('./services/McpPromptLoader.js');
describe('runNonInteractive', () => {
    let mockConfig;
    let mockSettings;
    let mockToolRegistry;
    let mockCoreExecuteToolCall;
    let mockShutdownTelemetry;
    let consoleErrorSpy;
    let processStdoutSpy;
    let mockGeminiClient;
    beforeEach(async () => {
        mockCoreExecuteToolCall = vi.mocked(executeToolCall);
        mockShutdownTelemetry = vi.mocked(shutdownTelemetry);
        mockCommandServiceCreate.mockResolvedValue({
            getCommands: mockGetCommands,
        });
        consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
        processStdoutSpy = vi
            .spyOn(process.stdout, 'write')
            .mockImplementation(() => true);
        vi.spyOn(process, 'exit').mockImplementation((code) => {
            throw new Error(`process.exit(${code}) called`);
        });
        mockToolRegistry = {
            getTool: vi.fn(),
            getFunctionDeclarations: vi.fn().mockReturnValue([]),
        };
        mockGeminiClient = {
            sendMessageStream: vi.fn(),
            getChatRecordingService: vi.fn(() => ({
                initialize: vi.fn(),
                recordMessage: vi.fn(),
                recordMessageTokens: vi.fn(),
                recordToolCalls: vi.fn(),
            })),
        };
        mockConfig = {
            initialize: vi.fn().mockResolvedValue(undefined),
            getGeminiClient: vi.fn().mockReturnValue(mockGeminiClient),
            getToolRegistry: vi.fn().mockReturnValue(mockToolRegistry),
            getMaxSessionTurns: vi.fn().mockReturnValue(10),
            getSessionId: vi.fn().mockReturnValue('test-session-id'),
            getProjectRoot: vi.fn().mockReturnValue('/test/project'),
            storage: {
                getProjectTempDir: vi.fn().mockReturnValue('/test/project/.gemini/tmp'),
            },
            getIdeMode: vi.fn().mockReturnValue(false),
            getContentGeneratorConfig: vi.fn().mockReturnValue({}),
            getDebugMode: vi.fn().mockReturnValue(false),
            getOutputFormat: vi.fn().mockReturnValue('text'),
            getFolderTrust: vi.fn().mockReturnValue(false),
            isTrustedFolder: vi.fn().mockReturnValue(false),
        };
        mockSettings = {
            system: { path: '', settings: {} },
            systemDefaults: { path: '', settings: {} },
            user: { path: '', settings: {} },
            workspace: { path: '', settings: {} },
            errors: [],
            setValue: vi.fn(),
            merged: {
                security: {
                    auth: {
                        enforcedType: undefined,
                    },
                },
            },
            isTrusted: true,
            migratedInMemorScopes: new Set(),
            forScope: vi.fn(),
            computeMergedSettings: vi.fn(),
        };
        const { handleAtCommand } = await import('./ui/hooks/atCommandProcessor.js');
        vi.mocked(handleAtCommand).mockImplementation(async ({ query }) => ({
            processedQuery: [{ text: query }],
            shouldProceed: true,
        }));
    });
    afterEach(() => {
        vi.restoreAllMocks();
    });
    async function* createStreamFromEvents(events) {
        for (const event of events) {
            yield event;
        }
    }
    it('should process input and write text output', async () => {
        const events = [
            { type: GeminiEventType.Content, value: 'Hello' },
            { type: GeminiEventType.Content, value: ' World' },
            {
                type: GeminiEventType.Finished,
                value: { reason: undefined, usageMetadata: { totalTokenCount: 10 } },
            },
        ];
        mockGeminiClient.sendMessageStream.mockReturnValue(createStreamFromEvents(events));
        await runNonInteractive(mockConfig, mockSettings, 'Test input', 'prompt-id-1');
        expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledWith([{ text: 'Test input' }], expect.any(AbortSignal), 'prompt-id-1');
        expect(processStdoutSpy).toHaveBeenCalledWith('Hello');
        expect(processStdoutSpy).toHaveBeenCalledWith(' World');
        expect(processStdoutSpy).toHaveBeenCalledWith('\n');
        expect(mockShutdownTelemetry).toHaveBeenCalled();
    });
    it('should handle a single tool call and respond', async () => {
        const toolCallEvent = {
            type: GeminiEventType.ToolCallRequest,
            value: {
                callId: 'tool-1',
                name: 'testTool',
                args: { arg1: 'value1' },
                isClientInitiated: false,
                prompt_id: 'prompt-id-2',
            },
        };
        const toolResponse = [{ text: 'Tool response' }];
        mockCoreExecuteToolCall.mockResolvedValue({
            status: 'success',
            request: {
                callId: 'tool-1',
                name: 'testTool',
                args: { arg1: 'value1' },
                isClientInitiated: false,
                prompt_id: 'prompt-id-2',
            },
            tool: {},
            invocation: {},
            response: {
                responseParts: toolResponse,
                callId: 'tool-1',
                error: undefined,
                errorType: undefined,
                contentLength: undefined,
            },
        });
        const firstCallEvents = [toolCallEvent];
        const secondCallEvents = [
            { type: GeminiEventType.Content, value: 'Final answer' },
            {
                type: GeminiEventType.Finished,
                value: { reason: undefined, usageMetadata: { totalTokenCount: 10 } },
            },
        ];
        mockGeminiClient.sendMessageStream
            .mockReturnValueOnce(createStreamFromEvents(firstCallEvents))
            .mockReturnValueOnce(createStreamFromEvents(secondCallEvents));
        await runNonInteractive(mockConfig, mockSettings, 'Use a tool', 'prompt-id-2');
        expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledTimes(2);
        expect(mockCoreExecuteToolCall).toHaveBeenCalledWith(mockConfig, expect.objectContaining({ name: 'testTool' }), expect.any(AbortSignal));
        expect(mockGeminiClient.sendMessageStream).toHaveBeenNthCalledWith(2, [{ text: 'Tool response' }], expect.any(AbortSignal), 'prompt-id-2');
        expect(processStdoutSpy).toHaveBeenCalledWith('Final answer');
        expect(processStdoutSpy).toHaveBeenCalledWith('\n');
    });
    it('should handle error during tool execution and should send error back to the model', async () => {
        const toolCallEvent = {
            type: GeminiEventType.ToolCallRequest,
            value: {
                callId: 'tool-1',
                name: 'errorTool',
                args: {},
                isClientInitiated: false,
                prompt_id: 'prompt-id-3',
            },
        };
        mockCoreExecuteToolCall.mockResolvedValue({
            status: 'error',
            request: {
                callId: 'tool-1',
                name: 'errorTool',
                args: {},
                isClientInitiated: false,
                prompt_id: 'prompt-id-3',
            },
            tool: {},
            response: {
                callId: 'tool-1',
                error: new Error('Execution failed'),
                errorType: ToolErrorType.EXECUTION_FAILED,
                responseParts: [
                    {
                        functionResponse: {
                            name: 'errorTool',
                            response: {
                                output: 'Error: Execution failed',
                            },
                        },
                    },
                ],
                resultDisplay: 'Execution failed',
                contentLength: undefined,
            },
        });
        const finalResponse = [
            {
                type: GeminiEventType.Content,
                value: 'Sorry, let me try again.',
            },
            {
                type: GeminiEventType.Finished,
                value: { reason: undefined, usageMetadata: { totalTokenCount: 10 } },
            },
        ];
        mockGeminiClient.sendMessageStream
            .mockReturnValueOnce(createStreamFromEvents([toolCallEvent]))
            .mockReturnValueOnce(createStreamFromEvents(finalResponse));
        await runNonInteractive(mockConfig, mockSettings, 'Trigger tool error', 'prompt-id-3');
        expect(mockCoreExecuteToolCall).toHaveBeenCalled();
        expect(consoleErrorSpy).toHaveBeenCalledWith('Error executing tool errorTool: Execution failed');
        expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledTimes(2);
        expect(mockGeminiClient.sendMessageStream).toHaveBeenNthCalledWith(2, [
            {
                functionResponse: {
                    name: 'errorTool',
                    response: {
                        output: 'Error: Execution failed',
                    },
                },
            },
        ], expect.any(AbortSignal), 'prompt-id-3');
        expect(processStdoutSpy).toHaveBeenCalledWith('Sorry, let me try again.');
    });
    it('should exit with error if sendMessageStream throws initially', async () => {
        const apiError = new Error('API connection failed');
        mockGeminiClient.sendMessageStream.mockImplementation(() => {
            throw apiError;
        });
        await expect(runNonInteractive(mockConfig, mockSettings, 'Initial fail', 'prompt-id-4')).rejects.toThrow(apiError);
    });
    it('should not exit if a tool is not found, and should send error back to model', async () => {
        const toolCallEvent = {
            type: GeminiEventType.ToolCallRequest,
            value: {
                callId: 'tool-1',
                name: 'nonexistentTool',
                args: {},
                isClientInitiated: false,
                prompt_id: 'prompt-id-5',
            },
        };
        mockCoreExecuteToolCall.mockResolvedValue({
            status: 'error',
            request: {
                callId: 'tool-1',
                name: 'nonexistentTool',
                args: {},
                isClientInitiated: false,
                prompt_id: 'prompt-id-5',
            },
            response: {
                callId: 'tool-1',
                error: new Error('Tool "nonexistentTool" not found in registry.'),
                resultDisplay: 'Tool "nonexistentTool" not found in registry.',
                responseParts: [],
                errorType: undefined,
                contentLength: undefined,
            },
        });
        const finalResponse = [
            {
                type: GeminiEventType.Content,
                value: "Sorry, I can't find that tool.",
            },
            {
                type: GeminiEventType.Finished,
                value: { reason: undefined, usageMetadata: { totalTokenCount: 10 } },
            },
        ];
        mockGeminiClient.sendMessageStream
            .mockReturnValueOnce(createStreamFromEvents([toolCallEvent]))
            .mockReturnValueOnce(createStreamFromEvents(finalResponse));
        await runNonInteractive(mockConfig, mockSettings, 'Trigger tool not found', 'prompt-id-5');
        expect(mockCoreExecuteToolCall).toHaveBeenCalled();
        expect(consoleErrorSpy).toHaveBeenCalledWith('Error executing tool nonexistentTool: Tool "nonexistentTool" not found in registry.');
        expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledTimes(2);
        expect(processStdoutSpy).toHaveBeenCalledWith("Sorry, I can't find that tool.");
    });
    it('should exit when max session turns are exceeded', async () => {
        vi.mocked(mockConfig.getMaxSessionTurns).mockReturnValue(0);
        await expect(runNonInteractive(mockConfig, mockSettings, 'Trigger loop', 'prompt-id-6')).rejects.toThrow('process.exit(53) called');
    });
    it('should preprocess @include commands before sending to the model', async () => {
        // 1. Mock the imported atCommandProcessor
        const { handleAtCommand } = await import('./ui/hooks/atCommandProcessor.js');
        const mockHandleAtCommand = vi.mocked(handleAtCommand);
        // 2. Define the raw input and the expected processed output
        const rawInput = 'Summarize @file.txt';
        const processedParts = [
            { text: 'Summarize @file.txt' },
            { text: '\n--- Content from referenced files ---\n' },
            { text: 'This is the content of the file.' },
            { text: '\n--- End of content ---' },
        ];
        // 3. Setup the mock to return the processed parts
        mockHandleAtCommand.mockResolvedValue({
            processedQuery: processedParts,
            shouldProceed: true,
        });
        // Mock a simple stream response from the Gemini client
        const events = [
            { type: GeminiEventType.Content, value: 'Summary complete.' },
            {
                type: GeminiEventType.Finished,
                value: { reason: undefined, usageMetadata: { totalTokenCount: 10 } },
            },
        ];
        mockGeminiClient.sendMessageStream.mockReturnValue(createStreamFromEvents(events));
        // 4. Run the non-interactive mode with the raw input
        await runNonInteractive(mockConfig, mockSettings, rawInput, 'prompt-id-7');
        // 5. Assert that sendMessageStream was called with the PROCESSED parts, not the raw input
        expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledWith(processedParts, expect.any(AbortSignal), 'prompt-id-7');
        // 6. Assert the final output is correct
        expect(processStdoutSpy).toHaveBeenCalledWith('Summary complete.');
    });
    it('should process input and write JSON output with stats', async () => {
        const events = [
            { type: GeminiEventType.Content, value: 'Hello World' },
            {
                type: GeminiEventType.Finished,
                value: { reason: undefined, usageMetadata: { totalTokenCount: 10 } },
            },
        ];
        mockGeminiClient.sendMessageStream.mockReturnValue(createStreamFromEvents(events));
        vi.mocked(mockConfig.getOutputFormat).mockReturnValue(OutputFormat.JSON);
        const mockMetrics = {
            models: {},
            tools: {
                totalCalls: 0,
                totalSuccess: 0,
                totalFail: 0,
                totalDurationMs: 0,
                totalDecisions: {
                    accept: 0,
                    reject: 0,
                    modify: 0,
                    auto_accept: 0,
                },
                byName: {},
            },
            files: {
                totalLinesAdded: 0,
                totalLinesRemoved: 0,
            },
        };
        vi.mocked(uiTelemetryService.getMetrics).mockReturnValue(mockMetrics);
        await runNonInteractive(mockConfig, mockSettings, 'Test input', 'prompt-id-1');
        expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledWith([{ text: 'Test input' }], expect.any(AbortSignal), 'prompt-id-1');
        expect(processStdoutSpy).toHaveBeenCalledWith(JSON.stringify({ response: 'Hello World', stats: mockMetrics }, null, 2));
    });
    it('should write JSON output with stats for tool-only commands (no text response)', async () => {
        // Test the scenario where a command completes successfully with only tool calls
        // but no text response - this would have caught the original bug
        const toolCallEvent = {
            type: GeminiEventType.ToolCallRequest,
            value: {
                callId: 'tool-1',
                name: 'testTool',
                args: { arg1: 'value1' },
                isClientInitiated: false,
                prompt_id: 'prompt-id-tool-only',
            },
        };
        const toolResponse = [{ text: 'Tool executed successfully' }];
        mockCoreExecuteToolCall.mockResolvedValue({
            status: 'success',
            request: {
                callId: 'tool-1',
                name: 'testTool',
                args: { arg1: 'value1' },
                isClientInitiated: false,
                prompt_id: 'prompt-id-tool-only',
            },
            tool: {},
            invocation: {},
            response: {
                responseParts: toolResponse,
                callId: 'tool-1',
                error: undefined,
                errorType: undefined,
                contentLength: undefined,
            },
        });
        // First call returns only tool call, no content
        const firstCallEvents = [
            toolCallEvent,
            {
                type: GeminiEventType.Finished,
                value: { reason: undefined, usageMetadata: { totalTokenCount: 5 } },
            },
        ];
        // Second call returns no content (tool-only completion)
        const secondCallEvents = [
            {
                type: GeminiEventType.Finished,
                value: { reason: undefined, usageMetadata: { totalTokenCount: 3 } },
            },
        ];
        mockGeminiClient.sendMessageStream
            .mockReturnValueOnce(createStreamFromEvents(firstCallEvents))
            .mockReturnValueOnce(createStreamFromEvents(secondCallEvents));
        vi.mocked(mockConfig.getOutputFormat).mockReturnValue(OutputFormat.JSON);
        const mockMetrics = {
            models: {},
            tools: {
                totalCalls: 1,
                totalSuccess: 1,
                totalFail: 0,
                totalDurationMs: 100,
                totalDecisions: {
                    accept: 1,
                    reject: 0,
                    modify: 0,
                    auto_accept: 0,
                },
                byName: {
                    testTool: {
                        count: 1,
                        success: 1,
                        fail: 0,
                        durationMs: 100,
                        decisions: {
                            accept: 1,
                            reject: 0,
                            modify: 0,
                            auto_accept: 0,
                        },
                    },
                },
            },
            files: {
                totalLinesAdded: 0,
                totalLinesRemoved: 0,
            },
        };
        vi.mocked(uiTelemetryService.getMetrics).mockReturnValue(mockMetrics);
        await runNonInteractive(mockConfig, mockSettings, 'Execute tool only', 'prompt-id-tool-only');
        expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledTimes(2);
        expect(mockCoreExecuteToolCall).toHaveBeenCalledWith(mockConfig, expect.objectContaining({ name: 'testTool' }), expect.any(AbortSignal));
        // This should output JSON with empty response but include stats
        expect(processStdoutSpy).toHaveBeenCalledWith(JSON.stringify({ response: '', stats: mockMetrics }, null, 2));
    });
    it('should write JSON output with stats for empty response commands', async () => {
        // Test the scenario where a command completes but produces no content at all
        const events = [
            {
                type: GeminiEventType.Finished,
                value: { reason: undefined, usageMetadata: { totalTokenCount: 1 } },
            },
        ];
        mockGeminiClient.sendMessageStream.mockReturnValue(createStreamFromEvents(events));
        vi.mocked(mockConfig.getOutputFormat).mockReturnValue(OutputFormat.JSON);
        const mockMetrics = {
            models: {},
            tools: {
                totalCalls: 0,
                totalSuccess: 0,
                totalFail: 0,
                totalDurationMs: 0,
                totalDecisions: {
                    accept: 0,
                    reject: 0,
                    modify: 0,
                    auto_accept: 0,
                },
                byName: {},
            },
            files: {
                totalLinesAdded: 0,
                totalLinesRemoved: 0,
            },
        };
        vi.mocked(uiTelemetryService.getMetrics).mockReturnValue(mockMetrics);
        await runNonInteractive(mockConfig, mockSettings, 'Empty response test', 'prompt-id-empty');
        expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledWith([{ text: 'Empty response test' }], expect.any(AbortSignal), 'prompt-id-empty');
        // This should output JSON with empty response but include stats
        expect(processStdoutSpy).toHaveBeenCalledWith(JSON.stringify({ response: '', stats: mockMetrics }, null, 2));
    });
    it('should handle errors in JSON format', async () => {
        vi.mocked(mockConfig.getOutputFormat).mockReturnValue(OutputFormat.JSON);
        const testError = new Error('Invalid input provided');
        mockGeminiClient.sendMessageStream.mockImplementation(() => {
            throw testError;
        });
        // Mock console.error to capture JSON error output
        const consoleErrorJsonSpy = vi
            .spyOn(console, 'error')
            .mockImplementation(() => { });
        let thrownError = null;
        try {
            await runNonInteractive(mockConfig, mockSettings, 'Test input', 'prompt-id-error');
            // Should not reach here
            expect.fail('Expected process.exit to be called');
        }
        catch (error) {
            thrownError = error;
        }
        // Should throw because of mocked process.exit
        expect(thrownError?.message).toBe('process.exit(1) called');
        expect(consoleErrorJsonSpy).toHaveBeenCalledWith(JSON.stringify({
            error: {
                type: 'Error',
                message: 'Invalid input provided',
                code: 1,
            },
        }, null, 2));
    });
    it('should handle FatalInputError with custom exit code in JSON format', async () => {
        vi.mocked(mockConfig.getOutputFormat).mockReturnValue(OutputFormat.JSON);
        const fatalError = new FatalInputError('Invalid command syntax provided');
        mockGeminiClient.sendMessageStream.mockImplementation(() => {
            throw fatalError;
        });
        // Mock console.error to capture JSON error output
        const consoleErrorJsonSpy = vi
            .spyOn(console, 'error')
            .mockImplementation(() => { });
        let thrownError = null;
        try {
            await runNonInteractive(mockConfig, mockSettings, 'Invalid syntax', 'prompt-id-fatal');
            // Should not reach here
            expect.fail('Expected process.exit to be called');
        }
        catch (error) {
            thrownError = error;
        }
        // Should throw because of mocked process.exit with custom exit code
        expect(thrownError?.message).toBe('process.exit(42) called');
        expect(consoleErrorJsonSpy).toHaveBeenCalledWith(JSON.stringify({
            error: {
                type: 'FatalInputError',
                message: 'Invalid command syntax provided',
                code: 42,
            },
        }, null, 2));
    });
    it('should execute a slash command that returns a prompt', async () => {
        const mockCommand = {
            name: 'testcommand',
            description: 'a test command',
            action: vi.fn().mockResolvedValue({
                type: 'submit_prompt',
                content: [{ text: 'Prompt from command' }],
            }),
        };
        mockGetCommands.mockReturnValue([mockCommand]);
        const events = [
            { type: GeminiEventType.Content, value: 'Response from command' },
            {
                type: GeminiEventType.Finished,
                value: { reason: undefined, usageMetadata: { totalTokenCount: 5 } },
            },
        ];
        mockGeminiClient.sendMessageStream.mockReturnValue(createStreamFromEvents(events));
        await runNonInteractive(mockConfig, mockSettings, '/testcommand', 'prompt-id-slash');
        // Ensure the prompt sent to the model is from the command, not the raw input
        expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledWith([{ text: 'Prompt from command' }], expect.any(AbortSignal), 'prompt-id-slash');
        expect(processStdoutSpy).toHaveBeenCalledWith('Response from command');
    });
    it('should throw FatalInputError if a command requires confirmation', async () => {
        const mockCommand = {
            name: 'confirm',
            description: 'a command that needs confirmation',
            action: vi.fn().mockResolvedValue({
                type: 'confirm_shell_commands',
                commands: ['rm -rf /'],
            }),
        };
        mockGetCommands.mockReturnValue([mockCommand]);
        await expect(runNonInteractive(mockConfig, mockSettings, '/confirm', 'prompt-id-confirm')).rejects.toThrow('Exiting due to a confirmation prompt requested by the command.');
    });
    it('should treat an unknown slash command as a regular prompt', async () => {
        // No commands are mocked, so any slash command is "unknown"
        mockGetCommands.mockReturnValue([]);
        const events = [
            { type: GeminiEventType.Content, value: 'Response to unknown' },
            {
                type: GeminiEventType.Finished,
                value: { reason: undefined, usageMetadata: { totalTokenCount: 5 } },
            },
        ];
        mockGeminiClient.sendMessageStream.mockReturnValue(createStreamFromEvents(events));
        await runNonInteractive(mockConfig, mockSettings, '/unknowncommand', 'prompt-id-unknown');
        // Ensure the raw input is sent to the model
        expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledWith([{ text: '/unknowncommand' }], expect.any(AbortSignal), 'prompt-id-unknown');
        expect(processStdoutSpy).toHaveBeenCalledWith('Response to unknown');
    });
    it('should throw for unhandled command result types', async () => {
        const mockCommand = {
            name: 'noaction',
            description: 'unhandled type',
            action: vi.fn().mockResolvedValue({
                type: 'unhandled',
            }),
        };
        mockGetCommands.mockReturnValue([mockCommand]);
        await expect(runNonInteractive(mockConfig, mockSettings, '/noaction', 'prompt-id-unhandled')).rejects.toThrow('Exiting due to command result that is not supported in non-interactive mode.');
    });
    it('should pass arguments to the slash command action', async () => {
        const mockAction = vi.fn().mockResolvedValue({
            type: 'submit_prompt',
            content: [{ text: 'Prompt from command' }],
        });
        const mockCommand = {
            name: 'testargs',
            description: 'a test command',
            action: mockAction,
        };
        mockGetCommands.mockReturnValue([mockCommand]);
        const events = [
            { type: GeminiEventType.Content, value: 'Acknowledged' },
            {
                type: GeminiEventType.Finished,
                value: { reason: undefined, usageMetadata: { totalTokenCount: 1 } },
            },
        ];
        mockGeminiClient.sendMessageStream.mockReturnValue(createStreamFromEvents(events));
        await runNonInteractive(mockConfig, mockSettings, '/testargs arg1 arg2', 'prompt-id-args');
        expect(mockAction).toHaveBeenCalledWith(expect.any(Object), 'arg1 arg2');
        expect(processStdoutSpy).toHaveBeenCalledWith('Acknowledged');
    });
    it('should instantiate CommandService with correct loaders for slash commands', async () => {
        // This test indirectly checks that handleSlashCommand is using the right loaders.
        const { FileCommandLoader } = await import('./services/FileCommandLoader.js');
        const { McpPromptLoader } = await import('./services/McpPromptLoader.js');
        mockGetCommands.mockReturnValue([]); // No commands found, so it will fall through
        const events = [
            { type: GeminiEventType.Content, value: 'Acknowledged' },
            {
                type: GeminiEventType.Finished,
                value: { reason: undefined, usageMetadata: { totalTokenCount: 1 } },
            },
        ];
        mockGeminiClient.sendMessageStream.mockReturnValue(createStreamFromEvents(events));
        await runNonInteractive(mockConfig, mockSettings, '/mycommand', 'prompt-id-loaders');
        // Check that loaders were instantiated with the config
        expect(FileCommandLoader).toHaveBeenCalledTimes(1);
        expect(FileCommandLoader).toHaveBeenCalledWith(mockConfig);
        expect(McpPromptLoader).toHaveBeenCalledTimes(1);
        expect(McpPromptLoader).toHaveBeenCalledWith(mockConfig);
        // Check that instances were passed to CommandService.create
        expect(mockCommandServiceCreate).toHaveBeenCalledTimes(1);
        const loadersArg = mockCommandServiceCreate.mock.calls[0][0];
        expect(loadersArg).toHaveLength(2);
        expect(loadersArg[0]).toBe(vi.mocked(McpPromptLoader).mock.instances[0]);
        expect(loadersArg[1]).toBe(vi.mocked(FileCommandLoader).mock.instances[0]);
    });
    it('should allow a normally-excluded tool when --allowed-tools is set', async () => {
        // By default, ShellTool is excluded in non-interactive mode.
        // This test ensures that --allowed-tools overrides this exclusion.
        vi.mocked(mockConfig.getToolRegistry).mockReturnValue({
            getTool: vi.fn().mockReturnValue({
                name: 'ShellTool',
                description: 'A shell tool',
                run: vi.fn(),
            }),
            getFunctionDeclarations: vi.fn().mockReturnValue([{ name: 'ShellTool' }]),
        });
        const toolCallEvent = {
            type: GeminiEventType.ToolCallRequest,
            value: {
                callId: 'tool-shell-1',
                name: 'ShellTool',
                args: { command: 'ls' },
                isClientInitiated: false,
                prompt_id: 'prompt-id-allowed',
            },
        };
        const toolResponse = [{ text: 'file.txt' }];
        mockCoreExecuteToolCall.mockResolvedValue({
            status: 'success',
            request: {
                callId: 'tool-shell-1',
                name: 'ShellTool',
                args: { command: 'ls' },
                isClientInitiated: false,
                prompt_id: 'prompt-id-allowed',
            },
            tool: {},
            invocation: {},
            response: {
                responseParts: toolResponse,
                callId: 'tool-shell-1',
                error: undefined,
                errorType: undefined,
                contentLength: undefined,
            },
        });
        const firstCallEvents = [toolCallEvent];
        const secondCallEvents = [
            { type: GeminiEventType.Content, value: 'file.txt' },
            {
                type: GeminiEventType.Finished,
                value: { reason: undefined, usageMetadata: { totalTokenCount: 10 } },
            },
        ];
        mockGeminiClient.sendMessageStream
            .mockReturnValueOnce(createStreamFromEvents(firstCallEvents))
            .mockReturnValueOnce(createStreamFromEvents(secondCallEvents));
        await runNonInteractive(mockConfig, mockSettings, 'List the files', 'prompt-id-allowed');
        expect(mockCoreExecuteToolCall).toHaveBeenCalledWith(mockConfig, expect.objectContaining({ name: 'ShellTool' }), expect.any(AbortSignal));
        expect(processStdoutSpy).toHaveBeenCalledWith('file.txt');
    });
});
//# sourceMappingURL=nonInteractiveCli.test.js.map