diff --git a/integrations/src/test/java/io/jenkins/plugins/casc/JobDslGlobalSecurityConfigurationTest.java b/integrations/src/test/java/io/jenkins/plugins/casc/JobDslGlobalSecurityConfigurationTest.java index cbeedc331a..586fc3a104 100644 --- a/integrations/src/test/java/io/jenkins/plugins/casc/JobDslGlobalSecurityConfigurationTest.java +++ b/integrations/src/test/java/io/jenkins/plugins/casc/JobDslGlobalSecurityConfigurationTest.java @@ -2,38 +2,52 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotNull; import javaposse.jobdsl.plugin.GlobalJobDslSecurityConfiguration; import jenkins.model.GlobalConfiguration; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runners.model.Statement; +import org.junit.jupiter.api.Test; import org.jvnet.hudson.test.Issue; -import org.jvnet.hudson.test.RestartableJenkinsRule; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.junit.jupiter.WithJenkins; /** * Created by odavid on 23/12/2017. */ -public class JobDslGlobalSecurityConfigurationTest { - - @Rule - public RestartableJenkinsRule j = new RestartableJenkinsRule(); +@WithJenkins +class JobDslGlobalSecurityConfigurationTest { @Test - public void test_global_dsl_security_can_be_applied() { - j.addStep(validateGlobalDSLSecurity); + void test_global_dsl_security_can_be_applied(@SuppressWarnings("unused") JenkinsRule j) { + GlobalJobDslSecurityConfiguration dslSecurity = getGlobalJobDslSecurityConfiguration(); + dslSecurity.setUseScriptSecurity(true); + + assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(true)); + configure(); + assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(false)); } @Test @Issue("#253") - public void test_global_dsl_security_can_be_reapplied_after_restart() { - j.addStep(validateGlobalDSLSecurity); - j.addStep(validateGlobalDSLSecurityAfterRestart, true); + void test_global_dsl_security_can_be_reapplied_after_restart(JenkinsRule j) throws Throwable { + GlobalJobDslSecurityConfiguration dslSecurity = getGlobalJobDslSecurityConfiguration(); + dslSecurity.setUseScriptSecurity(true); + + assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(true)); + configure(); + assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(false)); + + j.restart(); + + dslSecurity = getGlobalJobDslSecurityConfiguration(); + + assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(false)); + configure(); + assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(false)); } private GlobalJobDslSecurityConfiguration getGlobalJobDslSecurityConfiguration() { - final GlobalJobDslSecurityConfiguration dslSecurity = + GlobalJobDslSecurityConfiguration dslSecurity = GlobalConfiguration.all().get(GlobalJobDslSecurityConfiguration.class); assertNotNull(dslSecurity); return dslSecurity; @@ -45,34 +59,4 @@ private void configure() throws ConfiguratorException { .getResource("JobDslGlobalSecurityConfigurationTest.yml") .toExternalForm()); } - - private Statement validateGlobalDSLSecurity = new Statement() { - - @Override - public void evaluate() throws Throwable { - final GlobalJobDslSecurityConfiguration dslSecurity = getGlobalJobDslSecurityConfiguration(); - - dslSecurity.setUseScriptSecurity(true); - assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(true)); - - configure(); - - assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(false)); - } - }; - - private Statement validateGlobalDSLSecurityAfterRestart = new Statement() { - - @Override - public void evaluate() throws Throwable { - final GlobalJobDslSecurityConfiguration dslSecurity = getGlobalJobDslSecurityConfiguration(); - - // step 1 configuration still applies - assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(false)); - - configure(); - - assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(false)); - } - }; } diff --git a/integrations/src/test/java/io/jenkins/plugins/casc/RoundTripMailerTest.java b/integrations/src/test/java/io/jenkins/plugins/casc/RoundTripMailerTest.java index 3a73b9b76a..8b3067fe6c 100644 --- a/integrations/src/test/java/io/jenkins/plugins/casc/RoundTripMailerTest.java +++ b/integrations/src/test/java/io/jenkins/plugins/casc/RoundTripMailerTest.java @@ -1,15 +1,17 @@ package io.jenkins.plugins.casc; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import hudson.tasks.Mailer; -import io.jenkins.plugins.casc.misc.RoundTripAbstractTest; +import io.jenkins.plugins.casc.misc.junit.jupiter.AbstractRoundTripTest; import jenkins.model.Jenkins; -import org.jvnet.hudson.test.RestartableJenkinsRule; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.junit.jupiter.WithJenkins; -public class RoundTripMailerTest extends RoundTripAbstractTest { +@WithJenkins +class RoundTripMailerTest extends AbstractRoundTripTest { @Override - protected void assertConfiguredAsExpected(RestartableJenkinsRule j, String configContent) { + protected void assertConfiguredAsExpected(JenkinsRule j, String configContent) { final Jenkins jenkins = Jenkins.get(); final Mailer.DescriptorImpl descriptor = (Mailer.DescriptorImpl) jenkins.getDescriptor(Mailer.class); assertEquals("4441", descriptor.getSmtpPort()); diff --git a/pom.xml b/pom.xml index eacf3ef2f9..75fc4e9457 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.jenkins-ci.plugins plugin - 5.5 + 5.6 diff --git a/test-harness/src/main/java/io/jenkins/plugins/casc/misc/RoundTripAbstractTest.java b/test-harness/src/main/java/io/jenkins/plugins/casc/misc/RoundTripAbstractTest.java index 94382c89e1..b72f133453 100644 --- a/test-harness/src/main/java/io/jenkins/plugins/casc/misc/RoundTripAbstractTest.java +++ b/test-harness/src/main/java/io/jenkins/plugins/casc/misc/RoundTripAbstractTest.java @@ -44,7 +44,9 @@ * text configured. * * @since 1.20 + * @deprecated Consider migrating to JUnit5 and use {@link io.jenkins.plugins.casc.misc.junit.jupiter.AbstractRoundTripTest} instead. */ +@Deprecated public abstract class RoundTripAbstractTest { @Rule public RestartableJenkinsRule r = new RestartableJenkinsRule(); diff --git a/test-harness/src/main/java/io/jenkins/plugins/casc/misc/junit/jupiter/AbstractRoundTripTest.java b/test-harness/src/main/java/io/jenkins/plugins/casc/misc/junit/jupiter/AbstractRoundTripTest.java new file mode 100644 index 0000000000..b1dbef7ace --- /dev/null +++ b/test-harness/src/main/java/io/jenkins/plugins/casc/misc/junit/jupiter/AbstractRoundTripTest.java @@ -0,0 +1,211 @@ +package io.jenkins.plugins.casc.misc.junit.jupiter; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.htmlunit.HttpMethod.POST; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.jenkins.plugins.casc.ConfigurationAsCode; +import io.jenkins.plugins.casc.ConfiguratorException; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.Collections; +import java.util.logging.Level; +import org.apache.commons.io.IOUtils; +import org.htmlunit.WebRequest; +import org.htmlunit.WebResponse; +import org.htmlunit.util.NameValuePair; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.LogRecorder; +import org.jvnet.hudson.test.junit.jupiter.WithJenkins; + +/** + * Base test to check a complete test of each plugin configuration. + *

+ * What it does: + *

    + *
  1. Configure the instance with the {@link #configResource()} implemented.
  2. + *
  3. Check it was configured correctly.
  4. + *
  5. Check the configuration is valid via Web UI.
  6. + *
  7. Apply the configuration via Web UI.
  8. + *
  9. Write the configuration to $JENKINS_HOME/jenkins.yaml.
  10. + *
  11. Restart Jenkins.
  12. + *
  13. Check the {@link #stringInLogExpected()} is set during the restart.
  14. + *
  15. Check it is still configured correctly after the restart
  16. + *
+ *

+ * All the plugin author needs to do is override the methods providing: + *

    + *
  1. The resource with the yaml configuration of the plugin in case they use their own name for the file.
  2. + *
  3. A way to validate the configuration is established.
  4. + *
  5. A string that should be present in the logs (casc logger) that guarantees the config is loaded. Usually a weird text configured.
  6. + *
+ *

+ * This is the JUnit5 equivalent of {@link io.jenkins.plugins.casc.misc.RoundTripAbstractTest} + * + * @see io.jenkins.plugins.casc.misc.RoundTripAbstractTest + */ +@WithJenkins +public abstract class AbstractRoundTripTest { + + @TempDir + public Path tempFolder; + + /** + * A method to assert if the configuration was correctly loaded. The Jenkins rule and the + * content of the config supposedly loaded are passed. + * + * @param j a JenkinsRule instance. + * @param configContent expected configuration. + */ + protected abstract void assertConfiguredAsExpected(JenkinsRule j, String configContent); + + /** + * Return the resource path (yaml file) to be loaded. i.e: If the resource is in the same + * package of the implementor class, then: my-config.yaml + * + * @return the resource name and path. + */ + protected String configResource() { + return "configuration-as-code.yaml"; + } + + /** + * Return the string that should be in the logs of the JCasC logger to verify it's configured + * after a restart. This string should be unique to avoid interpreting that it was configured + * successfully, but it wasn't. + * + * @return the unique string to be in the logs to certify the configuration was done + * successfully. + */ + protected abstract String stringInLogExpected(); + + /** + * 1. Configure the instance with the {@link #configResource()} implemented. 2. Check it was + * configured correctly. 3. Check the configuration is valid via Web UI. 4. Apply the + * configuration via Web UI. 5. Write the configuration to $JENKINS_HOME/jenkins.yaml. 6. + * Restart Jenkins. 7. Check the {@link #stringInLogExpected()} is set during the restart. + * + * @throws IOException If an exception is thrown managing resources or files. + */ + @Test + public void roundTripTest(JenkinsRule r) throws Throwable { + String resourcePath = configResource(); + String resourceContent = getResourceContent(resourcePath); + + assertNotNull(resourcePath); + assertNotNull(resourceContent); + + // Configure and validate + configureWithResource(resourcePath); + assertConfiguredAsExpected(r, resourceContent); + + // Check config is valid via Web UI + String jenkinsConf = getResourceContent(resourcePath); + assertConfigViaWebUI(r, jenkinsConf); + + // Apply configuration via Web UI + applyConfigViaWebUI(r, jenkinsConf); + assertConfiguredAsExpected(r, resourceContent); + + // Configure Jenkins default JCasC file with the config file. It's already established, we check if applied + // looking at the logs. + putConfigInHome(r, jenkinsConf); + + // Start recording the logs just before restarting, to avoid capture the previous startup. We're look there + // if the "magic token" is there + try (LogRecorder recorder = + new LogRecorder().record("io.jenkins.plugins.casc", Level.FINER).capture(2048)) { + // Restart the testing instance + r.restart(); + + // Verify the log shows it's configured + assertLogAsExpected(recorder, stringInLogExpected()); + } + + // Verify the configuration set at home/jenkins.yaml is loaded + assertConfiguredAsExpected(r, resourceContent); + } + + private void configureWithResource(String config) throws ConfiguratorException { + ConfigurationAsCode.get().configure(this.getClass().getResource(config).toExternalForm()); + } + + private String getResourceContent(String resourcePath) throws IOException { + return IOUtils.toString(getClass().getResourceAsStream(resourcePath), StandardCharsets.UTF_8); + } + + private void writeToFile(String text, String path) throws FileNotFoundException { + File file = new File(path); + try (PrintWriter out = new PrintWriter(file)) { + out.print(text); + } + } + + private void putConfigInHome(JenkinsRule r, String config) throws Exception { + File configFile = new File(r.getWebAppRoot(), ConfigurationAsCode.DEFAULT_JENKINS_YAML_PATH); + + writeToFile(config, configFile.getAbsolutePath()); + assertTrue(configFile.exists(), ConfigurationAsCode.DEFAULT_JENKINS_YAML_PATH + " should be created"); + } + + private void assertConfigViaWebUI(JenkinsRule r, String jenkinsConfig) throws Exception { + // The UI requires the path to the config file + File f = File.createTempFile("junit", null, tempFolder.toFile()); + writeToFile(jenkinsConfig, f.getAbsolutePath()); + + // Call the check url + JenkinsRule.WebClient client = r.createWebClient(); + WebRequest request = new WebRequest(client.createCrumbedUrl("configuration-as-code/checkNewSource"), POST); + NameValuePair param = new NameValuePair("newSource", f.toURI().toURL().toExternalForm()); + request.setRequestParameters(Collections.singletonList(param)); + WebResponse response = client.loadWebResponse(request); + assertEquals( + 200, + response.getStatusCode(), + "Failed to POST to " + request.getUrl().toString()); + String res = response.getContentAsString(); + assertThat(res, containsString("The configuration can be applied")); + } + + private void applyConfigViaWebUI(JenkinsRule r, String jenkinsConfig) throws Exception { + // The UI requires the path to the config file + File f = File.createTempFile("junit", null, tempFolder.toFile()); + writeToFile(jenkinsConfig, f.getAbsolutePath()); + + // Call the replace url + JenkinsRule.WebClient client = r.createWebClient(); + WebRequest request = new WebRequest(client.createCrumbedUrl("configuration-as-code/replace"), POST); + NameValuePair param = new NameValuePair("_.newSource", f.toURI().toURL().toExternalForm()); + request.setRequestParameters(Collections.singletonList(param)); + request.setRequestParameters(Collections.singletonList(param)); + WebResponse response = client.loadWebResponse(request); + assertEquals( + 200, + response.getStatusCode(), + "Failed to POST to " + request.getUrl().toString()); + String res = response.getContentAsString(); + /* The result page has: + Configuration loaded from : +

+ path is the file used to store the configuration. + */ + assertThat(res, containsString(f.toURI().toURL().toExternalForm())); + } + + private void assertLogAsExpected(LogRecorder recorder, String uniqueText) { + assertTrue( + recorder.getMessages().stream().anyMatch(m -> m.contains(uniqueText)), + "The log should have '" + uniqueText + "'"); + } +}