@@ -16,7 +16,10 @@ vi.mock("vscode", () => ({
1616// Mock execa to test stdin behavior
1717const mockExeca = vi . fn ( )
1818const mockStdin = {
19- write : vi . fn ( ) ,
19+ write : vi . fn ( ( data , encoding , callback ) => {
20+ // Simulate successful write
21+ if ( callback ) callback ( null )
22+ } ) ,
2023 end : vi . fn ( ) ,
2124}
2225
@@ -87,6 +90,15 @@ describe("runClaudeCode", () => {
8790 beforeEach ( ( ) => {
8891 vi . clearAllMocks ( )
8992 mockExeca . mockReturnValue ( createMockProcess ( ) )
93+ // Mock setImmediate to run synchronously in tests
94+ vi . spyOn ( global , "setImmediate" ) . mockImplementation ( ( callback : any ) => {
95+ callback ( )
96+ return { } as any
97+ } )
98+ } )
99+
100+ afterEach ( ( ) => {
101+ vi . restoreAllMocks ( )
90102 } )
91103
92104 test ( "should export runClaudeCode function" , async ( ) => {
@@ -150,8 +162,8 @@ describe("runClaudeCode", () => {
150162 const [ , args ] = mockExeca . mock . calls [ 0 ]
151163 expect ( args ) . not . toContain ( JSON . stringify ( messages ) )
152164
153- // Verify messages were written to stdin
154- expect ( mockStdin . write ) . toHaveBeenCalledWith ( JSON . stringify ( messages ) , "utf8" )
165+ // Verify messages were written to stdin with callback
166+ expect ( mockStdin . write ) . toHaveBeenCalledWith ( JSON . stringify ( messages ) , "utf8" , expect . any ( Function ) )
155167 expect ( mockStdin . end ) . toHaveBeenCalled ( )
156168
157169 // Verify we got the expected mock output
@@ -200,4 +212,81 @@ describe("runClaudeCode", () => {
200212 const [ claudePath ] = mockExeca . mock . calls [ 0 ]
201213 expect ( claudePath ) . toBe ( "/custom/path/to/claude" )
202214 } )
215+
216+ test ( "should handle stdin write errors gracefully" , async ( ) => {
217+ const { runClaudeCode } = await import ( "../run" )
218+
219+ // Create a mock process with stdin that fails
220+ const mockProcessWithError = createMockProcess ( )
221+ mockProcessWithError . stdin . write = vi . fn ( ( data , encoding , callback ) => {
222+ // Simulate write error
223+ if ( callback ) callback ( new Error ( "EPIPE: broken pipe" ) )
224+ } )
225+
226+ // Mock console.error to verify error logging
227+ const consoleErrorSpy = vi . spyOn ( console , "error" ) . mockImplementation ( ( ) => { } )
228+
229+ mockExeca . mockReturnValueOnce ( mockProcessWithError )
230+
231+ const options = {
232+ systemPrompt : "You are a helpful assistant" ,
233+ messages : [ { role : "user" as const , content : "Hello" } ] ,
234+ }
235+
236+ const generator = runClaudeCode ( options )
237+
238+ // Try to consume the generator
239+ try {
240+ await generator . next ( )
241+ } catch ( error ) {
242+ // Expected to fail
243+ }
244+
245+ // Verify error was logged
246+ expect ( consoleErrorSpy ) . toHaveBeenCalledWith ( "Error writing to Claude Code stdin:" , expect . any ( Error ) )
247+
248+ // Verify process was killed
249+ expect ( mockProcessWithError . kill ) . toHaveBeenCalled ( )
250+
251+ // Clean up
252+ consoleErrorSpy . mockRestore ( )
253+ await generator . return ( undefined )
254+ } )
255+
256+ test ( "should handle stdin access errors gracefully" , async ( ) => {
257+ const { runClaudeCode } = await import ( "../run" )
258+
259+ // Create a mock process without stdin
260+ const mockProcessWithoutStdin = createMockProcess ( )
261+ mockProcessWithoutStdin . stdin = null as any
262+
263+ // Mock console.error to verify error logging
264+ const consoleErrorSpy = vi . spyOn ( console , "error" ) . mockImplementation ( ( ) => { } )
265+
266+ mockExeca . mockReturnValueOnce ( mockProcessWithoutStdin )
267+
268+ const options = {
269+ systemPrompt : "You are a helpful assistant" ,
270+ messages : [ { role : "user" as const , content : "Hello" } ] ,
271+ }
272+
273+ const generator = runClaudeCode ( options )
274+
275+ // Try to consume the generator
276+ try {
277+ await generator . next ( )
278+ } catch ( error ) {
279+ // Expected to fail
280+ }
281+
282+ // Verify error was logged
283+ expect ( consoleErrorSpy ) . toHaveBeenCalledWith ( "Error accessing Claude Code stdin:" , expect . any ( Error ) )
284+
285+ // Verify process was killed
286+ expect ( mockProcessWithoutStdin . kill ) . toHaveBeenCalled ( )
287+
288+ // Clean up
289+ consoleErrorSpy . mockRestore ( )
290+ await generator . return ( undefined )
291+ } )
203292} )
0 commit comments