/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { describe, it, expect, vi, afterEach, beforeEach, } from 'vitest';
import { getIdeProcessInfo } from './process-utils.js';
import os from 'node:os';
const mockedExec = vi.hoisted(() => vi.fn());
vi.mock('node:util', () => ({
    promisify: vi.fn().mockReturnValue(mockedExec),
}));
vi.mock('node:os', () => ({
    default: {
        platform: vi.fn(),
    },
}));
describe('getIdeProcessInfo', () => {
    beforeEach(() => {
        Object.defineProperty(process, 'pid', { value: 1000, configurable: true });
        mockedExec.mockReset();
    });
    afterEach(() => {
        vi.restoreAllMocks();
    });
    describe('on Unix', () => {
        it('should traverse up to find the shell and return grandparent process info', async () => {
            os.platform.mockReturnValue('linux');
            // process (1000) -> shell (800) -> IDE (700)
            mockedExec
                .mockResolvedValueOnce({ stdout: '800 /bin/bash' }) // pid 1000 -> ppid 800 (shell)
                .mockResolvedValueOnce({ stdout: '700 /usr/lib/vscode/code' }) // pid 800 -> ppid 700 (IDE)
                .mockResolvedValueOnce({ stdout: '700 /usr/lib/vscode/code' }); // get command for pid 700
            const result = await getIdeProcessInfo();
            expect(result).toEqual({ pid: 700, command: '/usr/lib/vscode/code' });
        });
        it('should return parent process info if grandparent lookup fails', async () => {
            os.platform.mockReturnValue('linux');
            mockedExec
                .mockResolvedValueOnce({ stdout: '800 /bin/bash' }) // pid 1000 -> ppid 800 (shell)
                .mockRejectedValueOnce(new Error('ps failed')) // lookup for ppid of 800 fails
                .mockResolvedValueOnce({ stdout: '800 /bin/bash' }); // get command for pid 800
            const result = await getIdeProcessInfo();
            expect(result).toEqual({ pid: 800, command: '/bin/bash' });
        });
    });
    describe('on Windows', () => {
        it('should traverse up and find the great-grandchild of the root process', async () => {
            os.platform.mockReturnValue('win32');
            const processInfoMap = new Map([
                [
                    1000,
                    {
                        stdout: '{"Name":"node.exe","ParentProcessId":900,"CommandLine":"node.exe"}',
                    },
                ],
                [
                    900,
                    {
                        stdout: '{"Name":"powershell.exe","ParentProcessId":800,"CommandLine":"powershell.exe"}',
                    },
                ],
                [
                    800,
                    {
                        stdout: '{"Name":"code.exe","ParentProcessId":700,"CommandLine":"code.exe"}',
                    },
                ],
                [
                    700,
                    {
                        stdout: '{"Name":"wininit.exe","ParentProcessId":0,"CommandLine":"wininit.exe"}',
                    },
                ],
            ]);
            mockedExec.mockImplementation((command) => {
                const pidMatch = command.match(/ProcessId=(\d+)/);
                if (pidMatch) {
                    const pid = parseInt(pidMatch[1], 10);
                    return Promise.resolve(processInfoMap.get(pid));
                }
                return Promise.reject(new Error('Invalid command for mock'));
            });
            const result = await getIdeProcessInfo();
            expect(result).toEqual({ pid: 900, command: 'powershell.exe' });
        });
        it('should handle non-existent process gracefully', async () => {
            os.platform.mockReturnValue('win32');
            mockedExec
                .mockResolvedValueOnce({ stdout: '' }) // Non-existent PID returns empty due to -ErrorAction SilentlyContinue
                .mockResolvedValueOnce({
                stdout: '{"Name":"fallback.exe","ParentProcessId":0,"CommandLine":"fallback.exe"}',
            }); // Fallback call
            const result = await getIdeProcessInfo();
            expect(result).toEqual({ pid: 1000, command: 'fallback.exe' });
        });
        it('should handle malformed JSON output gracefully', async () => {
            os.platform.mockReturnValue('win32');
            mockedExec
                .mockResolvedValueOnce({ stdout: '{"invalid":json}' }) // Malformed JSON
                .mockResolvedValueOnce({
                stdout: '{"Name":"fallback.exe","ParentProcessId":0,"CommandLine":"fallback.exe"}',
            }); // Fallback call
            const result = await getIdeProcessInfo();
            expect(result).toEqual({ pid: 1000, command: 'fallback.exe' });
        });
        it('should handle PowerShell errors without crashing the process chain', async () => {
            os.platform.mockReturnValue('win32');
            const processInfoMap = new Map([
                [1000, { stdout: '' }], // First process doesn't exist (empty due to -ErrorAction)
                [
                    1001,
                    {
                        stdout: '{"Name":"parent.exe","ParentProcessId":800,"CommandLine":"parent.exe"}',
                    },
                ],
                [
                    800,
                    {
                        stdout: '{"Name":"ide.exe","ParentProcessId":0,"CommandLine":"ide.exe"}',
                    },
                ],
            ]);
            // Mock the process.pid to test traversal with missing processes
            Object.defineProperty(process, 'pid', {
                value: 1001,
                configurable: true,
            });
            mockedExec.mockImplementation((command) => {
                const pidMatch = command.match(/ProcessId=(\d+)/);
                if (pidMatch) {
                    const pid = parseInt(pidMatch[1], 10);
                    return Promise.resolve(processInfoMap.get(pid) || { stdout: '' });
                }
                return Promise.reject(new Error('Invalid command for mock'));
            });
            const result = await getIdeProcessInfo();
            // Should return the current process command since traversal continues despite missing processes
            expect(result).toEqual({ pid: 1001, command: 'parent.exe' });
            // Reset process.pid
            Object.defineProperty(process, 'pid', {
                value: 1000,
                configurable: true,
            });
        });
        it('should handle partial JSON data with defaults', async () => {
            os.platform.mockReturnValue('win32');
            mockedExec
                .mockResolvedValueOnce({ stdout: '{"Name":"partial.exe"}' }) // Missing ParentProcessId, defaults to 0
                .mockResolvedValueOnce({
                stdout: '{"Name":"root.exe","ParentProcessId":0,"CommandLine":"root.exe"}',
            }); // Get grandparent info
            const result = await getIdeProcessInfo();
            expect(result).toEqual({ pid: 1000, command: 'root.exe' });
        });
    });
});
//# sourceMappingURL=process-utils.test.js.map