11import { toRecord } from '@sim/utils/object'
22import { describe , expect , it } from 'vitest'
33import { readHexMcp , searchHexMcp } from '@/lib/sim-search/live/hex-mcp'
4+ import { NativeSearchError } from '@/lib/sim-search/live/http'
45import type { ManagedSearchMcpClient } from '@/lib/sim-search/live/managed-mcp'
56
67const PROJECT = '00000000-0000-4000-8000-000000000001'
@@ -34,7 +35,13 @@ function cell(id: string, cellType = 'MARKDOWN', source = 'Reviewed deployment n
3435}
3536
3637function client (
37- options : { search ?: unknown ; workspace ?: string ; metadata ?: unknown [ ] ; pages ?: unknown [ ] } = { }
38+ options : {
39+ search ?: unknown
40+ workspace ?: string
41+ metadata ?: unknown [ ]
42+ pages ?: unknown [ ]
43+ projectFailure ?: Error
44+ } = { }
3845) {
3946 let page = 0
4047 let metadataRead = 0
@@ -57,6 +64,7 @@ function client(
5764 }
5865 )
5966 if ( name === 'get_project' ) {
67+ if ( args . projectId === OTHER && options . projectFailure ) throw options . projectFailure
6068 if ( args . projectId !== PROJECT ) throw new Error ( 'Unexpected project' )
6169 return options . metadata ?. [ metadataRead ++ ] ?? metadata
6270 }
@@ -107,6 +115,49 @@ describe('Hex source retrieval', () => {
107115 expect ( page . partial ) . toBe ( true )
108116 } )
109117
118+ it . each ( [
119+ new NativeSearchError (
120+ 'unavailable' ,
121+ 'Project no longer accessible' ,
122+ undefined ,
123+ undefined ,
124+ 'mcp_tool_error'
125+ ) ,
126+ new NativeSearchError ( 'reconnect' , 'Connection changed' ) ,
127+ new DOMException ( 'Search cancelled' , 'AbortError' ) ,
128+ new NativeSearchError ( 'rate_limited' , 'Rate limit reached' ) ,
129+ new NativeSearchError (
130+ 'unavailable' ,
131+ 'Request limit reached' ,
132+ undefined ,
133+ undefined ,
134+ 'request_budget_exceeded'
135+ ) ,
136+ ] ) (
137+ 'isolates project read errors while preserving connection and request failures: %s' ,
138+ async ( error ) => {
139+ const result = searchHexMcp (
140+ client ( {
141+ search : {
142+ orgId : WORKSPACE ,
143+ baseUrl : 'https://app.hex.tech' ,
144+ projects : [
145+ { projectId : OTHER , type : 'project' } ,
146+ { projectId : PROJECT , type : 'project' } ,
147+ ] ,
148+ } ,
149+ projectFailure : error ,
150+ } ) ,
151+ { query : 'deployment' , limit : 10 , scopes : [ ] }
152+ )
153+ if ( error instanceof NativeSearchError && error . diagnosticCode === 'mcp_tool_error' ) {
154+ const page = await result
155+ expect ( page . documents . map ( ( document ) => document . id ) ) . toEqual ( [ PROJECT ] )
156+ expect ( page . partial ) . toBe ( true )
157+ } else await expect ( result ) . rejects . toBe ( error )
158+ }
159+ )
160+
110161 it . each ( [ '' , '*' ] ) ( 'refuses unbounded search terms' , async ( query ) => {
111162 await expect ( searchHexMcp ( client ( ) , { query, limit : 10 , scopes : [ ] } ) ) . rejects . toThrow ( )
112163 } )
0 commit comments