From a803ecdf2621549ba5826265e55770bb1956fe61 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Sun, 17 Aug 2025 17:59:14 +0200 Subject: [PATCH] Test expected behavior for RetryTemplate with zero retries --- .../core/retry/RetryTemplateTests.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/spring-core/src/test/java/org/springframework/core/retry/RetryTemplateTests.java b/spring-core/src/test/java/org/springframework/core/retry/RetryTemplateTests.java index f8fe211f229..c3259e10871 100644 --- a/spring-core/src/test/java/org/springframework/core/retry/RetryTemplateTests.java +++ b/spring-core/src/test/java/org/springframework/core/retry/RetryTemplateTests.java @@ -32,6 +32,9 @@ import org.junit.jupiter.params.provider.Arguments.ArgumentSet; import org.junit.jupiter.params.provider.FieldSource; import org.mockito.InOrder; +import org.springframework.util.backoff.BackOff; +import org.springframework.util.backoff.FixedBackOff; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.junit.jupiter.params.provider.Arguments.argumentSet; @@ -87,6 +90,60 @@ class RetryTemplateTests { verifyNoInteractions(retryListener); } + @Test + void retryWithInitialFailureAndZeroRetriesRetryPolicy() { + RetryPolicy retryPolicy = throwable -> false; // Zero retries + RetryTemplate retryTemplate = new RetryTemplate(retryPolicy); + retryTemplate.setRetryListener(retryListener); + Exception exception = new RuntimeException("Boom!"); + Retryable retryable = () -> { + throw exception; + }; + + assertThatExceptionOfType(RetryException.class) + .isThrownBy(() -> retryTemplate.execute(retryable)) + .withMessageMatching("Retry policy for operation '.+?' exhausted; aborting execution") + .withCause(exception) + .satisfies(throwable -> assertThat(throwable.getSuppressed()).isEmpty()); + + // RetryListener interactions: + inOrder.verify(retryListener).onRetryPolicyExhaustion(retryPolicy, retryable, exception); + verifyNoMoreInteractions(retryListener); + } + + @Test + void retryWithInitialFailureAndZeroRetriesBackOffPolicy() { + RetryPolicy retryPolicy = new RetryPolicy() { + + @Override + public boolean shouldRetry(Throwable throwable) { + return true; + } + + @Override + public BackOff getBackOff() { + return new FixedBackOff(10, 0); // Zero retries + } + }; + + RetryTemplate retryTemplate = new RetryTemplate(retryPolicy); + retryTemplate.setRetryListener(retryListener); + Exception exception = new RuntimeException("Boom!"); + Retryable retryable = () -> { + throw exception; + }; + + assertThatExceptionOfType(RetryException.class) + .isThrownBy(() -> retryTemplate.execute(retryable)) + .withMessageMatching("Retry policy for operation '.+?' exhausted; aborting execution") + .withCause(exception) + .satisfies(throwable -> assertThat(throwable.getSuppressed()).isEmpty()); + + // RetryListener interactions: + inOrder.verify(retryListener).onRetryPolicyExhaustion(retryPolicy, retryable, exception); + verifyNoMoreInteractions(retryListener); + } + @Test void retryWithSuccessAfterInitialFailures() throws Exception { AtomicInteger invocationCount = new AtomicInteger();