@@ -13,8 +13,8 @@ function apiError(headers?: Record<string, string>): MessageV2.APIError {
1313describe ( "session.retry.getRetryDelayInMs" , ( ) => {
1414 test ( "doubles delay on each attempt when headers missing" , ( ) => {
1515 const error = apiError ( )
16- const delays = Array . from ( { length : 7 } , ( _ , index ) => SessionRetry . getRetryDelayInMs ( error , index + 1 ) )
17- expect ( delays ) . toStrictEqual ( [ 2000 , 4000 , 8000 , 16000 , 32000 , 64000 , 128000 ] )
16+ const delays = Array . from ( { length : 10 } , ( _ , index ) => SessionRetry . getRetryDelayInMs ( error , index + 1 ) )
17+ expect ( delays ) . toStrictEqual ( [ 2000 , 4000 , 8000 , 16000 , 32000 , 64000 , 128000 , 256000 , 512000 , undefined ] )
1818 } )
1919
2020 test ( "prefers retry-after-ms when shorter than exponential" , ( ) => {
@@ -27,11 +27,6 @@ describe("session.retry.getRetryDelayInMs", () => {
2727 expect ( SessionRetry . getRetryDelayInMs ( error , 3 ) ) . toBe ( 30000 )
2828 } )
2929
30- test ( "falls back to exponential when server delay is long" , ( ) => {
31- const error = apiError ( { "retry-after" : "120" } )
32- expect ( SessionRetry . getRetryDelayInMs ( error , 2 ) ) . toBe ( 4000 )
33- } )
34-
3530 test ( "accepts http-date retry-after values" , ( ) => {
3631 const date = new Date ( Date . now ( ) + 20000 ) . toUTCString ( )
3732 const error = apiError ( { "retry-after" : date } )
@@ -44,4 +39,134 @@ describe("session.retry.getRetryDelayInMs", () => {
4439 const error = apiError ( { "retry-after" : "not-a-number" } )
4540 expect ( SessionRetry . getRetryDelayInMs ( error , 1 ) ) . toBe ( 2000 )
4641 } )
42+
43+ test ( "ignores malformed date retry hints" , ( ) => {
44+ const error = apiError ( { "retry-after" : "Invalid Date String" } )
45+ expect ( SessionRetry . getRetryDelayInMs ( error , 1 ) ) . toBe ( 2000 )
46+ } )
47+
48+ test ( "ignores past date retry hints" , ( ) => {
49+ const pastDate = new Date ( Date . now ( ) - 5000 ) . toUTCString ( )
50+ const error = apiError ( { "retry-after" : pastDate } )
51+ expect ( SessionRetry . getRetryDelayInMs ( error , 1 ) ) . toBe ( 2000 )
52+ } )
53+
54+ test ( "returns undefined when delay exceeds 10 minutes" , ( ) => {
55+ const error = apiError ( )
56+ expect ( SessionRetry . getRetryDelayInMs ( error , 10 ) ) . toBeUndefined ( )
57+ } )
58+
59+ test ( "returns undefined when retry-after exceeds 10 minutes" , ( ) => {
60+ const error = apiError ( { "retry-after" : "50" } )
61+ expect ( SessionRetry . getRetryDelayInMs ( error , 1 ) ) . toBe ( 50000 )
62+
63+ const longError = apiError ( { "retry-after-ms" : "700000" } )
64+ expect ( SessionRetry . getRetryDelayInMs ( longError , 1 ) ) . toBeUndefined ( )
65+ } )
66+ } )
67+
68+ describe ( "session.retry.getBoundedDelay" , ( ) => {
69+ test ( "returns full delay when under time budget" , ( ) => {
70+ const error = apiError ( )
71+ const startTime = Date . now ( )
72+ const delay = SessionRetry . getBoundedDelay ( {
73+ error,
74+ attempt : 1 ,
75+ startTime,
76+ } )
77+ expect ( delay ) . toBe ( 2000 )
78+ } )
79+
80+ test ( "returns remaining time when delay exceeds budget" , ( ) => {
81+ const error = apiError ( )
82+ const startTime = Date . now ( ) - 598_000 // 598 seconds elapsed, 2 seconds remaining
83+ const delay = SessionRetry . getBoundedDelay ( {
84+ error,
85+ attempt : 1 ,
86+ startTime,
87+ } )
88+ expect ( delay ) . toBeGreaterThanOrEqual ( 1900 )
89+ expect ( delay ) . toBeLessThanOrEqual ( 2100 )
90+ } )
91+
92+ test ( "returns undefined when time budget exhausted" , ( ) => {
93+ const error = apiError ( )
94+ const startTime = Date . now ( ) - 600_000 // exactly 10 minutes elapsed
95+ const delay = SessionRetry . getBoundedDelay ( {
96+ error,
97+ attempt : 1 ,
98+ startTime,
99+ } )
100+ expect ( delay ) . toBeUndefined ( )
101+ } )
102+
103+ test ( "returns undefined when time budget exceeded" , ( ) => {
104+ const error = apiError ( )
105+ const startTime = Date . now ( ) - 700_000 // 11+ minutes elapsed
106+ const delay = SessionRetry . getBoundedDelay ( {
107+ error,
108+ attempt : 1 ,
109+ startTime,
110+ } )
111+ expect ( delay ) . toBeUndefined ( )
112+ } )
113+
114+ test ( "respects custom maxDuration" , ( ) => {
115+ const error = apiError ( )
116+ const startTime = Date . now ( ) - 58_000 // 58 seconds elapsed
117+ const delay = SessionRetry . getBoundedDelay ( {
118+ error,
119+ attempt : 1 ,
120+ startTime,
121+ maxDuration : 60_000 , // 1 minute max
122+ } )
123+ expect ( delay ) . toBeGreaterThanOrEqual ( 1900 )
124+ expect ( delay ) . toBeLessThanOrEqual ( 2100 )
125+ } )
126+
127+ test ( "caps exponential backoff to remaining time" , ( ) => {
128+ const error = apiError ( )
129+ const startTime = Date . now ( ) - 595_000 // 595 seconds elapsed, 5 seconds remaining
130+ const delay = SessionRetry . getBoundedDelay ( {
131+ error,
132+ attempt : 5 , // would normally be 32 seconds
133+ startTime,
134+ } )
135+ expect ( delay ) . toBeGreaterThanOrEqual ( 4900 )
136+ expect ( delay ) . toBeLessThanOrEqual ( 5100 )
137+ } )
138+
139+ test ( "respects server retry-after within budget" , ( ) => {
140+ const error = apiError ( { "retry-after" : "30" } )
141+ const startTime = Date . now ( ) - 550_000 // 550 seconds elapsed, 50 seconds remaining
142+ const delay = SessionRetry . getBoundedDelay ( {
143+ error,
144+ attempt : 1 ,
145+ startTime,
146+ } )
147+ expect ( delay ) . toBe ( 30000 )
148+ } )
149+
150+ test ( "caps server retry-after to remaining time" , ( ) => {
151+ const error = apiError ( { "retry-after" : "30" } )
152+ const startTime = Date . now ( ) - 590_000 // 590 seconds elapsed, 10 seconds remaining
153+ const delay = SessionRetry . getBoundedDelay ( {
154+ error,
155+ attempt : 1 ,
156+ startTime,
157+ } )
158+ expect ( delay ) . toBeGreaterThanOrEqual ( 9900 )
159+ expect ( delay ) . toBeLessThanOrEqual ( 10100 )
160+ } )
161+
162+ test ( "returns undefined when getRetryDelayInMs returns undefined" , ( ) => {
163+ const error = apiError ( )
164+ const startTime = Date . now ( )
165+ const delay = SessionRetry . getBoundedDelay ( {
166+ error,
167+ attempt : 10 , // exceeds RETRY_MAX_DELAY
168+ startTime,
169+ } )
170+ expect ( delay ) . toBeUndefined ( )
171+ } )
47172} )
0 commit comments