From d9422509057d4045c4c100001275a48b2e16219e Mon Sep 17 00:00:00 2001 From: xuewenjie Date: Fri, 19 Dec 2025 13:24:19 +0800 Subject: [PATCH] test: sync test files with code changes for IDE detection - Update detect-ide.test.ts to remove ideProcessInfo parameter (now optional) - Update process-utils.test.ts to match simplified Windows process detection logic - Remove tests for removed IDE detection strategies (Strategy 1-4) - All tests now passing (13 tests in detect-ide.test.ts, 6 tests in process-utils.test.ts) --- packages/core/src/ide/detect-ide.test.ts | 43 +++---- packages/core/src/ide/process-utils.test.ts | 117 ++++++-------------- 2 files changed, 51 insertions(+), 109 deletions(-) diff --git a/packages/core/src/ide/detect-ide.test.ts b/packages/core/src/ide/detect-ide.test.ts index 9bf1eb12..81ccdce5 100644 --- a/packages/core/src/ide/detect-ide.test.ts +++ b/packages/core/src/ide/detect-ide.test.ts @@ -8,9 +8,6 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { detectIde, IDE_DEFINITIONS } from './detect-ide.js'; describe('detectIde', () => { - const ideProcessInfo = { pid: 123, command: 'some/path/to/code' }; - const ideProcessInfoNoCode = { pid: 123, command: 'some/path/to/fork' }; - // Clear all IDE-related environment variables before each test beforeEach(() => { vi.stubEnv('__COG_BASHRC_SOURCED', ''); @@ -29,73 +26,65 @@ describe('detectIde', () => { it('should return undefined if TERM_PROGRAM is not vscode', () => { vi.stubEnv('TERM_PROGRAM', ''); - expect(detectIde(ideProcessInfo)).toBeUndefined(); + expect(detectIde()).toBeUndefined(); }); it('should detect Devin', () => { vi.stubEnv('TERM_PROGRAM', 'vscode'); vi.stubEnv('__COG_BASHRC_SOURCED', '1'); - expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.devin); + expect(detectIde()).toBe(IDE_DEFINITIONS.devin); }); it('should detect Replit', () => { vi.stubEnv('TERM_PROGRAM', 'vscode'); vi.stubEnv('REPLIT_USER', 'testuser'); - expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.replit); + expect(detectIde()).toBe(IDE_DEFINITIONS.replit); }); it('should detect Cursor', () => { vi.stubEnv('TERM_PROGRAM', 'vscode'); vi.stubEnv('CURSOR_TRACE_ID', 'some-id'); - expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.cursor); + expect(detectIde()).toBe(IDE_DEFINITIONS.cursor); }); it('should detect Codespaces', () => { vi.stubEnv('TERM_PROGRAM', 'vscode'); vi.stubEnv('CODESPACES', 'true'); - expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.codespaces); + expect(detectIde()).toBe(IDE_DEFINITIONS.codespaces); }); it('should detect Cloud Shell via EDITOR_IN_CLOUD_SHELL', () => { vi.stubEnv('TERM_PROGRAM', 'vscode'); vi.stubEnv('EDITOR_IN_CLOUD_SHELL', 'true'); - expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.cloudshell); + expect(detectIde()).toBe(IDE_DEFINITIONS.cloudshell); }); it('should detect Cloud Shell via CLOUD_SHELL', () => { vi.stubEnv('TERM_PROGRAM', 'vscode'); vi.stubEnv('CLOUD_SHELL', 'true'); - expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.cloudshell); + expect(detectIde()).toBe(IDE_DEFINITIONS.cloudshell); }); it('should detect Trae', () => { vi.stubEnv('TERM_PROGRAM', 'vscode'); vi.stubEnv('TERM_PRODUCT', 'Trae'); - expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.trae); + expect(detectIde()).toBe(IDE_DEFINITIONS.trae); }); it('should detect Firebase Studio via MONOSPACE_ENV', () => { vi.stubEnv('TERM_PROGRAM', 'vscode'); vi.stubEnv('MONOSPACE_ENV', 'true'); - expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.firebasestudio); + expect(detectIde()).toBe(IDE_DEFINITIONS.firebasestudio); }); - it('should detect VSCode when no other IDE is detected and command includes "code"', () => { + it('should detect VSCodeFork when no other IDE is detected and no process info provided', () => { vi.stubEnv('TERM_PROGRAM', 'vscode'); vi.stubEnv('MONOSPACE_ENV', ''); - expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.vscode); - }); - - it('should detect VSCodeFork when no other IDE is detected and command does not include "code"', () => { - vi.stubEnv('TERM_PROGRAM', 'vscode'); - vi.stubEnv('MONOSPACE_ENV', ''); - expect(detectIde(ideProcessInfoNoCode)).toBe(IDE_DEFINITIONS.vscodefork); + expect(detectIde()).toBe(IDE_DEFINITIONS.vscodefork); }); }); describe('detectIde with ideInfoFromFile', () => { - const ideProcessInfo = { pid: 123, command: 'some/path/to/code' }; - beforeEach(() => { vi.stubEnv('__COG_BASHRC_SOURCED', ''); vi.stubEnv('REPLIT_USER', ''); @@ -116,22 +105,22 @@ describe('detectIde with ideInfoFromFile', () => { name: 'custom-ide', displayName: 'Custom IDE', }; - expect(detectIde(ideProcessInfo, ideInfoFromFile)).toEqual(ideInfoFromFile); + expect(detectIde(undefined, ideInfoFromFile)).toEqual(ideInfoFromFile); }); it('should fall back to env detection if name is missing', () => { const ideInfoFromFile = { displayName: 'Custom IDE' }; vi.stubEnv('TERM_PROGRAM', 'vscode'); - expect(detectIde(ideProcessInfo, ideInfoFromFile)).toBe( - IDE_DEFINITIONS.vscode, + expect(detectIde(undefined, ideInfoFromFile)).toBe( + IDE_DEFINITIONS.vscodefork, ); }); it('should fall back to env detection if displayName is missing', () => { const ideInfoFromFile = { name: 'custom-ide' }; vi.stubEnv('TERM_PROGRAM', 'vscode'); - expect(detectIde(ideProcessInfo, ideInfoFromFile)).toBe( - IDE_DEFINITIONS.vscode, + expect(detectIde(undefined, ideInfoFromFile)).toBe( + IDE_DEFINITIONS.vscodefork, ); }); }); diff --git a/packages/core/src/ide/process-utils.test.ts b/packages/core/src/ide/process-utils.test.ts index 88433b00..a049406d 100644 --- a/packages/core/src/ide/process-utils.test.ts +++ b/packages/core/src/ide/process-utils.test.ts @@ -63,7 +63,7 @@ describe('getIdeProcessInfo', () => { }); describe('on Windows', () => { - it('should find known IDE process in the chain', async () => { + it('should return great-grandparent process using heuristic', async () => { (os.platform as Mock).mockReturnValue('win32'); const processes = [ @@ -101,88 +101,10 @@ describe('getIdeProcessInfo', () => { }); const result = await getIdeProcessInfo(); - // Strategy 1: Should find code.exe (known IDE process) in the chain // Process chain: 1000 (node.exe) -> 900 (powershell.exe) -> 800 (code.exe) -> 700 (wininit.exe) - // The function should identify code.exe as the IDE process - expect(result).toEqual({ pid: 800, command: 'code.exe' }); - }); - - it('should find shell parent when shell exists in chain', async () => { - (os.platform as Mock).mockReturnValue('win32'); - - const processes = [ - { - ProcessId: 1000, - ParentProcessId: 900, - Name: 'node.exe', - CommandLine: 'node.exe', - }, - { - ProcessId: 900, - ParentProcessId: 800, - Name: 'cmd.exe', - CommandLine: 'cmd.exe', - }, - { - ProcessId: 800, - ParentProcessId: 700, - Name: 'explorer.exe', - CommandLine: 'explorer.exe', - }, - { - ProcessId: 700, - ParentProcessId: 0, - Name: 'wininit.exe', - CommandLine: 'wininit.exe', - }, - ]; - - mockedExec.mockImplementation((file: string, _args: string[]) => { - if (file === 'powershell') { - return Promise.resolve({ stdout: JSON.stringify(processes) }); - } - return Promise.resolve({ stdout: '' }); - }); - - const result = await getIdeProcessInfo(); - // Strategy 3: Should find cmd.exe and return its parent (explorer.exe) - expect(result).toEqual({ pid: 800, command: 'explorer.exe' }); - }); - - it('should handle Git Bash with missing parent by finding IDE in process table', async () => { - (os.platform as Mock).mockReturnValue('win32'); - - const processes = [ - { - ProcessId: 1000, - ParentProcessId: 900, - Name: 'node.exe', - CommandLine: 'node.exe', - }, - { - ProcessId: 900, - ParentProcessId: 12345, // Parent doesn't exist in process table - Name: 'bash.exe', - CommandLine: 'bash.exe', - }, - { - ProcessId: 800, - ParentProcessId: 0, - Name: 'code.exe', - CommandLine: 'code.exe', - }, - ]; - - mockedExec.mockImplementation((file: string, _args: string[]) => { - if (file === 'powershell') { - return Promise.resolve({ stdout: JSON.stringify(processes) }); - } - return Promise.resolve({ stdout: '' }); - }); - - const result = await getIdeProcessInfo(); - // Strategy 2: Git Bash with missing parent should find code.exe in process table - expect(result).toEqual({ pid: 800, command: 'code.exe' }); + // ancestors = [1000, 900, 800, 700], length = 4 + // Heuristic: return ancestors[length-3] = ancestors[1] = 900 (powershell.exe) + expect(result).toEqual({ pid: 900, command: 'powershell.exe' }); }); it('should handle empty process list gracefully', async () => { @@ -201,5 +123,36 @@ describe('getIdeProcessInfo', () => { const result = await getIdeProcessInfo(); expect(result).toEqual({ pid: 1000, command: '' }); }); + + it('should return last ancestor if chain is too short', async () => { + (os.platform as Mock).mockReturnValue('win32'); + + const processes = [ + { + ProcessId: 1000, + ParentProcessId: 900, + Name: 'node.exe', + CommandLine: 'node.exe', + }, + { + ProcessId: 900, + ParentProcessId: 0, + Name: 'explorer.exe', + CommandLine: 'explorer.exe', + }, + ]; + + mockedExec.mockImplementation((file: string, _args: string[]) => { + if (file === 'powershell') { + return Promise.resolve({ stdout: JSON.stringify(processes) }); + } + return Promise.resolve({ stdout: '' }); + }); + + const result = await getIdeProcessInfo(); + // ancestors = [1000, 900], length = 2 (< 3) + // Heuristic: return ancestors[length-1] = ancestors[1] = 900 (explorer.exe) + expect(result).toEqual({ pid: 900, command: 'explorer.exe' }); + }); }); });