diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractExecutionContextDaoTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractExecutionContextDaoTests.java deleted file mode 100644 index ef017c014e..0000000000 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractExecutionContextDaoTests.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright 2008-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.batch.core.repository.dao; - -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.batch.core.BatchStatus; -import org.springframework.batch.core.job.JobExecution; -import org.springframework.batch.core.job.JobInstance; -import org.springframework.batch.core.job.parameters.JobParameters; -import org.springframework.batch.core.step.StepExecution; -import org.springframework.batch.infrastructure.item.ExecutionContext; -import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; -import org.springframework.transaction.annotation.Transactional; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -/** - * Tests for {@link ExecutionContextDao} implementations. - */ -public abstract class AbstractExecutionContextDaoTests extends AbstractTransactionalJUnit4SpringContextTests { - - private JobInstanceDao jobInstanceDao; - - private JobExecutionDao jobExecutionDao; - - private StepExecutionDao stepExecutionDao; - - private ExecutionContextDao contextDao; - - private JobExecution jobExecution; - - private StepExecution stepExecution; - - @BeforeEach - void setUp() { - jobInstanceDao = getJobInstanceDao(); - jobExecutionDao = getJobExecutionDao(); - stepExecutionDao = getStepExecutionDao(); - contextDao = getExecutionContextDao(); - - JobInstance ji = jobInstanceDao.createJobInstance("testJob", new JobParameters()); - jobExecution = new JobExecution(1L, ji, new JobParameters()); - jobExecutionDao.updateJobExecution(jobExecution); - stepExecution = new StepExecution(1L, "stepName", jobExecution); - stepExecutionDao.updateStepExecution(stepExecution); - - } - - /** - * @return Configured {@link ExecutionContextDao} implementation ready for use. - */ - protected abstract JobExecutionDao getJobExecutionDao(); - - /** - * @return Configured {@link ExecutionContextDao} implementation ready for use. - */ - protected abstract JobInstanceDao getJobInstanceDao(); - - /** - * @return Configured {@link ExecutionContextDao} implementation ready for use. - */ - protected abstract StepExecutionDao getStepExecutionDao(); - - /** - * @return Configured {@link ExecutionContextDao} implementation ready for use. - */ - protected abstract ExecutionContextDao getExecutionContextDao(); - - @Transactional - @Test - void testSaveAndFindJobContext() { - - ExecutionContext ctx = new ExecutionContext(Collections.singletonMap("key", "value")); - jobExecution.setExecutionContext(ctx); - contextDao.saveExecutionContext(jobExecution); - - ExecutionContext retrieved = contextDao.getExecutionContext(jobExecution); - assertEquals(ctx, retrieved); - } - - @Transactional - @Test - void testSaveAndFindExecutionContexts() { - - List stepExecutions = new ArrayList<>(); - for (int i = 0; i < 3; i++) { - JobInstance ji = jobInstanceDao.createJobInstance("testJob" + i, new JobParameters()); - JobExecution je = new JobExecution(i, ji, new JobParameters()); - jobExecutionDao.updateJobExecution(je); - StepExecution se = new StepExecution(1L, "step" + i, je); - se.setStatus(BatchStatus.STARTED); - se.setReadSkipCount(i); - se.setProcessSkipCount(i); - se.setWriteSkipCount(i); - se.setProcessSkipCount(i); - se.setRollbackCount(i); - se.setLastUpdated(LocalDateTime.now()); - se.setReadCount(i); - se.setFilterCount(i); - se.setWriteCount(i); - stepExecutions.add(se); - } - for (StepExecution stepExecution : stepExecutions) { - stepExecutionDao.updateStepExecution(stepExecution); - } - contextDao.saveExecutionContexts(stepExecutions); - - for (int i = 0; i < 3; i++) { - ExecutionContext retrieved = contextDao.getExecutionContext(stepExecutions.get(i).getJobExecution()); - assertEquals(stepExecutions.get(i).getExecutionContext(), retrieved); - } - } - - @Transactional - @Test - void testSaveNullExecutionContexts() { - assertThrows(IllegalArgumentException.class, () -> contextDao.saveExecutionContexts(null)); - } - - @Transactional - @Test - void testSaveEmptyExecutionContexts() { - contextDao.saveExecutionContexts(new ArrayList<>()); - } - - @Transactional - @Test - void testSaveAndFindEmptyJobContext() { - - ExecutionContext ctx = new ExecutionContext(); - jobExecution.setExecutionContext(ctx); - contextDao.saveExecutionContext(jobExecution); - - ExecutionContext retrieved = contextDao.getExecutionContext(jobExecution); - assertEquals(ctx, retrieved); - } - - @Transactional - @Test - void testUpdateContext() { - - ExecutionContext ctx = new ExecutionContext(Collections.singletonMap("key", "value")); - jobExecution.setExecutionContext(ctx); - contextDao.saveExecutionContext(jobExecution); - - ctx.putLong("longKey", 7); - contextDao.updateExecutionContext(jobExecution); - - ExecutionContext retrieved = contextDao.getExecutionContext(jobExecution); - assertEquals(ctx, retrieved); - assertEquals(7, retrieved.getLong("longKey")); - } - - @Transactional - @Test - void testSaveAndFindStepContext() { - - ExecutionContext ctx = new ExecutionContext(Collections.singletonMap("key", "value")); - stepExecution.setExecutionContext(ctx); - contextDao.saveExecutionContext(stepExecution); - - ExecutionContext retrieved = contextDao.getExecutionContext(stepExecution); - assertEquals(ctx, retrieved); - } - - @Transactional - @Test - void testSaveAndFindEmptyStepContext() { - - ExecutionContext ctx = new ExecutionContext(); - stepExecution.setExecutionContext(ctx); - contextDao.saveExecutionContext(stepExecution); - - ExecutionContext retrieved = contextDao.getExecutionContext(stepExecution); - assertEquals(ctx, retrieved); - } - - @Transactional - @Test - void testUpdateStepContext() { - - ExecutionContext ctx = new ExecutionContext(Collections.singletonMap("key", "value")); - stepExecution.setExecutionContext(ctx); - contextDao.saveExecutionContext(stepExecution); - - ctx.putLong("longKey", 7); - contextDao.updateExecutionContext(stepExecution); - - ExecutionContext retrieved = contextDao.getExecutionContext(stepExecution); - assertEquals(ctx, retrieved); - assertEquals(7, retrieved.getLong("longKey")); - } - - @Transactional - @Test - void testStoreInteger() { - - ExecutionContext ec = new ExecutionContext(); - ec.put("intValue", 343232); - stepExecution.setExecutionContext(ec); - contextDao.saveExecutionContext(stepExecution); - ExecutionContext restoredEc = contextDao.getExecutionContext(stepExecution); - assertEquals(ec, restoredEc); - } - - @Transactional - @Test - void testDeleteStepExecutionContext() { - // given - ExecutionContext ec = new ExecutionContext(); - stepExecution.setExecutionContext(ec); - contextDao.saveExecutionContext(stepExecution); - - // when - contextDao.deleteExecutionContext(stepExecution); - - // then - ExecutionContext restoredEc = contextDao.getExecutionContext(stepExecution); - // FIXME contextDao.getExecutionContext should return null and not an empty - // context - assertEquals(new ExecutionContext(), restoredEc); - } - - @Transactional - @Test - void testDeleteJobExecutionContext() { - // given - ExecutionContext ec = new ExecutionContext(); - jobExecution.setExecutionContext(ec); - contextDao.saveExecutionContext(jobExecution); - - // when - contextDao.deleteExecutionContext(jobExecution); - - // then - ExecutionContext restoredEc = contextDao.getExecutionContext(jobExecution); - // FIXME contextDao.getExecutionContext should return null and not an empty - // context - assertEquals(new ExecutionContext(), restoredEc); - } - -} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobExecutionDaoTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobExecutionDaoTests.java deleted file mode 100644 index 1cc27ef985..0000000000 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobExecutionDaoTests.java +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Copyright 2008-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.batch.core.repository.dao; - -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.batch.core.BatchStatus; -import org.springframework.batch.core.ExitStatus; -import org.springframework.batch.core.job.JobExecution; -import org.springframework.batch.core.job.JobInstance; -import org.springframework.batch.core.job.parameters.JobParameters; -import org.springframework.batch.core.step.StepExecution; -import org.springframework.batch.core.repository.dao.jdbc.JdbcJobExecutionDao; -import org.springframework.dao.OptimisticLockingFailureException; -import org.springframework.transaction.annotation.Transactional; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNotSame; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * Parent Test Class for {@link JdbcJobExecutionDao}. - */ -public abstract class AbstractJobExecutionDaoTests { - - protected JobExecutionDao dao; - - protected JobInstance jobInstance; - - protected JobExecution execution; - - protected JobParameters jobParameters; - - /** - * @return tested object ready for use - */ - protected abstract JobExecutionDao getJobExecutionDao(); - - protected abstract JobInstanceDao getJobInstanceDao(); - - /** - * @return tested object ready for use - */ - protected StepExecutionDao getStepExecutionDao() { - return null; - } - - @BeforeEach - void onSetUp() { - dao = getJobExecutionDao(); - jobParameters = new JobParameters(); - jobInstance = getJobInstanceDao().createJobInstance("execTestJob", jobParameters); - execution = new JobExecution(1L, jobInstance, new JobParameters()); - } - - /** - * Save and find a job execution. - */ - @Transactional - @Test - void testSaveAndFind() { - - execution.setStartTime(LocalDateTime.now()); - execution.setLastUpdated(LocalDateTime.now()); - execution.setExitStatus(ExitStatus.UNKNOWN); - execution.setEndTime(LocalDateTime.now()); - dao.updateJobExecution(execution); - - List executions = dao.findJobExecutions(jobInstance); - assertEquals(1, executions.size()); - assertEquals(execution, executions.get(0)); - assertExecutionsAreEqual(execution, executions.get(0)); - } - - /** - * Executions should be returned in the reverse order they were saved. - */ - @Transactional - @Test - void testFindExecutionsOrdering() { - - List execs = new ArrayList<>(); - - for (int i = 0; i < 10; i++) { - JobExecution exec = new JobExecution(1L, jobInstance, jobParameters); - exec.setCreateTime(LocalDateTime.now().plus(i, ChronoUnit.SECONDS)); - execs.add(exec); - dao.updateJobExecution(exec); - } - - List retrieved = dao.findJobExecutions(jobInstance); - Collections.reverse(retrieved); - - for (int i = 0; i < 10; i++) { - assertExecutionsAreEqual(execs.get(i), retrieved.get(i)); - } - - } - - /** - * Save and find a job execution. - */ - @Transactional - @Test - void testFindNonExistentExecutions() { - List executions = dao.findJobExecutions(jobInstance); - assertEquals(0, executions.size()); - } - - /** - * Saving sets id to the entity. - */ - @Transactional - @Test - void testSaveAddsIdAndVersion() { - - assertNull(execution.getId()); - assertNull(execution.getVersion()); - dao.updateJobExecution(execution); - assertNotNull(execution.getId()); - assertNotNull(execution.getVersion()); - } - - /** - * Update and retrieve job execution - check attributes have changed as expected. - */ - @Transactional - @Test - void testUpdateExecution() { - execution.setStatus(BatchStatus.STARTED); - dao.updateJobExecution(execution); - - execution.setLastUpdated(LocalDateTime.now()); - execution.setStatus(BatchStatus.COMPLETED); - dao.updateJobExecution(execution); - - JobExecution updated = dao.findJobExecutions(jobInstance).get(0); - assertEquals(execution, updated); - assertEquals(BatchStatus.COMPLETED, updated.getStatus()); - assertExecutionsAreEqual(execution, updated); - } - - /** - * Check the execution with most recent start time is returned - */ - @Transactional - @Test - void testGetLastExecution() { - JobExecution exec1 = new JobExecution(1L, jobInstance, jobParameters); - LocalDateTime now = LocalDateTime.now(); - exec1.setCreateTime(now); - - JobExecution exec2 = new JobExecution(1L, jobInstance, jobParameters); - exec2.setCreateTime(now.plus(1, ChronoUnit.SECONDS)); - - dao.updateJobExecution(exec1); - dao.updateJobExecution(exec2); - - JobExecution last = dao.getLastJobExecution(jobInstance); - assertEquals(exec2, last); - } - - /** - * Check the execution is returned - */ - @Transactional - @Test - void testGetMissingLastExecution() { - JobExecution value = dao.getLastJobExecution(jobInstance); - assertNull(value); - } - - /** - * Check the execution is returned - */ - @Transactional - @Test - void testFindRunningExecutions() { - // Normally completed JobExecution as EndTime is populated - JobExecution exec = new JobExecution(1L, jobInstance, jobParameters); - LocalDateTime now = LocalDateTime.now(); - exec.setCreateTime(now); - exec.setStartTime(now.plus(1, ChronoUnit.SECONDS)); - exec.setEndTime(now.plus(2, ChronoUnit.SECONDS)); - exec.setStatus(BatchStatus.COMPLETED); - exec.setLastUpdated(now.plus(3, ChronoUnit.SECONDS)); - dao.updateJobExecution(exec); - - // BATCH-2675 - // Abnormal JobExecution as both StartTime and EndTime are null - // This can occur when TaskExecutorJobLauncher#run() submission to taskExecutor - // throws a TaskRejectedException - exec = new JobExecution(1L, jobInstance, jobParameters); - exec.setLastUpdated(now.plus(3, ChronoUnit.SECONDS)); - dao.updateJobExecution(exec); - - // Stopping JobExecution as status is STOPPING - exec = new JobExecution(1L, jobInstance, jobParameters); - exec.setStartTime(now.plus(6, ChronoUnit.SECONDS)); - exec.setStatus(BatchStatus.STOPPING); - exec.setLastUpdated(now.plus(7, ChronoUnit.SECONDS)); - dao.updateJobExecution(exec); - - // Running JobExecution as StartTime is populated but EndTime is null - exec = new JobExecution(1L, jobInstance, jobParameters); - exec.setStartTime(now.plus(2, ChronoUnit.SECONDS)); - exec.setStatus(BatchStatus.STARTED); - exec.setLastUpdated(now.plus(3, ChronoUnit.SECONDS)); - - dao.updateJobExecution(exec); - - StepExecutionDao stepExecutionDao = getStepExecutionDao(); - if (stepExecutionDao != null) { - for (StepExecution stepExecution : exec.getStepExecutions()) { - stepExecutionDao.updateStepExecution(stepExecution); - } - } - - Set values = dao.findRunningJobExecutions(exec.getJobInstance().getJobName()); - - assertEquals(3, values.size()); - Long jobExecutionId = exec.getId(); - JobExecution value = values.stream() - .filter(jobExecution -> jobExecutionId.equals(jobExecution.getId())) - .findFirst() - .orElseThrow(); - assertEquals(now.plus(3, ChronoUnit.SECONDS), value.getLastUpdated()); - - } - - /** - * Check the execution is returned - */ - @Transactional - @Test - void testNoRunningExecutions() { - Set values = dao.findRunningJobExecutions("no-such-job"); - assertEquals(0, values.size()); - } - - /** - * Check the execution is returned - */ - @Transactional - @Test - void testGetExecution() { - JobExecution exec = new JobExecution(1L, jobInstance, jobParameters); - StepExecution stepExec = new StepExecution(1L, "step", exec); - exec.addStepExecution(stepExec); - exec.setCreateTime(LocalDateTime.now()); - - dao.updateJobExecution(exec); - StepExecutionDao stepExecutionDao = getStepExecutionDao(); - if (stepExecutionDao != null) { - for (StepExecution stepExecution : exec.getStepExecutions()) { - stepExecutionDao.updateStepExecution(stepExecution); - } - } - JobExecution value = dao.getJobExecution(exec.getId()); - - assertEquals(exec, value); - // N.B. the job instance is not re-hydrated in the JDBC case... - } - - /** - * Check the execution is returned - */ - @Transactional - @Test - void testGetMissingExecution() { - JobExecution value = dao.getJobExecution(54321L); - assertNull(value); - } - - /** - * Exception should be raised when the version of update argument doesn't match the - * version of persisted entity. - */ - @Transactional - @Test - void testConcurrentModificationException() { - - JobExecution exec1 = new JobExecution(1L, jobInstance, jobParameters); - dao.updateJobExecution(exec1); - - JobExecution exec2 = new JobExecution(1L, jobInstance, jobParameters); - - exec2.incrementVersion(); - assertEquals((Integer) 0, exec1.getVersion()); - assertEquals(exec1.getVersion(), exec2.getVersion()); - - dao.updateJobExecution(exec1); - assertEquals((Integer) 1, exec1.getVersion()); - - assertThrows(OptimisticLockingFailureException.class, () -> dao.updateJobExecution(exec2)); - } - - /** - * Successful synchronization from STARTED to STOPPING status. - */ - @Transactional - @Test - void testSynchronizeStatusUpgrade() { - - JobExecution exec1 = new JobExecution(1L, jobInstance, jobParameters); - exec1.setStatus(BatchStatus.STOPPING); - dao.updateJobExecution(exec1); - - JobExecution exec2 = new JobExecution(1L, jobInstance, jobParameters); - assertNotNull(exec1.getId()); - - exec2.setStatus(BatchStatus.STARTED); - exec2.setVersion(7); - assertNotSame(exec1.getVersion(), exec2.getVersion()); - assertNotSame(exec1.getStatus(), exec2.getStatus()); - - dao.synchronizeStatus(exec2); - - assertEquals(exec1.getVersion(), exec2.getVersion()); - assertEquals(exec1.getStatus(), exec2.getStatus()); - } - - /** - * UNKNOWN status won't be changed by synchronizeStatus, because it is the 'largest' - * BatchStatus (will not downgrade). - */ - @Transactional - @Test - void testSynchronizeStatusDowngrade() { - - JobExecution exec1 = new JobExecution(1L, jobInstance, jobParameters); - exec1.setStatus(BatchStatus.STARTED); - dao.updateJobExecution(exec1); - - JobExecution exec2 = new JobExecution(1L, jobInstance, jobParameters); - assertNotNull(exec1.getId()); - - exec2.setStatus(BatchStatus.UNKNOWN); - exec2.setVersion(7); - assertNotSame(exec1.getVersion(), exec2.getVersion()); - assertTrue(exec1.getStatus().isLessThan(exec2.getStatus())); - - dao.synchronizeStatus(exec2); - - assertEquals(exec1.getVersion(), exec2.getVersion()); - assertEquals(BatchStatus.UNKNOWN, exec2.getStatus()); - } - - /* - * Check to make sure the executions are equal. Normally, comparing the id's is - * sufficient. However, for testing purposes, especially of a DAO, we need to make - * sure all the fields are being stored/retrieved correctly. - */ - - private void assertExecutionsAreEqual(JobExecution lhs, JobExecution rhs) { - - assertEquals(lhs.getId(), rhs.getId()); - assertEquals(lhs.getStartTime(), rhs.getStartTime()); - assertEquals(lhs.getStatus(), rhs.getStatus()); - assertEquals(lhs.getEndTime(), rhs.getEndTime()); - assertEquals(lhs.getCreateTime(), rhs.getCreateTime()); - assertEquals(lhs.getLastUpdated(), rhs.getLastUpdated()); - assertEquals(lhs.getVersion(), rhs.getVersion()); - } - -}