Skip to content

Commit 9ff33b8

Browse files
committed
chore: add some edge case tests for WithRetryHelperTest
1 parent e12daee commit 9ff33b8

1 file changed

Lines changed: 114 additions & 0 deletions

File tree

sdk/src/test/java/software/amazon/lambda/durable/util/WithRetryHelperTest.java

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import software.amazon.lambda.durable.DurableContext;
1616
import software.amazon.lambda.durable.TypeToken;
1717
import software.amazon.lambda.durable.config.WithRetryConfig;
18+
import software.amazon.lambda.durable.exception.SerDesException;
1819
import software.amazon.lambda.durable.exception.UnrecoverableDurableExecutionException;
1920
import software.amazon.lambda.durable.execution.SuspendExecutionException;
2021
import software.amazon.lambda.durable.retry.RetryDecision;
@@ -180,6 +181,19 @@ void nullConfig_shouldThrow() {
180181
NullPointerException.class,
181182
() -> WithRetryHelper.retryOperation(context, "name", (ctx, a) -> "x", null));
182183
}
184+
185+
@Test
186+
void operationReturnsNull() {
187+
var config = WithRetryConfig.builder()
188+
.retryStrategy(RetryStrategies.Presets.NO_RETRY)
189+
.wrapInChildContext(false)
190+
.build();
191+
192+
var result = WithRetryHelper.retryOperation(
193+
context, "my-op", (WithRetry<String>) (ctx, attempt) -> null, config);
194+
195+
assertNull(result);
196+
}
183197
}
184198

185199
// --- Anonymous form tests ---
@@ -278,6 +292,38 @@ void nullConfig_shouldThrow() {
278292
assertThrows(
279293
NullPointerException.class, () -> WithRetryHelper.retryOperation(context, (ctx, a) -> "x", null));
280294
}
295+
296+
@Test
297+
void operationReturnsNull() {
298+
var config = WithRetryConfig.builder()
299+
.retryStrategy(RetryStrategies.Presets.NO_RETRY)
300+
.build();
301+
302+
var result = WithRetryHelper.retryOperation(context, (WithRetry<String>) (ctx, attempt) -> null, config);
303+
304+
assertNull(result);
305+
}
306+
307+
@Test
308+
void usesDefaultDelayWhenRetryDecisionDelayIsZero() {
309+
var config = WithRetryConfig.builder()
310+
.retryStrategy(
311+
(error, attempt) -> attempt < 2 ? RetryDecision.retry(Duration.ZERO) : RetryDecision.fail())
312+
.build();
313+
314+
var result = WithRetryHelper.retryOperation(
315+
context,
316+
(ctx, attempt) -> {
317+
if (attempt == 1) {
318+
throw new RuntimeException("fail");
319+
}
320+
return "ok";
321+
},
322+
config);
323+
324+
assertEquals("ok", result);
325+
verify(context).wait("retry-backoff-1", Duration.ofSeconds(1));
326+
}
281327
}
282328

283329
// --- Retry behavior tests ---
@@ -455,5 +501,73 @@ void propagatesUnrecoverableDurableExecutionExceptionWithoutRetrying() {
455501

456502
verify(context, never()).wait(anyString(), any(Duration.class));
457503
}
504+
505+
@Test
506+
void propagatesSuspendExecutionExceptionOnLaterAttempt() {
507+
var config = WithRetryConfig.builder()
508+
.retryStrategy((error, attempt) -> RetryDecision.retry(Duration.ofSeconds(1)))
509+
.build();
510+
511+
assertThrows(
512+
SuspendExecutionException.class,
513+
() -> WithRetryHelper.retryOperation(
514+
context,
515+
(ctx, attempt) -> {
516+
if (attempt == 1) {
517+
throw new RuntimeException("transient");
518+
}
519+
// Second attempt triggers suspend — must propagate, not retry
520+
throw new SuspendExecutionException();
521+
},
522+
config));
523+
524+
// First attempt retried (one backoff wait), second attempt suspended immediately
525+
verify(context, times(1)).wait(anyString(), any(Duration.class));
526+
}
527+
528+
@Test
529+
void propagatesUnrecoverableDurableExecutionExceptionOnLaterAttempt() {
530+
var config = WithRetryConfig.builder()
531+
.retryStrategy((error, attempt) -> RetryDecision.retry(Duration.ofSeconds(1)))
532+
.build();
533+
534+
assertThrows(
535+
UnrecoverableDurableExecutionException.class,
536+
() -> WithRetryHelper.retryOperation(
537+
context,
538+
(ctx, attempt) -> {
539+
if (attempt == 1) {
540+
throw new RuntimeException("transient");
541+
}
542+
throw new UnrecoverableDurableExecutionException(
543+
software.amazon.awssdk.services.lambda.model.ErrorObject.builder()
544+
.errorMessage("unrecoverable on attempt 2")
545+
.build());
546+
},
547+
config));
548+
549+
verify(context, times(1)).wait(anyString(), any(Duration.class));
550+
}
551+
552+
@Test
553+
void preservesCheckedExceptionSubclassType() {
554+
var config = WithRetryConfig.builder()
555+
.retryStrategy(RetryStrategies.Presets.NO_RETRY)
556+
.build();
557+
558+
var original = new SerDesException("deserialization failed", new RuntimeException("bad json"));
559+
560+
var thrown = assertThrows(
561+
SerDesException.class,
562+
() -> WithRetryHelper.retryOperation(
563+
context,
564+
(ctx, attempt) -> {
565+
throw original;
566+
},
567+
config));
568+
569+
assertSame(original, thrown);
570+
assertEquals("deserialization failed", thrown.getMessage());
571+
}
458572
}
459573
}

0 commit comments

Comments
 (0)