Skip to content

Commit 911edde

Browse files
authored
Add integration tests (#3)
1 parent 07df998 commit 911edde

File tree

6 files changed

+321
-6
lines changed

6 files changed

+321
-6
lines changed

Jenkinsfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@
22
See the documentation for more options:
33
https://github.com/jenkins-infra/pipeline-library/
44
*/
5-
buildPlugin()
5+
buildPlugin(configurations: [
6+
[ platform: "linux", jdk: "8", jenkins: null ],
7+
[ platform: "linux", jdk: "11", jenkins: null, javaLevel: 8 ]
8+
])

pom.xml

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
<dependency>
2828
<groupId>io.jenkins.tools.bom</groupId>
2929
<artifactId>bom-2.235.x</artifactId>
30-
<version>11</version>
30+
<version>12</version>
3131
<scope>import</scope>
3232
<type>pom</type>
3333
</dependency>
@@ -42,9 +42,14 @@
4242
<version>2.4.0</version>
4343
</dependency>
4444
<dependency>
45-
<groupId>com.fasterxml.jackson.core</groupId>
46-
<artifactId>jackson-databind</artifactId>
47-
<version>2.11.1</version>
45+
<groupId>org.apache.commons</groupId>
46+
<artifactId>commons-lang3</artifactId>
47+
<version>3.10</version>
48+
</dependency>
49+
<dependency>
50+
<groupId>org.jenkins-ci.plugins</groupId>
51+
<artifactId>jackson2-api</artifactId>
52+
<version>2.11.2</version>
4853
</dependency>
4954
</dependencies>
5055
</dependencyManagement>
@@ -64,12 +69,60 @@
6469
<dependency>
6570
<groupId>org.jenkins-ci.plugins</groupId>
6671
<artifactId>junit</artifactId>
67-
<version>1.35-SNAPSHOT</version>
72+
<version>1.35</version>
6873
</dependency>
6974
<dependency>
7075
<groupId>com.github.spotbugs</groupId>
7176
<artifactId>spotbugs-annotations</artifactId>
7277
</dependency>
78+
<dependency>
79+
<groupId>org.jenkins-ci.plugins.workflow</groupId>
80+
<artifactId>workflow-basic-steps</artifactId>
81+
<scope>test</scope>
82+
</dependency>
83+
<dependency>
84+
<groupId>org.jenkins-ci.plugins.workflow</groupId>
85+
<artifactId>workflow-durable-task-step</artifactId>
86+
<scope>test</scope>
87+
</dependency>
88+
<dependency>
89+
<groupId>org.jenkins-ci.plugins.workflow</groupId>
90+
<artifactId>workflow-job</artifactId>
91+
<scope>test</scope>
92+
</dependency>
93+
<dependency>
94+
<groupId>org.jenkins-ci.plugins.workflow</groupId>
95+
<artifactId>workflow-cps</artifactId>
96+
<scope>test</scope>
97+
</dependency>
98+
<dependency>
99+
<groupId>io.jenkins</groupId>
100+
<artifactId>configuration-as-code</artifactId>
101+
<scope>test</scope>
102+
</dependency>
103+
<dependency>
104+
<groupId>io.jenkins.configuration-as-code</groupId>
105+
<artifactId>test-harness</artifactId>
106+
<scope>test</scope>
107+
<exclusions>
108+
<exclusion>
109+
<groupId>com.fasterxml.jackson.core</groupId>
110+
<artifactId>jackson-databind</artifactId>
111+
</exclusion>
112+
</exclusions>
113+
</dependency>
114+
<dependency>
115+
<groupId>org.testcontainers</groupId>
116+
<artifactId>postgresql</artifactId>
117+
<version>1.14.3</version>
118+
<scope>test</scope>
119+
<exclusions>
120+
<exclusion>
121+
<groupId>org.apache.commons</groupId>
122+
<artifactId>commons-compress</artifactId>
123+
</exclusion>
124+
</exclusions>
125+
</dependency>
73126
</dependencies>
74127

75128
<licenses>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package io.jenkins.plugins.junit.storage.database;
2+
3+
import io.jenkins.plugins.casc.ConfigurationContext;
4+
import io.jenkins.plugins.casc.ConfiguratorRegistry;
5+
import io.jenkins.plugins.casc.misc.ConfiguredWithCode;
6+
import io.jenkins.plugins.casc.misc.JenkinsConfiguredWithCodeRule;
7+
import io.jenkins.plugins.casc.model.CNode;
8+
import io.jenkins.plugins.junit.storage.JunitTestResultStorageConfiguration;
9+
import org.junit.ClassRule;
10+
import org.junit.Test;
11+
12+
import static io.jenkins.plugins.casc.misc.Util.getUnclassifiedRoot;
13+
import static io.jenkins.plugins.casc.misc.Util.toStringFromYamlFile;
14+
import static io.jenkins.plugins.casc.misc.Util.toYamlString;
15+
import static org.hamcrest.MatcherAssert.assertThat;
16+
import static org.hamcrest.Matchers.is;
17+
import static org.hamcrest.Matchers.isA;
18+
19+
public class DatabaseJcascTestResultStorageTest {
20+
21+
@ClassRule
22+
@ConfiguredWithCode("configuration-as-code.yml")
23+
public static JenkinsConfiguredWithCodeRule j = new JenkinsConfiguredWithCodeRule();
24+
25+
@Test
26+
public void should_support_configuration_as_code() {
27+
assertThat(JunitTestResultStorageConfiguration.get().getStorage(), isA(DatabaseTestResultStorage.class));
28+
}
29+
30+
@Test
31+
public void should_support_configuration_export() throws Exception {
32+
ConfiguratorRegistry registry = ConfiguratorRegistry.get();
33+
ConfigurationContext context = new ConfigurationContext(registry);
34+
CNode yourAttribute = getUnclassifiedRoot(context).get("junitTestResultStorage");
35+
36+
String exported = toYamlString(yourAttribute);
37+
38+
String expected = toStringFromYamlFile(this, "configuration-as-code-expected.yml");
39+
40+
assertThat(exported, is(expected));
41+
}
42+
}
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
package io.jenkins.plugins.junit.storage.database;
2+
3+
import com.google.common.collect.ImmutableSet;
4+
import hudson.model.Label;
5+
import hudson.model.Result;
6+
import hudson.tasks.junit.CaseResult;
7+
import hudson.tasks.junit.PackageResult;
8+
import hudson.tasks.junit.TestDurationResultSummary;
9+
import hudson.tasks.junit.TestResultAction;
10+
import hudson.tasks.junit.TestResultSummary;
11+
import hudson.tasks.junit.TrendTestResultSummary;
12+
import hudson.util.Secret;
13+
import io.jenkins.plugins.junit.storage.JunitTestResultStorageConfiguration;
14+
import io.jenkins.plugins.junit.storage.TestResultImpl;
15+
import java.io.ByteArrayInputStream;
16+
import java.io.File;
17+
import java.nio.charset.StandardCharsets;
18+
import java.sql.Connection;
19+
import java.sql.PreparedStatement;
20+
import java.sql.ResultSet;
21+
import java.sql.ResultSetMetaData;
22+
import java.sql.SQLException;
23+
import java.util.List;
24+
import java.util.Set;
25+
import java.util.TreeSet;
26+
import javax.xml.parsers.DocumentBuilderFactory;
27+
import org.apache.commons.io.FileUtils;
28+
import org.jenkinsci.plugins.database.GlobalDatabaseConfiguration;
29+
import org.jenkinsci.plugins.database.postgresql.PostgreSQLDatabase;
30+
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
31+
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
32+
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
33+
import org.junit.ClassRule;
34+
import org.junit.Rule;
35+
import org.junit.Test;
36+
import org.jvnet.hudson.test.BuildWatcher;
37+
import org.jvnet.hudson.test.JenkinsRule;
38+
import org.testcontainers.containers.PostgreSQLContainer;
39+
import org.w3c.dom.Document;
40+
import org.w3c.dom.Element;
41+
import org.w3c.dom.Node;
42+
import org.w3c.dom.NodeList;
43+
44+
import static io.jenkins.plugins.junit.storage.database.DatabaseTestResultStorage.CASE_RESULTS_TABLE;
45+
import static java.util.Objects.requireNonNull;
46+
import static org.hamcrest.MatcherAssert.assertThat;
47+
import static org.hamcrest.Matchers.equalTo;
48+
import static org.hamcrest.Matchers.hasSize;
49+
import static org.hamcrest.core.Is.is;
50+
import static org.junit.Assert.assertEquals;
51+
import static org.junit.Assert.assertFalse;
52+
import static org.junit.Assert.assertNotNull;
53+
54+
public class DatabaseTestResultStorageTest {
55+
56+
@ClassRule
57+
public static BuildWatcher buildWatcher = new BuildWatcher();
58+
59+
@Rule
60+
public JenkinsRule r = new JenkinsRule();
61+
62+
@Test
63+
public void smokes() throws Exception {
64+
try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:12-alpine")) {
65+
setupPlugin(postgres);
66+
67+
r.createOnlineSlave(Label.get("remote"));
68+
WorkflowJob p = r.createProject(WorkflowJob.class, "p");
69+
p.setDefinition(new CpsFlowDefinition(
70+
"node('remote') {\n" +
71+
" writeFile file: 'x.xml', text: '''<testsuite name='sweet' time='200.0'>" +
72+
"<testcase classname='Klazz' name='test1' time='198.0'><error message='failure'/></testcase>" +
73+
"<testcase classname='Klazz' name='test2' time='2.0'/>" +
74+
"<testcase classname='other.Klazz' name='test3'><skipped message='Not actually run.'/></testcase>" +
75+
"</testsuite>'''\n" +
76+
" def s = junit 'x.xml'\n" +
77+
" echo(/summary: fail=$s.failCount skip=$s.skipCount pass=$s.passCount total=$s.totalCount/)\n" +
78+
" writeFile file: 'x.xml', text: '''<testsuite name='supersweet'>" +
79+
"<testcase classname='another.Klazz' name='test1'><error message='another failure'/></testcase>" +
80+
"</testsuite>'''\n" +
81+
" s = junit 'x.xml'\n" +
82+
" echo(/next summary: fail=$s.failCount skip=$s.skipCount pass=$s.passCount total=$s.totalCount/)\n" +
83+
"}", true));
84+
WorkflowRun b = p.scheduleBuild2(0).get();
85+
try (Connection connection = requireNonNull(GlobalDatabaseConfiguration.get().getDatabase()).getDataSource().getConnection();
86+
PreparedStatement statement = connection.prepareStatement("SELECT * FROM " + CASE_RESULTS_TABLE);
87+
ResultSet result = statement.executeQuery()) {
88+
printResultSet(result);
89+
}
90+
// TODO verify table structure
91+
r.assertBuildStatus(Result.UNSTABLE, b);
92+
r.assertLogContains("summary: fail=1 skip=1 pass=1 total=3", b);
93+
r.assertLogContains("next summary: fail=1 skip=0 pass=0 total=1", b);
94+
assertFalse(new File(b.getRootDir(), "junitResult.xml").isFile());
95+
{
96+
String buildXml = FileUtils.readFileToString(new File(b.getRootDir(), "build.xml"));
97+
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(buildXml.getBytes(StandardCharsets.UTF_8)));
98+
NodeList testResultActionList = doc.getElementsByTagName("hudson.tasks.junit.TestResultAction");
99+
assertEquals(buildXml, 1, testResultActionList.getLength());
100+
Element testResultAction = (Element) testResultActionList.item(0);
101+
NodeList childNodes = testResultAction.getChildNodes();
102+
Set<String> childNames = new TreeSet<>();
103+
for (int i = 0; i < childNodes.getLength(); i++) {
104+
Node item = childNodes.item(i);
105+
if (item instanceof Element) {
106+
childNames.add(((Element) item).getTagName());
107+
}
108+
}
109+
assertEquals(buildXml, ImmutableSet.of("healthScaleFactor", "testData", "descriptions"), childNames);
110+
}
111+
{
112+
TestResultAction a = b.getAction(TestResultAction.class);
113+
assertNotNull(a);
114+
assertEquals(2, a.getFailCount());
115+
assertEquals(1, a.getSkipCount());
116+
assertEquals(4, a.getTotalCount());
117+
assertEquals(2, a.getResult().getFailCount());
118+
assertEquals(1, a.getResult().getSkipCount());
119+
assertEquals(4, a.getResult().getTotalCount());
120+
assertEquals(1, a.getResult().getPassCount());
121+
List<CaseResult> failedTests = a.getFailedTests();
122+
assertEquals(2, failedTests.size());
123+
final CaseResult klazzTest1 = failedTests.get(0);
124+
assertEquals("Klazz", klazzTest1.getClassName());
125+
assertEquals("test1", klazzTest1.getName());
126+
assertEquals("failure", klazzTest1.getErrorDetails());
127+
assertThat(klazzTest1.getDuration(), is(198.0f));
128+
assertEquals("another.Klazz", failedTests.get(1).getClassName());
129+
assertEquals("test1", failedTests.get(1).getName());
130+
assertEquals("another failure", failedTests.get(1).getErrorDetails());
131+
132+
List<CaseResult> skippedTests = a.getSkippedTests();
133+
assertEquals(1, skippedTests.size());
134+
assertEquals("other.Klazz", skippedTests.get(0).getClassName());
135+
assertEquals("test3", skippedTests.get(0).getName());
136+
assertEquals("Not actually run.", skippedTests.get(0).getSkippedMessage());
137+
138+
List<CaseResult> passedTests = a.getPassedTests();
139+
assertEquals(1, passedTests.size());
140+
assertEquals("Klazz", passedTests.get(0).getClassName());
141+
assertEquals("test2", passedTests.get(0).getName());
142+
143+
PackageResult another = a.getResult().byPackage("another");
144+
List<CaseResult> packageFailedTests = another.getFailedTests();
145+
assertEquals(1, packageFailedTests.size());
146+
assertEquals("another.Klazz", packageFailedTests.get(0).getClassName());
147+
148+
PackageResult other = a.getResult().byPackage("other");
149+
List<CaseResult> packageSkippedTests = other.getSkippedTests();
150+
assertEquals(1, packageSkippedTests.size());
151+
assertEquals("other.Klazz", packageSkippedTests.get(0).getClassName());
152+
assertEquals("Not actually run.", packageSkippedTests.get(0).getSkippedMessage());
153+
154+
PackageResult root = a.getResult().byPackage("(root)");
155+
List<CaseResult> rootPassedTests = root.getPassedTests();
156+
assertEquals(1, rootPassedTests.size());
157+
assertEquals("Klazz", rootPassedTests.get(0).getClassName());
158+
159+
TestResultImpl pluggableStorage = requireNonNull(a.getResult().getPluggableStorage());
160+
List<TrendTestResultSummary> trendTestResultSummary = pluggableStorage.getTrendTestResultSummary();
161+
assertThat(trendTestResultSummary, hasSize(1));
162+
TestResultSummary testResultSummary = trendTestResultSummary.get(0).getTestResultSummary();
163+
assertThat(testResultSummary.getFailCount(), equalTo(2));
164+
assertThat(testResultSummary.getPassCount(), equalTo(1));
165+
assertThat(testResultSummary.getSkipCount(), equalTo(1));
166+
assertThat(testResultSummary.getTotalCount(), equalTo(4));
167+
168+
int countOfBuildsWithTestResults = pluggableStorage.getCountOfBuildsWithTestResults();
169+
assertThat(countOfBuildsWithTestResults, is(1));
170+
171+
final List<TestDurationResultSummary> testDurationResultSummary = pluggableStorage.getTestDurationResultSummary();
172+
assertThat(testDurationResultSummary.get(0).getDuration(), is(200));
173+
}
174+
}
175+
}
176+
177+
private void setupPlugin(PostgreSQLContainer<?> postgres) {
178+
// comment this out if you hit the below test containers issue
179+
postgres.start();
180+
181+
PostgreSQLDatabase database = new PostgreSQLDatabase(postgres.getHost() + ":" + postgres.getMappedPort(5432), postgres.getDatabaseName(), postgres.getUsername(), Secret.fromString(postgres.getPassword()), null);
182+
// Use the below if test containers doesn't work for you, i.e. MacOS edge release of docker broken Sep 2020
183+
// https://github.com/testcontainers/testcontainers-java/issues/3166
184+
// PostgreSQLDatabase database = new PostgreSQLDatabase("localhost", "postgres", "postgres", Secret.fromString("postgres"), null);
185+
database.setValidationQuery("SELECT 1");
186+
GlobalDatabaseConfiguration.get().setDatabase(database);
187+
JunitTestResultStorageConfiguration.get().setStorage(new DatabaseTestResultStorage());
188+
}
189+
190+
// https://gist.github.com/mikbuch/299568988fa7997cb28c7c84309232b1
191+
private static void printResultSet(ResultSet rs) throws SQLException {
192+
ResultSetMetaData rsmd = rs.getMetaData();
193+
int columnsNumber = rsmd.getColumnCount();
194+
for (int i = 1; i <= columnsNumber; i++) {
195+
if (i > 1) {
196+
System.out.print("\t|\t");
197+
}
198+
System.out.print(rsmd.getColumnName(i));
199+
}
200+
System.out.println();
201+
while (rs.next()) {
202+
for (int i = 1; i <= columnsNumber; i++) {
203+
if (i > 1) {
204+
System.out.print("\t|\t");
205+
}
206+
System.out.print(rs.getString(i));
207+
}
208+
System.out.println();
209+
}
210+
}
211+
212+
213+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
storage: "database"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
unclassified:
2+
junitTestResultStorage:
3+
storage: "database"

0 commit comments

Comments
 (0)