From c6f6fb00c9b51e222c03a488d0434ca8f2109582 Mon Sep 17 00:00:00 2001 From: Cynthia Long Date: Mon, 16 Mar 2026 21:30:16 +0000 Subject: [PATCH] add unit test for console patcher & noninteractive --- packages/cli/src/nonInteractiveCli.test.ts | 24 ++++ .../cli/src/ui/utils/ConsolePatcher.test.ts | 120 ++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 packages/cli/src/ui/utils/ConsolePatcher.test.ts diff --git a/packages/cli/src/nonInteractiveCli.test.ts b/packages/cli/src/nonInteractiveCli.test.ts index fb3095a04c..68667951ac 100644 --- a/packages/cli/src/nonInteractiveCli.test.ts +++ b/packages/cli/src/nonInteractiveCli.test.ts @@ -235,6 +235,30 @@ describe('runNonInteractive', () => { const getWrittenOutput = () => processStdoutSpy.mock.calls.map((c) => c[0]).join(''); + it('should initialize ConsolePatcher with correct value', async () => { + const { ConsolePatcher } = await import('./ui/utils/ConsolePatcher.js'); + const patcherSpy = vi.spyOn(ConsolePatcher.prototype, 'patch'); + + const events: ServerGeminiStreamEvent[] = [ + { + type: GeminiEventType.DONE, + metrics: { ...MOCK_SESSION_METRICS }, + }, + ]; + mockGeminiClient.sendMessageStream.mockReturnValueOnce( + createStreamFromEvents(events), + ); + + await runNonInteractive({ + config: mockConfig, + settings: mockSettings, + input: 'test input', + prompt_id: 'test-prompt-id', + }); + + expect(patcherSpy).toHaveBeenCalled(); + }); + it('should process input and write text output', async () => { const events: ServerGeminiStreamEvent[] = [ { type: GeminiEventType.Content, value: 'Hello' }, diff --git a/packages/cli/src/ui/utils/ConsolePatcher.test.ts b/packages/cli/src/ui/utils/ConsolePatcher.test.ts new file mode 100644 index 0000000000..308c3401c5 --- /dev/null +++ b/packages/cli/src/ui/utils/ConsolePatcher.test.ts @@ -0,0 +1,120 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { ConsolePatcher } from './ConsolePatcher.js'; + +describe('ConsolePatcher', () => { + let patcher: ConsolePatcher; + const onNewMessage = vi.fn(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + afterEach(() => { + if (patcher) { + patcher.cleanup(); + } + }); + + it('should suppress output when suppressConsoleOutput is true and debugMode is false', () => { + // We need to spy on the original console methods that ConsolePatcher will call + // ConsolePatcher captures the original methods at construction time or patch time? + // It captures them at construction time. + + const originalLog = console.log; + const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); + + patcher = new ConsolePatcher({ + debugMode: false, + suppressConsoleOutput: true, + stderr: true, + }); + patcher.patch(); + + console.log('test log'); + + expect(logSpy).not.toHaveBeenCalled(); + + patcher.cleanup(); + logSpy.mockRestore(); + console.log = originalLog; + }); + + it('should NOT suppress output when suppressConsoleOutput is true but debugMode is true', () => { + const originalError = console.error; + const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + + patcher = new ConsolePatcher({ + debugMode: true, + suppressConsoleOutput: true, + stderr: true, + }); + patcher.patch(); + + console.log('test log'); + // When stderr is true, log goes to originalConsoleError + expect(errorSpy).toHaveBeenCalled(); + + patcher.cleanup(); + errorSpy.mockRestore(); + console.error = originalError; + }); + + it('should NOT suppress output when suppressConsoleOutput is false', () => { + const originalError = console.error; + const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + + patcher = new ConsolePatcher({ + debugMode: false, + suppressConsoleOutput: false, + stderr: true, + }); + patcher.patch(); + + console.error('test error'); + expect(errorSpy).toHaveBeenCalled(); + + patcher.cleanup(); + errorSpy.mockRestore(); + console.error = originalError; + }); + + it('should call onNewMessage when stderr is false and not suppressed', () => { + patcher = new ConsolePatcher({ + debugMode: false, + suppressConsoleOutput: false, + stderr: false, + onNewMessage, + }); + patcher.patch(); + + console.log('test log'); + expect(onNewMessage).toHaveBeenCalledWith(expect.objectContaining({ + type: 'log', + content: 'test log', + })); + + patcher.cleanup(); + }); + + it('should NOT suppress output when suppressConsoleOutput is true but debugMode is true (explicit check)', () => { + const onNewMessage = vi.fn(); + patcher = new ConsolePatcher({ + debugMode: true, + suppressConsoleOutput: true, + stderr: false, + onNewMessage, + }); + patcher.patch(); + + console.log('test log'); + + expect(onNewMessage).toHaveBeenCalled(); + patcher.cleanup(); + }); +});