Skip to content

Commit

Permalink
Provide JUnit5 compatible alternative for RoundTripAbstractTest and R…
Browse files Browse the repository at this point in the history
…estartableJenkinsRule (#2623)

Co-authored-by: strangelookingnerd <[email protected]>
  • Loading branch information
strangelookingnerd and strangelookingnerd authored Jan 28, 2025
1 parent 01e7b59 commit 036b5ae
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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));
}
};
}
Original file line number Diff line number Diff line change
@@ -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());
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>5.5</version>
<version>5.6</version>
<relativePath />
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
* <p>
* What it does:
* <ol>
* <li>Configure the instance with the {@link #configResource()} implemented.</li>
* <li>Check it was configured correctly.</li>
* <li>Check the configuration is valid via Web UI.</li>
* <li>Apply the configuration via Web UI.</li>
* <li>Write the configuration to $JENKINS_HOME/jenkins.yaml.</li>
* <li>Restart Jenkins.</li>
* <li>Check the {@link #stringInLogExpected()} is set during the restart.</li>
* <li>Check it is still configured correctly after the restart</li>
* </ol>
* <p>
* All the plugin author needs to do is override the methods providing:
* <ol>
* <li>The resource with the yaml configuration of the plugin in case they use their own name for the file.</li>
* <li>A way to validate the configuration is established.</li>
* <li>A string that should be present in the logs (casc logger) that guarantees the config is loaded. Usually a weird text configured.</li>
* </ol>
* <p>
* 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 :
<ul>
<li>path</li>
</ul>
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 + "'");
}
}

0 comments on commit 036b5ae

Please sign in to comment.