Skip to content

Commit 638c183

Browse files
committed
Refine contribution #5082
- Move tests to ChunkOrientedStepTests - Remove mocks - Update tests to latest changes Related to #5078
1 parent 8ed93d1 commit 638c183

File tree

2 files changed

+108
-143
lines changed

2 files changed

+108
-143
lines changed

spring-batch-core/src/test/java/org/springframework/batch/core/step/builder/ChunkOrientedStepBuilderTests.java

Lines changed: 0 additions & 143 deletions
This file was deleted.

spring-batch-core/src/test/java/org/springframework/batch/core/step/item/ChunkOrientedStepTests.java

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.batch.core.step.item;
1717

1818
import java.util.List;
19+
import java.util.concurrent.atomic.AtomicInteger;
1920

2021
import org.junit.jupiter.api.Assertions;
2122
import org.junit.jupiter.api.Test;
@@ -26,12 +27,17 @@
2627
import org.springframework.batch.core.repository.JobRepository;
2728
import org.springframework.batch.core.repository.support.ResourcelessJobRepository;
2829
import org.springframework.batch.core.step.StepExecution;
30+
import org.springframework.batch.core.step.builder.ChunkOrientedStepBuilder;
2931
import org.springframework.batch.core.step.builder.StepBuilder;
32+
import org.springframework.batch.infrastructure.item.ItemProcessor;
3033
import org.springframework.batch.infrastructure.item.ItemReader;
3134
import org.springframework.batch.infrastructure.item.ItemWriter;
3235
import org.springframework.batch.infrastructure.item.support.ListItemReader;
36+
import org.springframework.batch.infrastructure.item.support.ListItemWriter;
37+
import org.springframework.batch.infrastructure.support.transaction.ResourcelessTransactionManager;
3338
import org.springframework.core.task.SimpleAsyncTaskExecutor;
3439

40+
import static org.junit.jupiter.api.Assertions.assertEquals;
3541
import static org.mockito.Mockito.mock;
3642
import static org.mockito.Mockito.times;
3743
import static org.mockito.Mockito.verify;
@@ -107,4 +113,106 @@ void testReadNoMoreThanAvailableItemsInConcurrentMode() throws Exception {
107113
verify(reader, times(6)).read();
108114
}
109115

116+
@Test
117+
void testRetryLimitWithoutRetryDoesNotRetryErrors() throws Exception {
118+
// Given: ItemProcessor that throws OutOfMemoryError
119+
AtomicInteger attempts = new AtomicInteger(0);
120+
ItemProcessor<String, String> processor = item -> {
121+
attempts.incrementAndGet();
122+
throw new OutOfMemoryError("Simulated OOM");
123+
};
124+
125+
ChunkOrientedStep<String, String> step = new ChunkOrientedStepBuilder<String, String>(
126+
new ResourcelessJobRepository(), 2)
127+
.reader(new ListItemReader<>(List.of("item1")))
128+
.processor(processor)
129+
.writer(items -> {
130+
})
131+
.transactionManager(new ResourcelessTransactionManager())
132+
.faultTolerant()
133+
.retryLimit(3)
134+
.build();
135+
136+
JobInstance jobInstance = new JobInstance(1L, "job");
137+
JobExecution jobExecution = new JobExecution(1L, jobInstance, new JobParameters());
138+
StepExecution stepExecution = new StepExecution(1L, "step", jobExecution);
139+
140+
// when
141+
step.execute(stepExecution);
142+
143+
// then: Should fail immediately without retry
144+
assertEquals(1, attempts.get(),
145+
"OutOfMemoryError should not be retried. Expected 1 attempt, but got " + attempts.get());
146+
}
147+
148+
@Test
149+
void testRetryLimitWithoutRetryRetriesExceptions() throws Exception {
150+
// Given: ItemProcessor that fails first 2 times with Exception
151+
AtomicInteger attempts = new AtomicInteger(0);
152+
ItemProcessor<String, String> processor = item -> {
153+
if (attempts.incrementAndGet() < 3) {
154+
throw new RuntimeException("Temporary failure");
155+
}
156+
return item.toUpperCase();
157+
};
158+
ListItemReader<String> listItemReader = new ListItemReader<>(List.of("item1"));
159+
ListItemWriter<String> listItemWriter = new ListItemWriter<>();
160+
ChunkOrientedStep<String, String> step = new ChunkOrientedStepBuilder<String, String>(
161+
new ResourcelessJobRepository(), 2)
162+
.reader(listItemReader)
163+
.processor(processor)
164+
.writer(listItemWriter)
165+
.transactionManager(new ResourcelessTransactionManager())
166+
.faultTolerant()
167+
.retryLimit(3)
168+
.build();
169+
170+
JobInstance jobInstance = new JobInstance(1L, "job");
171+
JobExecution jobExecution = new JobExecution(1L, jobInstance, new JobParameters());
172+
StepExecution stepExecution = new StepExecution(1L, "step", jobExecution);
173+
174+
// When: Execute step
175+
// Then: Should succeed after 2 retries
176+
step.execute(stepExecution);
177+
178+
// Should have retried 2 times (total 3 attempts)
179+
assertEquals(3, attempts.get(), "Should retry RuntimeException");
180+
assertEquals(List.of("ITEM1"), listItemWriter.getWrittenItems(), "Item should be processed successfully");
181+
}
182+
183+
@Test
184+
void testExplicitRetryConfigurationTakesPrecedence() throws Exception {
185+
// Given: Explicit retry configuration for IllegalStateException only
186+
AtomicInteger attempts = new AtomicInteger(0);
187+
ItemProcessor<String, String> processor = item -> {
188+
attempts.incrementAndGet();
189+
throw new RuntimeException("This should not be retried");
190+
};
191+
ListItemReader<String> listItemReader = new ListItemReader<>(List.of("item1"));
192+
ListItemWriter<String> listItemWriter = new ListItemWriter<>();
193+
194+
ChunkOrientedStep<String, String> step = new ChunkOrientedStepBuilder<String, String>(
195+
new ResourcelessJobRepository(), 2)
196+
.reader(listItemReader)
197+
.processor(processor)
198+
.writer(listItemWriter)
199+
.transactionManager(new ResourcelessTransactionManager())
200+
.faultTolerant()
201+
.retry(IllegalStateException.class)
202+
.retryLimit(3)
203+
.build();
204+
205+
JobInstance jobInstance = new JobInstance(1L, "job");
206+
JobExecution jobExecution = new JobExecution(1L, jobInstance, new JobParameters());
207+
StepExecution stepExecution = new StepExecution(1L, "step", jobExecution);
208+
209+
// When & Then: Should fail immediately without retry
210+
// because RuntimeException is not in the explicit retry list
211+
step.execute(stepExecution);
212+
213+
// Should not retry (only 1 attempt)
214+
assertEquals(1, attempts.get(),
215+
"RuntimeException should not be retried when only IllegalStateException is configured");
216+
}
217+
110218
}

0 commit comments

Comments
 (0)