]> source.dussan.org Git - sonarqube.git/blob
56604cb89379317911e954e1be1c285b0cd4a6ae
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.server.telemetry;
21
22 import com.tngtech.java.junit.dataprovider.DataProvider;
23 import com.tngtech.java.junit.dataprovider.DataProviderRunner;
24 import com.tngtech.java.junit.dataprovider.UseDataProvider;
25 import java.io.StringWriter;
26 import java.util.Arrays;
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.Map;
31 import java.util.Random;
32 import java.util.Set;
33 import java.util.stream.Collectors;
34 import java.util.stream.IntStream;
35 import org.apache.commons.codec.digest.DigestUtils;
36 import org.jetbrains.annotations.NotNull;
37 import org.junit.Test;
38 import org.junit.runner.RunWith;
39 import org.sonar.api.utils.System2;
40 import org.sonar.api.utils.text.JsonWriter;
41 import org.sonar.core.platform.EditionProvider;
42 import org.sonar.core.telemetry.TelemetryExtension;
43 import org.sonar.core.util.stream.MoreCollectors;
44 import org.sonar.db.user.UserTelemetryDto;
45
46 import static java.lang.String.format;
47 import static java.util.stream.Collectors.joining;
48 import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
49 import static org.assertj.core.api.Assertions.assertThat;
50 import static org.mockito.Mockito.mock;
51 import static org.mockito.Mockito.when;
52 import static org.sonar.server.telemetry.TelemetryDataJsonWriter.SCIM_PROPERTY;
53 import static org.sonar.test.JsonAssert.assertJson;
54
55 @RunWith(DataProviderRunner.class)
56 public class TelemetryDataJsonWriterTest {
57
58   private final Random random = new Random();
59
60   private final TelemetryExtension extension = mock(TelemetryExtension.class);
61
62   private final System2 system2 = mock(System2.class);
63
64   private final TelemetryDataJsonWriter underTest = new TelemetryDataJsonWriter(List.of(extension), system2);
65
66   @Test
67   public void write_server_id_version_and_sequence() {
68     TelemetryData data = telemetryBuilder().build();
69
70     String json = writeTelemetryData(data);
71
72     assertJson(json).isSimilarTo("{" +
73       "  \"id\": \"" + data.getServerId() + "\"," +
74       "  \"version\": \"" + data.getVersion() + "\"," +
75       "  \"messageSequenceNumber\": " + data.getMessageSequenceNumber() +
76       "}");
77   }
78
79   @Test
80   public void does_not_write_edition_if_null() {
81     TelemetryData data = telemetryBuilder().build();
82
83     String json = writeTelemetryData(data);
84
85     assertThat(json).doesNotContain("edition");
86   }
87
88   @Test
89   @UseDataProvider("allEditions")
90   public void writes_edition_if_non_null(EditionProvider.Edition edition) {
91     TelemetryData data = telemetryBuilder()
92       .setEdition(edition)
93       .build();
94
95     String json = writeTelemetryData(data);
96
97     assertJson(json).isSimilarTo("{" +
98       "  \"edition\": \"" + edition.name().toLowerCase(Locale.ENGLISH) + "\"" +
99       "}");
100   }
101
102   @Test
103   public void writes_database() {
104     String name = randomAlphabetic(12);
105     String version = randomAlphabetic(10);
106     TelemetryData data = telemetryBuilder()
107       .setDatabase(new TelemetryData.Database(name, version))
108       .build();
109
110     String json = writeTelemetryData(data);
111
112     assertJson(json).isSimilarTo("{" +
113       "  \"database\": {" +
114       "    \"name\": \"" + name + "\"," +
115       "    \"version\": \"" + version + "\"" +
116       "  }" +
117       "}");
118   }
119
120   @Test
121   public void writes_no_plugins() {
122     TelemetryData data = telemetryBuilder()
123       .setPlugins(Collections.emptyMap())
124       .build();
125
126     String json = writeTelemetryData(data);
127
128     assertJson(json).isSimilarTo("{" +
129       "  \"plugins\": []" +
130       "}");
131   }
132
133   @Test
134   public void writes_all_plugins() {
135     Map<String, String> plugins = IntStream.range(0, 1 + random.nextInt(10))
136       .boxed()
137       .collect(MoreCollectors.uniqueIndex(i -> "P" + i, i -> "V" + i));
138     TelemetryData data = telemetryBuilder()
139       .setPlugins(plugins)
140       .build();
141
142     String json = writeTelemetryData(data);
143
144     assertJson(json).isSimilarTo("{" +
145       "  \"plugins\": " +
146       "[" +
147       plugins.entrySet().stream().map(e -> "{\"name\":\"" + e.getKey() + "\",\"version\":\"" + e.getValue() + "\"}").collect(joining(",")) +
148       "]" +
149       "}");
150   }
151
152   @Test
153   public void does_not_write_installation_date_if_null() {
154     TelemetryData data = telemetryBuilder()
155       .setInstallationDate(null)
156       .build();
157
158     String json = writeTelemetryData(data);
159
160     assertThat(json).doesNotContain("installationDate");
161   }
162
163   @Test
164   public void write_installation_date_in_utc_format() {
165     TelemetryData data = telemetryBuilder()
166       .setInstallationDate(1_000L)
167       .build();
168
169     String json = writeTelemetryData(data);
170
171     assertJson(json).isSimilarTo("{" +
172       "  \"installationDate\":\"1970-01-01T00:00:01+0000\"," +
173       "}");
174   }
175
176   @Test
177   public void does_not_write_installation_version_if_null() {
178     TelemetryData data = telemetryBuilder()
179       .setInstallationVersion(null)
180       .build();
181
182     String json = writeTelemetryData(data);
183
184     assertThat(json).doesNotContain("installationVersion");
185   }
186
187   @Test
188   public void write_installation_version() {
189     String installationVersion = randomAlphabetic(5);
190     TelemetryData data = telemetryBuilder()
191       .setInstallationVersion(installationVersion)
192       .build();
193
194     String json = writeTelemetryData(data);
195
196     assertJson(json).isSimilarTo("{" +
197       "  \"installationVersion\":\"" + installationVersion + "\"" +
198       "}");
199   }
200
201   @Test
202   @UseDataProvider("getFeatureFlagEnabledStates")
203   public void write_docker_flag(boolean isInDocker) {
204     TelemetryData data = telemetryBuilder()
205       .setInDocker(isInDocker)
206       .build();
207
208     String json = writeTelemetryData(data);
209
210     assertJson(json).isSimilarTo("{" +
211       "  \"docker\":" + isInDocker +
212       "}");
213   }
214
215   @Test
216   @UseDataProvider("getFeatureFlagEnabledStates")
217   public void write_scim_feature_flag(boolean isScimEnabled) {
218     TelemetryData data = telemetryBuilder()
219       .setIsScimEnabled(isScimEnabled)
220       .build();
221
222     String json = writeTelemetryData(data);
223
224     assertJson(json).isSimilarTo("{" + format("  \"%s\":", SCIM_PROPERTY) + isScimEnabled + "}");
225   }
226
227   @Test
228   public void writes_security_custom_config() {
229     TelemetryData data = telemetryBuilder()
230       .setCustomSecurityConfigs(Set.of("php", "java"))
231       .build();
232
233     String json = writeTelemetryData(data);
234
235     assertJson(json).isSimilarTo("{" +
236       "  \"customSecurityConfig\": [\"php\", \"java\"]" +
237       "}");
238   }
239
240   @Test
241   public void writes_local_timestamp() {
242     when(system2.now()).thenReturn(1000L);
243
244     TelemetryData data = telemetryBuilder().build();
245     String json = writeTelemetryData(data);
246
247     assertJson(json).isSimilarTo("{" +
248       "  \"localTimestamp\": \"1970-01-01T00:00:01+0000\"" +
249       "}");
250   }
251
252   @Test
253   public void writes_all_users_with_anonymous_md5_uuids() {
254     TelemetryData data = telemetryBuilder()
255       .setUsers(attachUsers())
256       .build();
257
258     String json = writeTelemetryData(data);
259
260     assertJson(json).isSimilarTo("{" +
261       "  \"users\": [" +
262       "    {" +
263       "      \"userUuid\":\"" + DigestUtils.sha3_224Hex("uuid-0") + "\"," +
264       "      \"lastActivity\":\"1970-01-01T00:00:00+0000\"," +
265       "      \"identityProvider\":\"gitlab\"," +
266       "      \"lastSonarlintActivity\":\"1970-01-01T00:00:00+0000\"," +
267       "      \"status\":\"active\"" +
268       "    }," +
269       "    {" +
270       "      \"userUuid\":\"" + DigestUtils.sha3_224Hex("uuid-1") + "\"," +
271       "      \"lastActivity\":\"1970-01-01T00:00:00+0000\"," +
272       "      \"identityProvider\":\"gitlab\"," +
273       "      \"lastSonarlintActivity\":\"1970-01-01T00:00:00+0000\"," +
274       "      \"status\":\"inactive\"" +
275       "    }," +
276       "    {" +
277       "      \"userUuid\":\"" + DigestUtils.sha3_224Hex("uuid-2") + "\"," +
278       "      \"lastActivity\":\"1970-01-01T00:00:00+0000\"," +
279       "      \"identityProvider\":\"gitlab\"," +
280       "      \"lastSonarlintActivity\":\"1970-01-01T00:00:00+0000\"," +
281       "      \"status\":\"active\"" +
282       "    }" +
283       "  ]" +
284       "}");
285   }
286
287   @Test
288   public void writes_all_projects() {
289     TelemetryData data = telemetryBuilder()
290       .setProjects(attachProjects())
291       .build();
292
293     String json = writeTelemetryData(data);
294
295     assertJson(json).isSimilarTo("{" +
296       "  \"projects\": [" +
297       "    {" +
298       "      \"projectUuid\": \"uuid-0\"," +
299       "      \"lastAnalysis\":\"1970-01-01T00:00:00+0000\"," +
300       "      \"language\": \"lang-0\"," +
301       "      \"loc\": 2" +
302       "    }," +
303       "    {" +
304       "      \"projectUuid\": \"uuid-1\"," +
305       "      \"lastAnalysis\":\"1970-01-01T00:00:00+0000\"," +
306       "      \"language\": \"lang-1\"," +
307       "      \"loc\": 4" +
308       "    }," +
309       "    {" +
310       "      \"projectUuid\": \"uuid-2\"," +
311       "      \"lastAnalysis\":\"1970-01-01T00:00:00+0000\"," +
312       "      \"language\": \"lang-2\"," +
313       "      \"loc\": 6" +
314       "    }" +
315       "  ]" +
316       "}");
317   }
318
319   @Test
320   public void writes_all_projects_stats_with_analyzed_languages() {
321     TelemetryData data = telemetryBuilder()
322       .setProjectStatistics(attachProjectStats(true))
323       .build();
324
325     String json = writeTelemetryData(data);
326
327     assertJson(json).isSimilarTo("{" +
328       "  \"projects-general-stats\": [" +
329       "    {" +
330       "      \"projectUuid\": \"uuid-0\"," +
331       "      \"branchCount\": 2," +
332       "      \"pullRequestCount\": 2," +
333       "      \"scm\": \"scm-0\"," +
334       "      \"ci\": \"ci-0\"," +
335       "      \"devopsPlatform\": \"devops-0\"," +
336       "      \"hasUnanalyzedC\": true," +
337       "      \"hasUnanalyzedCpp\": false" +
338       "    }," +
339       "    {" +
340       "      \"projectUuid\": \"uuid-1\"," +
341       "      \"branchCount\": 4," +
342       "      \"pullRequestCount\": 4," +
343       "      \"scm\": \"scm-1\"," +
344       "      \"ci\": \"ci-1\"," +
345       "      \"devopsPlatform\": \"devops-1\"," +
346       "      \"hasUnanalyzedC\": false," +
347       "      \"hasUnanalyzedCpp\": true" +
348       "    }," +
349       "    {" +
350       "      \"projectUuid\": \"uuid-2\"," +
351       "      \"branchCount\": 6," +
352       "      \"pullRequestCount\": 6," +
353       "      \"scm\": \"scm-2\"," +
354       "      \"ci\": \"ci-2\"," +
355       "      \"devopsPlatform\": \"devops-2\"," +
356       "      \"hasUnanalyzedC\": true," +
357       "      \"hasUnanalyzedCpp\": false" +
358       "    }" +
359       "  ]" +
360       "}");
361   }
362
363   @Test
364   public void writes_all_projects_stats_with_unanalyzed_languages() {
365     TelemetryData data = telemetryBuilder()
366       .setProjectStatistics(attachProjectStats(false))
367       .build();
368
369     String json = writeTelemetryData(data);
370     assertThat(json).doesNotContain("hasUnanalyzedC", "hasUnanalyzedCpp");
371   }
372
373   private static TelemetryData.Builder telemetryBuilder() {
374     return TelemetryData.builder()
375       .setServerId("foo")
376       .setVersion("bar")
377       .setMessageSequenceNumber(1L)
378       .setPlugins(Collections.emptyMap())
379       .setDatabase(new TelemetryData.Database("H2", "11"));
380   }
381
382   @NotNull
383   private static List<UserTelemetryDto> attachUsers() {
384     return IntStream.range(0, 3)
385       .mapToObj(
386         i -> new UserTelemetryDto().setUuid("uuid-" + i).setActive(i % 2 == 0).setLastConnectionDate(1L).setLastSonarlintConnectionDate(2L).setExternalIdentityProvider("gitlab"))
387       .collect(Collectors.toList());
388   }
389
390   private static List<TelemetryData.Project> attachProjects() {
391     return IntStream.range(0, 3).mapToObj(i -> new TelemetryData.Project("uuid-" + i, 1L, "lang-" + i, (i + 1L) * 2L)).collect(Collectors.toList());
392   }
393
394   private List<TelemetryData.ProjectStatistics> attachProjectStats(boolean hasUnanalyzedLanguages) {
395     return IntStream.range(0, 3).mapToObj(i -> new TelemetryData.ProjectStatistics("uuid-" + i, (i + 1L) * 2L, (i + 1L) * 2L, hasUnanalyzedLanguages ? i % 2 == 0 : null, hasUnanalyzedLanguages ? i % 2 != 0 : null, "scm-" + i, "ci-" + i, "devops-" + i))
396       .collect(Collectors.toList());
397   }
398
399   @DataProvider
400   public static Object[][] allEditions() {
401     return Arrays.stream(EditionProvider.Edition.values())
402       .map(t -> new Object[] {t})
403       .toArray(Object[][]::new);
404   }
405
406   private String writeTelemetryData(TelemetryData data) {
407     StringWriter jsonString = new StringWriter();
408     try (JsonWriter json = JsonWriter.of(jsonString)) {
409       underTest.writeTelemetryData(json, data);
410     }
411     return jsonString.toString();
412   }
413
414   @DataProvider
415   public static Set<Boolean> getFeatureFlagEnabledStates() {
416     return Set.of(true, false);
417   }
418 }