From f8573591967c5c4370298db32d388acda1e75435 Mon Sep 17 00:00:00 2001 From: Glenn Renfro Date: Tue, 15 Mar 2022 08:32:39 -0400 Subject: [PATCH] Add support for mariadb resolves #833 PagingProvider and integration tests added --- pom.xml | 2 + spring-cloud-task-core/pom.xml | 32 +- .../support/MariaDbPagingQueryProvider.java | 37 +++ .../SqlPagingQueryProviderFactoryBean.java | 2 + .../task/repository/support/DatabaseType.java | 12 +- .../cloud/task/schema-mariadb.sql | 37 +++ ...MariaDbTaskRepositoryIntegrationTests.java | 89 ++++++ ...skExecutionDaoMariaDBIntegrationTests.java | 291 ++++++++++++++++++ .../repository/support/DatabaseTypeTests.java | 5 +- .../cloud/task/util/TestVerifierUtils.java | 30 +- 10 files changed, 526 insertions(+), 11 deletions(-) create mode 100644 spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/MariaDbPagingQueryProvider.java create mode 100644 spring-cloud-task-core/src/main/resources/org/springframework/cloud/task/schema-mariadb.sql create mode 100644 spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/MariaDbTaskRepositoryIntegrationTests.java create mode 100644 spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/dao/JdbcTaskExecutionDaoMariaDBIntegrationTests.java diff --git a/pom.xml b/pom.xml index 94afad20..93449189 100755 --- a/pom.xml +++ b/pom.xml @@ -120,6 +120,8 @@ 17 0.0.34 + 1.17.5 + 1.0.8 diff --git a/spring-cloud-task-core/pom.xml b/spring-cloud-task-core/pom.xml index 2ef20c63..4d65f633 100755 --- a/spring-cloud-task-core/pom.xml +++ b/spring-cloud-task-core/pom.xml @@ -59,6 +59,7 @@ org.springframework.batch spring-batch-infrastructure + 5.0.0-SNAPSHOT org.springframework.data @@ -164,6 +165,35 @@ zipkin-sender-urlconnection test - + + org.testcontainers + testcontainers + ${test.containers.version} + test + + + org.testcontainers + junit-jupiter + ${test.containers.version} + test + + + org.testcontainers + mariadb + ${test.containers.version} + test + + + org.rnorth.duct-tape + duct-tape + ${test.ducttape.version} + test + + + org.mariadb.jdbc + mariadb-java-client + test + + diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/MariaDbPagingQueryProvider.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/MariaDbPagingQueryProvider.java new file mode 100644 index 00000000..f2c789a2 --- /dev/null +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/MariaDbPagingQueryProvider.java @@ -0,0 +1,37 @@ +/* + * Copyright 2022-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.cloud.task.repository.database.support; + +import org.springframework.cloud.task.repository.database.PagingQueryProvider; +import org.springframework.data.domain.Pageable; + +/** + * MariaDB implementation of a {@link PagingQueryProvider} using database specific + * features. + * + * @author Glenn Renfro + */ +public class MariaDbPagingQueryProvider extends AbstractSqlPagingQueryProvider { + + @Override + public String getPageQuery(Pageable pageable) { + String topClause = new StringBuilder().append("LIMIT ").append(pageable.getOffset()).append(", ") + .append(pageable.getPageSize()).toString(); + return SqlPagingQueryUtils.generateLimitJumpToQuery(this, topClause); + } + +} diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/SqlPagingQueryProviderFactoryBean.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/SqlPagingQueryProviderFactoryBean.java index cc407d88..b9ce01ea 100755 --- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/SqlPagingQueryProviderFactoryBean.java +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/SqlPagingQueryProviderFactoryBean.java @@ -35,6 +35,7 @@ import static org.springframework.cloud.task.repository.support.DatabaseType.DB2 import static org.springframework.cloud.task.repository.support.DatabaseType.DB2ZOS; import static org.springframework.cloud.task.repository.support.DatabaseType.H2; import static org.springframework.cloud.task.repository.support.DatabaseType.HSQL; +import static org.springframework.cloud.task.repository.support.DatabaseType.MARIADB; import static org.springframework.cloud.task.repository.support.DatabaseType.MYSQL; import static org.springframework.cloud.task.repository.support.DatabaseType.ORACLE; import static org.springframework.cloud.task.repository.support.DatabaseType.POSTGRES; @@ -67,6 +68,7 @@ public class SqlPagingQueryProviderFactoryBean implements FactoryBean mariaDBContainer = new MariaDBContainer<>(MARIADB_IMAGE); + + @Test + public void testTaskExplorer() { + ApplicationContextRunner applicationContextRunner = new ApplicationContextRunner() + .withUserConfiguration(MariaDbTaskRepositoryIntegrationTests.TestConfiguration.class); + + applicationContextRunner.run((context -> { + TaskExplorer taskExplorer = context.getBean(TaskExplorer.class); + assertThat(taskExplorer.getTaskExecutionCount()).isOne(); + })); + applicationContextRunner.run((context -> { + TaskExplorer taskExplorer = context.getBean(TaskExplorer.class); + assertThat(taskExplorer.getTaskExecutionCount()).isEqualTo(2); + })); + } + + @EnableTask + @ImportAutoConfiguration(SimpleTaskAutoConfiguration.class) + static class TestConfiguration { + + public static boolean firstTime = true; + + @Bean + public DataSource dataSource() throws Exception { + MariaDbDataSource datasource = new MariaDbDataSource(); + datasource.setUrl(mariaDBContainer.getJdbcUrl()); + datasource.setUser(mariaDBContainer.getUsername()); + datasource.setPassword(mariaDBContainer.getPassword()); + if (firstTime) { + ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(); + databasePopulator + .addScript(new ClassPathResource("/org/springframework/cloud/task/schema-mariadb.sql")); + databasePopulator.execute(datasource); + firstTime = false; + } + return datasource; + } + + } + +} diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/dao/JdbcTaskExecutionDaoMariaDBIntegrationTests.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/dao/JdbcTaskExecutionDaoMariaDBIntegrationTests.java new file mode 100644 index 00000000..4d9b1859 --- /dev/null +++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/dao/JdbcTaskExecutionDaoMariaDBIntegrationTests.java @@ -0,0 +1,291 @@ +/* + * Copyright 2022-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.cloud.task.repository.dao; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.UUID; + +import javax.sql.DataSource; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mariadb.jdbc.MariaDbDataSource; +import org.testcontainers.containers.MariaDBContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.cloud.task.configuration.TestConfiguration; +import org.springframework.cloud.task.repository.TaskExecution; +import org.springframework.cloud.task.repository.TaskRepository; +import org.springframework.cloud.task.util.TestDBUtils; +import org.springframework.cloud.task.util.TestVerifierUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * Executes unit integration tests on JdbcTaskExecutionDao for MARIADB. + * + * @author Glenn Renfro + */ +@Tag("DockerRequired") +@Testcontainers +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { TestConfiguration.class, + JdbcTaskExecutionDaoMariaDBIntegrationTests.TestDataSourceConfiguration.class, + PropertyPlaceholderAutoConfiguration.class }) +public class JdbcTaskExecutionDaoMariaDBIntegrationTests extends BaseTaskExecutionDaoTestCases { + + private static final DockerImageName MARIADB_IMAGE = DockerImageName.parse("mariadb:10.9.3"); + + @Container + public static MariaDBContainer mariaDBContainer = new MariaDBContainer<>(MARIADB_IMAGE); + + @Autowired + TaskRepository repository; + + @Autowired + private DataSource dataSource; + + @BeforeEach + public void setup() { + final JdbcTaskExecutionDao dao = new JdbcTaskExecutionDao(this.dataSource); + dao.setTaskIncrementer(TestDBUtils.getIncrementer(this.dataSource)); + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); + jdbcTemplate.execute("TRUNCATE TABLE TASK_EXECUTION_PARAMS"); + jdbcTemplate.execute("DELETE FROM TASK_EXECUTION"); + jdbcTemplate.execute("ALTER SEQUENCE TASK_SEQ RESTART;"); + super.dao = dao; + } + + @Test + @DirtiesContext + public void testStartTaskExecution() { + TaskExecution expectedTaskExecution = this.dao.createTaskExecution(null, null, new ArrayList<>(0), null); + + expectedTaskExecution.setArguments(Collections.singletonList("foo=" + UUID.randomUUID().toString())); + expectedTaskExecution.setStartTime(LocalDateTime.now()); + expectedTaskExecution.setTaskName(UUID.randomUUID().toString()); + + this.dao.startTaskExecution(expectedTaskExecution.getExecutionId(), expectedTaskExecution.getTaskName(), + expectedTaskExecution.getStartTime(), expectedTaskExecution.getArguments(), + expectedTaskExecution.getExternalExecutionId()); + + TestVerifierUtils.verifyTaskExecution(expectedTaskExecution, + TestDBUtils.getTaskExecutionFromDB(this.dataSource, expectedTaskExecution.getExecutionId())); + } + + @Test + @DirtiesContext + public void createTaskExecution() { + TaskExecution expectedTaskExecution = TestVerifierUtils.createSampleTaskExecutionNoArg(); + expectedTaskExecution = this.dao.createTaskExecution(expectedTaskExecution.getTaskName(), + expectedTaskExecution.getStartTime(), expectedTaskExecution.getArguments(), + expectedTaskExecution.getExternalExecutionId()); + + TestVerifierUtils.verifyTaskExecution(expectedTaskExecution, + TestDBUtils.getTaskExecutionFromDB(this.dataSource, expectedTaskExecution.getExecutionId())); + } + + @Test + @DirtiesContext + public void createEmptyTaskExecution() { + TaskExecution expectedTaskExecution = this.dao.createTaskExecution(null, null, new ArrayList<>(0), null); + + TestVerifierUtils.verifyTaskExecution(expectedTaskExecution, + TestDBUtils.getTaskExecutionFromDB(this.dataSource, expectedTaskExecution.getExecutionId())); + } + + @Test + @DirtiesContext + public void completeTaskExecution() { + TaskExecution expectedTaskExecution = TestVerifierUtils.endSampleTaskExecutionNoArg(); + expectedTaskExecution = this.dao.createTaskExecution(expectedTaskExecution.getTaskName(), + expectedTaskExecution.getStartTime(), expectedTaskExecution.getArguments(), + expectedTaskExecution.getExternalExecutionId()); + this.dao.completeTaskExecution(expectedTaskExecution.getExecutionId(), expectedTaskExecution.getExitCode(), + expectedTaskExecution.getEndTime(), expectedTaskExecution.getExitMessage()); + TestVerifierUtils.verifyTaskExecution(expectedTaskExecution, + TestDBUtils.getTaskExecutionFromDB(this.dataSource, expectedTaskExecution.getExecutionId())); + } + + @Test + @DirtiesContext + public void completeTaskExecutionWithNoCreate() { + JdbcTaskExecutionDao dao = new JdbcTaskExecutionDao(this.dataSource); + + TaskExecution expectedTaskExecution = TestVerifierUtils.endSampleTaskExecutionNoArg(); + assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> { + dao.completeTaskExecution(expectedTaskExecution.getExecutionId(), expectedTaskExecution.getExitCode(), + expectedTaskExecution.getEndTime(), expectedTaskExecution.getExitMessage()); + }); + } + + @Test + @DirtiesContext + public void testFindAllPageableSort() { + initializeRepositoryNotInOrder(); + Sort sort = Sort.by(new Sort.Order(Sort.Direction.ASC, "EXTERNAL_EXECUTION_ID")); + Iterator iter = getPageIterator(0, 2, sort); + TaskExecution taskExecution = iter.next(); + assertThat(taskExecution.getTaskName()).isEqualTo("FOO2"); + taskExecution = iter.next(); + assertThat(taskExecution.getTaskName()).isEqualTo("FOO3"); + + iter = getPageIterator(1, 2, sort); + taskExecution = iter.next(); + assertThat(taskExecution.getTaskName()).isEqualTo("FOO1"); + } + + @Test + @DirtiesContext + public void testFindAllDefaultSort() { + initializeRepository(); + Iterator iter = getPageIterator(0, 2, null); + TaskExecution taskExecution = iter.next(); + assertThat(taskExecution.getTaskName()).isEqualTo("FOO1"); + taskExecution = iter.next(); + assertThat(taskExecution.getTaskName()).isEqualTo("FOO2"); + + iter = getPageIterator(1, 2, null); + taskExecution = iter.next(); + assertThat(taskExecution.getTaskName()).isEqualTo("FOO3"); + } + + @Test + @DirtiesContext + public void testStartExecutionWithNullExternalExecutionIdExisting() { + TaskExecution expectedTaskExecution = initializeTaskExecutionWithExternalExecutionId(); + + this.dao.startTaskExecution(expectedTaskExecution.getExecutionId(), expectedTaskExecution.getTaskName(), + expectedTaskExecution.getStartTime(), expectedTaskExecution.getArguments(), null); + TestVerifierUtils.verifyTaskExecution(expectedTaskExecution, + TestDBUtils.getTaskExecutionFromDB(this.dataSource, expectedTaskExecution.getExecutionId())); + } + + @Test + @DirtiesContext + public void testStartExecutionWithNullExternalExecutionIdNonExisting() { + TaskExecution expectedTaskExecution = initializeTaskExecutionWithExternalExecutionId(); + + this.dao.startTaskExecution(expectedTaskExecution.getExecutionId(), expectedTaskExecution.getTaskName(), + expectedTaskExecution.getStartTime(), expectedTaskExecution.getArguments(), "BAR"); + expectedTaskExecution.setExternalExecutionId("BAR"); + TestVerifierUtils.verifyTaskExecution(expectedTaskExecution, + TestDBUtils.getTaskExecutionFromDB(this.dataSource, expectedTaskExecution.getExecutionId())); + } + + @Test + @DirtiesContext + public void testFindRunningTaskExecutions() { + initializeRepositoryNotInOrderWithMultipleTaskExecutions(); + assertThat( + this.dao.findRunningTaskExecutions("FOO1", PageRequest.of(1, Integer.MAX_VALUE, Sort.by("START_TIME"))) + .getTotalElements()).isEqualTo(4); + } + + @Test + @DirtiesContext + public void testFindRunningTaskExecutionsIllegalSort() { + initializeRepositoryNotInOrderWithMultipleTaskExecutions(); + assertThatThrownBy(() -> this.dao + .findRunningTaskExecutions("FOO1", PageRequest.of(1, Integer.MAX_VALUE, Sort.by("ILLEGAL_SORT"))) + .getTotalElements()).isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid sort option selected: ILLEGAL_SORT"); + } + + @Test + @DirtiesContext + public void testFindRunningTaskExecutionsSortWithDifferentCase() { + initializeRepositoryNotInOrderWithMultipleTaskExecutions(); + assertThat( + this.dao.findRunningTaskExecutions("FOO1", PageRequest.of(1, Integer.MAX_VALUE, Sort.by("StArT_TiMe"))) + .getTotalElements()).isEqualTo(4); + } + + private TaskExecution initializeTaskExecutionWithExternalExecutionId() { + TaskExecution expectedTaskExecution = TestVerifierUtils.createSampleTaskExecutionNoArg(); + return this.dao.createTaskExecution(expectedTaskExecution.getTaskName(), expectedTaskExecution.getStartTime(), + expectedTaskExecution.getArguments(), "FOO1"); + } + + private Iterator getPageIterator(int pageNum, int pageSize, Sort sort) { + Pageable pageable = (sort == null) ? PageRequest.of(pageNum, pageSize) + : PageRequest.of(pageNum, pageSize, sort); + Page page = this.dao.findAll(pageable); + assertThat(page.getTotalElements()).isEqualTo(3); + assertThat(page.getTotalPages()).isEqualTo(2); + return page.iterator(); + } + + private void initializeRepository() { + this.repository.createTaskExecution(getTaskExecution("FOO3", "externalA")); + this.repository.createTaskExecution(getTaskExecution("FOO2", "externalB")); + this.repository.createTaskExecution(getTaskExecution("FOO1", "externalC")); + } + + private void initializeRepositoryNotInOrder() { + this.repository.createTaskExecution(getTaskExecution("FOO1", "externalC")); + this.repository.createTaskExecution(getTaskExecution("FOO2", "externalA")); + this.repository.createTaskExecution(getTaskExecution("FOO3", "externalB")); + } + + @Configuration + static class TestDataSourceConfiguration { + + public static boolean firstTime = true; + + @Bean + public DataSource dataSource() throws Exception { + MariaDbDataSource datasource = new MariaDbDataSource(); + datasource.setUrl(mariaDBContainer.getJdbcUrl()); + datasource.setUser(mariaDBContainer.getUsername()); + datasource.setPassword(mariaDBContainer.getPassword()); + if (firstTime) { + ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(); + databasePopulator + .addScript(new ClassPathResource("/org/springframework/cloud/task/schema-mariadb.sql")); + databasePopulator.execute(datasource); + firstTime = false; + } + return datasource; + } + + } + +} diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/DatabaseTypeTests.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/DatabaseTypeTests.java index b02bc89b..5462aa2d 100644 --- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/DatabaseTypeTests.java +++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/DatabaseTypeTests.java @@ -25,6 +25,7 @@ import org.springframework.cloud.task.util.TestDBUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.springframework.cloud.task.repository.support.DatabaseType.HSQL; +import static org.springframework.cloud.task.repository.support.DatabaseType.MARIADB; import static org.springframework.cloud.task.repository.support.DatabaseType.MYSQL; import static org.springframework.cloud.task.repository.support.DatabaseType.ORACLE; import static org.springframework.cloud.task.repository.support.DatabaseType.POSTGRES; @@ -46,7 +47,7 @@ public class DatabaseTypeTests { assertThat(fromProductName("Oracle")).isEqualTo(ORACLE); assertThat(fromProductName("PostgreSQL")).isEqualTo(POSTGRES); assertThat(fromProductName("MySQL")).isEqualTo(MYSQL); - assertThat(fromProductName("MariaDB")).isEqualTo(MYSQL); + assertThat(fromProductName("MariaDB")).isEqualTo(MARIADB); } @Test @@ -81,7 +82,7 @@ public class DatabaseTypeTests { @Test public void testFromMetaDataForMariaDB() throws Exception { DataSource ds = TestDBUtils.getMockDataSource("MariaDB"); - assertThat(DatabaseType.fromMetaData(ds)).isEqualTo(MYSQL); + assertThat(DatabaseType.fromMetaData(ds)).isEqualTo(MARIADB); } } diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestVerifierUtils.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestVerifierUtils.java index e5e2ef9e..012b4d51 100644 --- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestVerifierUtils.java +++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestVerifierUtils.java @@ -133,12 +133,34 @@ public final class TestVerifierUtils { assertThat(actualTaskExecution.getExecutionId()).as("taskExecutionId must be equal") .isEqualTo(expectedTaskExecution.getExecutionId()); if (actualTaskExecution.getStartTime() != null) { - assertThat(actualTaskExecution.getStartTime().isEqual(expectedTaskExecution.getStartTime())) - .as("startTime must be equal").isTrue(); + assertThat(actualTaskExecution.getStartTime().getHour() == expectedTaskExecution.getStartTime().getHour()) + .as("startTime hour must be equal").isTrue(); + assertThat( + actualTaskExecution.getStartTime().getMinute() == expectedTaskExecution.getStartTime().getMinute()) + .as("startTime minute must be equal").isTrue(); + assertThat( + actualTaskExecution.getStartTime().getSecond() == expectedTaskExecution.getStartTime().getSecond()) + .as("startTime second must be equal").isTrue(); + assertThat(actualTaskExecution.getStartTime().getDayOfYear() == expectedTaskExecution.getStartTime() + .getDayOfYear()).as("startTime day must be equal").isTrue(); + assertThat(actualTaskExecution.getStartTime().getYear() == expectedTaskExecution.getStartTime().getYear()) + .as("startTime year must be equal").isTrue(); + assertThat(actualTaskExecution.getStartTime().getMonthValue() == expectedTaskExecution.getStartTime() + .getMonthValue()).as("startTime month must be equal").isTrue(); } if (actualTaskExecution.getEndTime() != null) { - assertThat(actualTaskExecution.getEndTime().isEqual(expectedTaskExecution.getEndTime())) - .as("endTime must be equal").isTrue(); + assertThat(actualTaskExecution.getEndTime().getHour() == expectedTaskExecution.getEndTime().getHour()) + .as("endTime hour must be equal").isTrue(); + assertThat(actualTaskExecution.getEndTime().getMinute() == expectedTaskExecution.getEndTime().getMinute()) + .as("endTime minute must be equal").isTrue(); + assertThat(actualTaskExecution.getEndTime().getSecond() == expectedTaskExecution.getEndTime().getSecond()) + .as("endTime second must be equal").isTrue(); + assertThat(actualTaskExecution.getEndTime().getDayOfYear() == expectedTaskExecution.getEndTime() + .getDayOfYear()).as("endTime day must be equal").isTrue(); + assertThat(actualTaskExecution.getEndTime().getYear() == expectedTaskExecution.getEndTime().getYear()) + .as("endTime year must be equal").isTrue(); + assertThat(actualTaskExecution.getEndTime().getMonthValue() == expectedTaskExecution.getEndTime() + .getMonthValue()).as("endTime month must be equal").isTrue(); } assertThat(actualTaskExecution.getExitCode()).as("exitCode must be equal") .isEqualTo(expectedTaskExecution.getExitCode());