You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TelemetryDataLoaderImplIT.java 39KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 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. import com.tngtech.java.junit.dataprovider.DataProvider;
  22. import com.tngtech.java.junit.dataprovider.DataProviderRunner;
  23. import com.tngtech.java.junit.dataprovider.UseDataProvider;
  24. import java.sql.DatabaseMetaData;
  25. import java.sql.SQLException;
  26. import java.time.ZoneId;
  27. import java.time.ZonedDateTime;
  28. import java.util.Collections;
  29. import java.util.HashSet;
  30. import java.util.List;
  31. import java.util.Optional;
  32. import java.util.Set;
  33. import java.util.function.Consumer;
  34. import java.util.function.Function;
  35. import java.util.stream.IntStream;
  36. import org.junit.Before;
  37. import org.junit.Rule;
  38. import org.junit.Test;
  39. import org.junit.runner.RunWith;
  40. import org.sonar.api.config.Configuration;
  41. import org.sonar.api.impl.utils.TestSystem2;
  42. import org.sonar.core.platform.PlatformEditionProvider;
  43. import org.sonar.core.platform.PluginInfo;
  44. import org.sonar.core.platform.PluginRepository;
  45. import org.sonar.db.DbSession;
  46. import org.sonar.db.DbTester;
  47. import org.sonar.db.alm.setting.AlmSettingDto;
  48. import org.sonar.db.component.AnalysisPropertyDto;
  49. import org.sonar.db.component.ComponentDto;
  50. import org.sonar.db.component.ProjectData;
  51. import org.sonar.db.component.SnapshotDto;
  52. import org.sonar.db.metric.MetricDto;
  53. import org.sonar.db.newcodeperiod.NewCodePeriodType;
  54. import org.sonar.db.project.CreationMethod;
  55. import org.sonar.db.property.PropertyDto;
  56. import org.sonar.db.qualitygate.QualityGateConditionDto;
  57. import org.sonar.db.qualitygate.QualityGateDto;
  58. import org.sonar.db.qualityprofile.QProfileDto;
  59. import org.sonar.db.user.UserDbTester;
  60. import org.sonar.db.user.UserDto;
  61. import org.sonar.db.user.UserTelemetryDto;
  62. import org.sonar.server.management.ManagedInstanceService;
  63. import org.sonar.server.platform.ContainerSupport;
  64. import org.sonar.server.property.InternalProperties;
  65. import org.sonar.server.property.MapInternalProperties;
  66. import org.sonar.server.qualitygate.QualityGateCaycChecker;
  67. import org.sonar.server.qualitygate.QualityGateFinder;
  68. import org.sonar.server.qualityprofile.QProfileComparison;
  69. import org.sonar.server.telemetry.TelemetryData.Branch;
  70. import org.sonar.server.telemetry.TelemetryData.CloudUsage;
  71. import org.sonar.server.telemetry.TelemetryData.NewCodeDefinition;
  72. import org.sonar.server.telemetry.TelemetryData.ProjectStatistics;
  73. import org.sonar.updatecenter.common.Version;
  74. import static java.util.Arrays.asList;
  75. import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric;
  76. import static org.assertj.core.api.Assertions.assertThat;
  77. import static org.assertj.core.api.Assertions.entry;
  78. import static org.assertj.core.groups.Tuple.tuple;
  79. import static org.mockito.ArgumentMatchers.any;
  80. import static org.mockito.Mockito.mock;
  81. import static org.mockito.Mockito.spy;
  82. import static org.mockito.Mockito.when;
  83. import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
  84. import static org.sonar.api.measures.CoreMetrics.BUGS_KEY;
  85. import static org.sonar.api.measures.CoreMetrics.COVERAGE_KEY;
  86. import static org.sonar.api.measures.CoreMetrics.DEVELOPMENT_COST_KEY;
  87. import static org.sonar.api.measures.CoreMetrics.LINES_KEY;
  88. import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
  89. import static org.sonar.api.measures.CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION_KEY;
  90. import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_KEY;
  91. import static org.sonar.api.measures.CoreMetrics.TECHNICAL_DEBT_KEY;
  92. import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY;
  93. import static org.sonar.core.config.CorePropertyDefinitions.SONAR_ANALYSIS_DETECTEDCI;
  94. import static org.sonar.core.config.CorePropertyDefinitions.SONAR_ANALYSIS_DETECTEDSCM;
  95. import static org.sonar.core.platform.EditionProvider.Edition.COMMUNITY;
  96. import static org.sonar.core.platform.EditionProvider.Edition.DEVELOPER;
  97. import static org.sonar.core.platform.EditionProvider.Edition.ENTERPRISE;
  98. import static org.sonar.db.component.BranchType.BRANCH;
  99. import static org.sonar.server.metric.UnanalyzedLanguageMetrics.UNANALYZED_CPP_KEY;
  100. import static org.sonar.server.metric.UnanalyzedLanguageMetrics.UNANALYZED_C_KEY;
  101. import static org.sonar.server.qualitygate.QualityGateCaycStatus.NON_COMPLIANT;
  102. import static org.sonar.server.telemetry.TelemetryDataLoaderImpl.EXTERNAL_SECURITY_REPORT_EXPORTED_AT;
  103. @RunWith(DataProviderRunner.class)
  104. public class TelemetryDataLoaderImplIT {
  105. private final static Long NOW = 100_000_000L;
  106. public static final String SERVER_ID = "AU-TpxcB-iU5OvuD2FL7";
  107. private final TestSystem2 system2 = new TestSystem2().setNow(NOW);
  108. @Rule
  109. public DbTester db = DbTester.create(system2);
  110. private final FakeServer server = new FakeServer();
  111. private final PluginRepository pluginRepository = mock(PluginRepository.class);
  112. private final Configuration configuration = mock(Configuration.class);
  113. private final PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class);
  114. private final ContainerSupport containerSupport = mock(ContainerSupport.class);
  115. private final QualityGateCaycChecker qualityGateCaycChecker = mock(QualityGateCaycChecker.class);
  116. private final QualityGateFinder qualityGateFinder = new QualityGateFinder(db.getDbClient());
  117. private final QualityProfileDataProvider qualityProfileDataProvider = new QualityProfileDataProvider(db.getDbClient(), new QProfileComparison(db.getDbClient()));
  118. private final InternalProperties internalProperties = spy(new MapInternalProperties());
  119. private final ManagedInstanceService managedInstanceService = mock(ManagedInstanceService.class);
  120. private final CloudUsageDataProvider cloudUsageDataProvider = mock(CloudUsageDataProvider.class);
  121. private final TelemetryDataLoader communityUnderTest = new TelemetryDataLoaderImpl(server, db.getDbClient(), pluginRepository, editionProvider,
  122. internalProperties, configuration, containerSupport, qualityGateCaycChecker, qualityGateFinder, managedInstanceService, cloudUsageDataProvider, qualityProfileDataProvider);
  123. private final TelemetryDataLoader commercialUnderTest = new TelemetryDataLoaderImpl(server, db.getDbClient(), pluginRepository, editionProvider,
  124. internalProperties, configuration, containerSupport, qualityGateCaycChecker, qualityGateFinder, managedInstanceService, cloudUsageDataProvider, qualityProfileDataProvider);
  125. private QualityGateDto builtInDefaultQualityGate;
  126. private MetricDto bugsDto;
  127. private MetricDto vulnerabilitiesDto;
  128. private MetricDto securityHotspotsDto;
  129. private MetricDto technicalDebtDto;
  130. private MetricDto developmentCostDto;
  131. @Before
  132. public void setUpBuiltInQualityGate() {
  133. String builtInQgName = "Sonar way";
  134. builtInDefaultQualityGate = db.qualityGates().insertQualityGate(qg -> qg.setName(builtInQgName).setBuiltIn(true));
  135. when(qualityGateCaycChecker.checkCaycCompliant(any(), any())).thenReturn(NON_COMPLIANT);
  136. db.qualityGates().setDefaultQualityGate(builtInDefaultQualityGate);
  137. bugsDto = db.measures().insertMetric(m -> m.setKey(BUGS_KEY));
  138. vulnerabilitiesDto = db.measures().insertMetric(m -> m.setKey(VULNERABILITIES_KEY));
  139. securityHotspotsDto = db.measures().insertMetric(m -> m.setKey(SECURITY_HOTSPOTS_KEY));
  140. technicalDebtDto = db.measures().insertMetric(m -> m.setKey(TECHNICAL_DEBT_KEY));
  141. developmentCostDto = db.measures().insertMetric(m -> m.setKey(DEVELOPMENT_COST_KEY));
  142. }
  143. @Test
  144. public void send_telemetry_data() {
  145. String version = "7.5.4";
  146. Long analysisDate = 1L;
  147. Long lastConnectionDate = 5L;
  148. server.setId(SERVER_ID);
  149. server.setVersion(version);
  150. List<PluginInfo> plugins = asList(newPlugin("java", "4.12.0.11033"), newPlugin("scmgit", "1.2"), new PluginInfo("other"));
  151. when(pluginRepository.getPluginInfos()).thenReturn(plugins);
  152. when(editionProvider.get()).thenReturn(Optional.of(DEVELOPER));
  153. List<UserDto> activeUsers = composeActiveUsers(3);
  154. // update last connection
  155. activeUsers.forEach(u -> db.users().updateLastConnectionDate(u, 5L));
  156. UserDto inactiveUser = db.users().insertUser(u -> u.setActive(false).setExternalIdentityProvider("provider0"));
  157. MetricDto lines = db.measures().insertMetric(m -> m.setKey(LINES_KEY));
  158. MetricDto ncloc = db.measures().insertMetric(m -> m.setKey(NCLOC_KEY));
  159. MetricDto coverage = db.measures().insertMetric(m -> m.setKey(COVERAGE_KEY));
  160. MetricDto nclocDistrib = db.measures().insertMetric(m -> m.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY));
  161. ProjectData projectData1 = db.components().insertPrivateProject();
  162. ComponentDto mainBranch1 = projectData1.getMainBranchComponent();
  163. var branch1 = db.components().insertProjectBranch(mainBranch1, branchDto -> branchDto.setKey("reference"));
  164. var branch2 = db.components().insertProjectBranch(mainBranch1, branchDto -> branchDto.setKey("custom"));
  165. db.measures().insertLiveMeasure(mainBranch1, lines, m -> m.setValue(110d));
  166. db.measures().insertLiveMeasure(mainBranch1, ncloc, m -> m.setValue(110d));
  167. db.measures().insertLiveMeasure(mainBranch1, coverage, m -> m.setValue(80d));
  168. db.measures().insertLiveMeasure(mainBranch1, nclocDistrib, m -> m.setValue(null).setData("java=70;js=30;kotlin=10"));
  169. db.measures().insertLiveMeasure(mainBranch1, bugsDto, m -> m.setValue(1d));
  170. db.measures().insertLiveMeasure(mainBranch1, vulnerabilitiesDto, m -> m.setValue(1d).setData((String) null));
  171. db.measures().insertLiveMeasure(mainBranch1, securityHotspotsDto, m -> m.setValue(1d).setData((String) null));
  172. db.measures().insertLiveMeasure(mainBranch1, developmentCostDto, m -> m.setData("50").setValue(null));
  173. db.measures().insertLiveMeasure(mainBranch1, technicalDebtDto, m -> m.setValue(5d).setData((String) null));
  174. // Measures on other branches
  175. db.measures().insertLiveMeasure(branch1, technicalDebtDto, m -> m.setValue(6d).setData((String) null));
  176. db.measures().insertLiveMeasure(branch2, technicalDebtDto, m -> m.setValue(7d).setData((String) null));
  177. ProjectData projectData2 = db.components().insertPrivateProject();
  178. ComponentDto mainBranch2 = projectData2.getMainBranchComponent();
  179. db.measures().insertLiveMeasure(mainBranch2, lines, m -> m.setValue(200d));
  180. db.measures().insertLiveMeasure(mainBranch2, ncloc, m -> m.setValue(200d));
  181. db.measures().insertLiveMeasure(mainBranch2, coverage, m -> m.setValue(80d));
  182. db.measures().insertLiveMeasure(mainBranch2, nclocDistrib, m -> m.setValue(null).setData("java=180;js=20"));
  183. SnapshotDto project1Analysis = db.components().insertSnapshot(mainBranch1, t -> t.setLast(true).setAnalysisDate(analysisDate));
  184. SnapshotDto project2Analysis = db.components().insertSnapshot(mainBranch2, t -> t.setLast(true).setAnalysisDate(analysisDate));
  185. db.measures().insertMeasure(mainBranch1, project1Analysis, nclocDistrib, m -> m.setData("java=70;js=30;kotlin=10"));
  186. db.measures().insertMeasure(mainBranch2, project2Analysis, nclocDistrib, m -> m.setData("java=180;js=20"));
  187. insertAnalysisProperty(project1Analysis, "prop-uuid-1", SONAR_ANALYSIS_DETECTEDCI, "ci-1");
  188. insertAnalysisProperty(project2Analysis, "prop-uuid-2", SONAR_ANALYSIS_DETECTEDCI, "ci-2");
  189. insertAnalysisProperty(project1Analysis, "prop-uuid-3", SONAR_ANALYSIS_DETECTEDSCM, "scm-1");
  190. insertAnalysisProperty(project2Analysis, "prop-uuid-4", SONAR_ANALYSIS_DETECTEDSCM, "scm-2");
  191. // alm
  192. db.almSettings().insertAzureAlmSetting();
  193. db.almSettings().insertGitHubAlmSetting();
  194. AlmSettingDto almSettingDto = db.almSettings().insertAzureAlmSetting(a -> a.setUrl("https://dev.azure.com"));
  195. AlmSettingDto gitHubAlmSetting = db.almSettings().insertGitHubAlmSetting(a -> a.setUrl("https://api.github.com"));
  196. db.almSettings().insertAzureProjectAlmSetting(almSettingDto, projectData1.getProjectDto());
  197. db.almSettings().insertGitlabProjectAlmSetting(gitHubAlmSetting, projectData2.getProjectDto(), true);
  198. // quality gates
  199. QualityGateDto qualityGate1 = db.qualityGates().insertQualityGate(qg -> qg.setName("QG1").setBuiltIn(true));
  200. QualityGateDto qualityGate2 = db.qualityGates().insertQualityGate(qg -> qg.setName("QG2"));
  201. QualityGateConditionDto condition1 = db.qualityGates().addCondition(qualityGate1, vulnerabilitiesDto, c -> c.setOperator("GT").setErrorThreshold("80"));
  202. QualityGateConditionDto condition2 = db.qualityGates().addCondition(qualityGate2, securityHotspotsDto, c -> c.setOperator("LT").setErrorThreshold("2"));
  203. // quality profiles
  204. QProfileDto javaQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("java"));
  205. QProfileDto kotlinQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("kotlin"));
  206. QProfileDto jsQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("js"));
  207. db.qualityProfiles().associateWithProject(projectData1.getProjectDto(), javaQP, kotlinQP, jsQP);
  208. db.qualityProfiles().associateWithProject(projectData2.getProjectDto(), javaQP, jsQP);
  209. QProfileDto qualityProfile1 = db.qualityProfiles().insert(qp -> qp.setIsBuiltIn(true));
  210. QProfileDto qualityProfile2 = db.qualityProfiles().insert();
  211. db.qualityProfiles().setAsDefault(qualityProfile1, qualityProfile2);
  212. // link one project to a non-default QG
  213. db.qualityGates().associateProjectToQualityGate(db.components().getProjectDtoByMainBranch(mainBranch1), qualityGate1);
  214. db.newCodePeriods().insert(projectData1.projectUuid(), NewCodePeriodType.NUMBER_OF_DAYS, "30");
  215. db.newCodePeriods().insert(projectData1.projectUuid(), branch2.branchUuid(), NewCodePeriodType.REFERENCE_BRANCH, "reference");
  216. var instanceNcdId = NewCodeDefinition.getInstanceDefault().hashCode();
  217. var projectNcdId = new NewCodeDefinition(NewCodePeriodType.NUMBER_OF_DAYS.name(), "30", "project").hashCode();
  218. var branchNcdId = new NewCodeDefinition(NewCodePeriodType.REFERENCE_BRANCH.name(), branch1.uuid(), "branch").hashCode();
  219. TelemetryData data = communityUnderTest.load();
  220. assertThat(data.getServerId()).isEqualTo(SERVER_ID);
  221. assertThat(data.getVersion()).isEqualTo(version);
  222. assertThat(data.getEdition()).contains(DEVELOPER);
  223. assertThat(data.getDefaultQualityGate()).isEqualTo(builtInDefaultQualityGate.getUuid());
  224. assertThat(data.getSonarWayQualityGate()).isEqualTo(builtInDefaultQualityGate.getUuid());
  225. assertThat(data.getNcdId()).isEqualTo(NewCodeDefinition.getInstanceDefault().hashCode());
  226. assertThat(data.getMessageSequenceNumber()).isOne();
  227. assertDatabaseMetadata(data.getDatabase());
  228. assertThat(data.getPlugins()).containsOnly(
  229. entry("java", "4.12.0.11033"), entry("scmgit", "1.2"), entry("other", "undefined"));
  230. assertThat(data.isInContainer()).isFalse();
  231. assertThat(data.getUserTelemetries())
  232. .extracting(UserTelemetryDto::getUuid, UserTelemetryDto::getLastConnectionDate, UserTelemetryDto::getLastSonarlintConnectionDate, UserTelemetryDto::isActive)
  233. .containsExactlyInAnyOrder(
  234. tuple(activeUsers.get(0).getUuid(), lastConnectionDate, activeUsers.get(0).getLastSonarlintConnectionDate(), true),
  235. tuple(activeUsers.get(1).getUuid(), lastConnectionDate, activeUsers.get(1).getLastSonarlintConnectionDate(), true),
  236. tuple(activeUsers.get(2).getUuid(), lastConnectionDate, activeUsers.get(2).getLastSonarlintConnectionDate(), true),
  237. tuple(inactiveUser.getUuid(), null, inactiveUser.getLastSonarlintConnectionDate(), false));
  238. assertThat(data.getProjects())
  239. .extracting(TelemetryData.Project::projectUuid, TelemetryData.Project::language, TelemetryData.Project::loc, TelemetryData.Project::lastAnalysis)
  240. .containsExactlyInAnyOrder(
  241. tuple(projectData1.projectUuid(), "java", 70L, analysisDate),
  242. tuple(projectData1.projectUuid(), "js", 30L, analysisDate),
  243. tuple(projectData1.projectUuid(), "kotlin", 10L, analysisDate),
  244. tuple(projectData2.projectUuid(), "java", 180L, analysisDate),
  245. tuple(projectData2.projectUuid(), "js", 20L, analysisDate));
  246. assertThat(data.getProjectStatistics())
  247. .extracting(
  248. ProjectStatistics::getBranchCount,
  249. ProjectStatistics::getPullRequestCount,
  250. ProjectStatistics::getQualityGate,
  251. ProjectStatistics::getScm,
  252. ProjectStatistics::getCi,
  253. ProjectStatistics::getDevopsPlatform,
  254. ProjectStatistics::getBugs,
  255. ProjectStatistics::getVulnerabilities,
  256. ProjectStatistics::getSecurityHotspots,
  257. ProjectStatistics::getDevelopmentCost,
  258. ProjectStatistics::getTechnicalDebt,
  259. ProjectStatistics::getNcdId,
  260. ProjectStatistics::isMonorepo)
  261. .containsExactlyInAnyOrder(
  262. tuple(3L, 0L, qualityGate1.getUuid(), "scm-1", "ci-1", "azure_devops_cloud", Optional.of(1L), Optional.of(1L), Optional.of(1L), Optional.of(50L), Optional.of(5L),
  263. projectNcdId, false),
  264. tuple(1L, 0L, builtInDefaultQualityGate.getUuid(), "scm-2", "ci-2", "github_cloud", Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(),
  265. Optional.empty(), instanceNcdId, true));
  266. assertThat(data.getBranches())
  267. .extracting(Branch::branchUuid, Branch::ncdId)
  268. .containsExactlyInAnyOrder(
  269. tuple(branch1.uuid(), projectNcdId),
  270. tuple(branch2.uuid(), branchNcdId),
  271. tuple(mainBranch1.uuid(), projectNcdId),
  272. tuple(mainBranch2.uuid(), instanceNcdId));
  273. assertThat(data.getNewCodeDefinitions())
  274. .extracting(NewCodeDefinition::scope, NewCodeDefinition::type, NewCodeDefinition::value)
  275. .containsExactlyInAnyOrder(
  276. tuple("instance", NewCodePeriodType.PREVIOUS_VERSION.name(), ""),
  277. tuple("project", NewCodePeriodType.NUMBER_OF_DAYS.name(), "30"),
  278. tuple("branch", NewCodePeriodType.REFERENCE_BRANCH.name(), branch1.uuid()));
  279. assertThat(data.getQualityGates())
  280. .extracting(TelemetryData.QualityGate::uuid, TelemetryData.QualityGate::caycStatus,
  281. qg -> qg.conditions().stream()
  282. .map(condition -> tuple(condition.getMetricKey(), condition.getOperator().getDbValue(), condition.getErrorThreshold(), condition.isOnLeakPeriod()))
  283. .toList())
  284. .containsExactlyInAnyOrder(
  285. tuple(builtInDefaultQualityGate.getUuid(), "non-compliant", Collections.emptyList()),
  286. tuple(qualityGate1.getUuid(), "non-compliant", List.of(tuple(vulnerabilitiesDto.getKey(), condition1.getOperator(), condition1.getErrorThreshold(), false))),
  287. tuple(qualityGate2.getUuid(), "non-compliant", List.of(tuple(securityHotspotsDto.getKey(), condition2.getOperator(), condition2.getErrorThreshold(), false))));
  288. assertThat(data.getQualityProfiles())
  289. .extracting(TelemetryData.QualityProfile::uuid, TelemetryData.QualityProfile::isBuiltIn)
  290. .containsExactlyInAnyOrder(
  291. tuple(qualityProfile1.getKee(), qualityProfile1.isBuiltIn()),
  292. tuple(qualityProfile2.getKee(), qualityProfile2.isBuiltIn()),
  293. tuple(jsQP.getKee(), jsQP.isBuiltIn()),
  294. tuple(javaQP.getKee(), javaQP.isBuiltIn()),
  295. tuple(kotlinQP.getKee(), kotlinQP.isBuiltIn()));
  296. }
  297. @Test
  298. public void send_branch_measures_data() {
  299. Long analysisDate = ZonedDateTime.now(ZoneId.systemDefault()).toInstant().toEpochMilli();
  300. MetricDto qg = db.measures().insertMetric(m -> m.setKey(ALERT_STATUS_KEY));
  301. ProjectData projectData1 = db.components().insertPrivateProject();
  302. ComponentDto mainBranch1 = projectData1.getMainBranchComponent();
  303. ProjectData projectData2 = db.components().insertPrivateProject();
  304. ComponentDto mainBranch2 = projectData2.getMainBranchComponent();
  305. SnapshotDto project1Analysis1 = db.components().insertSnapshot(mainBranch1, t -> t.setLast(true).setAnalysisDate(analysisDate));
  306. SnapshotDto project1Analysis2 = db.components().insertSnapshot(mainBranch1, t -> t.setLast(true).setAnalysisDate(analysisDate));
  307. SnapshotDto project2Analysis = db.components().insertSnapshot(mainBranch2, t -> t.setLast(true).setAnalysisDate(analysisDate));
  308. db.measures().insertMeasure(mainBranch1, project1Analysis1, qg, pm -> pm.setData("OK"));
  309. db.measures().insertMeasure(mainBranch1, project1Analysis2, qg, pm -> pm.setData("ERROR"));
  310. db.measures().insertMeasure(mainBranch2, project2Analysis, qg, pm -> pm.setData("ERROR"));
  311. var branch1 = db.components().insertProjectBranch(mainBranch1, branchDto -> branchDto.setKey("reference"));
  312. var branch2 = db.components().insertProjectBranch(mainBranch1, branchDto -> branchDto.setKey("custom"));
  313. db.newCodePeriods().insert(projectData1.projectUuid(), NewCodePeriodType.NUMBER_OF_DAYS, "30");
  314. db.newCodePeriods().insert(projectData1.projectUuid(), branch2.branchUuid(), NewCodePeriodType.REFERENCE_BRANCH, "reference");
  315. var instanceNcdId = NewCodeDefinition.getInstanceDefault().hashCode();
  316. var projectNcdId = new NewCodeDefinition(NewCodePeriodType.NUMBER_OF_DAYS.name(), "30", "project").hashCode();
  317. var branchNcdId = new NewCodeDefinition(NewCodePeriodType.REFERENCE_BRANCH.name(), branch1.uuid(), "branch").hashCode();
  318. TelemetryData data = communityUnderTest.load();
  319. assertThat(data.getBranches())
  320. .extracting(Branch::branchUuid, Branch::ncdId, Branch::greenQualityGateCount, Branch::analysisCount)
  321. .containsExactlyInAnyOrder(
  322. tuple(branch1.uuid(), projectNcdId, 0, 0),
  323. tuple(branch2.uuid(), branchNcdId, 0, 0),
  324. tuple(mainBranch1.uuid(), projectNcdId, 1, 2),
  325. tuple(mainBranch2.uuid(), instanceNcdId, 0, 1));
  326. }
  327. private List<UserDto> composeActiveUsers(int count) {
  328. UserDbTester userDbTester = db.users();
  329. Function<Integer, Consumer<UserDto>> userConfigurator = index -> user -> user.setExternalIdentityProvider("provider" + index).setLastSonarlintConnectionDate(index * 2L);
  330. return IntStream
  331. .rangeClosed(1, count)
  332. .mapToObj(userConfigurator::apply)
  333. .map(userDbTester::insertUser)
  334. .toList();
  335. }
  336. private void assertDatabaseMetadata(TelemetryData.Database database) {
  337. try (DbSession dbSession = db.getDbClient().openSession(false)) {
  338. DatabaseMetaData metadata = dbSession.getConnection().getMetaData();
  339. assertThat(database.name()).isEqualTo("H2");
  340. assertThat(database.version()).isEqualTo(metadata.getDatabaseProductVersion());
  341. } catch (SQLException e) {
  342. throw new RuntimeException(e);
  343. }
  344. }
  345. @Test
  346. public void take_largest_branch_snapshot_project_data() {
  347. server.setId(SERVER_ID).setVersion("7.5.4");
  348. MetricDto lines = db.measures().insertMetric(m -> m.setKey(LINES_KEY));
  349. MetricDto ncloc = db.measures().insertMetric(m -> m.setKey(NCLOC_KEY));
  350. MetricDto coverage = db.measures().insertMetric(m -> m.setKey(COVERAGE_KEY));
  351. MetricDto nclocDistrib = db.measures().insertMetric(m -> m.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY));
  352. ProjectData projectData = db.components().insertPublicProject();
  353. QProfileDto javaQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("java"));
  354. QProfileDto kotlinQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("kotlin"));
  355. QProfileDto jsQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("js"));
  356. db.qualityProfiles().associateWithProject(projectData.getProjectDto(), javaQP, kotlinQP, jsQP);
  357. ComponentDto mainBranch = projectData.getMainBranchComponent();
  358. db.measures().insertLiveMeasure(mainBranch, lines, m -> m.setValue(110d));
  359. db.measures().insertLiveMeasure(mainBranch, ncloc, m -> m.setValue(110d));
  360. db.measures().insertLiveMeasure(mainBranch, coverage, m -> m.setValue(80d));
  361. db.measures().insertLiveMeasure(mainBranch, nclocDistrib, m -> m.setValue(null).setData("java=70;js=30;kotlin=10"));
  362. ComponentDto branch = db.components().insertProjectBranch(mainBranch, b -> b.setBranchType(BRANCH));
  363. db.measures().insertLiveMeasure(branch, lines, m -> m.setValue(180d));
  364. db.measures().insertLiveMeasure(branch, ncloc, m -> m.setValue(180d));
  365. db.measures().insertLiveMeasure(branch, coverage, m -> m.setValue(80d));
  366. db.measures().insertLiveMeasure(branch, nclocDistrib, m -> m.setValue(null).setData("java=100;js=50;kotlin=30"));
  367. SnapshotDto project1Analysis = db.components().insertSnapshot(mainBranch, t -> t.setLast(true));
  368. SnapshotDto project2Analysis = db.components().insertSnapshot(branch, t -> t.setLast(true));
  369. db.measures().insertMeasure(mainBranch, project1Analysis, nclocDistrib, m -> m.setData("java=70;js=30;kotlin=10"));
  370. db.measures().insertMeasure(branch, project2Analysis, nclocDistrib, m -> m.setData("java=100;js=50;kotlin=30"));
  371. TelemetryData data = communityUnderTest.load();
  372. assertThat(data.getProjects()).extracting(TelemetryData.Project::projectUuid, TelemetryData.Project::language, TelemetryData.Project::loc)
  373. .containsExactlyInAnyOrder(
  374. tuple(projectData.projectUuid(), "java", 100L),
  375. tuple(projectData.projectUuid(), "js", 50L),
  376. tuple(projectData.projectUuid(), "kotlin", 30L));
  377. assertThat(data.getProjectStatistics())
  378. .extracting(ProjectStatistics::getBranchCount, ProjectStatistics::getPullRequestCount,
  379. ProjectStatistics::getScm, ProjectStatistics::getCi)
  380. .containsExactlyInAnyOrder(
  381. tuple(2L, 0L, "undetected", "undetected"));
  382. }
  383. @Test
  384. public void load_shouldProvideQualityProfileInProjectSection() {
  385. server.setId(SERVER_ID).setVersion("7.5.4");
  386. MetricDto ncloc = db.measures().insertMetric(m -> m.setKey(NCLOC_KEY));
  387. MetricDto nclocDistrib = db.measures().insertMetric(m -> m.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY));
  388. ProjectData projectData = db.components().insertPublicProject();
  389. // default quality profile
  390. QProfileDto javaQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("java"));
  391. QProfileDto kotlinQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("kotlin"));
  392. db.qualityProfiles().setAsDefault(javaQP, kotlinQP);
  393. // selected quality profile
  394. QProfileDto jsQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("js"));
  395. db.qualityProfiles().associateWithProject(projectData.getProjectDto(), jsQP);
  396. ComponentDto mainBranch = projectData.getMainBranchComponent();
  397. db.measures().insertLiveMeasure(mainBranch, ncloc, m -> m.setValue(110d));
  398. db.measures().insertLiveMeasure(mainBranch, nclocDistrib, m -> m.setValue(null).setData("java=70;js=30;kotlin=10"));
  399. ComponentDto branch = db.components().insertProjectBranch(mainBranch, b -> b.setBranchType(BRANCH));
  400. db.measures().insertLiveMeasure(branch, ncloc, m -> m.setValue(180d));
  401. db.measures().insertLiveMeasure(branch, nclocDistrib, m -> m.setValue(null).setData("java=100;js=50;kotlin=30"));
  402. SnapshotDto project1Analysis = db.components().insertSnapshot(mainBranch, t -> t.setLast(true));
  403. SnapshotDto project2Analysis = db.components().insertSnapshot(branch, t -> t.setLast(true));
  404. db.measures().insertMeasure(mainBranch, project1Analysis, nclocDistrib, m -> m.setData("java=70;js=30;kotlin=10"));
  405. db.measures().insertMeasure(branch, project2Analysis, nclocDistrib, m -> m.setData("java=100;js=50;kotlin=30"));
  406. TelemetryData data = communityUnderTest.load();
  407. assertThat(data.getProjects()).extracting(TelemetryData.Project::projectUuid, TelemetryData.Project::language, TelemetryData.Project::qualityProfile)
  408. .containsExactlyInAnyOrder(
  409. tuple(projectData.projectUuid(), "java", javaQP.getKee()),
  410. tuple(projectData.projectUuid(), "js", jsQP.getKee()),
  411. tuple(projectData.projectUuid(), "kotlin", kotlinQP.getKee()));
  412. }
  413. @Test
  414. public void load_shouldProvideCreationMethodInProjectStatisticsSection() {
  415. server.setId(SERVER_ID).setVersion("7.5.4");
  416. ProjectData projectData1 = db.components().insertPrivateProjectWithCreationMethod(CreationMethod.LOCAL_API);
  417. ProjectData projectData2 = db.components().insertPrivateProjectWithCreationMethod(CreationMethod.LOCAL_BROWSER);
  418. ProjectData projectData3 = db.components().insertPrivateProjectWithCreationMethod(CreationMethod.UNKNOWN);
  419. ProjectData projectData4 = db.components().insertPrivateProjectWithCreationMethod(CreationMethod.SCANNER_API);
  420. ProjectData projectData5 = db.components().insertPrivateProjectWithCreationMethod(CreationMethod.ALM_IMPORT_BROWSER);
  421. ProjectData projectData6 = db.components().insertPrivateProjectWithCreationMethod(CreationMethod.ALM_IMPORT_API);
  422. TelemetryData data = communityUnderTest.load();
  423. assertThat(data.getProjectStatistics()).extracting(TelemetryData.ProjectStatistics::getProjectUuid, TelemetryData.ProjectStatistics::getCreationMethod)
  424. .containsExactlyInAnyOrder(
  425. tuple(projectData1.projectUuid(), CreationMethod.LOCAL_API),
  426. tuple(projectData2.projectUuid(), CreationMethod.LOCAL_BROWSER),
  427. tuple(projectData3.projectUuid(), CreationMethod.UNKNOWN),
  428. tuple(projectData4.projectUuid(), CreationMethod.SCANNER_API),
  429. tuple(projectData5.projectUuid(), CreationMethod.ALM_IMPORT_BROWSER),
  430. tuple(projectData6.projectUuid(), CreationMethod.ALM_IMPORT_API));
  431. }
  432. @Test
  433. public void test_ncd_on_community_edition() {
  434. server.setId(SERVER_ID).setVersion("7.5.4");
  435. when(editionProvider.get()).thenReturn(Optional.of(COMMUNITY));
  436. ProjectData project = db.components().insertPublicProject();
  437. ComponentDto branch = db.components().insertProjectBranch(project.getMainBranchComponent(), b -> b.setBranchType(BRANCH));
  438. db.newCodePeriods().insert(project.projectUuid(), branch.branchUuid(), NewCodePeriodType.NUMBER_OF_DAYS, "30");
  439. var projectNcdId = new NewCodeDefinition(NewCodePeriodType.NUMBER_OF_DAYS.name(), "30", "project").hashCode();
  440. TelemetryData data = communityUnderTest.load();
  441. assertThat(data.getProjectStatistics())
  442. .extracting(ProjectStatistics::getBranchCount, ProjectStatistics::getNcdId)
  443. .containsExactlyInAnyOrder(tuple(2L, projectNcdId));
  444. assertThat(data.getBranches())
  445. .extracting(Branch::branchUuid, Branch::ncdId)
  446. .contains(tuple(branch.uuid(), projectNcdId));
  447. }
  448. @Test
  449. public void data_contains_weekly_count_sonarlint_users() {
  450. db.users().insertUser(c -> c.setLastSonarlintConnectionDate(NOW - 100_000L));
  451. db.users().insertUser(c -> c.setLastSonarlintConnectionDate(NOW));
  452. // these don't count
  453. db.users().insertUser(c -> c.setLastSonarlintConnectionDate(NOW - 1_000_000_000L));
  454. db.users().insertUser();
  455. TelemetryData data = communityUnderTest.load();
  456. assertThat(data.getUserTelemetries())
  457. .hasSize(4);
  458. }
  459. @Test
  460. public void send_server_id_and_version() {
  461. String id = randomAlphanumeric(40);
  462. String version = randomAlphanumeric(10);
  463. server.setId(id);
  464. server.setVersion(version);
  465. TelemetryData data = communityUnderTest.load();
  466. assertThat(data.getServerId()).isEqualTo(id);
  467. assertThat(data.getVersion()).isEqualTo(version);
  468. data = commercialUnderTest.load();
  469. assertThat(data.getServerId()).isEqualTo(id);
  470. assertThat(data.getVersion()).isEqualTo(version);
  471. }
  472. @Test
  473. public void send_server_installation_date_and_installation_version() {
  474. String installationVersion = "7.9.BEST.LTS.EVER";
  475. Long installationDate = 1546300800000L; // 2019/01/01
  476. internalProperties.write(InternalProperties.INSTALLATION_DATE, String.valueOf(installationDate));
  477. internalProperties.write(InternalProperties.INSTALLATION_VERSION, installationVersion);
  478. TelemetryData data = communityUnderTest.load();
  479. assertThat(data.getInstallationDate()).isEqualTo(installationDate);
  480. assertThat(data.getInstallationVersion()).isEqualTo(installationVersion);
  481. }
  482. @Test
  483. public void send_correct_sequence_number() {
  484. internalProperties.write(TelemetryDaemon.I_PROP_MESSAGE_SEQUENCE, "10");
  485. TelemetryData data = communityUnderTest.load();
  486. assertThat(data.getMessageSequenceNumber()).isEqualTo(11L);
  487. }
  488. @Test
  489. public void do_not_send_server_installation_details_if_missing_property() {
  490. TelemetryData data = communityUnderTest.load();
  491. assertThat(data.getInstallationDate()).isNull();
  492. assertThat(data.getInstallationVersion()).isNull();
  493. data = commercialUnderTest.load();
  494. assertThat(data.getInstallationDate()).isNull();
  495. assertThat(data.getInstallationVersion()).isNull();
  496. }
  497. @Test
  498. public void send_unanalyzed_languages_flags_when_edition_is_community() {
  499. when(editionProvider.get()).thenReturn(Optional.of(COMMUNITY));
  500. MetricDto unanalyzedC = db.measures().insertMetric(m -> m.setKey(UNANALYZED_C_KEY));
  501. MetricDto unanalyzedCpp = db.measures().insertMetric(m -> m.setKey(UNANALYZED_CPP_KEY));
  502. ComponentDto project1 = db.components().insertPublicProject().getMainBranchComponent();
  503. ComponentDto project2 = db.components().insertPublicProject().getMainBranchComponent();
  504. db.measures().insertLiveMeasure(project1, unanalyzedC);
  505. db.measures().insertLiveMeasure(project2, unanalyzedC);
  506. db.measures().insertLiveMeasure(project2, unanalyzedCpp);
  507. TelemetryData data = communityUnderTest.load();
  508. assertThat(data.hasUnanalyzedC().get()).isTrue();
  509. assertThat(data.hasUnanalyzedCpp().get()).isTrue();
  510. }
  511. @Test
  512. public void do_not_send_unanalyzed_languages_flags_when_edition_is_not_community() {
  513. when(editionProvider.get()).thenReturn(Optional.of(DEVELOPER));
  514. MetricDto unanalyzedC = db.measures().insertMetric(m -> m.setKey(UNANALYZED_C_KEY));
  515. MetricDto unanalyzedCpp = db.measures().insertMetric(m -> m.setKey(UNANALYZED_CPP_KEY));
  516. ComponentDto project1 = db.components().insertPublicProject().getMainBranchComponent();
  517. ComponentDto project2 = db.components().insertPublicProject().getMainBranchComponent();
  518. db.measures().insertLiveMeasure(project1, unanalyzedC);
  519. db.measures().insertLiveMeasure(project2, unanalyzedCpp);
  520. TelemetryData data = communityUnderTest.load();
  521. assertThat(data.hasUnanalyzedC()).isEmpty();
  522. assertThat(data.hasUnanalyzedCpp()).isEmpty();
  523. }
  524. @Test
  525. public void unanalyzed_languages_flags_are_set_to_false_when_no_unanalyzed_languages_and_edition_is_community() {
  526. when(editionProvider.get()).thenReturn(Optional.of(COMMUNITY));
  527. TelemetryData data = communityUnderTest.load();
  528. assertThat(data.hasUnanalyzedC().get()).isFalse();
  529. assertThat(data.hasUnanalyzedCpp().get()).isFalse();
  530. }
  531. @Test
  532. public void populate_security_custom_config_for_languages_on_enterprise() {
  533. when(editionProvider.get()).thenReturn(Optional.of(ENTERPRISE));
  534. when(configuration.get("sonar.security.config.javasecurity")).thenReturn(Optional.of("{}"));
  535. when(configuration.get("sonar.security.config.phpsecurity")).thenReturn(Optional.of("{}"));
  536. when(configuration.get("sonar.security.config.pythonsecurity")).thenReturn(Optional.of("{}"));
  537. when(configuration.get("sonar.security.config.roslyn.sonaranalyzer.security.cs")).thenReturn(Optional.of("{}"));
  538. TelemetryData data = commercialUnderTest.load();
  539. assertThat(data.getCustomSecurityConfigs())
  540. .containsExactlyInAnyOrder("java", "php", "python", "csharp");
  541. }
  542. @Test
  543. public void skip_security_custom_config_on_community() {
  544. when(editionProvider.get()).thenReturn(Optional.of(COMMUNITY));
  545. TelemetryData data = communityUnderTest.load();
  546. assertThat(data.getCustomSecurityConfigs()).isEmpty();
  547. }
  548. @Test
  549. public void undetected_alm_ci_slm_data() {
  550. server.setId(SERVER_ID).setVersion("7.5.4");
  551. db.components().insertPublicProject().getMainBranchComponent();
  552. TelemetryData data = communityUnderTest.load();
  553. assertThat(data.getProjectStatistics())
  554. .extracting(ProjectStatistics::getDevopsPlatform, ProjectStatistics::getScm, ProjectStatistics::getCi)
  555. .containsExactlyInAnyOrder(tuple("undetected", "undetected", "undetected"));
  556. }
  557. @Test
  558. public void givenExistingExternalSecurityReport_whenTelemetryIsGenerated_payloadShouldContainLastUsageDate() {
  559. server.setId(SERVER_ID).setVersion("7.5.4");
  560. ProjectData projectData = db.components().insertPublicProject();
  561. db.getDbClient().propertiesDao().saveProperty(new PropertyDto().setKey(EXTERNAL_SECURITY_REPORT_EXPORTED_AT).setEntityUuid(projectData.projectUuid()).setValue("1"));
  562. TelemetryData data = communityUnderTest.load();
  563. assertThat(data.getProjectStatistics()).isNotEmpty();
  564. assertThat(data.getProjectStatistics().get(0).getExternalSecurityReportExportedAt()).isPresent()
  565. .get().isEqualTo(1L);
  566. }
  567. @Test
  568. @UseDataProvider("getManagedInstanceData")
  569. public void managedInstanceData_containsCorrectInformation(boolean isManaged, String provider) {
  570. when(managedInstanceService.isInstanceExternallyManaged()).thenReturn(isManaged);
  571. when(managedInstanceService.getProviderName()).thenReturn(provider);
  572. TelemetryData data = commercialUnderTest.load();
  573. TelemetryData.ManagedInstanceInformation managedInstance = data.getManagedInstanceInformation();
  574. assertThat(managedInstance.isManaged()).isEqualTo(isManaged);
  575. assertThat(managedInstance.provider()).isEqualTo(provider);
  576. }
  577. @Test
  578. public void load_shouldContainCloudUsage() {
  579. CloudUsage cloudUsage = new CloudUsage(true, "1.27", "linux/amd64", "5.4.181-99.354.amzn2.x86_64", "10.1.0", "docker", false);
  580. when(cloudUsageDataProvider.getCloudUsage()).thenReturn(cloudUsage);
  581. TelemetryData data = commercialUnderTest.load();
  582. assertThat(data.getCloudUsage()).isEqualTo(cloudUsage);
  583. }
  584. @Test
  585. public void default_quality_gate_sent_with_project() {
  586. db.components().insertPublicProject().getMainBranchComponent();
  587. QualityGateDto qualityGate = db.qualityGates().insertQualityGate(qg -> qg.setName("anything").setBuiltIn(true));
  588. db.qualityGates().setDefaultQualityGate(qualityGate);
  589. TelemetryData data = communityUnderTest.load();
  590. assertThat(data.getProjectStatistics())
  591. .extracting(ProjectStatistics::getQualityGate)
  592. .containsOnly(qualityGate.getUuid());
  593. }
  594. private PluginInfo newPlugin(String key, String version) {
  595. return new PluginInfo(key)
  596. .setVersion(Version.create(version));
  597. }
  598. private void insertAnalysisProperty(SnapshotDto snapshotDto, String uuid, String key, String value) {
  599. db.getDbClient().analysisPropertiesDao().insert(db.getSession(), new AnalysisPropertyDto()
  600. .setUuid(uuid)
  601. .setAnalysisUuid(snapshotDto.getUuid())
  602. .setKey(key)
  603. .setValue(value)
  604. .setCreatedAt(1L));
  605. }
  606. @DataProvider
  607. public static Set<String> getScimFeatureStatues() {
  608. HashSet<String> result = new HashSet<>();
  609. result.add("true");
  610. result.add("false");
  611. result.add(null);
  612. return result;
  613. }
  614. @DataProvider
  615. public static Object[][] getManagedInstanceData() {
  616. return new Object[][] {
  617. {true, "scim"},
  618. {true, "github"},
  619. {true, "gitlab"},
  620. {false, null},
  621. };
  622. }
  623. }