View Javadoc
1   /*
2    * The MIT License (MIT)
3    *
4    * Copyright (c) 2013-2026 The Coveralls Maven Plugin Project Contributors:
5    *     https://github.com/hazendaz/coveralls-maven-plugin/graphs/contributors
6    *
7    * Permission is hereby granted, free of charge, to any person obtaining a copy
8    * of this software and associated documentation files (the "Software"), to deal
9    * in the Software without restriction, including without limitation the rights
10   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11   * copies of the Software, and to permit persons to whom the Software is
12   * furnished to do so, subject to the following conditions:
13   *
14   * The above copyright notice and this permission notice shall be included in
15   * all copies or substantial portions of the Software.
16   *
17   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23   * THE SOFTWARE.
24   */
25  package org.eluder.coveralls.maven.plugin.json;
26  
27  import com.fasterxml.jackson.core.JsonProcessingException;
28  import com.fasterxml.jackson.databind.ObjectMapper;
29  
30  import java.io.File;
31  import java.io.IOException;
32  import java.nio.file.Path;
33  import java.time.Instant;
34  import java.time.ZoneId;
35  import java.time.format.DateTimeFormatter;
36  import java.util.Arrays;
37  import java.util.Collection;
38  import java.util.HashMap;
39  import java.util.List;
40  import java.util.Map;
41  import java.util.Properties;
42  
43  import org.eluder.coveralls.maven.plugin.ProcessingException;
44  import org.eluder.coveralls.maven.plugin.domain.Git;
45  import org.eluder.coveralls.maven.plugin.domain.Job;
46  import org.eluder.coveralls.maven.plugin.domain.Source;
47  import org.eluder.coveralls.maven.plugin.util.TestIoUtil;
48  import org.junit.jupiter.api.Assertions;
49  import org.junit.jupiter.api.BeforeEach;
50  import org.junit.jupiter.api.Test;
51  import org.junit.jupiter.api.io.CleanupMode;
52  import org.junit.jupiter.api.io.TempDir;
53  
54  /**
55   * The Class JsonWriterTest.
56   */
57  class JsonWriterTest {
58  
59      /** The Constant TEST_TIME. */
60      static final long TEST_TIME = 1357009200000L;
61  
62      /** The folder. */
63      @TempDir(cleanup = CleanupMode.NEVER)
64      Path folder;
65  
66      /** The file. */
67      File file;
68  
69      /**
70       * Inits the Json Writer.
71       */
72      @BeforeEach
73      void init() {
74          this.file = this.folder.resolve("file").toFile();
75      }
76  
77      /**
78       * Sub directory creation.
79       *
80       * @throws IOException
81       *             Signals that an I/O exception has occurred.
82       */
83      @Test
84      void subDirectoryCreation() throws IOException {
85          final var f = this.folder.resolve("sub1").resolve("sub2");
86          final var job = this.job();
87          try (var writer = new JsonWriter(job, f.toFile())) {
88              Assertions.assertTrue(writer.getCoverallsFile().getParentFile().isDirectory());
89          }
90      }
91  
92      /**
93       * Test get job.
94       *
95       * @throws IOException
96       *             Signals that an I/O exception has occurred.
97       */
98      @Test
99      void jobGet() throws IOException {
100         final var job = this.job();
101         try (var writer = new JsonWriter(job, this.file)) {
102             Assertions.assertSame(job, writer.getJob());
103         }
104     }
105 
106     /**
107      * Test get coveralls file.
108      *
109      * @throws IOException
110      *             Signals that an I/O exception has occurred.
111      */
112     @Test
113     void coverallsFile() throws IOException {
114         final var job = this.job();
115         try (var writer = new JsonWriter(job, this.file)) {
116             Assertions.assertSame(this.file, writer.getCoverallsFile());
117         }
118     }
119 
120     /**
121      * Write start and end.
122      *
123      * @throws IOException
124      *             Signals that an I/O exception has occurred.
125      * @throws ProcessingException
126      *             the processing exception
127      */
128     @SuppressWarnings("rawtypes")
129     @Test
130     void writeStartAndEnd() throws IOException, ProcessingException {
131         try (var writer = new JsonWriter(this.job(), this.file)) {
132             writer.onBegin();
133             writer.onComplete();
134         }
135         final var content = TestIoUtil.readFileContent(this.file);
136         final var jsonMap = this.stringToJsonMap(content);
137         Assertions.assertEquals("service", jsonMap.get("service_name"));
138         Assertions.assertEquals("job123", jsonMap.get("service_job_id"));
139         Assertions.assertEquals("build5", jsonMap.get("service_number"));
140         Assertions.assertEquals("https://ci.com/build5", jsonMap.get("service_build_url"));
141         Assertions.assertEquals("foobar", ((Map) jsonMap.get("environment")).get("custom_property"));
142         Assertions.assertEquals("master", jsonMap.get("service_branch"));
143         Assertions.assertEquals("pull10", jsonMap.get("service_pull_request"));
144 
145         final var formatter = DateTimeFormatter.ofPattern(JsonWriter.TIMESTAMP_FORMAT).withZone(ZoneId.systemDefault());
146         final var expectedRunAt = formatter.format(Instant.ofEpochMilli(JsonWriterTest.TEST_TIME));
147         Assertions.assertEquals(expectedRunAt, jsonMap.get("run_at"));
148 
149         Assertions.assertEquals("af456fge34acd", ((Map) jsonMap.get("git")).get("branch"));
150         Assertions.assertEquals("aefg837fge", ((Map) ((Map) jsonMap.get("git")).get("head")).get("id"));
151         Assertions.assertEquals(0, ((Collection<?>) jsonMap.get("source_files")).size());
152     }
153 
154     /**
155      * Test on source.
156      *
157      * @throws IOException
158      *             Signals that an I/O exception has occurred.
159      * @throws ProcessingException
160      *             the processing exception
161      */
162     @Test
163     void onSource() throws IOException, ProcessingException {
164         try (var writer = new JsonWriter(this.job(), this.file)) {
165             writer.onBegin();
166             writer.onSource(this.source());
167             writer.onComplete();
168         }
169         final var content = TestIoUtil.readFileContent(this.file);
170         var jsonMap = this.stringToJsonMap(content);
171         if (jsonMap.get("source_files") instanceof List) {
172             jsonMap = ((List<Map<String, Object>>) jsonMap.get("source_files")).get(0);
173         }
174         Assertions.assertEquals("Foo.java", jsonMap.get("name"));
175         Assertions.assertEquals("6E0F89B516198DC6AB743EA5FBFB3108", jsonMap.get("source_digest"));
176         Assertions.assertEquals(1, ((Collection<?>) jsonMap.get("coverage")).size());
177     }
178 
179     /**
180      * Write start and end with parallel enabled.
181      *
182      * @throws IOException
183      *             Signals that an I/O exception has occurred.
184      * @throws ProcessingException
185      *             the processing exception
186      */
187     @SuppressWarnings("rawtypes")
188     @Test
189     void writeStartAndEndWithParallel() throws IOException, ProcessingException {
190         final var job = new Job().withServiceName("service").withParallel(true);
191         try (var writer = new JsonWriter(job, this.file)) {
192             writer.onBegin();
193             writer.onComplete();
194         }
195         final var content = TestIoUtil.readFileContent(this.file);
196         final var jsonMap = this.stringToJsonMap(content);
197         Assertions.assertEquals(Boolean.TRUE, jsonMap.get("parallel"));
198     }
199 
200     /**
201      * Write start and end with minimal job (no optional fields).
202      *
203      * @throws IOException
204      *             Signals that an I/O exception has occurred.
205      * @throws ProcessingException
206      *             the processing exception
207      */
208     @Test
209     void writeStartAndEndMinimalJob() throws IOException, ProcessingException {
210         final var job = new Job();
211         try (var writer = new JsonWriter(job, this.file)) {
212             writer.onBegin();
213             writer.onComplete();
214         }
215         final var content = TestIoUtil.readFileContent(this.file);
216         final var jsonMap = this.stringToJsonMap(content);
217         Assertions.assertNull(jsonMap.get("service_name"));
218         Assertions.assertNull(jsonMap.get("repo_token"));
219         Assertions.assertNull(jsonMap.get("git"));
220         Assertions.assertNull(jsonMap.get("run_at"));
221         Assertions.assertNull(jsonMap.get("environment"));
222         Assertions.assertNull(jsonMap.get("parallel"));
223     }
224 
225     /**
226      * Job.
227      *
228      * @return the job
229      */
230     Job job() {
231         final var head = new Git.Head("aefg837fge", "john", "john@mail.com", "john", "john@mail.com", "test commit");
232         final var remote = new Git.Remote("origin", "git@git.com:foo.git");
233         final var environment = new Properties();
234         environment.setProperty("custom_property", "foobar");
235         return new Job().withServiceName("service").withServiceJobId("job123").withServiceBuildNumber("build5")
236                 .withServiceBuildUrl("https://ci.com/build5").withServiceEnvironment(environment).withBranch("master")
237                 .withPullRequest("pull10").withTimestamp(JsonWriterTest.TEST_TIME)
238                 .withGit(new Git(null, head, "af456fge34acd", Arrays.asList(remote)));
239     }
240 
241     /**
242      * Source.
243      *
244      * @return the source
245      */
246     Source source() {
247         return new Source("Foo.java", "public class Foo { }", "6E0F89B516198DC6AB743EA5FBFB3108");
248     }
249 
250     /**
251      * String to json map.
252      *
253      * @param content
254      *            the content
255      *
256      * @return the map
257      *
258      * @throws JsonProcessingException
259      *             the json processing exception
260      */
261     Map<String, Object> stringToJsonMap(final String content) throws JsonProcessingException {
262         final var mapper = new ObjectMapper();
263         final var type = mapper.getTypeFactory().constructMapType(HashMap.class, String.class, Object.class);
264         return mapper.readValue(content, type);
265     }
266 
267 }