3 * Copyright (C) 2009-2022 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 package org.sonar.server.telemetry;
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;
31 import java.util.Random;
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.text.JsonWriter;
40 import org.sonar.core.platform.EditionProvider;
41 import org.sonar.core.util.stream.MoreCollectors;
42 import org.sonar.db.user.UserTelemetryDto;
44 import static java.lang.String.format;
45 import static java.util.stream.Collectors.joining;
46 import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
47 import static org.assertj.core.api.Assertions.assertThat;
48 import static org.sonar.server.telemetry.TelemetryDataJsonWriter.SCIM_PROPERTY;
49 import static org.sonar.test.JsonAssert.assertJson;
51 @RunWith(DataProviderRunner.class)
52 public class TelemetryDataJsonWriterTest {
54 private final Random random = new Random();
56 private final TelemetryDataJsonWriter underTest = new TelemetryDataJsonWriter();
59 public void write_server_id_and_version() {
60 TelemetryData data = telemetryBuilder().build();
62 String json = writeTelemetryData(data);
64 assertJson(json).isSimilarTo("{" +
65 " \"id\": \"" + data.getServerId() + "\"," +
66 " \"version\": \"" + data.getVersion() + "\"" +
71 public void does_not_write_edition_if_null() {
72 TelemetryData data = telemetryBuilder().build();
74 String json = writeTelemetryData(data);
76 assertThat(json).doesNotContain("edition");
80 @UseDataProvider("allEditions")
81 public void writes_edition_if_non_null(EditionProvider.Edition edition) {
82 TelemetryData data = telemetryBuilder()
86 String json = writeTelemetryData(data);
88 assertJson(json).isSimilarTo("{" +
89 " \"edition\": \"" + edition.name().toLowerCase(Locale.ENGLISH) + "\"" +
94 public void does_not_write_license_type_if_null() {
95 TelemetryData data = telemetryBuilder().build();
97 String json = writeTelemetryData(data);
99 assertThat(json).doesNotContain("licenseType");
103 public void writes_licenseType_if_non_null() {
104 String expected = randomAlphabetic(12);
105 TelemetryData data = telemetryBuilder()
106 .setLicenseType(expected)
109 String json = writeTelemetryData(data);
111 assertJson(json).isSimilarTo("{" +
112 " \"licenseType\": \"" + expected + "\"" +
117 public void writes_database() {
118 String name = randomAlphabetic(12);
119 String version = randomAlphabetic(10);
120 TelemetryData data = telemetryBuilder()
121 .setDatabase(new TelemetryData.Database(name, version))
124 String json = writeTelemetryData(data);
126 assertJson(json).isSimilarTo("{" +
128 " \"name\": \"" + name + "\"," +
129 " \"version\": \"" + version + "\"" +
135 public void writes_no_plugins() {
136 TelemetryData data = telemetryBuilder()
137 .setPlugins(Collections.emptyMap())
140 String json = writeTelemetryData(data);
142 assertJson(json).isSimilarTo("{" +
148 public void writes_all_plugins() {
149 Map<String, String> plugins = IntStream.range(0, 1 + random.nextInt(10))
151 .collect(MoreCollectors.uniqueIndex(i -> "P" + i, i -> "V" + i));
152 TelemetryData data = telemetryBuilder()
156 String json = writeTelemetryData(data);
158 assertJson(json).isSimilarTo("{" +
161 plugins.entrySet().stream().map(e -> "{\"name\":\"" + e.getKey() + "\",\"version\":\"" + e.getValue() + "\"}").collect(joining(",")) +
167 public void does_not_write_installation_date_if_null() {
168 TelemetryData data = telemetryBuilder()
169 .setInstallationDate(null)
172 String json = writeTelemetryData(data);
174 assertThat(json).doesNotContain("installationDate");
178 public void write_installation_date_in_utc_format() {
179 TelemetryData data = telemetryBuilder()
180 .setInstallationDate(1_000L)
183 String json = writeTelemetryData(data);
185 assertJson(json).isSimilarTo("{" +
186 " \"installationDate\":\"1970-01-01T00:00:01+0000\"," +
191 public void does_not_write_installation_version_if_null() {
192 TelemetryData data = telemetryBuilder()
193 .setInstallationVersion(null)
196 String json = writeTelemetryData(data);
198 assertThat(json).doesNotContain("installationVersion");
202 public void write_installation_version() {
203 String installationVersion = randomAlphabetic(5);
204 TelemetryData data = telemetryBuilder()
205 .setInstallationVersion(installationVersion)
208 String json = writeTelemetryData(data);
210 assertJson(json).isSimilarTo("{" +
211 " \"installationVersion\":\"" + installationVersion + "\"" +
216 @UseDataProvider("getFeatureFlagEnabledStates")
217 public void write_docker_flag(boolean isInDocker) {
218 TelemetryData data = telemetryBuilder()
219 .setInDocker(isInDocker)
222 String json = writeTelemetryData(data);
224 assertJson(json).isSimilarTo("{" +
225 " \"docker\":" + isInDocker +
230 @UseDataProvider("getFeatureFlagEnabledStates")
231 public void write_scim_feature_flag(boolean isScimEnabled) {
232 TelemetryData data = telemetryBuilder()
233 .setIsScimEnabled(isScimEnabled)
236 String json = writeTelemetryData(data);
238 assertJson(json).isSimilarTo("{" + format(" \"%s\":", SCIM_PROPERTY) + isScimEnabled + "}");
242 public void writes_security_custom_config() {
243 TelemetryData data = telemetryBuilder()
244 .setCustomSecurityConfigs(Set.of("php", "java"))
247 String json = writeTelemetryData(data);
249 assertJson(json).isSimilarTo("{" +
250 " \"customSecurityConfig\": [\"php\", \"java\"]" +
255 public void writes_all_users_with_anonymous_md5_uuids() {
256 TelemetryData data = telemetryBuilder()
257 .setUsers(attachUsers())
260 String json = writeTelemetryData(data);
262 assertJson(json).isSimilarTo("{" +
265 " \"userUuid\":\"" + DigestUtils.sha3_224Hex("uuid-0") + "\"," +
266 " \"lastActivity\":\"1970-01-01T00:00:00+0000\"," +
267 " \"identityProvider\":\"gitlab\"," +
268 " \"lastSonarlintActivity\":\"1970-01-01T00:00:00+0000\"," +
269 " \"status\":\"active\"" +
272 " \"userUuid\":\"" + DigestUtils.sha3_224Hex("uuid-1") + "\"," +
273 " \"lastActivity\":\"1970-01-01T00:00:00+0000\"," +
274 " \"identityProvider\":\"gitlab\"," +
275 " \"lastSonarlintActivity\":\"1970-01-01T00:00:00+0000\"," +
276 " \"status\":\"inactive\"" +
279 " \"userUuid\":\"" + DigestUtils.sha3_224Hex("uuid-2") + "\"," +
280 " \"lastActivity\":\"1970-01-01T00:00:00+0000\"," +
281 " \"identityProvider\":\"gitlab\"," +
282 " \"lastSonarlintActivity\":\"1970-01-01T00:00:00+0000\"," +
283 " \"status\":\"active\"" +
290 public void writes_all_projects() {
291 TelemetryData data = telemetryBuilder()
292 .setProjects(attachProjects())
295 String json = writeTelemetryData(data);
297 assertJson(json).isSimilarTo("{" +
300 " \"projectUuid\": \"uuid-0\"," +
301 " \"lastAnalysis\":\"1970-01-01T00:00:00+0000\"," +
302 " \"language\": \"lang-0\"," +
306 " \"projectUuid\": \"uuid-1\"," +
307 " \"lastAnalysis\":\"1970-01-01T00:00:00+0000\"," +
308 " \"language\": \"lang-1\"," +
312 " \"projectUuid\": \"uuid-2\"," +
313 " \"lastAnalysis\":\"1970-01-01T00:00:00+0000\"," +
314 " \"language\": \"lang-2\"," +
322 public void writes_all_projects_stats_with_analyzed_languages() {
323 TelemetryData data = telemetryBuilder()
324 .setProjectStatistics(attachProjectStats(true))
327 String json = writeTelemetryData(data);
329 assertJson(json).isSimilarTo("{" +
330 " \"projects-general-stats\": [" +
332 " \"projectUuid\": \"uuid-0\"," +
333 " \"branchCount\": 2," +
334 " \"pullRequestCount\": 2," +
335 " \"scm\": \"scm-0\"," +
336 " \"ci\": \"ci-0\"," +
337 " \"devopsPlatform\": \"devops-0\"," +
338 " \"hasUnanalyzedC\": true," +
339 " \"hasUnanalyzedCpp\": false" +
342 " \"projectUuid\": \"uuid-1\"," +
343 " \"branchCount\": 4," +
344 " \"pullRequestCount\": 4," +
345 " \"scm\": \"scm-1\"," +
346 " \"ci\": \"ci-1\"," +
347 " \"devopsPlatform\": \"devops-1\"," +
348 " \"hasUnanalyzedC\": false," +
349 " \"hasUnanalyzedCpp\": true" +
352 " \"projectUuid\": \"uuid-2\"," +
353 " \"branchCount\": 6," +
354 " \"pullRequestCount\": 6," +
355 " \"scm\": \"scm-2\"," +
356 " \"ci\": \"ci-2\"," +
357 " \"devopsPlatform\": \"devops-2\"," +
358 " \"hasUnanalyzedC\": true," +
359 " \"hasUnanalyzedCpp\": false" +
366 public void writes_all_projects_stats_with_unanalyzed_languages() {
367 TelemetryData data = telemetryBuilder()
368 .setProjectStatistics(attachProjectStats(false))
371 String json = writeTelemetryData(data);
372 assertThat(json).doesNotContain("hasUnanalyzedC", "hasUnanalyzedCpp");
375 private static TelemetryData.Builder telemetryBuilder() {
376 return TelemetryData.builder()
379 .setPlugins(Collections.emptyMap())
380 .setDatabase(new TelemetryData.Database("H2", "11"));
384 private static List<UserTelemetryDto> attachUsers() {
385 return IntStream.range(0, 3)
387 i -> new UserTelemetryDto().setUuid("uuid-" + i).setActive(i % 2 == 0).setLastConnectionDate(1L).setLastSonarlintConnectionDate(2L).setExternalIdentityProvider("gitlab"))
388 .collect(Collectors.toList());
391 private static List<TelemetryData.Project> attachProjects() {
392 return IntStream.range(0, 3).mapToObj(i -> new TelemetryData.Project("uuid-" + i, 1L, "lang-" + i, (i + 1L) * 2L)).collect(Collectors.toList());
395 private List<TelemetryData.ProjectStatistics> attachProjectStats(boolean hasUnanalyzedLanguages) {
396 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))
397 .collect(Collectors.toList());
401 public static Object[][] allEditions() {
402 return Arrays.stream(EditionProvider.Edition.values())
403 .map(t -> new Object[] {t})
404 .toArray(Object[][]::new);
407 private String writeTelemetryData(TelemetryData data) {
408 StringWriter jsonString = new StringWriter();
409 try (JsonWriter json = JsonWriter.of(jsonString)) {
410 underTest.writeTelemetryData(json, data);
412 return jsonString.toString();
416 public static Set<Boolean> getFeatureFlagEnabledStates() {
417 return Set.of(true, false);