basant307/AI_Governance_Project
045
1/**2 * @license3 * Copyright 2025 Google LLC4 * SPDX-License-Identifier: Apache-2.05 */6 7import {8 describe,9 it,10 expect,11 vi,12 afterEach,13 beforeEach,14 type Mock,15} from 'vitest';16import { getIdeProcessInfo } from './process-utils.js';17import os from 'node:os';18 19const mockedExec = vi.hoisted(() => vi.fn());20vi.mock('node:util', () => ({21 promisify: vi.fn().mockReturnValue(mockedExec),22}));23vi.mock('node:os', () => ({24 default: {25 platform: vi.fn(),26 },27}));28 29describe('getIdeProcessInfo', () => {30 beforeEach(() => {31 Object.defineProperty(process, 'pid', { value: 1000, configurable: true });32 mockedExec.mockReset();33 });34 35 afterEach(() => {36 vi.restoreAllMocks();37 });38 39 describe('on Unix', () => {40 it('should traverse up to find the shell and return grandparent process info', async () => {41 (os.platform as Mock).mockReturnValue('linux');42 // process (1000) -> shell (800) -> IDE (700)43 mockedExec44 .mockResolvedValueOnce({ stdout: '800 /bin/bash' }) // pid 1000 -> ppid 800 (shell)45 .mockResolvedValueOnce({ stdout: '700 /usr/lib/vscode/code' }) // pid 800 -> ppid 700 (IDE)46 .mockResolvedValueOnce({ stdout: '700 /usr/lib/vscode/code' }); // get command for pid 70047 48 const result = await getIdeProcessInfo();49 50 expect(result).toEqual({ pid: 700, command: '/usr/lib/vscode/code' });51 });52 53 it('should return shell process info if grandparent lookup fails', async () => {54 (os.platform as Mock).mockReturnValue('linux');55 mockedExec56 .mockResolvedValueOnce({ stdout: '800 /bin/bash' }) // pid 1000 -> ppid 800 (shell)57 .mockRejectedValueOnce(new Error('ps failed')) // lookup for ppid of 800 fails58 .mockResolvedValueOnce({ stdout: '800 /bin/bash' }); // get command for pid 80059 60 const result = await getIdeProcessInfo();61 expect(result).toEqual({ pid: 800, command: '/bin/bash' });62 });63 });64 65 describe('on Windows', () => {66 it('should return great-grandparent process using heuristic', async () => {67 (os.platform as Mock).mockReturnValue('win32');68 69 const processes = [70 {71 ProcessId: 1000,72 ParentProcessId: 900,73 Name: 'node.exe',74 CommandLine: 'node.exe',75 },76 {77 ProcessId: 900,78 ParentProcessId: 800,79 Name: 'powershell.exe',80 CommandLine: 'powershell.exe',81 },82 {83 ProcessId: 800,84 ParentProcessId: 700,85 Name: 'code.exe',86 CommandLine: 'code.exe',87 },88 {89 ProcessId: 700,90 ParentProcessId: 0,91 Name: 'wininit.exe',92 CommandLine: 'wininit.exe',93 },94 ];95 96 mockedExec.mockImplementation((file: string, _args: string[]) => {97 if (file === 'powershell') {98 return Promise.resolve({ stdout: JSON.stringify(processes) });99 }100 return Promise.resolve({ stdout: '' });101 });102 103 const result = await getIdeProcessInfo();104 // Process chain: 1000 (node.exe) -> 900 (powershell.exe) -> 800 (code.exe) -> 700 (wininit.exe)105 // ancestors = [1000, 900, 800, 700], length = 4106 // Heuristic: return ancestors[length-3] = ancestors[1] = 900 (powershell.exe)107 expect(result).toEqual({ pid: 900, command: 'powershell.exe' });108 });109 110 it('should handle empty process list gracefully', async () => {111 (os.platform as Mock).mockReturnValue('win32');112 mockedExec.mockResolvedValue({ stdout: '[]' });113 114 const result = await getIdeProcessInfo();115 // Should return current pid and empty command because process not found in map116 expect(result).toEqual({ pid: 1000, command: '' });117 });118 119 it('should handle malformed JSON output gracefully', async () => {120 (os.platform as Mock).mockReturnValue('win32');121 mockedExec.mockResolvedValue({ stdout: '{"invalid":json}' });122 123 const result = await getIdeProcessInfo();124 expect(result).toEqual({ pid: 1000, command: '' });125 });126 127 it('should return last ancestor if chain is too short', async () => {128 (os.platform as Mock).mockReturnValue('win32');129 130 const processes = [131 {132 ProcessId: 1000,133 ParentProcessId: 900,134 Name: 'node.exe',135 CommandLine: 'node.exe',136 },137 {138 ProcessId: 900,139 ParentProcessId: 0,140 Name: 'explorer.exe',141 CommandLine: 'explorer.exe',142 },143 ];144 145 mockedExec.mockImplementation((file: string, _args: string[]) => {146 if (file === 'powershell') {147 return Promise.resolve({ stdout: JSON.stringify(processes) });148 }149 return Promise.resolve({ stdout: '' });150 });151 152 const result = await getIdeProcessInfo();153 // ancestors = [1000, 900], length = 2 (< 3)154 // Heuristic: return ancestors[length-1] = ancestors[1] = 900 (explorer.exe)155 expect(result).toEqual({ pid: 900, command: 'explorer.exe' });156 });157 });158});159 