diff options
Diffstat (limited to 'packages/cli/src/gemini.test.tsx')
| -rw-r--r-- | packages/cli/src/gemini.test.tsx | 66 |
1 files changed, 64 insertions, 2 deletions
diff --git a/packages/cli/src/gemini.test.tsx b/packages/cli/src/gemini.test.tsx index 20350815..505841c7 100644 --- a/packages/cli/src/gemini.test.tsx +++ b/packages/cli/src/gemini.test.tsx @@ -6,12 +6,13 @@ import stripAnsi from 'strip-ansi'; import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { main } from './gemini.js'; +import { main, setupUnhandledRejectionHandler } from './gemini.js'; import { LoadedSettings, SettingsFile, loadSettings, } from './config/settings.js'; +import { appEvents, AppEvent } from './utils/events.js'; // Custom error to identify mock process.exit calls class MockProcessExitError extends Error { @@ -55,6 +56,16 @@ vi.mock('update-notifier', () => ({ })), })); +vi.mock('./utils/events.js', async (importOriginal) => { + const actual = await importOriginal<typeof import('./utils/events.js')>(); + return { + ...actual, + appEvents: { + emit: vi.fn(), + }, + }; +}); + vi.mock('./utils/sandbox.js', () => ({ sandbox_command: vi.fn(() => ''), // Default to no sandbox command start_sandbox: vi.fn(() => Promise.resolve()), // Mock as an async function that resolves @@ -65,6 +76,8 @@ describe('gemini.tsx main function', () => { let loadSettingsMock: ReturnType<typeof vi.mocked<typeof loadSettings>>; let originalEnvGeminiSandbox: string | undefined; let originalEnvSandbox: string | undefined; + let initialUnhandledRejectionListeners: NodeJS.UnhandledRejectionListener[] = + []; const processExitSpy = vi .spyOn(process, 'exit') @@ -82,6 +95,8 @@ describe('gemini.tsx main function', () => { delete process.env.SANDBOX; consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + initialUnhandledRejectionListeners = + process.listeners('unhandledRejection'); }); afterEach(() => { @@ -96,6 +111,15 @@ describe('gemini.tsx main function', () => { } else { delete process.env.SANDBOX; } + + const currentListeners = process.listeners('unhandledRejection'); + const addedListener = currentListeners.find( + (listener) => !initialUnhandledRejectionListeners.includes(listener), + ); + + if (addedListener) { + process.removeListener('unhandledRejection', addedListener); + } vi.restoreAllMocks(); }); @@ -145,7 +169,45 @@ describe('gemini.tsx main function', () => { 'Please fix /test/settings.json and try again.', ); - // Verify process.exit was called (indirectly, via the thrown error) + // Verify process.exit was called. expect(processExitSpy).toHaveBeenCalledWith(1); }); + + it('should log unhandled promise rejections and open debug console on first error', async () => { + const appEventsMock = vi.mocked(appEvents); + const rejectionError = new Error('Test unhandled rejection'); + + setupUnhandledRejectionHandler(); + // Simulate an unhandled rejection. + // We are not using Promise.reject here as vitest will catch it. + // Instead we will dispatch the event manually. + process.emit('unhandledRejection', rejectionError, Promise.resolve()); + + // We need to wait for the rejection handler to be called. + await new Promise(process.nextTick); + + expect(appEventsMock.emit).toHaveBeenCalledWith(AppEvent.OpenDebugConsole); + expect(appEventsMock.emit).toHaveBeenCalledWith( + AppEvent.LogError, + expect.stringContaining('Unhandled Promise Rejection'), + ); + expect(appEventsMock.emit).toHaveBeenCalledWith( + AppEvent.LogError, + expect.stringContaining('Please file a bug report using the /bug tool.'), + ); + + // Simulate a second rejection + const secondRejectionError = new Error('Second test unhandled rejection'); + process.emit('unhandledRejection', secondRejectionError, Promise.resolve()); + await new Promise(process.nextTick); + + // Ensure emit was only called once for OpenDebugConsole + const openDebugConsoleCalls = appEventsMock.emit.mock.calls.filter( + (call) => call[0] === AppEvent.OpenDebugConsole, + ); + expect(openDebugConsoleCalls.length).toBe(1); + + // Avoid the process.exit error from being thrown. + processExitSpy.mockRestore(); + }); }); |
