From ecd86afd62907fedabf978e0e63cc37ad7e7a051 Mon Sep 17 00:00:00 2001 From: Chris Bono Date: Wed, 1 Nov 2023 17:01:21 -0500 Subject: [PATCH] Polish "Add async task execution cleanup" --- .github/workflows/ci-pr.yml | 9 +- spring-cloud-dataflow-server-core/pom.xml | 5 + ...va => DataflowAsyncAutoConfiguration.java} | 36 ++-- .../controller/TaskExecutionController.java | 6 +- .../main/resources/META-INF/spring.factories | 2 +- ...kExecutionControllerCleanupAsyncTests.java | 155 ++++++++++++++++++ .../TaskExecutionControllerTests.java | 119 +++++++------- 7 files changed, 258 insertions(+), 74 deletions(-) rename spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/config/{DataflowAsyncConfiguration.java => DataflowAsyncAutoConfiguration.java} (57%) create mode 100644 spring-cloud-dataflow-server-core/src/test/java/org/springframework/cloud/dataflow/server/controller/TaskExecutionControllerCleanupAsyncTests.java diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index 650ce7cb3b..428a20e25a 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -31,14 +31,13 @@ jobs: - name: Build run: | mvn -B -s .github/settings.xml clean install - - name: Test Report - uses: dorny/test-reporter@v1 - if: ${{ success() || failure() }} + - name: Capture Test Results + if: failure() + uses: actions/upload-artifact@v3 with: name: Unit Tests path: '**/surefire-reports/*.xml' - reporter: java-junit - list-tests: failed + retention-days: 3 # clean m2 cache - name: Clean cache run: | diff --git a/spring-cloud-dataflow-server-core/pom.xml b/spring-cloud-dataflow-server-core/pom.xml index 480abb443d..420ad3b4b9 100644 --- a/spring-cloud-dataflow-server-core/pom.xml +++ b/spring-cloud-dataflow-server-core/pom.xml @@ -244,6 +244,11 @@ junit-jupiter test + + org.awaitility + awaitility + test + org.testcontainers mariadb diff --git a/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/config/DataflowAsyncConfiguration.java b/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/config/DataflowAsyncAutoConfiguration.java similarity index 57% rename from spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/config/DataflowAsyncConfiguration.java rename to spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/config/DataflowAsyncAutoConfiguration.java index 5b331c4f43..149de5c003 100644 --- a/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/config/DataflowAsyncConfiguration.java +++ b/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/config/DataflowAsyncAutoConfiguration.java @@ -21,37 +21,51 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; import org.springframework.boot.task.TaskExecutorBuilder; import org.springframework.cloud.dataflow.core.DataFlowPropertyKeys; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; -import static org.springframework.cloud.dataflow.server.config.DataflowAsyncConfiguration.ASYNC_PREFIX; +import static org.springframework.cloud.dataflow.server.config.DataflowAsyncAutoConfiguration.ASYNC_PROPS_PREFIX; /** - * Class to override the executor at the application level. It also enables async executions for the Spring Cloud Data Flow Server. + * Enables async executions for the Spring Cloud Dataflow server. + * Uses the Spring Boot autoconfigured {@code TaskExecutorBuilder} to create an async executor and register it + * with name {@link #DATAFLOW_ASYNC_EXECUTOR}. * * @author Tobias Soloschenko */ @Configuration(proxyBeanMethods = false) -@ConditionalOnProperty(prefix = ASYNC_PREFIX, name = "enabled") +@ConditionalOnBean({EnableDataFlowServerConfiguration.Marker.class}) +@ConditionalOnProperty(prefix = ASYNC_PROPS_PREFIX, name = "enabled", havingValue = "true") +@AutoConfigureAfter(TaskExecutionAutoConfiguration.class) @EnableAsync -class DataflowAsyncConfiguration implements AsyncConfigurer { +public class DataflowAsyncAutoConfiguration implements AsyncConfigurer { - private static final Logger logger = LoggerFactory.getLogger(DataflowAsyncConfiguration.class); + private static final Logger logger = LoggerFactory.getLogger(DataflowAsyncAutoConfiguration.class); - public static final String ASYNC_PREFIX = DataFlowPropertyKeys.PREFIX + "async"; + public static final String ASYNC_PROPS_PREFIX = DataFlowPropertyKeys.PREFIX + "async"; + + public static final String DATAFLOW_ASYNC_EXECUTOR = "dataflowAsyncExecutor"; private static final String THREAD_NAME_PREFIX = "scdf-async-"; - @Bean(name = "asyncExecutor") - Executor getAsyncExecutor(TaskExecutorBuilder taskExecutorBuilder) { - return taskExecutorBuilder.threadNamePrefix(THREAD_NAME_PREFIX).build(); + private final TaskExecutorBuilder taskExecutorBuilder; + + public DataflowAsyncAutoConfiguration(TaskExecutorBuilder taskExecutorBuilder) { + this.taskExecutorBuilder = taskExecutorBuilder; + } + + @Bean(name = DATAFLOW_ASYNC_EXECUTOR) + @Override + public Executor getAsyncExecutor() { + return this.taskExecutorBuilder.threadNamePrefix(THREAD_NAME_PREFIX).build(); } @Override diff --git a/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/controller/TaskExecutionController.java b/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/controller/TaskExecutionController.java index e51663551c..0c5d2d9197 100644 --- a/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/controller/TaskExecutionController.java +++ b/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/controller/TaskExecutionController.java @@ -48,6 +48,7 @@ import org.springframework.cloud.dataflow.rest.util.TaskSanitizer; import org.springframework.cloud.dataflow.schema.AggregateTaskExecution; import org.springframework.cloud.dataflow.schema.SchemaVersionTarget; +import org.springframework.cloud.dataflow.server.config.DataflowAsyncAutoConfiguration; import org.springframework.cloud.dataflow.server.controller.support.TaskExecutionControllerDeleteAction; import org.springframework.cloud.dataflow.server.repository.NoSuchTaskDefinitionException; import org.springframework.cloud.dataflow.server.repository.NoSuchTaskExecutionException; @@ -337,6 +338,9 @@ public void cleanup( * Cleanup resources associated with one or more task executions. The * optional {@code actions} and {@code completed} parameters can be used to not only clean up task execution resources, * but can also trigger the deletion of task execution and job data in the persistence store. + *

