diff --git a/Jenkinsfile b/Jenkinsfile index 71142c6..ed00625 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -2,6 +2,6 @@ def configurations = [ [ platform: 'linux', jdk: '8' ], - [ platform: 'linux', jdk: '11', jenkins: '2.332.2' ], + [ platform: 'linux', jdk: '11', jenkins: '2.440.3' ], ] buildPlugin(configurations: configurations) diff --git a/README.adoc b/README.adoc index 97237ee..4e24172 100644 --- a/README.adoc +++ b/README.adoc @@ -45,7 +45,7 @@ horreumUpload ( ---- -You can access the response status code and content within a groovy script: +You can access the response within a groovy script: [source,groovy] ---- @@ -60,13 +60,32 @@ def response = horreumUpload ( abortOnfailure: false, quiet: false ) -println("Status: "+response.status) -println("Content: "+response.content) +println("Response: "+response) ---- +The created Run ID by Horreum is provided in the response. + == Building -The plugin can be built and tested locally using a Maven HPI: +The plugin can be built using Maven + +[source, bash] +---- +mvn package -DskipTests=true +---- + +== Testing + +Testing using Maven and JUnit: + +[source, bash] +---- +mvn test +---- + +== Stand up Jenkins server instance + +The [Maven HPI plugin](https://jenkinsci.github.io/maven-hpi-plugin/) is used for standing up a Jenkins instance. [source, bash] ---- diff --git a/pom.xml b/pom.xml index 8dc9ea6..6d2afca 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ THE SOFTWARE. org.jenkins-ci.plugins plugin - 4.40 + 4.52 io.jenkins.plugins @@ -61,16 +61,22 @@ THE SOFTWARE. HEAD jenkinsci/horreum-plugin - 2.332.3 + 2.440.3 + 3.37 1C - 2.14.1 + 2.17.2 1.25 - 1.17.6 5.9.1 - 0.7 + 0.15.1 + 25.0.2 + 17 + + jenkins-releases + https://repo.jenkins-ci.org/releases/ + repo.jenkins-ci.org https://repo.jenkins-ci.org/public/ @@ -119,6 +125,9 @@ THE SOFTWARE. org.jenkins-ci.tools maven-hpi-plugin true + + true + org.codehaus.mojo @@ -134,247 +143,150 @@ THE SOFTWARE. io.jenkins.tools.bom - bom-2.332.x - 1422.v0773c659e58f - import - pom - - - io.netty - netty-bom - 4.1.77.Final + bom-2.440.x + 3193.v330d8248d39e pom import - - commons-io - commons-io - 2.11.0 - - - org.slf4j - slf4j-api - 1.7.36 - - - io.smallrye.config - smallrye-config - 2.10.0 - - - org.jboss.logging - jboss-logging - - - - - io.smallrye.common - smallrye-common-annotation - 1.11.0 - - - org.eclipse.microprofile.config - microprofile-config-api - 2.0.1 - - - org.eclipse.microprofile.context-propagation - microprofile-context-propagation-api - 1.3 - - - io.vertx - vertx-core - 4.2.7 - - - org.apache.commons - commons-compress - 1.22 - - - org.apache.httpcomponents - httpclient - 4.5.13 - - - org.apache.httpcomponents - httpasyncclient - 4.1.5 - - - org.apache.httpcomponents - httpcore - 4.4.15 - - - org.apache.httpcomponents - httpcore-nio - 4.4.15 - - - org.jboss.logging - jboss-logging - 3.4.3.Final - - - org.apache.logging.log4j - log4j-api - 2.17.1 - + + org.jenkins-ci.plugins.workflow + workflow-cps + test + + + + org.jenkins-ci.plugins.workflow + workflow-job + + io.hyperfoil.tools horreum-client ${horreum.version} - - - org.slf4j - slf4j-api - - - org.jboss.logging - jboss-logging - - - com.fasterxml.jackson.core - jackson-core - com.fasterxml.jackson.core jackson-databind - - io.smallrye.config - smallrye-config - - - io.smallrye.common - smallrye-common-annotation - org.eclipse.microprofile.config microprofile-config-api - org.eclipse.microprofile.context-propagation - microprofile-context-propagation-api - - - io.vertx - vertx-core + com.fasterxml.jackson.core + jackson-annotations - com.fasterxml.jackson.core - jackson-core - ${jackson.version} + org.apache.maven.wagon + wagon-http-shared + 3.5.3 - com.fasterxml.jackson.core - jackson-annotations - ${jackson.version} + org.keycloak + keycloak-core + ${keycloak.version} + + + com.fasterxml.jackson.core + jackson-databind + + com.fasterxml.jackson.core - jackson-databind + jackson-core ${jackson.version} - com.github.fge - jackson-coreutils - 1.8 + org.apache.commons + commons-compress + 1.26.0 - org.apache.commons - commons-compress + javax.annotation + javax.annotation-api + 1.3.2 - org.jenkins-ci.plugins - credentials + org.jenkins-ci.main + jenkins-core + ${jenkins.version} + org.jenkins-ci.plugins - plain-credentials + credentials + 1087.1089.v2f1b_9a_b_040e4 - org.jenkins-ci.plugins.workflow - workflow-step-api - true + javax.ws.rs + javax.ws.rs-api + 2.1.1 - - io.hyperfoil.tools - horreum-integration - ${horreum.version} - test + commons-io + commons-io + 2.15.1 + io.hyperfoil.tools - horreum-integration + horreum-api ${horreum.version} - test-jar - test - - - - org.jenkins-ci.plugins.workflow - workflow-step-api - tests - test org.jenkins-ci.plugins.workflow workflow-basic-steps - - - - org.jenkins-ci.plugins.workflow - workflow-cps test - org.jenkins-ci.plugins.workflow - workflow-durable-task-step + io.hyperfoil.tools + horreum-infra-common + ${horreum.version} + test - org.jenkins-ci.plugins.workflow - workflow-job + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} test - org.jboss.logging - jboss-logging + org.testcontainers + testcontainers + 1.17.6 + test - org.junit.jupiter - junit-jupiter-engine - ${junit.jupiter.version} + org.postgresql + postgresql + 42.5.4 test - org.testcontainers - testcontainers - ${testcontainers.version} + org.apache.logging.log4j + log4j-slf4j-impl + 2.19.0 test @@ -390,5 +302,10 @@ THE SOFTWARE. Radim Vansa rvansa@redhat.com + + jwhiting + Jeremy Whiting + jwhiting@redhat.com + diff --git a/src/main/java/jenkins/plugins/horreum/BaseExecutionContext.java b/src/main/java/jenkins/plugins/horreum/BaseExecutionContext.java index 956dc42..495ee39 100644 --- a/src/main/java/jenkins/plugins/horreum/BaseExecutionContext.java +++ b/src/main/java/jenkins/plugins/horreum/BaseExecutionContext.java @@ -47,11 +47,9 @@ public BaseExecutionContext(String url, String credentials, PrintStream logger) this.remoteLogger = new RemoteOutputStream(new CloseProofOutputStream(logger)); this.localLogger = logger; - //Retrieve Credentials - //TODO:: pass in DomainRequirement List credentialsList = CredentialsProvider.lookupCredentials( - StandardCredentials.class, // (1) - Jenkins.get(), // (1) + StandardCredentials.class, + Jenkins.get(), ACL.SYSTEM, Collections.emptyList() ) ; @@ -143,9 +141,6 @@ private static ByteArrayOutputStream toByteArrayOutputStream(InputStream stream) protected HorreumClient createClient() { HorreumClient.Builder clientBuilder = new HorreumClient.Builder() .horreumUrl(url) - .keycloakUrl(keycloak.getBaseUrl()) - .keycloakRealm(keycloak.getRealm()) - .clientId(keycloak.getClientId()) .horreumUser(usernameCredentials.getUsername()) .horreumPassword(usernameCredentials.getPassword().getPlainText()); return clientBuilder.build(); diff --git a/src/main/java/jenkins/plugins/horreum/auth/KeycloakAuthentication.java b/src/main/java/jenkins/plugins/horreum/auth/KeycloakAuthentication.java index d3f5897..fc20f92 100644 --- a/src/main/java/jenkins/plugins/horreum/auth/KeycloakAuthentication.java +++ b/src/main/java/jenkins/plugins/horreum/auth/KeycloakAuthentication.java @@ -1,18 +1,10 @@ package jenkins.plugins.horreum.auth; import java.io.Serializable; -import java.util.List; - -import com.cloudbees.plugins.credentials.CredentialsMatchers; -import com.cloudbees.plugins.credentials.CredentialsProvider; -import com.cloudbees.plugins.credentials.common.StandardCredentials; -import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials; import hudson.Extension; import hudson.model.AbstractDescribableImpl; import hudson.model.Descriptor; -import hudson.security.ACL; -import jenkins.model.Jenkins; public class KeycloakAuthentication extends AbstractDescribableImpl implements Serializable { diff --git a/src/main/java/jenkins/plugins/horreum/upload/HorreumUploadExecutionContext.java b/src/main/java/jenkins/plugins/horreum/upload/HorreumUploadExecutionContext.java index 302c11a..510d388 100644 --- a/src/main/java/jenkins/plugins/horreum/upload/HorreumUploadExecutionContext.java +++ b/src/main/java/jenkins/plugins/horreum/upload/HorreumUploadExecutionContext.java @@ -13,7 +13,6 @@ import java.util.function.Supplier; import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -25,7 +24,8 @@ import hudson.model.Run; import hudson.model.TaskListener; import io.hyperfoil.tools.HorreumClient; -import io.hyperfoil.tools.horreum.entity.json.Access; +import io.hyperfoil.tools.horreum.api.data.Access; +import jakarta.ws.rs.core.Response; import jenkins.model.Jenkins; import jenkins.plugins.horreum.BaseExecutionContext; import jenkins.plugins.horreum.HorreumGlobalConfig; diff --git a/src/test/java/jenkins/plugins/horreum/HorreumExpectStepTest.java b/src/test/java/jenkins/plugins/horreum/HorreumExpectStepTest.java index a9c59fa..68de42f 100644 --- a/src/test/java/jenkins/plugins/horreum/HorreumExpectStepTest.java +++ b/src/test/java/jenkins/plugins/horreum/HorreumExpectStepTest.java @@ -1,41 +1,42 @@ package jenkins.plugins.horreum; -import static io.hyperfoil.tools.HorreumTestClientExtension.dummyTest; -import static io.hyperfoil.tools.HorreumTestClientExtension.horreumClient; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static jenkins.plugins.horreum.junit.HorreumTestExtension.*; +import static jenkins.plugins.horreum.junit.HorreumTestClientExtension.getHorreumClient; import java.util.List; import java.util.stream.Collectors; +import io.hyperfoil.tools.horreum.api.alerting.RunExpectation; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; import org.junit.jupiter.api.Test; - -import io.hyperfoil.tools.horreum.entity.alerting.RunExpectation; +import org.junit.jupiter.api.TestInfo; public class HorreumExpectStepTest extends HorreumPluginTestBase { @Test - public void testExpect() throws Exception { + public void testExpect(TestInfo info) throws Exception { WorkflowJob proj = j.jenkins.createProject(WorkflowJob.class, "Horreum-Expect-Pipeline"); + io.hyperfoil.tools.horreum.api.data.Test dummyTest = createTest(info.getTestClass() + "-expect-step", "dev-team"); proj.setDefinition(new CpsFlowDefinition( - "node {\n" + - "horreumExpect(\n" + - "credentials: '" + HorreumPluginTestBase.HORREUM_UPLOAD_CREDENTIALS + "',\n" + - "test: '" + dummyTest.name + "',\n" + - "timeout: 60,\n" + - "expectedBy: 'Jenkins CI',\n" + - "backlink: \"${env.BUILD_URL}\",\n" + - ")\n" + - "}\n", - true)); + "node {\n" + + "horreumExpect(\n" + + "credentials: '" + HORREUM_UPLOAD_CREDENTIALS + "',\n" + + "test: '" + dummyTest.name + "',\n" + + "timeout: 60,\n" + + "expectedBy: 'Jenkins CI',\n" + + "backlink: \"${env.BUILD_URL}\",\n" + + ")\n" + + "}\n", + true)); WorkflowRun run = proj.scheduleBuild2(0).get(); j.assertBuildStatusSuccess(run); - List expectations = horreumClient.alertingService.expectations(); + List expectations = getHorreumClient().alertingService.expectations(); expectations = expectations.stream().filter(e -> e.testId == dummyTest.id).collect(Collectors.toList()); assertEquals(1, expectations.size()); RunExpectation runExpectation = expectations.get(0); diff --git a/src/test/java/jenkins/plugins/horreum/HorreumExpectTest.java b/src/test/java/jenkins/plugins/horreum/HorreumExpectTest.java index e571e1b..8c9d51a 100644 --- a/src/test/java/jenkins/plugins/horreum/HorreumExpectTest.java +++ b/src/test/java/jenkins/plugins/horreum/HorreumExpectTest.java @@ -1,36 +1,38 @@ package jenkins.plugins.horreum; -import static io.hyperfoil.tools.HorreumTestClientExtension.dummyTest; -import static io.hyperfoil.tools.HorreumTestClientExtension.horreumClient; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static jenkins.plugins.horreum.junit.HorreumTestExtension.*; +import static jenkins.plugins.horreum.junit.HorreumTestClientExtension.getHorreumClient; import java.util.List; import java.util.stream.Collectors; +import io.hyperfoil.tools.horreum.api.alerting.RunExpectation; import org.junit.jupiter.api.Test; import hudson.model.FreeStyleBuild; import hudson.model.FreeStyleProject; -import io.hyperfoil.tools.horreum.entity.alerting.RunExpectation; import jenkins.plugins.horreum.expect.HorreumExpect; +import org.junit.jupiter.api.TestInfo; public class HorreumExpectTest extends HorreumPluginTestBase { @Test - public void testExpectRun() throws Exception { + public void testExpectRun(TestInfo info) throws Exception { + io.hyperfoil.tools.horreum.api.data.Test dummyTest = createTest(info.getTestClass() + "-expect", "dev-team"); HorreumExpect horreumExpect = new HorreumExpect( - HORREUM_UPLOAD_CREDENTIALS, dummyTest.name, 60, "Jenkins CI", "$BUILD_URL" + HORREUM_UPLOAD_CREDENTIALS, dummyTest.name, 60L, "Jenkins CI", "$BUILD_URL" ); // Run build - FreeStyleProject project = this.j.createFreeStyleProject("Horreum-Expect-Freestyle"); + FreeStyleProject project = j.createFreeStyleProject("Horreum-Expect-Freestyle"); project.getBuildersList().add(horreumExpect); FreeStyleBuild build = project.scheduleBuild2(0).get(); // Check expectations j.assertBuildStatusSuccess(build); - List expectations = horreumClient.alertingService.expectations(); + List expectations = getHorreumClient().alertingService.expectations(); expectations = expectations.stream().filter(e -> e.testId == dummyTest.id).collect(Collectors.toList()); assertEquals(1, expectations.size()); RunExpectation runExpectation = expectations.get(0); diff --git a/src/test/java/jenkins/plugins/horreum/HorreumPluginTestBase.java b/src/test/java/jenkins/plugins/horreum/HorreumPluginTestBase.java index 85c17cb..7b0de00 100644 --- a/src/test/java/jenkins/plugins/horreum/HorreumPluginTestBase.java +++ b/src/test/java/jenkins/plugins/horreum/HorreumPluginTestBase.java @@ -1,52 +1,25 @@ package jenkins.plugins.horreum; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.jupiter.api.BeforeEach; +import io.hyperfoil.tools.horreum.api.data.Test; +import jenkins.plugins.horreum.junit.HorreumTestClientExtension; +import jenkins.plugins.horreum.junit.HorreumTestExtension; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.api.extension.RegisterExtension; - -import com.cloudbees.plugins.credentials.Credentials; -import com.cloudbees.plugins.credentials.CredentialsScope; -import com.cloudbees.plugins.credentials.SystemCredentialsProvider; -import com.cloudbees.plugins.credentials.domains.Domain; -import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl; -import io.hyperfoil.tools.HorreumTestClientExtension; -import io.hyperfoil.tools.HorreumTestExtension; +import static jenkins.plugins.horreum.junit.HorreumTestClientExtension.getHorreumClient; @ExtendWith(HorreumTestClientExtension.class) public class HorreumPluginTestBase { - public static final String HORREUM_UPLOAD_CREDENTIALS = "horreum-creds"; - @RegisterExtension - public JenkinsExtension j = new JenkinsExtension(); - private Map> credentials; - - void registerBasicCredential(String id, String username, String password) { - credentials.get(Domain.global()).add( - new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, - id, "", username, password)); - SystemCredentialsProvider.getInstance().setDomainCredentialsMap(credentials); + Test createTest(String name, String owner) { + Test test = new Test(); + test.name = name; + test.owner = owner; + return getHorreumClient().testService.add(test); } - @BeforeEach - public void init() { - credentials = new HashMap<>(); - credentials.put(Domain.global(), new ArrayList()); - this.registerBasicCredential(HORREUM_UPLOAD_CREDENTIALS, "user", "secret"); - - HorreumGlobalConfig globalConfig = HorreumGlobalConfig.get(); - if (globalConfig != null) { - globalConfig.setKeycloakRealm("horreum"); - globalConfig.setClientId("horreum-ui"); - globalConfig.setKeycloakBaseUrl(HorreumTestExtension.HORREUM_KEYCLOAK_BASE_URL); - globalConfig.setBaseUrl(HorreumTestExtension.HORREUM_BASE_URL); - } else { - System.out.println("Can not find Horreum Global Config"); - } + @BeforeAll + public static void before() throws Exception { + HorreumTestExtension.beforeAll(); } } diff --git a/src/test/java/jenkins/plugins/horreum/HorreumUploadStepTest.java b/src/test/java/jenkins/plugins/horreum/HorreumUploadStepTest.java index 277f294..cf4d4b9 100644 --- a/src/test/java/jenkins/plugins/horreum/HorreumUploadStepTest.java +++ b/src/test/java/jenkins/plugins/horreum/HorreumUploadStepTest.java @@ -1,84 +1,88 @@ package jenkins.plugins.horreum; -import static io.hyperfoil.tools.HorreumTestClientExtension.dummyTest; -import static io.hyperfoil.tools.HorreumTestClientExtension.horreumClient; +import static jenkins.plugins.horreum.junit.HorreumTestExtension.*; import static org.junit.jupiter.api.Assertions.*; +import static jenkins.plugins.horreum.junit.HorreumTestClientExtension.getHorreumClient; import java.net.URL; -import java.util.Map; +import com.fasterxml.jackson.databind.node.ObjectNode; import hudson.FilePath; +import io.hyperfoil.tools.horreum.api.services.RunService; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; import org.junit.jupiter.api.Test; - -import io.hyperfoil.tools.horreum.api.RunService; +import org.junit.jupiter.api.TestInfo; public class HorreumUploadStepTest extends HorreumPluginTestBase { @Test public void testUpload() throws Exception { URL jsonResource = Thread.currentThread().getContextClassLoader().getResource("data/config-quickstart.jvm.json"); - WorkflowJob proj = j.jenkins.createProject(WorkflowJob.class, "Horreum-Upload-Pipeline"); + WorkflowJob proj = j.jenkins.createProject(WorkflowJob.class, "Horreum-Upload-Pipeline-testUpload"); + io.hyperfoil.tools.horreum.api.data.Test dummyTest = createTest("upload-single", "dev-team"); proj.setDefinition(new CpsFlowDefinition( - "node {\n" + - "def id = horreumUpload(\n" + - "credentials: '" + HorreumPluginTestBase.HORREUM_UPLOAD_CREDENTIALS + "',\n" + - "test: '" + dummyTest.name + "',\n" + - "owner: '" + dummyTest.owner + "',\n" + - "access: 'PUBLIC',\n" + - "start: '$.build-timestamp',\n" + - "stop: '$.build-timestamp',\n" + - "jsonFile: '" + jsonResource.getPath() + "',\n" + - "addBuildInfo: true\n" + - ")\n" + - "println(id)\n" + - "}\n", - true)); + "node {\n" + + "def id = horreumUpload(\n" + + "credentials: '" + HORREUM_UPLOAD_CREDENTIALS + "',\n" + + "test: '" + dummyTest.name + "',\n" + + "owner: '" + dummyTest.owner + "',\n" + + "access: 'PUBLIC',\n" + + "start: '$.build-timestamp',\n" + + "stop: '$.build-timestamp',\n" + + "jsonFile: '" + jsonResource.getPath() + "',\n" + + "addBuildInfo: true\n" + + ")\n" + + "println(id)\n" + + "}\n", + true)); WorkflowRun run = proj.scheduleBuild2(0).get(); j.assertBuildStatusSuccess(run); - RunService.RunsSummary summary = horreumClient.runService.listTestRuns(dummyTest.id, false, null, null, "", null); + RunService.RunsSummary summary = getHorreumClient().runService.listTestRuns(dummyTest.id, false, null, null, "", null); assertEquals(1, summary.total); assertEquals(1, summary.runs.size()); } @Test - public void testUploadMultiple() throws Exception { + public void testUploadMultiple(TestInfo info) throws Exception { URL jsonResource1 = Thread.currentThread().getContextClassLoader().getResource("data/config-quickstart.jvm.json"); URL jsonResource2 = Thread.currentThread().getContextClassLoader().getResource("data/another-file.json"); - WorkflowJob proj = j.jenkins.createProject(WorkflowJob.class, "Horreum-Upload-Pipeline"); + assertNotNull(j); + assertNotNull(j.jenkins); + WorkflowJob proj = j.jenkins.createProject(WorkflowJob.class, "Horreum-Upload-Pipeline-testUploadMultiple"); FilePath folder = j.jenkins.getWorkspaceFor(proj).child("run"); folder.child("config-quickstart.jvm.json").copyFrom(jsonResource1); folder.child("another-file.json").copyFrom(jsonResource2); + io.hyperfoil.tools.horreum.api.data.Test dummyTest = createTest(info.getTestClass() + "-upload-multiple", "dev-team"); proj.setDefinition(new CpsFlowDefinition( - "node {\n" + - "def id = horreumUpload(\n" + - "credentials: '" + HorreumPluginTestBase.HORREUM_UPLOAD_CREDENTIALS + "',\n" + - "test: '" + dummyTest.name + "',\n" + - "owner: '" + dummyTest.owner + "',\n" + - "access: 'PUBLIC',\n" + - "start: '1970-01-01T00:00:00.00Z',\n" + - "stop: '1970-01-01T00:00:01.00Z',\n" + - "files: '**/*.json',\n"+ - "addBuildInfo: true\n" + - ")\n" + - "println(id)\n" + - "}\n", - true)); + "node {\n" + + "def id = horreumUpload(\n" + + "credentials: '" + HORREUM_UPLOAD_CREDENTIALS + "',\n" + + "test: '" + dummyTest.name + "',\n" + + "owner: '" + dummyTest.owner + "',\n" + + "access: 'PUBLIC',\n" + + "start: '1970-01-01T00:00:00.00Z',\n" + + "stop: '1970-01-01T00:00:01.00Z',\n" + + "files: '**/*.json',\n"+ + "addBuildInfo: true\n" + + ")\n" + + "println(id)\n" + + "}\n", + true)); WorkflowRun run = proj.scheduleBuild2(0).get(); j.assertBuildStatusSuccess(run); - RunService.RunsSummary summary = horreumClient.runService.listTestRuns(dummyTest.id, false, null, null, "", null); + RunService.RunsSummary summary = getHorreumClient().runService.listTestRuns(dummyTest.id, false, null, null, "", null); assertEquals(1, summary.total); assertEquals(1, summary.runs.size()); - Object runObject = horreumClient.runService.getRun(summary.runs.get(0).id,summary.runs.get(0).token); + RunService.RunExtended runObject = getHorreumClient().runService.getRun(summary.runs.get(0).id,summary.runs.get(0).token); assertNotNull(runObject); - assertTrue(runObject instanceof Map,"run should return a map"); - Object data = ((Map)runObject).get("data"); + assertInstanceOf(RunService.RunExtended.class, runObject,"run should return a RunService.RunExtended"); + Object data = runObject.data; assertNotNull(data); - assertTrue(data instanceof Map,"data should be a map"); - assertEquals(2,((Map) data).size(),"data should have an entry for each file"); + assertInstanceOf(ObjectNode.class, data,"data should be a ObjectNode"); + assertEquals(2,((ObjectNode) data).size(),"data should have an entry for each file"); } } diff --git a/src/test/java/jenkins/plugins/horreum/HorreumUploadTest.java b/src/test/java/jenkins/plugins/horreum/HorreumUploadTest.java index d8669b3..4973f1c 100644 --- a/src/test/java/jenkins/plugins/horreum/HorreumUploadTest.java +++ b/src/test/java/jenkins/plugins/horreum/HorreumUploadTest.java @@ -1,7 +1,7 @@ package jenkins.plugins.horreum; -import static io.hyperfoil.tools.HorreumTestClientExtension.dummyTest; -import static io.hyperfoil.tools.HorreumTestClientExtension.horreumClient; +import static jenkins.plugins.horreum.junit.HorreumTestExtension.*; +import static jenkins.plugins.horreum.junit.HorreumTestClientExtension.getHorreumClient; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -10,89 +10,94 @@ import java.io.InputStreamReader; import java.net.URL; import java.util.Arrays; +import java.util.Objects; import java.util.stream.Collectors; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; import org.jvnet.hudson.test.CreateFileBuilder; import hudson.model.FreeStyleBuild; import hudson.model.FreeStyleProject; import hudson.tasks.Builder; -import io.hyperfoil.tools.horreum.api.RunService; -import io.hyperfoil.tools.horreum.entity.json.Access; -import io.hyperfoil.tools.horreum.entity.json.Schema; +import io.hyperfoil.tools.horreum.api.services.RunService; +import io.hyperfoil.tools.horreum.api.data.Access; +import io.hyperfoil.tools.horreum.api.data.Schema; import jenkins.plugins.horreum.upload.HorreumUpload; public class HorreumUploadTest extends HorreumPluginTestBase { @Test - public void testUpload() throws Exception { + public void testUpload(TestInfo info) throws Exception { URL jsonResource = Thread.currentThread().getContextClassLoader().getResource("data/config-quickstart.jvm.json"); + io.hyperfoil.tools.horreum.api.data.Test dummyTest = createTest(info.getTestClass() + "-upload-single", "dev-team"); // Prepare HttpRequest# HorreumUpload horreumUpload = new HorreumUpload( - HORREUM_UPLOAD_CREDENTIALS, - dummyTest.name, - dummyTest.owner, - "PUBLIC", - "$.build-timestamp", - "$.build-timestamp", - "", - jsonResource.getPath(), - null, - true + HORREUM_UPLOAD_CREDENTIALS, + dummyTest.name, + dummyTest.owner, + "PUBLIC", + "$.build-timestamp", + "$.build-timestamp", + "", + jsonResource.getPath(), + null, + true ); - runInFreeStyleProject("Horreum-Upload-Freestyle", horreumUpload); + runInFreeStyleProject("Horreum-Upload-Freestyle", dummyTest, horreumUpload); } @Test - public void testUploadMultiple() throws Exception { + public void testUploadMultiple(TestInfo info) throws Exception { String json1 = readFile("data/config-quickstart.jvm.json"); String json2 = readFile("data/another-file.json"); + io.hyperfoil.tools.horreum.api.data.Test dummyTest = createTest(info.getTestClass() + "-upload-multiple", "dev-team"); - addSchema("Some schema", "urn:some-schema"); - addSchema("Foobar", "urn:foobar"); + addSchema("Some schema", "urn:some-schema", dummyTest); + addSchema("Foobar", "urn:foobar", dummyTest); // Prepare HttpRequest# HorreumUpload horreumUpload = new HorreumUpload( - HORREUM_UPLOAD_CREDENTIALS, - dummyTest.name, - dummyTest.owner, - "PUBLIC", - "2022-12-07T01:23:45Z", // cannot use JSONPath with multiple files... - "2022-12-07T01:23:45Z", - "urn:some-schema", - null, - "**/*.json", - true + HORREUM_UPLOAD_CREDENTIALS, + dummyTest.name, + dummyTest.owner, + "PUBLIC", + "2022-12-07T01:23:45Z", // cannot use JSONPath with multiple files... + "2022-12-07T01:23:45Z", + "urn:some-schema", + null, + "**/*.json", + true ); - RunService.RunSummary summary = runInFreeStyleProject("Horreum-Upload-Multiple", - new CreateFileBuilder("file1.json", json1), - new CreateFileBuilder("file2.json", json2), - horreumUpload); + RunService.RunSummary summary = runInFreeStyleProject("Horreum-Upload-Multiple", dummyTest, + new CreateFileBuilder("file1.json", json1), + new CreateFileBuilder("file2.json", json2), + horreumUpload); assertEquals(2, summary.schemas.size()); } - private void addSchema(String name, String uri) { + + private void addSchema(String name, String uri, io.hyperfoil.tools.horreum.api.data.Test dummyTest) { Schema schema = new Schema(); schema.name = name; schema.uri = uri; schema.owner = dummyTest.owner; schema.access = Access.PUBLIC; - horreumClient.schemaService.add(schema); + getHorreumClient().schemaService.add(schema); } private String readFile(String filename) throws IOException { ClassLoader cl = Thread.currentThread().getContextClassLoader(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(cl.getResourceAsStream(filename)))) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(cl.getResourceAsStream(filename))))) { return reader.lines().collect(Collectors.joining("\n")); } } - private RunService.RunSummary runInFreeStyleProject(String name, Builder... builders) throws Exception { + private RunService.RunSummary runInFreeStyleProject(String name, io.hyperfoil.tools.horreum.api.data.Test dummyTest, Builder... builders) throws Exception { // Run build - FreeStyleProject project = this.j.createFreeStyleProject(name); + FreeStyleProject project = j.createFreeStyleProject(name); assertTrue(builders.length > 0); assertTrue(Arrays.stream(builders).anyMatch(b -> b instanceof HorreumUpload)); project.getBuildersList().addAll(Arrays.asList(builders)); @@ -101,7 +106,7 @@ private RunService.RunSummary runInFreeStyleProject(String name, Builder... buil // Check expectations j.assertBuildStatusSuccess(build); - RunService.RunsSummary summary = horreumClient.runService.listTestRuns(dummyTest.id, false, null, null, "", null); + RunService.RunsSummary summary = getHorreumClient().runService.listTestRuns(dummyTest.id, false, null, null, "", null); assertEquals(1, summary.total); assertEquals(1, summary.runs.size()); return summary.runs.get(0); diff --git a/src/test/java/jenkins/plugins/horreum/JenkinsExtension.java b/src/test/java/jenkins/plugins/horreum/JenkinsExtension.java index c0a3c79..9fc3a14 100644 --- a/src/test/java/jenkins/plugins/horreum/JenkinsExtension.java +++ b/src/test/java/jenkins/plugins/horreum/JenkinsExtension.java @@ -9,6 +9,7 @@ // Copied from https://issues.jenkins.io/browse/JENKINS-48466 public class JenkinsExtension extends org.jvnet.hudson.test.JenkinsRule implements BeforeEachCallback, AfterEachCallback { + @Override public void beforeEach(ExtensionContext context) throws Exception { this.testDescription = Description.createTestDescription( diff --git a/src/test/java/jenkins/plugins/horreum/it/JenkinsResources.java b/src/test/java/jenkins/plugins/horreum/it/JenkinsResources.java new file mode 100644 index 0000000..e44c3be --- /dev/null +++ b/src/test/java/jenkins/plugins/horreum/it/JenkinsResources.java @@ -0,0 +1,49 @@ +package jenkins.plugins.horreum.it; + +import io.hyperfoil.tools.horreum.infra.common.HorreumResources; +import io.hyperfoil.tools.horreum.infra.common.resources.ArtemisMQResource; +import io.hyperfoil.tools.horreum.infra.common.resources.HorreumResource; +import org.testcontainers.containers.Network; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class JenkinsResources extends HorreumResources { + public static ArtemisMQResource amqpResource = new ArtemisMQResource(); + public static HorreumResource horreumResource = new HorreumResource(); + + public static Map startAMQPContainer(Map initArgs) { + Map envVariables = new HashMap<>(initArgs); + Optional optionalNetwork= Optional.of(HorreumResources.getNetwork()); + + amqpResource.init(envVariables); + envVariables.putAll(amqpResource.start(optionalNetwork)); + return envVariables; + } + + + public static Map startHorreumContainer(Map initArgs) { + Map envVariables = new HashMap<>(initArgs); + Optional optionalNetwork= Optional.of(HorreumResources.getNetwork()); + horreumResource.init(envVariables); + envVariables.putAll(horreumResource.start(optionalNetwork)); + return envVariables; + } + + public static void stopContainers() { + stopAMQPContainer(); + stopHorreumContainer(); + } + + public static void stopAMQPContainer() { + if (amqpResource != null) { + amqpResource.stop(); + } + } + public static void stopHorreumContainer() { + if (horreumResource != null) { + horreumResource.stop(); + } + } +} diff --git a/src/test/java/jenkins/plugins/horreum/junit/HorreumTestClientExtension.java b/src/test/java/jenkins/plugins/horreum/junit/HorreumTestClientExtension.java new file mode 100644 index 0000000..f9f9a6d --- /dev/null +++ b/src/test/java/jenkins/plugins/horreum/junit/HorreumTestClientExtension.java @@ -0,0 +1,33 @@ +package jenkins.plugins.horreum.junit; + +import io.hyperfoil.tools.HorreumClient; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +public class HorreumTestClientExtension extends HorreumTestExtension implements BeforeAllCallback { + private static HorreumClient horreumClient; + + public static void instantiateClient() { + if (horreumClient == null) { + String horreumUrl = "http://".concat(horreumHost).concat(":").concat(horreumPort).concat("/"); + horreumClient = new HorreumClient.Builder() + .horreumUrl(horreumUrl) + .horreumUser("horreum.bootstrap") + .horreumPassword("secret") + .build(); + Assertions.assertNotNull(horreumClient); + } + } + + public static HorreumClient getHorreumClient() { + if (horreumClient == null) { + instantiateClient(); + } + return horreumClient; + } + + @Override + public void beforeAll(ExtensionContext extensionContext) { + } +} diff --git a/src/test/java/jenkins/plugins/horreum/junit/HorreumTestExtension.java b/src/test/java/jenkins/plugins/horreum/junit/HorreumTestExtension.java new file mode 100644 index 0000000..964bafa --- /dev/null +++ b/src/test/java/jenkins/plugins/horreum/junit/HorreumTestExtension.java @@ -0,0 +1,141 @@ +package jenkins.plugins.horreum.junit; + +import com.cloudbees.plugins.credentials.Credentials; +import com.cloudbees.plugins.credentials.CredentialsScope; +import com.cloudbees.plugins.credentials.SystemCredentialsProvider; +import com.cloudbees.plugins.credentials.domains.Domain; +import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl; +import io.hyperfoil.tools.horreum.api.services.ConfigService; +import io.hyperfoil.tools.horreum.infra.common.SelfSignedCert; +import jenkins.plugins.horreum.HorreumGlobalConfig; +import jenkins.plugins.horreum.JenkinsExtension; +import org.jboss.logging.Logger; +import org.junit.jupiter.api.extension.*; + +import java.io.InputStream; +import java.util.*; + +import static io.hyperfoil.tools.horreum.infra.common.Const.*; +import static io.hyperfoil.tools.horreum.infra.common.HorreumResources.startContainers; +import static java.lang.System.getProperty; +import static jenkins.plugins.horreum.it.JenkinsResources.*; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class HorreumTestExtension { + + private static final Logger log = Logger.getLogger(HorreumTestExtension.class); + private static boolean started = false; + public static String horreumHost; + public static String horreumPort = "8080"; + private static final Map> credentials = new HashMap<>(); + public static final String HORREUM_UPLOAD_CREDENTIALS = "horreum-creds"; + public static final String DEFAULT_BOOTSTRAP_USERNAME = "horreum.bootstrap"; + public static final String DEFAULT_BOOTSTRAP_PASSWORD = "secret"; + + @RegisterExtension + public static JenkinsExtension j = new JenkinsExtension(); + + public static void beforeAll() throws Exception { + synchronized (HorreumTestExtension.class) { + if (!started) { + log.info("Starting Jenkins IT resources"); + + try { + String keycloakImage, postgresImage, amqpImage, horreumImage; + try (InputStream is = HorreumTestExtension.class.getClassLoader().getResourceAsStream("META-INF/horreum.dev.properties")){ + Properties properties = new Properties(); + properties.load(is); + keycloakImage = properties.getProperty ("dev.images.keycloak"); + postgresImage = properties.getProperty ("dev.images.postgres"); + amqpImage = properties.getProperty ("dev.images.amq"); + horreumImage = properties.getProperty ("dev.images.horreum"); + } + + if ( keycloakImage == null || postgresImage == null || amqpImage == null || horreumImage == null){ + throw new RuntimeException("Test container images are not defined as system properties"); + } + + SelfSignedCert postgresSelfSignedCert = new SelfSignedCert("RSA", "SHA256withRSA", "localhost", 123); + Map containerArgs = new HashMap<>(); + containerArgs.putAll( + Map.ofEntries( + Map.entry(HORREUM_DEV_KEYCLOAK_ENABLED, "true"), + Map.entry(HORREUM_DEV_KEYCLOAK_IMAGE, keycloakImage), + Map.entry(HORREUM_DEV_KEYCLOAK_NETWORK_ALIAS, DEFAULT_KEYCLOAK_NETWORK_ALIAS), + Map.entry(HORREUM_DEV_POSTGRES_ENABLED, "true"), + Map.entry(HORREUM_DEV_POSTGRES_IMAGE, postgresImage), + Map.entry(HORREUM_DEV_POSTGRES_NETWORK_ALIAS, DEFAULT_POSTGRES_NETWORK_ALIAS), + Map.entry(HORREUM_DEV_POSTGRES_SSL_CERTIFICATE, postgresSelfSignedCert.getCertString()), + Map.entry(HORREUM_DEV_POSTGRES_SSL_CERTIFICATE_KEY, postgresSelfSignedCert.getKeyString()), + Map.entry(HORREUM_DEV_KEYCLOAK_DB_USERNAME, DEFAULT_KC_DB_USERNAME), + Map.entry(HORREUM_DEV_KEYCLOAK_DB_PASSWORD, DEFAULT_KC_DB_PASSWORD), + Map.entry(HORREUM_DEV_KEYCLOAK_ADMIN_USERNAME, DEFAULT_KC_ADMIN_USERNAME), + Map.entry(HORREUM_DEV_KEYCLOAK_ADMIN_PASSWORD, DEFAULT_KC_ADMIN_PASSWORD), + Map.entry(HORREUM_DEV_HORREUM_HORREUM_IMAGE, horreumImage), + Map.entry("horreum.roles.provider", "database"), + Map.entry("quarkus.keycloak.admin-client.client-id", "horreum-client"), + Map.entry("quarkus.keycloak.admin-client.realm", "horreum"), + Map.entry("quarkus.keycloak.admin-client.client-secret", "secret"), + Map.entry("quarkus.keycloak.admin-client.grant-type", "client_credentials"), + Map.entry("keycloak.use.https", "false"), + Map.entry("keycloak.service.client", "horreum-client"), + Map.entry("keycloak.realm", "horreum"), + Map.entry("keycloak.token.admin-roles", "admin,manager,tester,viewer,uploader"), + Map.entry(HORREUM_DEV_AMQP_ENABLED, "true"), + Map.entry(HORREUM_DEV_AMQP_IMAGE, amqpImage), + Map.entry(HORREUM_DEV_AMQP_NETWORK_ALIAS, DEFAULT_AMQP_NETWORK_ALIAS), + Map.entry("amqp-username", DEFAULT_AMQP_USERNAME), + Map.entry("amqp-password", DEFAULT_AMQP_PASSWORD), + Map.entry("inContainer", "true"), + Map.entry("quarkus.http.host", DEFAULT_HORREUM_NETWORK_ALIAS), + Map.entry("quarkus.http.port", "8080"), + Map.entry("quarkus.profile", "dev"), + Map.entry(HORREUM_DEV_HORREUM_NETWORK_ALIAS, DEFAULT_HORREUM_NETWORK_ALIAS) + )); + containerArgs.putAll(startContainers(containerArgs)); + containerArgs.putAll(startAMQPContainer(containerArgs)); + containerArgs.putAll(startHorreumContainer(containerArgs)); + try { + j.before(); + started = true; + } catch (Throwable e){ + log.fatal("Could not start Jenkins service", e); + throw new Exception(e); + } + horreumHost = "localhost"; + horreumPort = containerArgs.get("horreum.container.port"); + } catch (Exception e){ + log.fatal("Could not start services", e); + throw new RuntimeException("Could not start Jenkins/Horreum services", e); + } + try { + HorreumGlobalConfig globalConfig = HorreumGlobalConfig.get(); + if (globalConfig != null) { + globalConfig.setKeycloakRealm("horreum"); + globalConfig.setClientId("horreum-ui"); + globalConfig.setKeycloakBaseUrl(ConfigService.KEYCLOAK_BOOTSTRAP_URL); + String baseUrl = String.format("http://%s:%s", horreumHost, horreumPort); + globalConfig.setBaseUrl(baseUrl); + } else { + System.out.println("Can not find Horreum Global Config"); + } + credentials.put(Domain.global(), new ArrayList()); + registerBasicCredential(HORREUM_UPLOAD_CREDENTIALS, DEFAULT_BOOTSTRAP_USERNAME, DEFAULT_BOOTSTRAP_PASSWORD); + } catch (Throwable throwable) { + log.fatal("Could not configure basic credentials re-configure Keycloak", throwable); + throw new RuntimeException(throwable); + } + + } + } + } + + static void registerBasicCredential(String id, String username, String password) { + credentials.get(Domain.global()).add( + new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, + id, "", username, password)); + SystemCredentialsProvider scp = SystemCredentialsProvider.getInstance(); + assertNotNull(scp); + scp.setDomainCredentialsMap(credentials); + } +} diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..41c39c0 --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,3 @@ +horreum.username=horreum.bootstrap +horreum.password=secret +quarkus.datasource.password=secret diff --git a/src/test/resources/env.properties b/src/test/resources/env.properties new file mode 100644 index 0000000..e69de29