@@ -1080,6 +1080,66 @@ describe('Oracle Fusion Financials provider', () => {
10801080 expect ( mockSleep ) . toHaveBeenCalledTimes ( 2 )
10811081 } )
10821082
1083+ it ( 'retries classified transient transport failures within the existing bound' , async ( ) => {
1084+ mockSecureFetch
1085+ . mockRejectedValueOnce ( Object . assign ( new Error ( 'socket reset' ) , { code : 'ECONNRESET' } ) )
1086+ . mockRejectedValueOnce ( new Error ( 'Request timed out after 30000ms' ) )
1087+ . mockResolvedValueOnce ( response ( 200 , page ( [ ] ) ) )
1088+
1089+ await requestOracleFusionJson ( AUTH , { path : `${ RESOURCE_PATH } /invoices` } )
1090+
1091+ expect ( mockSecureFetch ) . toHaveBeenCalledTimes ( 3 )
1092+ expect ( mockBackoff ) . toHaveBeenNthCalledWith ( 1 , 1 , null , {
1093+ baseMs : 250 ,
1094+ maxMs : 5_000 ,
1095+ } )
1096+ expect ( mockBackoff ) . toHaveBeenNthCalledWith ( 2 , 2 , null , {
1097+ baseMs : 250 ,
1098+ maxMs : 5_000 ,
1099+ } )
1100+ expect ( mockSleep ) . toHaveBeenCalledTimes ( 2 )
1101+ } )
1102+
1103+ it ( 'stops after two transient transport retries and keeps the failure sanitized' , async ( ) => {
1104+ const reset = ( ) => Object . assign ( new Error ( 'provider-host-canary' ) , { code : 'ECONNRESET' } )
1105+ mockSecureFetch
1106+ . mockRejectedValueOnce ( reset ( ) )
1107+ . mockRejectedValueOnce ( reset ( ) )
1108+ . mockRejectedValueOnce ( reset ( ) )
1109+
1110+ await expect (
1111+ requestOracleFusionJson ( AUTH , { path : `${ RESOURCE_PATH } /invoices` } )
1112+ ) . rejects . toThrow ( 'Could not reach Oracle Fusion Financials' )
1113+ expect ( mockSecureFetch ) . toHaveBeenCalledTimes ( 3 )
1114+ expect ( mockSleep ) . toHaveBeenCalledTimes ( 2 )
1115+ } )
1116+
1117+ it ( 'retries a classified transport failure while reading the response body' , async ( ) => {
1118+ mockSecureFetch
1119+ . mockResolvedValueOnce ( {
1120+ ...response ( 200 , { } ) ,
1121+ text : async ( ) => {
1122+ throw Object . assign ( new Error ( 'socket reset' ) , { code : 'ECONNRESET' } )
1123+ } ,
1124+ } )
1125+ . mockResolvedValueOnce ( response ( 200 , page ( [ ] ) ) )
1126+
1127+ await requestOracleFusionJson ( AUTH , { path : `${ RESOURCE_PATH } /invoices` } )
1128+
1129+ expect ( mockSecureFetch ) . toHaveBeenCalledTimes ( 2 )
1130+ expect ( mockSleep ) . toHaveBeenCalledTimes ( 1 )
1131+ } )
1132+
1133+ it ( 'does not retry unclassified transport failures' , async ( ) => {
1134+ mockSecureFetch . mockRejectedValueOnce ( new TypeError ( 'invalid pinned request' ) )
1135+
1136+ await expect (
1137+ requestOracleFusionJson ( AUTH , { path : `${ RESOURCE_PATH } /invoices` } )
1138+ ) . rejects . toThrow ( 'Could not reach Oracle Fusion Financials' )
1139+ expect ( mockSecureFetch ) . toHaveBeenCalledTimes ( 1 )
1140+ expect ( mockSleep ) . not . toHaveBeenCalled ( )
1141+ } )
1142+
10831143 it ( 'stops after two retries and surfaces a sanitized Oracle error' , async ( ) => {
10841144 const accessToken = 'short/lived+access~token='
10851145 const encodedAccessToken = encodeURIComponent ( accessToken )
@@ -1127,6 +1187,22 @@ describe('Oracle Fusion Financials provider', () => {
11271187 requestOracleFusionJson ( AUTH , { path : `${ RESOURCE_PATH } /invoices` } , duringRetry . signal )
11281188 ) . rejects . toMatchObject ( { name : 'AbortError' } )
11291189 expect ( mockSecureFetch ) . toHaveBeenCalledTimes ( 1 )
1190+
1191+ const duringTransportRetry = new AbortController ( )
1192+ mockSecureFetch . mockRejectedValueOnce (
1193+ Object . assign ( new Error ( 'socket reset' ) , { code : 'ECONNRESET' } )
1194+ )
1195+ mockSleep . mockImplementationOnce ( async ( ) => {
1196+ duringTransportRetry . abort ( new DOMException ( 'cancelled' , 'AbortError' ) )
1197+ } )
1198+ await expect (
1199+ requestOracleFusionJson (
1200+ AUTH ,
1201+ { path : `${ RESOURCE_PATH } /invoices` } ,
1202+ duringTransportRetry . signal
1203+ )
1204+ ) . rejects . toMatchObject ( { name : 'AbortError' } )
1205+ expect ( mockSecureFetch ) . toHaveBeenCalledTimes ( 2 )
11301206 } )
11311207
11321208 it ( 'maps invalid caller input to 400 and malformed Oracle responses to a sanitized 502' , async ( ) => {
0 commit comments