@@ -212,6 +212,9 @@ const createMediaEntriesWithType = (contentType: string, ...paths: string[]) =>
212212 paths . map ( ( path ) => ( { path, contentType } ) ) ;
213213const createHostedContentsWithType = ( contentType : string , ...ids : string [ ] ) =>
214214 ids . map ( ( id ) => ( { id, contentType, contentBytes : PNG_BASE64 } ) ) ;
215+ /** Like createHostedContentsWithType but with contentBytes: null to exercise the $value fetch path. */
216+ const createHostedContentsNullBytes = ( contentType : string , ...ids : string [ ] ) =>
217+ ids . map ( ( id ) => ( { id, contentType, contentBytes : null } ) ) ;
215218const createImageMediaEntries = ( ...paths : string [ ] ) =>
216219 createMediaEntriesWithType ( CONTENT_TYPE_IMAGE_PNG , ...paths ) ;
217220const createHostedImageContents = ( ...ids : string [ ] ) =>
@@ -848,6 +851,20 @@ describe("msteams attachments", () => {
848851 expectAttachmentMediaLength ( media , 0 ) ;
849852 expect ( fetchMock ) . toHaveBeenCalledTimes ( 1 ) ;
850853 } ) ;
854+
855+ it ( "appends /views/original to Bot Framework attachment URLs" , async ( ) => {
856+ const bfUrl = "https://smba.trafficmanager.net/amer/72f988bf/v3/attachments/0-wus-d11-abc" ;
857+ const media = await downloadAttachmentsWithFetch (
858+ createContentUrlAttachments ( "image/*" , bfUrl ) ,
859+ fetchRemoteMediaMock ,
860+ { allowHosts : [ "smba.trafficmanager.net" ] , resolveFn : publicResolveFn } ,
861+ ) ;
862+
863+ expect ( fetchRemoteMediaMock ) . toHaveBeenCalledWith (
864+ expect . objectContaining ( { url : `${ bfUrl } /views/original` } ) ,
865+ ) ;
866+ expectAttachmentMediaLength ( media , 1 ) ;
867+ } ) ;
851868 } ) ;
852869
853870 describe ( "buildMSTeamsGraphMessageUrls" , ( ) => {
@@ -927,6 +944,41 @@ describe("msteams attachments", () => {
927944 expect ( calledUrls . some ( ( url ) => url . startsWith ( GRAPH_SHARES_URL_PREFIX ) ) ) . toBe ( true ) ;
928945 expect ( calledUrls ) . not . toContain ( escapedUrl ) ;
929946 } ) ;
947+
948+ it ( "skips hosted content when $value fetch fails" , async ( ) => {
949+ const tokenProvider = createTokenProvider ( ) ;
950+ const graphFetchMock = vi . fn ( async ( url : string ) => {
951+ if ( url . endsWith ( "/hostedContents" ) ) {
952+ // contentBytes: null forces the $value fetch path
953+ return createGraphCollectionResponse (
954+ createHostedContentsNullBytes ( CONTENT_TYPE_IMAGE_PNG , "1" ) ,
955+ ) ;
956+ }
957+ if ( url . includes ( "/hostedContents/1/$value" ) ) {
958+ return createNotFoundResponse ( ) ;
959+ }
960+ if ( url . endsWith ( "/attachments" ) ) {
961+ return createGraphCollectionResponse ( [ ] ) ;
962+ }
963+ return createNotFoundResponse ( ) ;
964+ } ) as unknown as FetchFn ;
965+
966+ const { media } = await downloadGraphMediaWithMockOptions (
967+ { } ,
968+ {
969+ fetchFn : graphFetchMock ,
970+ tokenProvider,
971+ } ,
972+ ) ;
973+
974+ expectAttachmentMediaLength ( media . media , 0 ) ;
975+ expect ( saveMediaBufferMock ) . not . toHaveBeenCalled ( ) ;
976+ // Verify the $value fetch was actually attempted with the correct URL.
977+ expect ( graphFetchMock ) . toHaveBeenCalledWith (
978+ expect . stringContaining ( "/hostedContents/1/$value" ) ,
979+ expect . anything ( ) ,
980+ ) ;
981+ } ) ;
930982 } ) ;
931983
932984 describe ( "buildMSTeamsMediaPayload" , ( ) => {
0 commit comments