Download packages/cli/src/utils/readStdin_safety.test.ts from SaylorTwift/gemini-cli: direct link, hf CLI and curl.
- Browser
- Download file 2.54 kB
-
https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/cli/src/utils/readStdin_safety.test.ts
- Command line
-
hf download hf://SaylorTwift/gemini-cli/packages/cli/src/utils/readStdin_safety.test.ts
-
curl -L -o readStdin_safety.test.ts https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/cli/src/utils/readStdin_safety.test.ts
2.54 kB
| /** | |
| * @license | |
| * Copyright 2025 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import { vi, describe, expect, it, beforeEach, afterEach } from 'vitest'; | |
| import { readStdin } from './readStdin.js'; | |
| import { EventEmitter } from 'node:events'; | |
| // Mock debugLogger to avoid clutter | |
| vi.mock('@google/gemini-cli-core', () => ({ | |
| debugLogger: { | |
| warn: vi.fn(), | |
| }, | |
| })); | |
| describe('readStdin EIO Reproduction', () => { | |
| let originalStdin: typeof process.stdin; | |
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | |
| let fakeStdin: EventEmitter & { setEncoding: any; read: any; destroy: any }; | |
| beforeEach(() => { | |
| originalStdin = process.stdin; | |
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | |
| fakeStdin = new EventEmitter() as any; | |
| fakeStdin.setEncoding = vi.fn(); | |
| fakeStdin.read = vi.fn().mockReturnValue(null); // Return null to simulate end of reading or no data | |
| fakeStdin.destroy = vi.fn(); | |
| Object.defineProperty(process, 'stdin', { | |
| value: fakeStdin, | |
| writable: true, | |
| configurable: true, | |
| }); | |
| }); | |
| afterEach(() => { | |
| Object.defineProperty(process, 'stdin', { | |
| value: originalStdin, | |
| writable: true, | |
| configurable: true, | |
| }); | |
| vi.restoreAllMocks(); | |
| }); | |
| it('crashes (throws unhandled error) if EIO happens after readStdin completes', async () => { | |
| const promise = readStdin(); | |
| fakeStdin.emit('end'); | |
| await promise; | |
| // Verify listeners are removed (implementation detail check) | |
| // We expect 1 listener now (our no-op handler) because we started with 0. | |
| expect(fakeStdin.listenerCount('error')).toBe(1); | |
| // This mimics the crash. | |
| // We expect this NOT to throw now that we've added a no-op handler. | |
| expect(() => { | |
| fakeStdin.emit('error', new Error('EIO')); | |
| }).not.toThrow(); | |
| }); | |
| it('does NOT add a no-op handler if another error listener is present', async () => { | |
| const customErrorHandler = vi.fn(); | |
| fakeStdin.on('error', customErrorHandler); | |
| const promise = readStdin(); | |
| fakeStdin.emit('end'); | |
| await promise; | |
| // It should have exactly 1 listener (our custom one), not 2. | |
| expect(fakeStdin.listenerCount('error')).toBe(1); | |
| expect(fakeStdin.listeners('error')).toContain(customErrorHandler); | |
| // Triggering error should call our handler and NOT crash (because there is a listener) | |
| const error = new Error('EIO'); | |
| fakeStdin.emit('error', error); | |
| expect(customErrorHandler).toHaveBeenCalledWith(error); | |
| }); | |
| }); | |