@@ -667,6 +667,194 @@ describe('useMarketingConsent', () => {
667667 expect ( response ) . toEqual ( mockResponse )
668668 } )
669669
670+ test ( 'throws when bulk response contains failed items (207 Multi-Status)' , async ( ) => {
671+ const mock207Response = {
672+ results : [
673+ {
674+ channel : 'email' ,
675+ contactPointValue : 'customer@example.com' ,
676+ error : {
677+ code : 'UPDATE_FAILED' ,
678+ message : 'Failed to update consent subscription'
679+ } ,
680+ status : 'opt_in' ,
681+ subscriptionId : 'marketing-email' ,
682+ success : false
683+ }
684+ ]
685+ }
686+ const mockMutateAsync = jest . fn ( ) . mockResolvedValue ( mock207Response )
687+ useShopperConsentsMutation . mockImplementation ( ( mutationType ) => {
688+ if ( mutationType === ShopperConsentsMutations . UpdateSubscriptions ) {
689+ return {
690+ ...mockUseMutationResult ,
691+ mutateAsync : mockMutateAsync
692+ }
693+ }
694+ return mockUseMutationResult
695+ } )
696+
697+ const { result} = renderHook ( ( ) => useMarketingConsent ( ) )
698+
699+ await expect (
700+ result . current . updateSubscriptions ( [
701+ {
702+ subscriptionId : 'marketing-email' ,
703+ channel : 'email' ,
704+ status : 'opt_in' ,
705+ contactPointValue : 'customer@example.com'
706+ }
707+ ] )
708+ ) . rejects . toThrow ( '1 of 1 subscription update(s) failed.' )
709+ } )
710+
711+ test ( 'attaches response and failures to error thrown on 207' , async ( ) => {
712+ const mock207Response = {
713+ results : [
714+ {
715+ subscriptionId : 'marketing-email' ,
716+ success : false ,
717+ error : { code : 'UPDATE_FAILED' , message : 'Failed' }
718+ } ,
719+ {
720+ subscriptionId : 'marketing-sms' ,
721+ success : true
722+ }
723+ ]
724+ }
725+ const mockMutateAsync = jest . fn ( ) . mockResolvedValue ( mock207Response )
726+ useShopperConsentsMutation . mockImplementation ( ( mutationType ) => {
727+ if ( mutationType === ShopperConsentsMutations . UpdateSubscriptions ) {
728+ return {
729+ ...mockUseMutationResult ,
730+ mutateAsync : mockMutateAsync
731+ }
732+ }
733+ return mockUseMutationResult
734+ } )
735+
736+ const { result} = renderHook ( ( ) => useMarketingConsent ( ) )
737+
738+ const promise = result . current . updateSubscriptions ( [
739+ {
740+ subscriptionId : 'marketing-email' ,
741+ channel : 'email' ,
742+ status : 'opt_in' ,
743+ contactPointValue : 'customer@example.com'
744+ }
745+ ] )
746+
747+ await expect ( promise ) . rejects . toThrow ( '1 of 2 subscription update(s) failed.' )
748+
749+ const err = await promise . catch ( ( e ) => e )
750+ expect ( err . response ) . toEqual ( mock207Response )
751+ expect ( err . failures ) . toHaveLength ( 1 )
752+ expect ( err . failures [ 0 ] . subscriptionId ) . toBe ( 'marketing-email' )
753+ } )
754+
755+ test ( 'resolves successfully when all results have success: true' , async ( ) => {
756+ const allSuccessResponse = {
757+ results : [
758+ { subscriptionId : 'marketing-email' , success : true } ,
759+ { subscriptionId : 'marketing-sms' , success : true }
760+ ]
761+ }
762+ const mockMutateAsync = jest . fn ( ) . mockResolvedValue ( allSuccessResponse )
763+ useShopperConsentsMutation . mockImplementation ( ( mutationType ) => {
764+ if ( mutationType === ShopperConsentsMutations . UpdateSubscriptions ) {
765+ return {
766+ ...mockUseMutationResult ,
767+ mutateAsync : mockMutateAsync
768+ }
769+ }
770+ return mockUseMutationResult
771+ } )
772+
773+ const { result} = renderHook ( ( ) => useMarketingConsent ( ) )
774+
775+ const response = await result . current . updateSubscriptions ( [
776+ {
777+ subscriptionId : 'marketing-email' ,
778+ channel : 'email' ,
779+ status : 'opt_in' ,
780+ contactPointValue : 'customer@example.com'
781+ }
782+ ] )
783+
784+ expect ( response ) . toEqual ( allSuccessResponse )
785+ } )
786+
787+ test ( 'resolves successfully when response has no results array' , async ( ) => {
788+ const noResultsResponse = { success : true }
789+ const mockMutateAsync = jest . fn ( ) . mockResolvedValue ( noResultsResponse )
790+ useShopperConsentsMutation . mockImplementation ( ( mutationType ) => {
791+ if ( mutationType === ShopperConsentsMutations . UpdateSubscriptions ) {
792+ return {
793+ ...mockUseMutationResult ,
794+ mutateAsync : mockMutateAsync
795+ }
796+ }
797+ return mockUseMutationResult
798+ } )
799+
800+ const { result} = renderHook ( ( ) => useMarketingConsent ( ) )
801+
802+ const response = await result . current . updateSubscriptions ( [
803+ {
804+ subscriptionId : 'marketing-email' ,
805+ channel : 'email' ,
806+ status : 'opt_in' ,
807+ contactPointValue : 'customer@example.com'
808+ }
809+ ] )
810+
811+ expect ( response ) . toEqual ( noResultsResponse )
812+ } )
813+
814+ test ( 'logs each failed item error to console on 207' , async ( ) => {
815+ const consoleSpy = jest . spyOn ( console , 'error' ) . mockImplementation ( )
816+ const mock207Response = {
817+ results : [
818+ {
819+ subscriptionId : 'marketing-email' ,
820+ success : false ,
821+ error : { code : 'UPDATE_FAILED' , message : 'Failed' }
822+ }
823+ ]
824+ }
825+ const mockMutateAsync = jest . fn ( ) . mockResolvedValue ( mock207Response )
826+ useShopperConsentsMutation . mockImplementation ( ( mutationType ) => {
827+ if ( mutationType === ShopperConsentsMutations . UpdateSubscriptions ) {
828+ return {
829+ ...mockUseMutationResult ,
830+ mutateAsync : mockMutateAsync
831+ }
832+ }
833+ return mockUseMutationResult
834+ } )
835+
836+ const { result} = renderHook ( ( ) => useMarketingConsent ( ) )
837+
838+ try {
839+ await result . current . updateSubscriptions ( [
840+ {
841+ subscriptionId : 'marketing-email' ,
842+ channel : 'email' ,
843+ status : 'opt_in' ,
844+ contactPointValue : 'customer@example.com'
845+ }
846+ ] )
847+ } catch {
848+ // expected
849+ }
850+
851+ expect ( consoleSpy ) . toHaveBeenCalledWith (
852+ '[useMarketingConsent] Bulk update item failed:' ,
853+ { code : 'UPDATE_FAILED' , message : 'Failed' }
854+ )
855+ consoleSpy . mockRestore ( )
856+ } )
857+
670858 test ( 'reflects loading state during mutation' , ( ) => {
671859 useShopperConsentsMutation . mockImplementation ( ( mutationType ) => {
672860 if ( mutationType === ShopperConsentsMutations . UpdateSubscriptions ) {
0 commit comments