@@ -68,6 +68,7 @@ import {
6868 readLiveDocument ,
6969 searchLiveKnowledge ,
7070} from '@/lib/sim-search/live/application'
71+ import { readDrive } from '@/lib/sim-search/live/google'
7172import { NativeSearchError } from '@/lib/sim-search/live/http'
7273import { createPolicyVerifier } from '@/lib/sim-search/live/policy'
7374import { defaultLiveSearchPolicy } from '@/lib/sim-search/live/policy-schema'
@@ -432,6 +433,62 @@ describe('authorized live retrieval', () => {
432433 ) . rejects . toThrow ( 'Revoked' )
433434 expect ( mocks . read ) . not . toHaveBeenCalled ( )
434435 } )
436+ it . each ( [
437+ [ 'a stop reason' , 'user_stop:test' ] ,
438+ [ 'an AbortError' , new DOMException ( 'The operation was aborted.' , 'AbortError' ) ] ,
439+ [ 'a TimeoutError' , new DOMException ( 'The operation timed out.' , 'TimeoutError' ) ] ,
440+ ] ) (
441+ 'rejects a read cancelled with %s instead of returning partial discussion coverage' ,
442+ async ( _ , reason ) => {
443+ const search = await searchLiveKnowledge . execute ( { principal, input } )
444+ const controller = new AbortController ( )
445+ mocks . read . mockImplementation ( ( _provider , client ) => readDrive ( client , 'doc' ) )
446+ mocks . json . mockImplementation ( async ( path : string ) => {
447+ if ( ! path . endsWith ( '/comments' ) )
448+ return {
449+ id : 'doc' ,
450+ name : 'Launch' ,
451+ mimeType : 'application/pdf' ,
452+ webViewLink : document . url ,
453+ }
454+ controller . abort ( reason )
455+ throw controller . signal . reason
456+ } )
457+ await expect (
458+ readLiveDocument . execute ( {
459+ principal,
460+ input : {
461+ workspaceId : 'workspace' ,
462+ documentId : search . results [ 0 ] ! . documentId ,
463+ limit : 1 ,
464+ resultSecretRegistry : new ResolvedSecretTraceRegistry ( [ ] ) ,
465+ signal : controller . signal ,
466+ } ,
467+ } )
468+ ) . rejects . toBe ( reason )
469+ }
470+ )
471+ it ( 'rejects a read cancelled while its current scope was being verified' , async ( ) => {
472+ const search = await searchLiveKnowledge . execute ( { principal, input } )
473+ const controller = new AbortController ( )
474+ mocks . service . mockResolvedValueOnce ( undefined )
475+ mocks . service . mockImplementationOnce ( async ( ) => {
476+ controller . abort ( 'user_stop:test' )
477+ return { policy : defaultLiveSearchPolicy ( ) , verify : async ( ) => true , partial : false }
478+ } )
479+ await expect (
480+ readLiveDocument . execute ( {
481+ principal,
482+ input : {
483+ workspaceId : 'workspace' ,
484+ documentId : search . results [ 0 ] ! . documentId ,
485+ limit : 1 ,
486+ resultSecretRegistry : new ResolvedSecretTraceRegistry ( [ ] ) ,
487+ signal : controller . signal ,
488+ } ,
489+ } )
490+ ) . rejects . toBe ( 'user_stop:test' )
491+ } )
435492 it ( 'rejects cross-user document references before token resolution' , async ( ) => {
436493 const search = await searchLiveKnowledge . execute ( { principal, input } )
437494 const reference = decodeLiveReference ( search . results [ 0 ] . documentId )
0 commit comments