@@ -148,6 +148,27 @@ const createJsonResponse = (payload: unknown, status = 200) =>
148148 new Response ( JSON . stringify ( payload ) , { status } ) ;
149149const createGraphCollectionResponse = ( value : unknown [ ] ) => createJsonResponse ( { value } ) ;
150150const createNotFoundResponse = ( ) => new Response ( "not found" , { status : 404 } ) ;
151+ function cancelTrackedResponse (
152+ text : string ,
153+ init : ResponseInit ,
154+ ) : {
155+ response : Response ;
156+ wasCanceled : ( ) => boolean ;
157+ } {
158+ let canceled = false ;
159+ const stream = new ReadableStream < Uint8Array > ( {
160+ start ( controller ) {
161+ controller . enqueue ( new TextEncoder ( ) . encode ( text ) ) ;
162+ } ,
163+ cancel ( ) {
164+ canceled = true ;
165+ } ,
166+ } ) ;
167+ return {
168+ response : new Response ( stream , init ) ,
169+ wasCanceled : ( ) => canceled ,
170+ } ;
171+ }
151172const createRedirectResponse = ( location : string , status = 302 ) =>
152173 new Response ( null , { status, headers : { location } } ) ;
153174const asFetchFn = ( fetchFn : unknown ) : FetchFn => fetchFn as FetchFn ;
@@ -324,6 +345,32 @@ describe("msteams graph attachments", () => {
324345
325346 it . each < GraphMediaSuccessCase > ( GRAPH_MEDIA_SUCCESS_CASES ) ( "$label" , runGraphMediaSuccessCase ) ;
326347
348+ it ( "cancels non-OK Graph collection bodies before returning empty hosted content" , async ( ) => {
349+ const tracked = cancelTrackedResponse ( "missing hosted contents" , { status : 404 } ) ;
350+ const fetchMock = vi . fn ( async ( input : RequestInfo | URL ) => {
351+ const url = resolveRequestUrl ( input ) ;
352+ if ( url === DEFAULT_MESSAGE_URL ) {
353+ return createJsonResponse ( { attachments : [ ] } ) ;
354+ }
355+ if ( url === `${ DEFAULT_MESSAGE_URL } /hostedContents` ) {
356+ return tracked . response ;
357+ }
358+ return createNotFoundResponse ( ) ;
359+ } ) ;
360+
361+ const media = await downloadMSTeamsGraphMedia ( {
362+ messageUrl : DEFAULT_MESSAGE_URL ,
363+ tokenProvider : createTokenProvider ( ) ,
364+ maxBytes : DEFAULT_MAX_BYTES ,
365+ fetchFn : asFetchFn ( fetchMock ) ,
366+ resolveFn : resolvePublicHost ,
367+ } ) ;
368+
369+ expect ( media . media ) . toEqual ( [ ] ) ;
370+ expect ( media . hostedStatus ) . toBe ( 404 ) ;
371+ expect ( tracked . wasCanceled ( ) ) . toBe ( true ) ;
372+ } ) ;
373+
327374 it ( "does not forward Authorization for SharePoint redirects outside auth allowlist" , async ( ) => {
328375 const tokenProvider = createTokenProvider ( "top-secret-token" ) ;
329376 const escapedUrl = "https://example.com/collect" ;
0 commit comments