@@ -8,11 +8,14 @@ import { getOllamaModels } from "../fetchers/ollama"
88
99// Mock the ollama package
1010const mockChat = vitest . fn ( )
11+ const mockAbort = vitest . fn ( )
1112vitest . mock ( "ollama" , ( ) => {
1213 return {
13- Ollama : vitest . fn ( ) . mockImplementation ( function ( ) {
14+ Ollama : vitest . fn ( ) . mockImplementation ( function ( options ?: any ) {
1415 return {
1516 chat : mockChat ,
17+ abort : mockAbort ,
18+ _host : options ?. host ?? "http://localhost:11434" ,
1619 }
1720 } ) ,
1821 Message : vitest . fn ( ) ,
@@ -366,6 +369,78 @@ describe("NativeOllamaHandler", () => {
366369 const result = await handler . completePrompt ( "Test prompt" )
367370 expect ( result ) . toBe ( "Response" )
368371 } )
372+
373+ it ( "should call client.abort() when timeoutMs is reached" , async ( ) => {
374+ const testTimeout = 5000
375+ let capturedFn : ( ( ) => void ) | undefined
376+
377+ vitest . spyOn ( global , "setTimeout" ) . mockImplementation ( ( fn : any , ms ?: number ) => {
378+ if ( ms === testTimeout ) {
379+ capturedFn = fn as ( ) => void
380+ return 0 as any
381+ }
382+ return 0 as any
383+ } )
384+
385+ mockChat . mockResolvedValue ( {
386+ message : { content : "Response" } ,
387+ } )
388+
389+ await handler . completePrompt ( "Test prompt" , { timeoutMs : testTimeout } )
390+
391+ expect ( capturedFn ) . toBeDefined ( )
392+ if ( capturedFn ) capturedFn ( )
393+ expect ( mockAbort ) . toHaveBeenCalledTimes ( 1 )
394+ } )
395+
396+ it ( "should call client.abort() when abortSignal is aborted" , async ( ) => {
397+ const controller = new AbortController ( )
398+ mockChat . mockResolvedValue ( {
399+ message : { content : "Response" } ,
400+ } )
401+
402+ const promise = handler . completePrompt ( "Test prompt" , { abortSignal : controller . signal } )
403+ controller . abort ( )
404+ await promise
405+
406+ expect ( mockAbort ) . toHaveBeenCalledTimes ( 1 )
407+ } )
408+
409+ it ( "should call client.abort() immediately when abortSignal is already aborted" , async ( ) => {
410+ const controller = new AbortController ( )
411+ controller . abort ( )
412+
413+ mockChat . mockResolvedValue ( {
414+ message : { content : "Response" } ,
415+ } )
416+
417+ await handler . completePrompt ( "Test prompt" , { abortSignal : controller . signal } )
418+
419+ expect ( mockAbort ) . toHaveBeenCalledTimes ( 1 )
420+ } )
421+
422+ it ( "should clear timeoutId in finally block on success" , async ( ) => {
423+ let capturedDelay : number | undefined
424+
425+ vitest . spyOn ( global , "setTimeout" ) . mockImplementation ( ( fn : any , ms ?: number ) => {
426+ if ( ms === 5000 ) {
427+ capturedDelay = ms
428+ return 1 as any // Return truthy value so timeoutId is set
429+ }
430+ return 0 as any
431+ } )
432+
433+ vitest . spyOn ( global , "clearTimeout" ) . mockImplementation ( ( ) => { } )
434+
435+ mockChat . mockResolvedValue ( {
436+ message : { content : "Response" } ,
437+ } )
438+
439+ await handler . completePrompt ( "Test prompt" , { timeoutMs : 5000 } )
440+
441+ // setTimeout should have been called with the correct delay
442+ expect ( capturedDelay ) . toBe ( 5000 )
443+ } )
369444 } )
370445
371446 describe ( "error handling" , ( ) => {
0 commit comments