@@ -160,6 +160,109 @@ void linearBackoff_withCustomDelays_shouldIncreaseByIncrement() {
160160 assertEquals (Duration .ofSeconds (11 ), decision4 .delay ());
161161 }
162162
163+ @ Test
164+ void linearBackoff_withOldOverload_shouldRemainUncappedAndDeterministic () {
165+ var strategy = RetryStrategies .linearBackoff (5 , Duration .ofSeconds (10 ), Duration .ofSeconds (10 ));
166+
167+ assertEquals (
168+ Duration .ofSeconds (10 ),
169+ strategy .makeRetryDecision (new RuntimeException ("test" ), 1 ).delay ());
170+ assertEquals (
171+ Duration .ofSeconds (20 ),
172+ strategy .makeRetryDecision (new RuntimeException ("test" ), 2 ).delay ());
173+ assertEquals (
174+ Duration .ofSeconds (30 ),
175+ strategy .makeRetryDecision (new RuntimeException ("test" ), 3 ).delay ());
176+ assertEquals (
177+ Duration .ofSeconds (40 ),
178+ strategy .makeRetryDecision (new RuntimeException ("test" ), 4 ).delay ());
179+ }
180+
181+ @ Test
182+ void linearBackoff_withMaxDelay_shouldCapDelay () {
183+ var strategy = RetryStrategies .linearBackoff (
184+ 6 , Duration .ofSeconds (2 ), Duration .ofSeconds (7 ), Duration .ofSeconds (3 ), JitterStrategy .NONE );
185+
186+ assertEquals (
187+ Duration .ofSeconds (2 ),
188+ strategy .makeRetryDecision (new RuntimeException ("test" ), 1 ).delay ());
189+ assertEquals (
190+ Duration .ofSeconds (5 ),
191+ strategy .makeRetryDecision (new RuntimeException ("test" ), 2 ).delay ());
192+ assertEquals (
193+ Duration .ofSeconds (7 ),
194+ strategy .makeRetryDecision (new RuntimeException ("test" ), 3 ).delay ());
195+ assertEquals (
196+ Duration .ofSeconds (7 ),
197+ strategy .makeRetryDecision (new RuntimeException ("test" ), 4 ).delay ());
198+ }
199+
200+ @ Test
201+ void linearBackoff_withMaxDelay_shouldCapBeforeOverflow () {
202+ var strategy = RetryStrategies .linearBackoff (
203+ Integer .MAX_VALUE ,
204+ Duration .ofSeconds (1 ),
205+ Duration .ofSeconds (5 ),
206+ Duration .ofSeconds (Long .MAX_VALUE ),
207+ JitterStrategy .NONE );
208+
209+ var decision = assertDoesNotThrow (() -> strategy .makeRetryDecision (new RuntimeException ("test" ), 2 ));
210+
211+ assertTrue (decision .shouldRetry ());
212+ assertEquals (Duration .ofSeconds (5 ), decision .delay ());
213+ }
214+
215+ @ Test
216+ void linearBackoff_withNewOverload_shouldStopAtMaxAttempts () {
217+ var strategy = RetryStrategies .linearBackoff (
218+ 3 , Duration .ofSeconds (1 ), Duration .ofSeconds (5 ), Duration .ofSeconds (1 ), JitterStrategy .NONE );
219+
220+ var decision1 = strategy .makeRetryDecision (new RuntimeException ("test" ), 1 );
221+ var decision2 = strategy .makeRetryDecision (new RuntimeException ("test" ), 2 );
222+ var decision3 = strategy .makeRetryDecision (new RuntimeException ("test" ), 3 );
223+
224+ assertTrue (decision1 .shouldRetry ());
225+ assertEquals (Duration .ofSeconds (1 ), decision1 .delay ());
226+ assertTrue (decision2 .shouldRetry ());
227+ assertEquals (Duration .ofSeconds (2 ), decision2 .delay ());
228+ assertFalse (decision3 .shouldRetry ());
229+ }
230+
231+ @ Test
232+ void linearBackoff_withMaxDelayLessThanInitialDelay_shouldCapFirstRetry () {
233+ var strategy = RetryStrategies .linearBackoff (
234+ 4 , Duration .ofSeconds (10 ), Duration .ofSeconds (5 ), Duration .ofSeconds (3 ), JitterStrategy .NONE );
235+
236+ assertEquals (
237+ Duration .ofSeconds (5 ),
238+ strategy .makeRetryDecision (new RuntimeException ("test" ), 1 ).delay ());
239+ assertEquals (
240+ Duration .ofSeconds (5 ),
241+ strategy .makeRetryDecision (new RuntimeException ("test" ), 2 ).delay ());
242+ }
243+
244+ @ Test
245+ void linearBackoff_withJitter_shouldProduceDelayInExpectedRange () {
246+ var fullStrategy = RetryStrategies .linearBackoff (
247+ 5 , Duration .ofSeconds (10 ), Duration .ofSeconds (15 ), Duration .ofSeconds (10 ), JitterStrategy .FULL );
248+ var halfStrategy = RetryStrategies .linearBackoff (
249+ 5 , Duration .ofSeconds (10 ), Duration .ofSeconds (15 ), Duration .ofSeconds (10 ), JitterStrategy .HALF );
250+
251+ for (int i = 0 ; i < 10 ; i ++) {
252+ var fullDelay = fullStrategy
253+ .makeRetryDecision (new RuntimeException ("test" ), 2 )
254+ .delay ()
255+ .toSeconds ();
256+ assertTrue (fullDelay >= 1 && fullDelay <= 15 );
257+
258+ var halfDelay = halfStrategy
259+ .makeRetryDecision (new RuntimeException ("test" ), 2 )
260+ .delay ()
261+ .toSeconds ();
262+ assertTrue (halfDelay >= 8 && halfDelay <= 15 );
263+ }
264+ }
265+
163266 @ Test
164267 void linearPreset_shouldUseOneThroughFiveSecondDelays () {
165268 var strategy = RetryStrategies .Presets .LINEAR ;
@@ -236,6 +339,39 @@ void linearBackoff_withNullIncrement_shouldThrow() {
236339 assertTrue (exception .getMessage ().contains ("cannot be null" ));
237340 }
238341
342+ @ Test
343+ void linearBackoff_withSubSecondMaxDelay_shouldThrow () {
344+ var exception = assertThrows (
345+ IllegalArgumentException .class ,
346+ () -> RetryStrategies .linearBackoff (
347+ 3 , Duration .ofSeconds (1 ), Duration .ofMillis (500 ), Duration .ofSeconds (1 ), JitterStrategy .NONE ));
348+
349+ assertTrue (exception .getMessage ().contains ("maxDelay" ));
350+ assertTrue (exception .getMessage ().contains ("at least 1 second" ));
351+ }
352+
353+ @ Test
354+ void linearBackoff_withNullMaxDelay_shouldThrow () {
355+ var exception = assertThrows (
356+ IllegalArgumentException .class ,
357+ () -> RetryStrategies .linearBackoff (
358+ 3 , Duration .ofSeconds (1 ), null , Duration .ofSeconds (1 ), JitterStrategy .NONE ));
359+
360+ assertTrue (exception .getMessage ().contains ("maxDelay" ));
361+ assertTrue (exception .getMessage ().contains ("cannot be null" ));
362+ }
363+
364+ @ Test
365+ void linearBackoff_withNullJitter_shouldThrow () {
366+ var exception = assertThrows (
367+ IllegalArgumentException .class ,
368+ () -> RetryStrategies .linearBackoff (
369+ 3 , Duration .ofSeconds (1 ), Duration .ofSeconds (5 ), Duration .ofSeconds (1 ), null ));
370+
371+ assertTrue (exception .getMessage ().contains ("jitter" ));
372+ assertTrue (exception .getMessage ().contains ("cannot be null" ));
373+ }
374+
239375 @ Test
240376 void testInvalidParameters () {
241377 assertThrows (
0 commit comments