Skip to content

Commit

Permalink
Add capturing checks publisher, make publishChecks withChecksName awa…
Browse files Browse the repository at this point in the history
…re (#55)

* Add capturing checks publisher, make publishChecks withChecksName aware

* Move CapturingChecksPublisher to test root, add to test-jar, add docs

* Add link to example usage of capturing publisher.

* Improve test assertions, name logic
  • Loading branch information
mrginglymus authored Dec 22, 2020
1 parent 3ca4e19 commit 3a298a6
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 4 deletions.
9 changes: 9 additions & 0 deletions docs/consumers-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,12 @@ getContext().get(ChecksInfo.class)
Currently, the `ChecksInfo` object only includes a `name` specified by users,
it is recommended that you look for this name and set it over your default checks name

## Integration Testing

An implementation of `ChecksPublisher` that captures all published `ChecksDetails` is provided
in the `test` classifier, as `io.jenkins.plugins.checks.api.test.CapturingChecksPublisher`.

Adding the factory for this publisher as a `TestExtension` will allow inspection of published checks after running a job
on a `JenkinsRule`.

An example of this can be found in [PublishChecksStepITest](../src/test/java/io/jenkins/plugins/checks/steps/PublishChecksStepITest.java).
16 changes: 16 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,22 @@
</analysisConfiguration>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
<configuration>
<includes combine.children="append">
<include>**/api/test/**</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,16 @@ protected Void run() throws IOException, InterruptedException {
}

@VisibleForTesting
ChecksDetails extractChecksDetails() {
ChecksDetails extractChecksDetails() throws IOException, InterruptedException {
// If a checks name has been provided as part of the step, use that.
// If not, check to see if there is an active ChecksInfo context (e.g. from withChecks).
String checksName = StringUtils.defaultIfEmpty(step.getName(),
Optional.ofNullable(getContext().get(ChecksInfo.class))
.map(ChecksInfo::getName)
.orElse(StringUtils.EMPTY)
);
return new ChecksDetails.ChecksDetailsBuilder()
.withName(step.getName())
.withName(checksName)
.withStatus(step.getStatus())
.withConclusion(step.getConclusion())
.withDetailsURL(step.getDetailsURL())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package io.jenkins.plugins.checks.api.test;

import hudson.ExtensionList;
import hudson.model.Job;
import hudson.model.Run;
import hudson.model.TaskListener;
import io.jenkins.plugins.checks.api.ChecksDetails;
import io.jenkins.plugins.checks.api.ChecksPublisher;
import io.jenkins.plugins.checks.api.ChecksPublisherFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
* Implementation of {@link ChecksPublisher} for use in testing, that records each captured checks in a simple list.
*
* For example:
*
* <pre>

This comment has been minimized.

Copy link
@uhafner

uhafner Dec 28, 2020

Member

Maybe this is a little bit late, but if you would use <pre>{@code (see https://reflectoring.io/howto-format-code-snippets-in-javadoc/#pre--code) then GitHub will render those comments correctly.

This comment has been minimized.

Copy link
@mrginglymus

mrginglymus Dec 28, 2020

Author Contributor

I did try with pre, I think, and it made something very sad I’m sure.

* public class ChecksPublishingTest extends IntegrationTestWithJenkinsPerTest {
*
* &#64;TestExtension
* public static final CapturingChecksPublisher.Factory PUBLISHER_FACTORY = new CapturingChecksPublisher.Factory();
*
* &#64;After
* public void clearPublishedChecks() {
* PUBLISHER_FACTORY.getPublishedChecks().clear();
* }
*
* &#64;Test
* public void testChecksPublishing() {
*
* // ...Run a test job...
*
* List&lt;ChecksDetails&gt; publishedDetails = PUBLISHER_FACTORY.getPublishedChecks();
*
* // ...Inspect published checks...
* }
* }
* </pre>
*
* An example of this can be found in {@link io.jenkins.plugins.checks.steps.PublishChecksStepITest}
*/
public class CapturingChecksPublisher extends ChecksPublisher {

private final List<ChecksDetails> publishedChecks = new ArrayList<>();

@Override
public void publish(final ChecksDetails details) {
publishedChecks.add(details);
}

/**
* Implementation of {@link ChecksPublisherFactory} that returns a {@link CapturingChecksPublisher}.
*/
public static class Factory extends ChecksPublisherFactory {

private final CapturingChecksPublisher publisher = new CapturingChecksPublisher();

@Override
protected Optional<ChecksPublisher> createPublisher(final Run<?, ?> run, final TaskListener listener) {
return Optional.of(publisher);
}

@Override
protected Optional<ChecksPublisher> createPublisher(final Job<?, ?> job, final TaskListener listener) {
return Optional.of(publisher);
}

public List<ChecksDetails> getPublishedChecks() {
return ExtensionList.lookup(Factory.class).get(0).publisher.publishedChecks;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package io.jenkins.plugins.checks.steps;

import io.jenkins.plugins.checks.api.ChecksConclusion;
import io.jenkins.plugins.checks.api.ChecksDetails;
import io.jenkins.plugins.checks.api.ChecksOutput;
import io.jenkins.plugins.checks.api.ChecksStatus;
import io.jenkins.plugins.checks.api.test.CapturingChecksPublisher;
import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerTest;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.TestExtension;

import java.io.IOException;

Expand All @@ -13,6 +19,13 @@
* Tests the pipeline step to publish checks.
*/
public class PublishChecksStepITest extends IntegrationTestWithJenkinsPerTest {

/**
* Provide a {@link CapturingChecksPublisher} to check published checks on each test.
*/
@TestExtension
public static final CapturingChecksPublisher.Factory PUBLISHER_FACTORY = new CapturingChecksPublisher.Factory();

/**
* Tests that the step "publishChecks" can be used in pipeline script.
*
Expand All @@ -26,7 +39,20 @@ public void shouldPublishChecksWhenUsingPipeline() throws IOException {
+ "text: 'Pipeline support for checks', status: 'IN_PROGRESS', conclusion: 'NONE'"));

assertThat(JenkinsRule.getLog(buildSuccessfully(job)))
.contains("[Pipeline] publishChecks")
.contains("[Checks API] No suitable checks publisher found.");
.contains("[Pipeline] publishChecks");

assertThat(PUBLISHER_FACTORY.getPublishedChecks().size()).isEqualTo(1);

ChecksDetails details = PUBLISHER_FACTORY.getPublishedChecks().get(0);

assertThat(details.getName()).isPresent().get().isEqualTo("customized-check");
assertThat(details.getOutput()).isPresent();
assertThat(details.getStatus()).isEqualTo(ChecksStatus.IN_PROGRESS);
assertThat(details.getConclusion()).isEqualTo(ChecksConclusion.NONE);

ChecksOutput output = details.getOutput().get();
assertThat(output.getTitle()).isPresent().get().isEqualTo("Publish Checks Step");
assertThat(output.getSummary()).isPresent().get().isEqualTo("customized check created in pipeline");
assertThat(output.getText()).isPresent().get().isEqualTo("Pipeline support for checks");
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package io.jenkins.plugins.checks.steps;

import hudson.model.Run;
import io.jenkins.plugins.checks.api.ChecksConclusion;
import io.jenkins.plugins.checks.api.ChecksDetails;
import io.jenkins.plugins.checks.api.ChecksStatus;
import io.jenkins.plugins.checks.api.test.CapturingChecksPublisher;
import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerTest;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.steps.*;
import org.junit.After;
import org.junit.Test;
import org.jvnet.hudson.test.TestExtension;
import org.kohsuke.stapler.DataBoundConstructor;
Expand All @@ -20,6 +25,7 @@
* Tests the "withChecks" step.
*/
public class WithChecksStepITest extends IntegrationTestWithJenkinsPerTest {

/**
* Tests that the step can inject the {@link ChecksInfo} into the closure.
*/
Expand All @@ -31,6 +37,66 @@ public void shouldInjectChecksInfoIntoClosure() {
buildSuccessfully(job);
}

/**
* Provide a {@link CapturingChecksPublisher} to check published checks on each test.
*/
@TestExtension
public static final CapturingChecksPublisher.Factory PUBLISHER_FACTORY = new CapturingChecksPublisher.Factory();

/**
* Clear captured checks on the {@link WithChecksStepITest#PUBLISHER_FACTORY} after each test.
*/
@After
public void clearPublisher() {
PUBLISHER_FACTORY.getPublishedChecks().clear();
}

/**
* Test that the publishChecks step picks up names from the withChecks context.
*/
@Test
public void publishChecksShouldTakeNameFromWithChecks() {
WorkflowJob job = createPipeline();
job.setDefinition(asStage("withChecks('test injection') { publishChecks() }"));

buildSuccessfully(job);

assertThat(PUBLISHER_FACTORY.getPublishedChecks().size()).isEqualTo(2);
ChecksDetails autoChecks = PUBLISHER_FACTORY.getPublishedChecks().get(0);
ChecksDetails manualChecks = PUBLISHER_FACTORY.getPublishedChecks().get(1);

assertThat(autoChecks.getName()).isPresent().get().isEqualTo("test injection");
assertThat(autoChecks.getStatus()).isEqualTo(ChecksStatus.IN_PROGRESS);
assertThat(autoChecks.getConclusion()).isEqualTo(ChecksConclusion.NONE);

assertThat(manualChecks.getName()).isPresent().get().isEqualTo("test injection");
assertThat(manualChecks.getStatus()).isEqualTo(ChecksStatus.COMPLETED);
assertThat(manualChecks.getConclusion()).isEqualTo(ChecksConclusion.SUCCESS);
}

/**
* Tests that withChecks step ignores names from the withChecks context if one has been explicitly set.
*/
@Test
public void publishChecksShouldTakeNameFromWithChecksUnlessOverridden() {
WorkflowJob job = createPipeline();
job.setDefinition(asStage("withChecks('test injection') { publishChecks name: 'test override' }"));

buildSuccessfully(job);

assertThat(PUBLISHER_FACTORY.getPublishedChecks().size()).isEqualTo(2);
ChecksDetails autoChecks = PUBLISHER_FACTORY.getPublishedChecks().get(0);
ChecksDetails manualChecks = PUBLISHER_FACTORY.getPublishedChecks().get(1);

assertThat(autoChecks.getName()).isPresent().get().isEqualTo("test injection");
assertThat(autoChecks.getStatus()).isEqualTo(ChecksStatus.IN_PROGRESS);
assertThat(autoChecks.getConclusion()).isEqualTo(ChecksConclusion.NONE);

assertThat(manualChecks.getName()).isPresent().get().isEqualTo("test override");
assertThat(manualChecks.getStatus()).isEqualTo(ChecksStatus.COMPLETED);
assertThat(manualChecks.getConclusion()).isEqualTo(ChecksConclusion.SUCCESS);
}

/**
* Assert that the injected {@link ChecksInfo} is as expected.
*/
Expand Down

0 comments on commit 3a298a6

Please sign in to comment.