Skip to content

Commit 036b5ae

Browse files
Provide JUnit5 compatible alternative for RoundTripAbstractTest and RestartableJenkinsRule (#2623)
Co-authored-by: strangelookingnerd <[email protected]>
1 parent 01e7b59 commit 036b5ae

5 files changed

Lines changed: 250 additions & 51 deletions

File tree

integrations/src/test/java/io/jenkins/plugins/casc/JobDslGlobalSecurityConfigurationTest.java

Lines changed: 29 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,52 @@
22

33
import static org.hamcrest.MatcherAssert.assertThat;
44
import static org.hamcrest.Matchers.is;
5-
import static org.junit.Assert.assertNotNull;
5+
import static org.junit.jupiter.api.Assertions.assertNotNull;
66

77
import javaposse.jobdsl.plugin.GlobalJobDslSecurityConfiguration;
88
import jenkins.model.GlobalConfiguration;
9-
import org.junit.Rule;
10-
import org.junit.Test;
11-
import org.junit.runners.model.Statement;
9+
import org.junit.jupiter.api.Test;
1210
import org.jvnet.hudson.test.Issue;
13-
import org.jvnet.hudson.test.RestartableJenkinsRule;
11+
import org.jvnet.hudson.test.JenkinsRule;
12+
import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
1413

1514
/**
1615
* Created by odavid on 23/12/2017.
1716
*/
18-
public class JobDslGlobalSecurityConfigurationTest {
19-
20-
@Rule
21-
public RestartableJenkinsRule j = new RestartableJenkinsRule();
17+
@WithJenkins
18+
class JobDslGlobalSecurityConfigurationTest {
2219

2320
@Test
24-
public void test_global_dsl_security_can_be_applied() {
25-
j.addStep(validateGlobalDSLSecurity);
21+
void test_global_dsl_security_can_be_applied(@SuppressWarnings("unused") JenkinsRule j) {
22+
GlobalJobDslSecurityConfiguration dslSecurity = getGlobalJobDslSecurityConfiguration();
23+
dslSecurity.setUseScriptSecurity(true);
24+
25+
assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(true));
26+
configure();
27+
assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(false));
2628
}
2729

2830
@Test
2931
@Issue("#253")
30-
public void test_global_dsl_security_can_be_reapplied_after_restart() {
31-
j.addStep(validateGlobalDSLSecurity);
32-
j.addStep(validateGlobalDSLSecurityAfterRestart, true);
32+
void test_global_dsl_security_can_be_reapplied_after_restart(JenkinsRule j) throws Throwable {
33+
GlobalJobDslSecurityConfiguration dslSecurity = getGlobalJobDslSecurityConfiguration();
34+
dslSecurity.setUseScriptSecurity(true);
35+
36+
assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(true));
37+
configure();
38+
assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(false));
39+
40+
j.restart();
41+
42+
dslSecurity = getGlobalJobDslSecurityConfiguration();
43+
44+
assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(false));
45+
configure();
46+
assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(false));
3347
}
3448

3549
private GlobalJobDslSecurityConfiguration getGlobalJobDslSecurityConfiguration() {
36-
final GlobalJobDslSecurityConfiguration dslSecurity =
50+
GlobalJobDslSecurityConfiguration dslSecurity =
3751
GlobalConfiguration.all().get(GlobalJobDslSecurityConfiguration.class);
3852
assertNotNull(dslSecurity);
3953
return dslSecurity;
@@ -45,34 +59,4 @@ private void configure() throws ConfiguratorException {
4559
.getResource("JobDslGlobalSecurityConfigurationTest.yml")
4660
.toExternalForm());
4761
}
48-
49-
private Statement validateGlobalDSLSecurity = new Statement() {
50-
51-
@Override
52-
public void evaluate() throws Throwable {
53-
final GlobalJobDslSecurityConfiguration dslSecurity = getGlobalJobDslSecurityConfiguration();
54-
55-
dslSecurity.setUseScriptSecurity(true);
56-
assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(true));
57-
58-
configure();
59-
60-
assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(false));
61-
}
62-
};
63-
64-
private Statement validateGlobalDSLSecurityAfterRestart = new Statement() {
65-
66-
@Override
67-
public void evaluate() throws Throwable {
68-
final GlobalJobDslSecurityConfiguration dslSecurity = getGlobalJobDslSecurityConfiguration();
69-
70-
// step 1 configuration still applies
71-
assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(false));
72-
73-
configure();
74-
75-
assertThat("ScriptSecurity", dslSecurity.isUseScriptSecurity(), is(false));
76-
}
77-
};
7862
}

