From 9b50d884094baa2709815f0f4ccdac78b25ff9a1 Mon Sep 17 00:00:00 2001 From: Stefan Nemeth Date: Fri, 14 Feb 2025 23:45:19 +0100 Subject: [PATCH] Add test label --- .../app/core/modules/openapi/schemas.gen.ts | 6 +-- .../src/app/core/modules/openapi/types.gen.ts | 6 +-- .../project-settings.component.ts | 3 +- server/application-server/openapi.yaml | 6 ++- .../aet/helios/tests/TestResultProcessor.java | 37 +++++++++++++++---- .../helios/tests/TestResultRepository.java | 4 +- .../tum/cit/aet/helios/tests/TestsConfig.java | 24 ++++++++++++ .../tum/cit/aet/helios/workflow/Workflow.java | 3 +- .../aet/helios/workflow/WorkflowService.java | 7 ++++ .../db/migration/V9__test_results.sql | 6 +-- 10 files changed, 78 insertions(+), 24 deletions(-) create mode 100644 server/application-server/src/main/java/de/tum/cit/aet/helios/tests/TestsConfig.java diff --git a/client/src/app/core/modules/openapi/schemas.gen.ts b/client/src/app/core/modules/openapi/schemas.gen.ts index b5a8305a9..ec0483ec1 100644 --- a/client/src/app/core/modules/openapi/schemas.gen.ts +++ b/client/src/app/core/modules/openapi/schemas.gen.ts @@ -605,10 +605,10 @@ export const PullRequestSchema = { merged: { type: 'boolean', }, - pullRequest: { + draft: { type: 'boolean', }, - draft: { + pullRequest: { type: 'boolean', }, locked: { @@ -855,7 +855,7 @@ export const WorkflowDtoSchema = { }, label: { type: 'string', - enum: ['BUILD', 'DEPLOYMENT', 'NONE'], + enum: ['BUILD', 'DEPLOYMENT', 'NONE', 'TEST'], }, createdAt: { type: 'string', diff --git a/client/src/app/core/modules/openapi/types.gen.ts b/client/src/app/core/modules/openapi/types.gen.ts index 8b2bca277..0b189105a 100644 --- a/client/src/app/core/modules/openapi/types.gen.ts +++ b/client/src/app/core/modules/openapi/types.gen.ts @@ -181,8 +181,8 @@ export type PullRequest = { requestedReviewers?: Array; workflowRuns?: Array; merged?: boolean; - pullRequest?: boolean; draft?: boolean; + pullRequest?: boolean; locked?: boolean; }; @@ -266,7 +266,7 @@ export type WorkflowDto = { url?: string; htmlUrl?: string; badgeUrl?: string; - label: 'BUILD' | 'DEPLOYMENT' | 'NONE'; + label: 'BUILD' | 'DEPLOYMENT' | 'NONE' | 'TEST'; createdAt?: string; updatedAt?: string; }; @@ -453,7 +453,7 @@ export type BranchDetailsDto = { }; export type UpdateWorkflowLabelData = { - body: 'BUILD' | 'DEPLOYMENT' | 'NONE'; + body: 'BUILD' | 'DEPLOYMENT' | 'NONE' | 'TEST'; path: { workflowId: number; }; diff --git a/client/src/app/pages/project-settings/project-settings.component.ts b/client/src/app/pages/project-settings/project-settings.component.ts index ca69160f3..c4b3cfdab 100644 --- a/client/src/app/pages/project-settings/project-settings.component.ts +++ b/client/src/app/pages/project-settings/project-settings.component.ts @@ -67,7 +67,7 @@ export class ProjectSettingsComponent { showAddGroupDialog = false; newGroupName = ''; // Store the previous label temporarily for the confirmation dialog - private previousLabel: 'BUILD' | 'DEPLOYMENT' | 'NONE' = 'NONE'; + private previousLabel: 'BUILD' | 'DEPLOYMENT' | 'NONE' | 'TEST' = 'NONE'; // Drag & Drop logic for groupedWorkflowsArray private dragIndex: number | null = null; @@ -253,6 +253,7 @@ export class ProjectSettingsComponent {
  • DEPLOYMENT: This label sets the workflow to trigger server deployments.
  • BUILD: This label sets the workflow to trigger build processes.
  • +
  • TEST: This label sets the workflow to be searched for test artifacts.
  • NONE: No label is set for this workflow.
diff --git a/server/application-server/openapi.yaml b/server/application-server/openapi.yaml index b62771a1d..b0f009735 100644 --- a/server/application-server/openapi.yaml +++ b/server/application-server/openapi.yaml @@ -34,6 +34,7 @@ paths: - BUILD - DEPLOYMENT - NONE + - TEST required: true responses: "200": @@ -1344,10 +1345,10 @@ components: uniqueItems: true merged: type: boolean - pullRequest: - type: boolean draft: type: boolean + pullRequest: + type: boolean locked: type: boolean required: @@ -1553,6 +1554,7 @@ components: - BUILD - DEPLOYMENT - NONE + - TEST createdAt: type: string format: date-time diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/tests/TestResultProcessor.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/tests/TestResultProcessor.java index 0390cdf6c..52268f43b 100644 --- a/server/application-server/src/main/java/de/tum/cit/aet/helios/tests/TestResultProcessor.java +++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/tests/TestResultProcessor.java @@ -3,7 +3,10 @@ import de.tum.cit.aet.helios.github.GitHubService; import de.tum.cit.aet.helios.tests.parsers.JunitParser; import de.tum.cit.aet.helios.tests.parsers.TestParserResult; +import de.tum.cit.aet.helios.workflow.Workflow; import de.tum.cit.aet.helios.workflow.WorkflowRun; +import de.tum.cit.aet.helios.workflow.WorkflowService; +import java.io.FilterInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -24,34 +27,42 @@ public class TestResultProcessor { private final GitHubService gitHubService; private final TestResultRepository testResultRepository; private final JunitParser junitParser; + private final WorkflowService workflowService; @Value("${tests.artifactName:Test Results}") private String testArtifactName; - @Value("${tests.runName:Test}") - private String testRunName; - public boolean shouldProcess(WorkflowRun workflowRun) { log.debug( "Checking if test results should be processed for workflow run {}", workflowRun.getName()); - if (!workflowRun.getName().equals(this.testRunName)) { + if (workflowRun.getStatus() != WorkflowRun.Status.COMPLETED) { return false; } - if (workflowRun.getStatus() != WorkflowRun.Status.COMPLETED) { + final Workflow testWorkflow = + this.workflowService.getTestWorkflow(workflowRun.getRepository().getRepositoryId()); + + if (testWorkflow == null || workflowRun.getWorkflowId() != testWorkflow.getId()) { return false; } return this.testResultRepository.findByWorkflowRun(workflowRun).isEmpty(); } - @Async + @Async("testResultProcessorExecutor") public void processRun(WorkflowRun workflowRun) { log.debug("Processing test results for workflow run {}", workflowRun.getName()); GHArtifact testResultsArtifact = null; + // thread sleep 2 seconds + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + try { PagedIterable artifacts = this.gitHubService.getWorkflowRunArtifacts( @@ -103,6 +114,10 @@ private List processTestResultArtifact(GHArtifact artifact) th // Download the ZIP artifact, find all parsable XML files and parse them return artifact.download( stream -> { + if (stream.available() == 0) { + throw new TestResultException("Empty artifact stream"); + } + List results = new ArrayList<>(); try (ZipInputStream zipInput = new ZipInputStream(stream)) { @@ -111,7 +126,15 @@ private List processTestResultArtifact(GHArtifact artifact) th while ((entry = zipInput.getNextEntry()) != null) { if (!entry.isDirectory()) { if (this.junitParser.supports(entry.getName())) { - results.add(this.junitParser.parse(zipInput)); + var nonClosingStream = + new FilterInputStream(zipInput) { + @Override + public void close() throws IOException { + // Do nothing, so the underlying stream stays open. + } + }; + + results.add(this.junitParser.parse(nonClosingStream)); } } zipInput.closeEntry(); diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/tests/TestResultRepository.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/tests/TestResultRepository.java index 013725835..11daeed68 100644 --- a/server/application-server/src/main/java/de/tum/cit/aet/helios/tests/TestResultRepository.java +++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/tests/TestResultRepository.java @@ -1,11 +1,11 @@ package de.tum.cit.aet.helios.tests; import de.tum.cit.aet.helios.workflow.WorkflowRun; -import java.util.Optional; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface TestResultRepository extends JpaRepository { - Optional findByWorkflowRun(WorkflowRun workflowRun); + List findByWorkflowRun(WorkflowRun workflowRun); } diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/tests/TestsConfig.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/tests/TestsConfig.java new file mode 100644 index 000000000..29260769a --- /dev/null +++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/tests/TestsConfig.java @@ -0,0 +1,24 @@ +package de.tum.cit.aet.helios.tests; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.TaskExecutor; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +@Configuration +@EnableAsync +public class TestsConfig { + /** + * Creates a TaskExecutor bean named "testResultProcessorExecutor". This executor is used to + * process test results asynchronously. + * + * @return a configured ThreadPoolTaskExecutor instance + */ + @Bean("testResultProcessorExecutor") + public TaskExecutor taskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setThreadNamePrefix("test-result-processor-"); + return executor; + } +} diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/workflow/Workflow.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/workflow/Workflow.java index 8e61f5fd2..0a5f6ccb9 100644 --- a/server/application-server/src/main/java/de/tum/cit/aet/helios/workflow/Workflow.java +++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/workflow/Workflow.java @@ -54,6 +54,7 @@ public enum State { public enum Label { BUILD, DEPLOYMENT, - NONE + NONE, + TEST } } diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/workflow/WorkflowService.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/workflow/WorkflowService.java index b1895a773..453566632 100644 --- a/server/application-server/src/main/java/de/tum/cit/aet/helios/workflow/WorkflowService.java +++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/workflow/WorkflowService.java @@ -55,4 +55,11 @@ public Workflow getDeploymentWorkflow(Long repositoryId) { Workflow.Label.DEPLOYMENT, repositoryId); return workflow; } + + public Workflow getTestWorkflow(Long repositoryId) { + Workflow workflow = + workflowRepository.findFirstByLabelAndRepositoryRepositoryIdOrderByCreatedAtDesc( + Workflow.Label.TEST, repositoryId); + return workflow; + } } diff --git a/server/application-server/src/main/resources/db/migration/V9__test_results.sql b/server/application-server/src/main/resources/db/migration/V9__test_results.sql index 2e89973ad..7abff9343 100644 --- a/server/application-server/src/main/resources/db/migration/V9__test_results.sql +++ b/server/application-server/src/main/resources/db/migration/V9__test_results.sql @@ -10,8 +10,4 @@ create table test_result ( references workflow_run ( id ) ); --- We only want one test result per workflow run -create unique index idx_test_result_workflow_run_id on - test_result ( - workflow_run_id - ); \ No newline at end of file +alter table workflow drop constraint workflow_label_check; \ No newline at end of file