Hotfix for issue #7730 (#7739)

Co-authored-by: Taylor Mullen <ntaylormullen@google.com>
Co-authored-by: Arya Gummadi <aryagummadi@google.com>
This commit is contained in:
Shreya Keshive
2025-09-04 12:46:02 -07:00
committed by GitHub
parent 76553622f6
commit 2fb14ead1f
4 changed files with 139 additions and 14 deletions

View File

@@ -83,7 +83,7 @@ vi.mock('@google/gemini-cli-core', async () => {
return {
...actualServer,
IdeClient: {
getInstance: vi.fn().mockReturnValue({
getInstance: vi.fn().mockResolvedValue({
getConnectionStatus: vi.fn(),
initialize: vi.fn(),
shutdown: vi.fn(),

View File

@@ -100,6 +100,16 @@ vi.mock('../services/gitService.js', () => {
return { GitService: GitServiceMock };
});
vi.mock('../ide/ide-client.js', () => ({
IdeClient: {
getInstance: vi.fn().mockResolvedValue({
getConnectionStatus: vi.fn(),
initialize: vi.fn(),
shutdown: vi.fn(),
}),
},
}));
describe('Server Config (config.ts)', () => {
const MODEL = 'gemini-pro';
const SANDBOX: SandboxConfig = {

View File

@@ -66,13 +66,34 @@ describe('getIdeProcessInfo', () => {
it('should traverse up and find the great-grandchild of the root process', async () => {
(os.platform as Mock).mockReturnValue('win32');
const processInfoMap = new Map([
[1000, { stdout: 'ParentProcessId=900\r\nCommandLine=node.exe\r\n' }],
[
1000,
{
stdout:
'{"Name":"node.exe","ParentProcessId":900,"CommandLine":"node.exe"}',
},
],
[
900,
{ stdout: 'ParentProcessId=800\r\nCommandLine=powershell.exe\r\n' },
{
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"}',
},
],
[800, { stdout: 'ParentProcessId=700\r\nCommandLine=code.exe\r\n' }],
[700, { stdout: 'ParentProcessId=0\r\nCommandLine=wininit.exe\r\n' }],
]);
mockedExec.mockImplementation((command: string) => {
const pidMatch = command.match(/ProcessId=(\d+)/);
@@ -86,5 +107,90 @@ describe('getIdeProcessInfo', () => {
const result = await getIdeProcessInfo();
expect(result).toEqual({ pid: 900, command: 'powershell.exe' });
});
it('should handle non-existent process gracefully', async () => {
(os.platform as Mock).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 as Mock).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 as Mock).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: string) => {
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 as Mock).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' });
});
});
});

View File

@@ -26,15 +26,24 @@ async function getProcessInfo(pid: number): Promise<{
}> {
const platform = os.platform();
if (platform === 'win32') {
const command = `wmic process where "ProcessId=${pid}" get Name,ParentProcessId,CommandLine /value`;
const { stdout } = await execAsync(command);
const nameMatch = stdout.match(/Name=([^\n]*)/);
const processName = nameMatch ? nameMatch[1].trim() : '';
const ppidMatch = stdout.match(/ParentProcessId=(\d+)/);
const parentPid = ppidMatch ? parseInt(ppidMatch[1], 10) : 0;
const commandLineMatch = stdout.match(/CommandLine=([^\n]*)/);
const commandLine = commandLineMatch ? commandLineMatch[1].trim() : '';
return { parentPid, name: processName, command: commandLine };
const powershellCommand = [
'$p = Get-CimInstance Win32_Process',
`-Filter 'ProcessId=${pid}'`,
'-ErrorAction SilentlyContinue;',
'if ($p) {',
'@{Name=$p.Name;ParentProcessId=$p.ParentProcessId;CommandLine=$p.CommandLine}',
'| ConvertTo-Json',
'}',
].join(' ');
const { stdout } = await execAsync(`powershell "${powershellCommand}"`);
const output = stdout.trim();
if (!output) return { parentPid: 0, name: '', command: '' };
const {
Name = '',
ParentProcessId = 0,
CommandLine = '',
} = JSON.parse(output);
return { parentPid: ParentProcessId, name: Name, command: CommandLine };
} else {
const command = `ps -o ppid=,command= -p ${pid}`;
const { stdout } = await execAsync(command);