integrations/src/test/java/io/jenkins/plugins/casc/RoundTripMailerTest.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
package io.jenkins.plugins.casc;
22

3-
import static org.junit.Assert.assertEquals;
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
44

55
import hudson.tasks.Mailer;
6-
import io.jenkins.plugins.casc.misc.RoundTripAbstractTest;
6+
import io.jenkins.plugins.casc.misc.junit.jupiter.AbstractRoundTripTest;
77
import jenkins.model.Jenkins;
8-
import org.jvnet.hudson.test.RestartableJenkinsRule;
8+
import org.jvnet.hudson.test.JenkinsRule;
9+
import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
910

10-
public class RoundTripMailerTest extends RoundTripAbstractTest {
11+
@WithJenkins
12+
class RoundTripMailerTest extends AbstractRoundTripTest {
1113
@Override
12-
protected void assertConfiguredAsExpected(RestartableJenkinsRule j, String configContent) {
14+
protected void assertConfiguredAsExpected(JenkinsRule j, String configContent) {
1315
final Jenkins jenkins = Jenkins.get();
1416
final Mailer.DescriptorImpl descriptor = (Mailer.DescriptorImpl) jenkins.getDescriptor(Mailer.class);
1517
assertEquals("4441", descriptor.getSmtpPort());

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<parent>
55
<groupId>org.jenkins-ci.plugins</groupId>
66
<artifactId>plugin</artifactId>
7-
<version>5.5</version>
7+
<version>5.6</version>
88
<relativePath />
99
</parent>
1010

test-harness/src/main/java/io/jenkins/plugins/casc/misc/RoundTripAbstractTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@
4444
* text configured.
4545
*
4646
* @since 1.20
47+
* @deprecated Consider migrating to JUnit5 and use {@link io.jenkins.plugins.casc.misc.junit.jupiter.AbstractRoundTripTest} instead.
4748
*/
49+
@Deprecated
4850
public abstract class RoundTripAbstractTest {
4951
@Rule
5052
public RestartableJenkinsRule r = new RestartableJenkinsRule();
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
package io.jenkins.plugins.casc.misc.junit.jupiter;
2+
3+
import static org.hamcrest.MatcherAssert.assertThat;
4+
import static org.hamcrest.Matchers.containsString;
5+
import static org.htmlunit.HttpMethod.POST;
6+
import static org.junit.jupiter.api.Assertions.assertEquals;
7+
import static org.junit.jupiter.api.Assertions.assertNotNull;
8+
import static org.junit.jupiter.api.Assertions.assertTrue;
9+
10+
import io.jenkins.plugins.casc.ConfigurationAsCode;
11+
import io.jenkins.plugins.casc.ConfiguratorException;
12+
import java.io.File;
13+
import java.io.FileNotFoundException;
14+
import java.io.IOException;
15+
import java.io.PrintWriter;
16+
import java.nio.charset.StandardCharsets;
17+
import java.nio.file.Path;
18+
import java.util.Collections;
19+
import java.util.logging.Level;
20+
import org.apache.commons.io.IOUtils;
21+
import org.htmlunit.WebRequest;
22+
import org.htmlunit.WebResponse;
23+
import org.htmlunit.util.NameValuePair;
24+
import org.junit.jupiter.api.Test;
25+
import org.junit.jupiter.api.io.TempDir;
26+
import org.jvnet.hudson.test.JenkinsRule;
27+
import org.jvnet.hudson.test.LogRecorder;
28+
import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
29+
30+
/**
31+
* Base test to check a complete test of each plugin configuration.
32+
* <p>
33+
* What it does:
34+
* <ol>
35+
* <li>Configure the instance with the {@link #configResource()} implemented.</li>
36+
* <li>Check it was configured correctly.</li>
37+
* <li>Check the configuration is valid via Web UI.</li>
38+
* <li>Apply the configuration via Web UI.</li>
39+
* <li>Write the configuration to $JENKINS_HOME/jenkins.yaml.</li>
40+
* <li>Restart Jenkins.</li>
41+
* <li>Check the {@link #stringInLogExpected()} is set during the restart.</li>
42+
* <li>Check it is still configured correctly after the restart</li>
43+
* </ol>
44+
* <p>
45+
* All the plugin author needs to do is override the methods providing:
46+
* <ol>
47+
* <li>The resource with the yaml configuration of the plugin in case they use their own name for the file.</li>
48+
* <li>A way to validate the configuration is established.</li>
49+
* <li>A string that should be present in the logs (casc logger) that guarantees the config is loaded. Usually a weird text configured.</li>
50+
* </ol>
51+
* <p>
52+
* This is the JUnit5 equivalent of {@link io.jenkins.plugins.casc.misc.RoundTripAbstractTest}
53+
*
54+
* @see io.jenkins.plugins.casc.misc.RoundTripAbstractTest
55+
*/
56+
@WithJenkins
57+
public abstract class AbstractRoundTripTest {
58+
59+
@TempDir
60+
public Path tempFolder;
61+
62+
/**
63+
* A method to assert if the configuration was correctly loaded. The Jenkins rule and the
64+
* content of the config supposedly loaded are passed.
65+
*
66+
* @param j a JenkinsRule instance.
67+
* @param configContent expected configuration.
68+
*/
69+
protected abstract void assertConfiguredAsExpected(JenkinsRule j, String configContent);
70+
71+
/**
72+
* Return the resource path (yaml file) to be loaded. i.e: If the resource is in the same
73+
* package of the implementor class, then: my-config.yaml
74+
*
75+
* @return the resource name and path.
76+
*/
77+
protected String configResource() {
78+
return "configuration-as-code.yaml";
79+
}
80+
81+
/**
82+
* Return the string that should be in the logs of the JCasC logger to verify it's configured
83+
* after a restart. This string should be unique to avoid interpreting that it was configured
84+
* successfully, but it wasn't.
85+
*
86+
* @return the unique string to be in the logs to certify the configuration was done
87+
* successfully.
88+
*/
89+
protected abstract String stringInLogExpected();
90+
91+
/**
92+
* 1. Configure the instance with the {@link #configResource()} implemented. 2. Check it was
93+
* configured correctly. 3. Check the configuration is valid via Web UI. 4. Apply the
94+
* configuration via Web UI. 5. Write the configuration to $JENKINS_HOME/jenkins.yaml. 6.
95+
* Restart Jenkins. 7. Check the {@link #stringInLogExpected()} is set during the restart.
96+
*
97+
* @throws IOException If an exception is thrown managing resources or files.
98+
*/
99+
@Test
100+
public void roundTripTest(JenkinsRule r) throws Throwable {
101+
String resourcePath = configResource();
102+
String resourceContent = getResourceContent(resourcePath);
103+
104+
assertNotNull(resourcePath);
105+
assertNotNull(resourceContent);
106+
107+
// Configure and validate
108+
configureWithResource(resourcePath);
109+
assertConfiguredAsExpected(r, resourceContent);
110+
111+
// Check config is valid via Web UI
112+
String jenkinsConf = getResourceContent(resourcePath);
113+
assertConfigViaWebUI(r, jenkinsConf);
114+
115+
// Apply configuration via Web UI
116+
applyConfigViaWebUI(r, jenkinsConf);
117+
assertConfiguredAsExpected(r, resourceContent);
118+
119+
// Configure Jenkins default JCasC file with the config file. It's already established, we check if applied
120+
// looking at the logs.
121+
putConfigInHome(r, jenkinsConf);
122+
123+
// Start recording the logs just before restarting, to avoid capture the previous startup. We're look there
124+
// if the "magic token" is there
125+
try (LogRecorder recorder =
126+
new LogRecorder().record("io.jenkins.plugins.casc", Level.FINER).capture(2048)) {
127+
// Restart the testing instance
128+
r.restart();
129+
130+
// Verify the log shows it's configured
131+
assertLogAsExpected(recorder, stringInLogExpected());
132+
}
133+
134+
// Verify the configuration set at home/jenkins.yaml is loaded
135+
assertConfiguredAsExpected(r, resourceContent);
136+
}
137+
138+
private void configureWithResource(String config) throws ConfiguratorException {
139+
ConfigurationAsCode.get().configure(this.getClass().getResource(config).toExternalForm());
140+
}
141+
142+
private String getResourceContent(String resourcePath) throws IOException {
143+
return IOUtils.toString(getClass().getResourceAsStream(resourcePath), StandardCharsets.UTF_8);
144+
}
145+
146+
private void writeToFile(String text, String path) throws FileNotFoundException {
147+
File file = new File(path);
148+
try (PrintWriter out = new PrintWriter(file)) {
149+
out.print(text);
150+
}
151+
}
152+
153+
private void putConfigInHome(JenkinsRule r, String config) throws Exception {
154+
File configFile = new File(r.getWebAppRoot(), ConfigurationAsCode.DEFAULT_JENKINS_YAML_PATH);
155+
156+
writeToFile(config, configFile.getAbsolutePath());
157+
assertTrue(configFile.exists(), ConfigurationAsCode.DEFAULT_JENKINS_YAML_PATH + " should be created");
158+
}
159+
160+
private void assertConfigViaWebUI(JenkinsRule r, String jenkinsConfig) throws Exception {
161+
// The UI requires the path to the config file
162+
File f = File.createTempFile("junit", null, tempFolder.toFile());
163+
writeToFile(jenkinsConfig, f.getAbsolutePath());
164+
165+
// Call the check url
166+
JenkinsRule.WebClient client = r.createWebClient();
167+
WebRequest request = new WebRequest(client.createCrumbedUrl("configuration-as-code/checkNewSource"), POST);
168+
NameValuePair param = new NameValuePair("newSource", f.toURI().toURL().toExternalForm());
169+
request.setRequestParameters(Collections.singletonList(param));
170+
WebResponse response = client.loadWebResponse(request);
171+
assertEquals(
172+
200,
173+
response.getStatusCode(),
174+
"Failed to POST to " + request.getUrl().toString());
175+
String res = response.getContentAsString();
176+
assertThat(res, containsString("The configuration can be applied"));
177+
}
178+
179+
private void applyConfigViaWebUI(JenkinsRule r, String jenkinsConfig) throws Exception {
180+
// The UI requires the path to the config file
181+
File f = File.createTempFile("junit", null, tempFolder.toFile());
182+
writeToFile(jenkinsConfig, f.getAbsolutePath());
183+
184+
// Call the replace url
185+
JenkinsRule.WebClient client = r.createWebClient();
186+
WebRequest request = new WebRequest(client.createCrumbedUrl("configuration-as-code/replace"), POST);
187+
NameValuePair param = new NameValuePair("_.newSource", f.toURI().toURL().toExternalForm());
188+
request.setRequestParameters(Collections.singletonList(param));
189+
request.setRequestParameters(Collections.singletonList(param));
190+
WebResponse response = client.loadWebResponse(request);
191+
assertEquals(
192+
200,
193+
response.getStatusCode(),
194+
"Failed to POST to " + request.getUrl().toString());
195+
String res = response.getContentAsString();
196+
/* The result page has:
197+
Configuration loaded from :
198+
<ul>
199+
<li>path</li>
200+
</ul>
201+
path is the file used to store the configuration.
202+
*/
203+
assertThat(res, containsString(f.toURI().toURL().toExternalForm()));
204+
}
205+
206+
private void assertLogAsExpected(LogRecorder recorder, String uniqueText) {
207+
assertTrue(
208+
recorder.getMessages().stream().anyMatch(m -> m.contains(uniqueText)),
209+
"The log should have '" + uniqueText + "'");
210+
}
211+
}

0 commit comments

Comments
 (0)