From 2cbbd6e7d47aedc6741ceb3e4c88d94cfab25f62 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Mon, 17 Feb 2025 15:55:51 +0100 Subject: [PATCH] Should make all the tests pass --- .github/workflows/build.yml | 2 +- build.gradle | 31 ++++++++++ .../release/common/GithubActions.java | 57 ++++++++++--------- .../SingleProjectGithubActionsE2eTests.java | 13 +++-- 4 files changed, 68 insertions(+), 35 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3de4968..c21d5ec 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Build & Deploy +name: Build on: push: diff --git a/build.gradle b/build.gradle index 7a1edf9..5938dd1 100644 --- a/build.gradle +++ b/build.gradle @@ -41,6 +41,24 @@ repositories { group = 'io.micrometer.release' println "I'm configuring $project.name with version $project.version" +sourceSets { + e2eTest { + java { + srcDir 'src/test/java' + include '**/*E2e*.java' + destinationDirectory = project.layout.buildDirectory.dir('classes/java/e2eTest') + } + compileClasspath = sourceSets.test.compileClasspath + sourceSets.test.output + runtimeClasspath = sourceSets.test.runtimeClasspath + sourceSets.test.output + files("${buildDir}/classes/java/e2eTest") + } + + test { + java { + exclude '**/*E2e*.java' + } + } +} + dependencies { checkstyle libs.javaFormatForPlugins @@ -85,11 +103,21 @@ test { } tasks.register('e2e', Test) { + description = 'Runs E2E tests.' + group = 'verification' + + dependsOn e2eTestClasses + + testClassesDirs = sourceSets.e2eTest.output.classesDirs + classpath = sourceSets.e2eTest.runtimeClasspath + useJUnitPlatform() { includeTags("e2e") } } +formatE2eTest.dependsOn("formatTest") + license { header rootProject.file('gradle/licenseHeader.txt') strictCheck true @@ -124,12 +152,15 @@ tasks.check { pitest { junit5PluginVersion = libs.versions.pitestJunit5 + threads = 4 // Parallel threads for mutation testing outputFormats = ['HTML'] // Generate an HTML report timestampedReports = false // Avoid timestamped reports for consistent file paths testStrengthThreshold.set(90) mutationThreshold.set(80) setCoverageThreshold(80) + + testSourceSets = [sourceSets.test] // Only use the main test source set } build.dependsOn("pitest") diff --git a/src/test/java/io/micrometer/release/common/GithubActions.java b/src/test/java/io/micrometer/release/common/GithubActions.java index a381169..75e30e4 100644 --- a/src/test/java/io/micrometer/release/common/GithubActions.java +++ b/src/test/java/io/micrometer/release/common/GithubActions.java @@ -48,8 +48,8 @@ public interface GithubActions { static void resetsMilestones() throws InterruptedException { assertThat(System.getenv("GH_TOKEN")).as("GH_TOKEN env var must be set!").isNotBlank(); log.info( - "This test requires GH connection and will operate on [{}] repository. It's quite slow because it runs GH actions so please be patient...", - REPO); + "This test requires GH connection and will operate on [{}] repository. It's quite slow because it runs GH actions so please be patient...", + REPO); resetMilestones(); } @@ -64,7 +64,8 @@ static void runWorkflow(String workflowName, List commands) { processRunner.run(commands); try { waitForWorkflowCompletion(workflowName); - } catch (InterruptedException e) { + } + catch (InterruptedException e) { throw new IllegalStateException(e); } } @@ -77,10 +78,8 @@ private static void waitForWorkflowCompletion(String workflowFile) throws Interr int maxAttempts = 30; int attempts = 0; while (!completed && attempts < maxAttempts) { // 5 minute timeout - List status = processRunner.run("gh", "run", "list", "--workflow", workflowFile, - "--limit", "1"); - log.info("Workflow [{}] not completed yet - attempt [{}]/[{}]", workflowFile, - attempts + 1, maxAttempts); + List status = processRunner.run("gh", "run", "list", "--workflow", workflowFile, "--limit", "1"); + log.info("Workflow [{}] not completed yet - attempt [{}]/[{}]", workflowFile, attempts + 1, maxAttempts); completed = status.stream().anyMatch(line -> line.contains("completed")); if (!completed) { Thread.sleep(10_000); @@ -88,8 +87,7 @@ private static void waitForWorkflowCompletion(String workflowFile) throws Interr } } if (!completed) { - throw new RuntimeException( - "Workflow " + workflowFile + " did not complete within timeout"); + throw new RuntimeException("Workflow " + workflowFile + " did not complete within timeout"); } log.info("Workflow [{}] completed successfully!", workflowFile); } @@ -117,35 +115,41 @@ class GithubClient { GithubClient(String token, String repo) { this.repo = repo; this.processRunner = new ProcessRunner(repo).withEnvVars( - Map.of("JAVA_HOME", JavaHomeFinder.findJavaHomePath(), "GH_TOKEN", - token != null ? token : "")); + Map.of("JAVA_HOME", JavaHomeFinder.findJavaHomePath(), "GH_TOKEN", token != null ? token : "")); this.objectMapper = new ObjectMapper().registerModule(new JavaTimeModule()) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); } public Release getRelease(String tag) throws JsonProcessingException { String output = String.join("\n", - processRunner.run("gh", "api", "/repos/" + repo + "/releases/tags/" + tag)); + processRunner.run("gh", "api", "/repos/" + repo + "/releases/tags/" + tag)); return parseReleaseFromJson(output); } + public void createReleaseAndTag(String tagName) { + log.info("Creating tag and release [{}]", tagName); + + processRunner.run("gh", "release", "create", tagName, "--title", tagName.replace("v", ""), "--notes", + "Release " + tagName, "--target", "main"); + + log.info("Successfully created tag and release [{}]", tagName); + } + public Milestone getMilestoneByTitle(String title) throws JsonProcessingException { String output = String.join("\n", - processRunner.run("gh", "api", "/repos/" + repo + "/milestones")); + processRunner.run("gh", "api", "/repos/" + repo + "/milestones?state=all")); return parseMilestoneFromJson(output, title); } - public List getIssuesForMilestone(int milestoneNumber) - throws JsonProcessingException { + public List getIssuesForMilestone(int milestoneNumber) throws JsonProcessingException { String output = String.join("\n", processRunner.run("gh", "api", - "/repos/" + repo + "/issues?milestone=" + milestoneNumber)); + "/repos/" + repo + "/issues?milestone=" + milestoneNumber + "&state=all")); return parseIssuesFromJson(output); } - public List getClosedIssuesForMilestone(int milestoneNumber) - throws JsonProcessingException { + public List getClosedIssuesForMilestone(int milestoneNumber) throws JsonProcessingException { String output = String.join("\n", processRunner.run("gh", "api", - "/repos/" + repo + "/issues?milestone=" + milestoneNumber + "&state=closed")); + "/repos/" + repo + "/issues?milestone=" + milestoneNumber + "&state=closed")); return parseIssuesFromJson(output); } @@ -154,17 +158,14 @@ private Release parseReleaseFromJson(String json) throws JsonProcessingException return new Release(root.get("body").asText()); } - private Milestone parseMilestoneFromJson(String json, String title) - throws JsonProcessingException { + private Milestone parseMilestoneFromJson(String json, String title) throws JsonProcessingException { JsonNode root = objectMapper.readTree(json); for (JsonNode milestone : root) { if (milestone.get("title").asText().equals(title)) { - return new Milestone(milestone.get("number").asInt(), - milestone.get("state").asText(), - milestone.get("title").asText(), - milestone.get("due_on") != null && !milestone.get("due_on").isNull() - ? LocalDate.parse(milestone.get("due_on").asText().substring(0, 10)) - : null); + return new Milestone(milestone.get("number").asInt(), milestone.get("state").asText(), + milestone.get("title").asText(), + milestone.get("due_on") != null && !milestone.get("due_on").isNull() + ? LocalDate.parse(milestone.get("due_on").asText().substring(0, 10)) : null); } } throw new RuntimeException("Milestone with title " + title + " not found"); @@ -175,7 +176,7 @@ private List parseIssuesFromJson(String json) throws JsonProcessingExcept List issues = new ArrayList<>(); for (JsonNode issue : root) { issues.add(new Issue(issue.get("number").asInt(), issue.get("state").asText(), - issue.get("title").asText())); + issue.get("title").asText())); } return issues; } diff --git a/src/test/java/io/micrometer/release/single/SingleProjectGithubActionsE2eTests.java b/src/test/java/io/micrometer/release/single/SingleProjectGithubActionsE2eTests.java index a6d63e7..958ea1b 100644 --- a/src/test/java/io/micrometer/release/single/SingleProjectGithubActionsE2eTests.java +++ b/src/test/java/io/micrometer/release/single/SingleProjectGithubActionsE2eTests.java @@ -30,6 +30,7 @@ class SingleProjectGithubActionsE2eTests implements GithubActions { @BeforeAll static void should_go_through_whole_flow() { + githubClient.createReleaseAndTag("v0.1.1"); runPostReleaseWorkflow(); } @@ -41,13 +42,13 @@ void should_verify_release_notes_content() throws JsonProcessingException { """ ## :star: New Features - - Closed enhancement in generic [#5](https://github.com/marcingrzejszczak/gh-actions-test/issues/5) + - Closed enhancement in generic 0.1.x [#8](https://github.com/marcingrzejszczak/gh-actions-test/issues/8) - Foo [#2](https://github.com/micrometer-metrics/build-test/issues/2) ## :lady_beetle: Bug Fixes - - Closed bug in concrete [#3](https://github.com/marcingrzejszczak/gh-actions-test/issues/3) - - Closed bug in generic [#4](https://github.com/marcingrzejszczak/gh-actions-test/issues/4) + - Closed bug in concrete 0.1.1 [#12](https://github.com/marcingrzejszczak/gh-actions-test/issues/12) + - Closed bug in generic 0.1.x [#9](https://github.com/marcingrzejszczak/gh-actions-test/issues/9) - Foo2 [#53](https://github.com/micrometer-metrics/build-test/issues/53) ## :hammer: Dependency Upgrades @@ -71,8 +72,8 @@ void should_verify_current_milestone() throws JsonProcessingException { List issues = githubClient.getIssuesForMilestone(milestone.number()); assertThat(issues).extracting(Issue::state).containsOnly("closed"); assertThat(issues).extracting(Issue::title) - .containsOnly("Closed issue in generic", "Closed bug in concrete", "Closed bug in generic", - "Closed enhancement in generic"); + .containsOnly("Closed issue in generic 0.1.x", "Closed bug in concrete 0.1.1", + "Closed bug in generic 0.1.x", "Closed enhancement in generic 0.1.x"); } @Test @@ -83,7 +84,7 @@ void should_verify_next_milestone() throws JsonProcessingException { assertThat(milestone.dueOn()).isEqualTo(ReleaseDateCalculator.calculateDueDate(LocalDate.now())); List issues = githubClient.getIssuesForMilestone(milestone.number()); assertThat(issues).extracting(Issue::state).containsOnly("open"); - assertThat(issues).extracting(Issue::title).containsOnly("Open issue in concrete"); + assertThat(issues).extracting(Issue::title).containsOnly("Open issue in concrete 0.1.1"); } @Test