+ * When the {@code spring.cloud.dataflow.async.enabled} property is set to {@code true} the cleanup will happen + * asynchronously. * * @param actions the actions to perform (default 'CLEANUP') * @param completed whether to include only completed task executions (default false) @@ -345,7 +349,7 @@ public void cleanup( */ @RequestMapping(method = RequestMethod.DELETE) @ResponseStatus(HttpStatus.OK) - @Async + @Async(DataflowAsyncAutoConfiguration.DATAFLOW_ASYNC_EXECUTOR) public void cleanupAll( @RequestParam(defaultValue = "CLEANUP", name = "action") TaskExecutionControllerDeleteAction[] actions, @RequestParam(defaultValue = "false", name = "completed") boolean completed, diff --git a/spring-cloud-dataflow-server-core/src/main/resources/META-INF/spring.factories b/spring-cloud-dataflow-server-core/src/main/resources/META-INF/spring.factories index a38df9d9d1..b71d04dc0e 100644 --- a/spring-cloud-dataflow-server-core/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-dataflow-server-core/src/main/resources/META-INF/spring.factories @@ -6,7 +6,7 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.dataflow.server.config.DataFlowServerAutoConfiguration,\ org.springframework.cloud.dataflow.server.config.DataFlowControllerAutoConfiguration, \ org.springframework.cloud.dataflow.server.config.SpringDocAutoConfiguration, \ - org.springframework.cloud.dataflow.server.config.DataflowAsyncConfiguration + org.springframework.cloud.dataflow.server.config.DataflowAsyncAutoConfiguration org.springframework.context.ApplicationContextInitializer=\ org.springframework.cloud.dataflow.common.flyway.FlywayVendorReplacingApplicationContextInitializer diff --git a/spring-cloud-dataflow-server-core/src/test/java/org/springframework/cloud/dataflow/server/controller/TaskExecutionControllerCleanupAsyncTests.java b/spring-cloud-dataflow-server-core/src/test/java/org/springframework/cloud/dataflow/server/controller/TaskExecutionControllerCleanupAsyncTests.java new file mode 100644 index 0000000000..c43547d766 --- /dev/null +++ b/spring-cloud-dataflow-server-core/src/test/java/org/springframework/cloud/dataflow/server/controller/TaskExecutionControllerCleanupAsyncTests.java @@ -0,0 +1,155 @@ +/* + * Copyright 2016-2020 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.dataflow.server.controller; + +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import org.awaitility.Awaitility; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.batch.BatchProperties; +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.dataflow.aggregate.task.AggregateExecutionSupport; +import org.springframework.cloud.dataflow.aggregate.task.TaskDefinitionReader; +import org.springframework.cloud.dataflow.core.Launcher; +import org.springframework.cloud.dataflow.core.TaskDefinition; +import org.springframework.cloud.dataflow.core.TaskDeployment; +import org.springframework.cloud.dataflow.core.TaskPlatform; +import org.springframework.cloud.dataflow.schema.SchemaVersionTarget; +import org.springframework.cloud.dataflow.server.EnableDataFlowServer; +import org.springframework.cloud.dataflow.server.config.DataflowAsyncAutoConfiguration; +import org.springframework.cloud.dataflow.server.config.apps.CommonApplicationProperties; +import org.springframework.cloud.dataflow.server.configuration.JobDependencies; +import org.springframework.cloud.dataflow.server.job.LauncherRepository; +import org.springframework.cloud.dataflow.server.repository.TaskDefinitionRepository; +import org.springframework.cloud.dataflow.server.repository.TaskDeploymentRepository; +import org.springframework.cloud.dataflow.server.repository.TaskExecutionDaoContainer; +import org.springframework.cloud.deployer.spi.task.TaskLauncher; +import org.springframework.cloud.task.repository.TaskExecution; +import org.springframework.cloud.task.repository.dao.TaskExecutionDao; +import org.springframework.http.MediaType; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.internal.verification.VerificationModeFactory.times; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Unit tests for the {@link TaskExecutionController#cleanupAll async cleanup} API. + * + * @author Chris Bono + */ +@SpringBootTest( + properties = "spring.cloud.dataflow.async.enabled=true", + classes = { JobDependencies.class, TaskExecutionAutoConfiguration.class, DataflowAsyncAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, BatchProperties.class}) +@EnableConfigurationProperties({CommonApplicationProperties.class}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) +@AutoConfigureTestDatabase(replace = Replace.ANY) +@EnableDataFlowServer +public class TaskExecutionControllerCleanupAsyncTests { + + @Autowired + private TaskExecutionDaoContainer daoContainer; + + @Autowired + private TaskDefinitionRepository taskDefinitionRepository; + + private MockMvc mockMvc; + + @Autowired + private WebApplicationContext wac; + + @Autowired + private AggregateExecutionSupport aggregateExecutionSupport; + + @Autowired + private TaskLauncher taskLauncher; + + @Autowired + private LauncherRepository launcherRepository; + + @Autowired + private TaskPlatform taskPlatform; + + @Autowired + private TaskDeploymentRepository taskDeploymentRepository; + + @Autowired + TaskDefinitionReader taskDefinitionReader; + + @BeforeEach + public void setupMockMVC() { + assertThat(this.launcherRepository.findByName("default")).isNull(); + Launcher launcher = new Launcher("default", "local", taskLauncher); + launcherRepository.save(launcher); + taskPlatform.setLaunchers(Collections.singletonList(launcher)); + this.mockMvc = MockMvcBuilders.webAppContextSetup(wac) + .defaultRequest(get("/").accept(MediaType.APPLICATION_JSON)).build(); + } + + @Test + void cleanupAll() throws Exception { + String taskExecutionId = "asyncCleanupAllTaskExecId"; + setupTaskExecutions("asyncCleanupAllTaskName", taskExecutionId); + mockMvc.perform(delete("/tasks/executions")) + .andDo(print()) + .andExpect(status().is(200)); + verify(taskLauncher, times(0)).cleanup(taskExecutionId); + Awaitility.await() + .atMost(Duration.ofSeconds(3)) + .untilAsserted(() -> verify(taskLauncher, times(2)).cleanup(taskExecutionId)); + } + + private void setupTaskExecutions(String taskName, String taskExecutionId) { + taskDefinitionRepository.save(new TaskDefinition(taskName, "taskDslGoesHere")); + SchemaVersionTarget schemaVersionTarget = aggregateExecutionSupport.findSchemaVersionTarget(taskName, taskDefinitionReader); + TaskExecutionDao taskExecutionDao = daoContainer.get(schemaVersionTarget.getName()); + + List taskArgs = new ArrayList<>(); + taskArgs.add("foo=bar"); + TaskExecution taskExecution1 = taskExecutionDao.createTaskExecution(taskName, new Date(), taskArgs, taskExecutionId); + taskExecutionDao.createTaskExecution(taskName, new Date(), taskArgs, taskExecutionId, taskExecution1.getExecutionId()); + + TaskDeployment taskDeployment = new TaskDeployment(); + taskDeployment.setTaskDefinitionName(taskName); + taskDeployment.setTaskDeploymentId(taskExecutionId); + taskDeployment.setPlatformName("default"); + taskDeployment.setCreatedOn(Instant.now()); + taskDeploymentRepository.save(taskDeployment); + } + +} diff --git a/spring-cloud-dataflow-server-core/src/test/java/org/springframework/cloud/dataflow/server/controller/TaskExecutionControllerTests.java b/spring-cloud-dataflow-server-core/src/test/java/org/springframework/cloud/dataflow/server/controller/TaskExecutionControllerTests.java index 763ea5a64c..20f6896ef9 100644 --- a/spring-cloud-dataflow-server-core/src/test/java/org/springframework/cloud/dataflow/server/controller/TaskExecutionControllerTests.java +++ b/spring-cloud-dataflow-server-core/src/test/java/org/springframework/cloud/dataflow/server/controller/TaskExecutionControllerTests.java @@ -28,9 +28,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobInstance; @@ -39,6 +38,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.batch.BatchProperties; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; @@ -57,6 +57,7 @@ import org.springframework.cloud.dataflow.schema.AppBootSchemaVersion; import org.springframework.cloud.dataflow.schema.SchemaVersionTarget; import org.springframework.cloud.dataflow.schema.service.SchemaService; +import org.springframework.cloud.dataflow.server.config.DataflowAsyncAutoConfiguration; import org.springframework.cloud.dataflow.server.config.apps.CommonApplicationProperties; import org.springframework.cloud.dataflow.server.configuration.JobDependencies; import org.springframework.cloud.dataflow.server.job.LauncherRepository; @@ -76,13 +77,13 @@ import org.springframework.hateoas.mediatype.hal.Jackson2HalModule; import org.springframework.http.MediaType; import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.hasSize; @@ -91,6 +92,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.internal.verification.VerificationModeFactory.times; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -104,9 +106,11 @@ * @author Ilayaperumal Gopinathan * @author David Turanski * @author Gunnar Hillert + * @author Chris Bono */ -@RunWith(SpringRunner.class) -@SpringBootTest(classes = {JobDependencies.class, PropertyPlaceholderAutoConfiguration.class, BatchProperties.class}) +@SpringBootTest( + classes = { JobDependencies.class, TaskExecutionAutoConfiguration.class, DataflowAsyncAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, BatchProperties.class}) @EnableConfigurationProperties({CommonApplicationProperties.class}) @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) @AutoConfigureTestDatabase(replace = Replace.ANY) @@ -183,7 +187,7 @@ public class TaskExecutionControllerTests { TaskDefinitionReader taskDefinitionReader; - @Before + @BeforeEach public void setupMockMVC() { assertThat(this.launcherRepository.findByName("default")).isNull(); Launcher launcher = new Launcher("default", "local", taskLauncher); @@ -238,22 +242,21 @@ public void setupMockMVC() { } } - @Test(expected = IllegalArgumentException.class) - public void testTaskExecutionControllerConstructorMissingExplorer() { - new TaskExecutionController( + @Test + void taskExecutionControllerConstructorMissingExplorer() { + assertThatIllegalArgumentException().isThrownBy(() -> new TaskExecutionController( null, aggregateExecutionSupport, taskExecutionService, taskDefinitionRepository, taskDefinitionReader, taskExecutionInfoService, taskDeleteService, - taskJobService - ); + taskJobService)); } - @Test(expected = IllegalArgumentException.class) - public void testTaskExecutionControllerConstructorMissingTaskService() { - new TaskExecutionController( + @Test + void taskExecutionControllerConstructorMissingTaskService() { + assertThatIllegalArgumentException().isThrownBy(() -> new TaskExecutionController( taskExplorer, aggregateExecutionSupport, null, @@ -261,68 +264,63 @@ public void testTaskExecutionControllerConstructorMissingTaskService() { taskDefinitionReader, taskExecutionInfoService, taskDeleteService, - taskJobService - ); + taskJobService)); } - @Test(expected = IllegalArgumentException.class) - public void testTaskExecutionControllerConstructorMissingTaskDefinitionRepository() { - new TaskExecutionController( + @Test + void taskExecutionControllerConstructorMissingTaskDefinitionRepository() { + assertThatIllegalArgumentException().isThrownBy(() -> new TaskExecutionController( taskExplorer, aggregateExecutionSupport, taskExecutionService, null, taskDefinitionReader, taskExecutionInfoService, taskDeleteService, - taskJobService - ); + taskJobService)); } - @Test(expected = IllegalArgumentException.class) - public void testTaskExecutionControllerConstructorMissingTaskDefinitionRetriever() { - new TaskExecutionController(taskExplorer, + @Test + void taskExecutionControllerConstructorMissingTaskDefinitionRetriever() { + assertThatIllegalArgumentException().isThrownBy(() -> new TaskExecutionController(taskExplorer, aggregateExecutionSupport, taskExecutionService, taskDefinitionRepository, taskDefinitionReader, null, taskDeleteService, - taskJobService - ); + taskJobService)); } - @Test(expected = IllegalArgumentException.class) - public void testTaskExecutionControllerConstructorMissingDeleteTaskService() { - new TaskExecutionController(taskExplorer, + @Test + void taskExecutionControllerConstructorMissingDeleteTaskService() { + assertThatIllegalArgumentException().isThrownBy(() -> new TaskExecutionController(taskExplorer, aggregateExecutionSupport, taskExecutionService, taskDefinitionRepository, taskDefinitionReader, taskExecutionInfoService, null, - taskJobService - ); + taskJobService)); } - @Test(expected = IllegalArgumentException.class) - public void testTaskExecutionControllerConstructorMissingDeleteTaskJobService() { - new TaskExecutionController(taskExplorer, + @Test + void taskExecutionControllerConstructorMissingDeleteTaskJobService() { + assertThatIllegalArgumentException().isThrownBy(() -> new TaskExecutionController(taskExplorer, aggregateExecutionSupport, taskExecutionService, taskDefinitionRepository, taskDefinitionReader, taskExecutionInfoService, taskDeleteService, - null - ); + null)); } @Test - public void testGetExecutionNotFound() throws Exception { + void getExecutionNotFound() throws Exception { mockMvc.perform(get("/tasks/executions/1345345345345").accept(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().isNotFound()); } @Test - public void testGetExecution() throws Exception { + void getExecution() throws Exception { verifyTaskArgs(SAMPLE_CLEANSED_ARGUMENT_LIST, "", mockMvc.perform(get("/tasks/executions/1").accept(MediaType.APPLICATION_JSON)) .andDo(print()) @@ -333,7 +331,7 @@ public void testGetExecution() throws Exception { } @Test - public void testGetChildTaskExecution() throws Exception { + void getChildTaskExecution() throws Exception { verifyTaskArgs(SAMPLE_CLEANSED_ARGUMENT_LIST, "", mockMvc.perform(get("/tasks/executions/2").accept(MediaType.APPLICATION_JSON)) .andDo(print()) @@ -343,7 +341,7 @@ public void testGetChildTaskExecution() throws Exception { } @Test - public void testGetExecutionForJob() throws Exception { + void getExecutionForJob() throws Exception { mockMvc.perform(get("/tasks/executions/4").accept(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().isOk()) @@ -353,7 +351,7 @@ public void testGetExecutionForJob() throws Exception { } @Test - public void testGetAllExecutions() throws Exception { + void getAllExecutions() throws Exception { verifyTaskArgs(SAMPLE_CLEANSED_ARGUMENT_LIST, "$._embedded.taskExecutionResourceList[0].", mockMvc.perform(get("/tasks/executions/").accept(MediaType.APPLICATION_JSON)) .andDo(print()) @@ -364,7 +362,7 @@ public void testGetAllExecutions() throws Exception { } @Test - public void testGetCurrentExecutions() throws Exception { + void getCurrentExecutions() throws Exception { when(taskLauncher.getRunningTaskExecutionCount()).thenReturn(4); mockMvc.perform(get("/tasks/executions/current").accept(MediaType.APPLICATION_JSON)) .andDo(print()) @@ -374,7 +372,7 @@ public void testGetCurrentExecutions() throws Exception { } @Test - public void testBoot3Execution() throws Exception { + void boot3Execution() throws Exception { if (appRegistryService.getDefaultApp("timestamp3", ApplicationType.task) == null) { appRegistryService.save("timestamp3", ApplicationType.task, @@ -424,7 +422,7 @@ public void testBoot3Execution() throws Exception { } @Test - public void testInvalidBoot3Execution() throws Exception { + void invalidBoot3Execution() throws Exception { if (appRegistryService.getDefaultApp("timestamp3", ApplicationType.task) == null) { appRegistryService.save("timestamp3", ApplicationType.task, @@ -448,7 +446,7 @@ public void testInvalidBoot3Execution() throws Exception { } @Test - public void testBoot2Execution() throws Exception { + void boot2Execution() throws Exception { if (appRegistryService.getDefaultApp("timestamp2", ApplicationType.task) == null) { appRegistryService.save("timestamp2", ApplicationType.task, @@ -499,7 +497,7 @@ public void testBoot2Execution() throws Exception { } @Test - public void testGetExecutionsByName() throws Exception { + void getExecutionsByName() throws Exception { verifyTaskArgs(SAMPLE_CLEANSED_ARGUMENT_LIST, "$._embedded.taskExecutionResourceList[0].", mockMvc.perform(get("/tasks/executions/").param("name", TASK_NAME_ORIG).accept(MediaType.APPLICATION_JSON)) .andDo(print()) @@ -512,7 +510,7 @@ public void testGetExecutionsByName() throws Exception { } @Test - public void testGetExecutionsByNameNotFound() throws Exception { + void getExecutionsByNameNotFound() throws Exception { mockMvc.perform(get("/tasks/executions/").param("name", "BAZ").accept(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().is4xxClientError()).andReturn().getResponse().getContentAsString() @@ -520,7 +518,7 @@ public void testGetExecutionsByNameNotFound() throws Exception { } @Test - public void testCleanup() throws Exception { + void cleanup() throws Exception { mockMvc.perform(delete("/tasks/executions/1")) .andDo(print()) .andExpect(status().is(200)); @@ -528,7 +526,15 @@ public void testCleanup() throws Exception { } @Test - public void testCleanupWithActionParam() throws Exception { + void cleanupAll() throws Exception { + mockMvc.perform(delete("/tasks/executions")) + .andDo(print()) + .andExpect(status().is(200)); + verify(taskLauncher, times(2)).cleanup("foobar"); + } + + @Test + void cleanupWithActionParam() throws Exception { mockMvc.perform(delete("/tasks/executions/1").param("action", "CLEANUP")) .andDo(print()) .andExpect(status().is(200)); @@ -536,7 +542,7 @@ public void testCleanupWithActionParam() throws Exception { } @Test - public void testCleanupWithInvalidAction() throws Exception { + void cleanupWithInvalidAction() throws Exception { mockMvc.perform(delete("/tasks/executions/1").param("action", "does_not_exist").accept(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().is(400)) @@ -545,7 +551,7 @@ public void testCleanupWithInvalidAction() throws Exception { } @Test - public void testCleanupByIdNotFound() throws Exception { + void cleanupByIdNotFound() throws Exception { mockMvc.perform(delete("/tasks/executions/10")) .andDo(print()) .andExpect(status().is(404)) @@ -555,7 +561,7 @@ public void testCleanupByIdNotFound() throws Exception { } @Test - public void testDeleteSingleTaskExecutionById() throws Exception { + void deleteSingleTaskExecutionById() throws Exception { verifyTaskArgs(SAMPLE_CLEANSED_ARGUMENT_LIST, "$._embedded.taskExecutionResourceList[0].", mockMvc.perform(get("/tasks/executions/").accept(MediaType.APPLICATION_JSON)) .andDo(print()) @@ -579,7 +585,7 @@ public void testDeleteSingleTaskExecutionById() throws Exception { * execution will be deleted as well. */ @Test - public void testDeleteThreeTaskExecutionsById() throws Exception { + void deleteThreeTaskExecutionsById() throws Exception { verifyTaskArgs(SAMPLE_CLEANSED_ARGUMENT_LIST, "$._embedded.taskExecutionResourceList[0].", mockMvc.perform(get("/tasks/executions/").accept(MediaType.APPLICATION_JSON)) .andDo(print()) @@ -598,7 +604,7 @@ public void testDeleteThreeTaskExecutionsById() throws Exception { } @Test - public void testDeleteAllTaskExecutions() throws Exception { + void deleteAllTaskExecutions() throws Exception { verifyTaskArgs(SAMPLE_CLEANSED_ARGUMENT_LIST, "$._embedded.taskExecutionResourceList[0].", mockMvc.perform(get("/tasks/executions/").accept(MediaType.APPLICATION_JSON)) .andDo(print()) @@ -623,7 +629,7 @@ private ResultActions verifyTaskArgs(List expectedArgs, String prefix, R } @Test - public void testSorting() throws Exception { + void sorting() throws Exception { mockMvc.perform(get("/tasks/executions").param("sort", "TASK_EXECUTION_ID").accept(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().isOk()); @@ -640,4 +646,5 @@ public void testSorting() throws Exception { .andExpect(status().is5xxServerError()) .andExpect(content().string(containsString("Sorting column wrong_field not allowed"))); } + }