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.

MeasureRepositoryImplTest.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2021 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.ce.task.projectanalysis.measure;
  21. import com.google.common.base.Function;
  22. import com.google.common.collect.ImmutableList;
  23. import com.tngtech.java.junit.dataprovider.DataProvider;
  24. import com.tngtech.java.junit.dataprovider.DataProviderRunner;
  25. import com.tngtech.java.junit.dataprovider.UseDataProvider;
  26. import java.util.List;
  27. import java.util.Map;
  28. import java.util.Optional;
  29. import javax.annotation.Nullable;
  30. import org.junit.Before;
  31. import org.junit.Rule;
  32. import org.junit.Test;
  33. import org.junit.runner.RunWith;
  34. import org.sonar.api.utils.System2;
  35. import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
  36. import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
  37. import org.sonar.ce.task.projectanalysis.component.Component;
  38. import org.sonar.ce.task.projectanalysis.component.ReportComponent;
  39. import org.sonar.ce.task.projectanalysis.metric.Metric;
  40. import org.sonar.ce.task.projectanalysis.metric.MetricImpl;
  41. import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
  42. import org.sonar.ce.task.projectanalysis.metric.ReportMetricValidator;
  43. import org.sonar.db.DbClient;
  44. import org.sonar.db.DbSession;
  45. import org.sonar.db.DbTester;
  46. import org.sonar.db.component.ComponentDto;
  47. import org.sonar.db.component.SnapshotDto;
  48. import org.sonar.db.measure.MeasureDto;
  49. import org.sonar.db.metric.MetricDto;
  50. import org.sonar.scanner.protocol.output.ScannerReport;
  51. import org.sonar.scanner.protocol.output.ScannerReport.Measure.StringValue;
  52. import static com.google.common.collect.FluentIterable.from;
  53. import static java.lang.String.format;
  54. import static org.assertj.core.api.Assertions.assertThat;
  55. import static org.assertj.core.api.Assertions.assertThatThrownBy;
  56. import static org.junit.Assert.fail;
  57. import static org.mockito.Mockito.mock;
  58. import static org.mockito.Mockito.verifyNoMoreInteractions;
  59. import static org.mockito.Mockito.verifyZeroInteractions;
  60. import static org.mockito.Mockito.when;
  61. import static org.sonar.db.component.ComponentTesting.newFileDto;
  62. @RunWith(DataProviderRunner.class)
  63. public class MeasureRepositoryImplTest {
  64. @Rule
  65. public DbTester dbTester = DbTester.create(System2.INSTANCE);
  66. @Rule
  67. public BatchReportReaderRule reportReader = new BatchReportReaderRule();
  68. private static final String FILE_COMPONENT_KEY = "file cpt key";
  69. private static final ReportComponent FILE_COMPONENT = ReportComponent.builder(Component.Type.FILE, 1).setKey(FILE_COMPONENT_KEY).build();
  70. private static final ReportComponent OTHER_COMPONENT = ReportComponent.builder(Component.Type.FILE, 2).setKey("some other key").build();
  71. private static final String METRIC_KEY_1 = "metric 1";
  72. private static final int METRIC_ID_1 = 1;
  73. private static final String METRIC_KEY_2 = "metric 2";
  74. private static final int METRIC_ID_2 = 2;
  75. private final Metric metric1 = mock(Metric.class);
  76. private final Metric metric2 = mock(Metric.class);
  77. private static final String LAST_ANALYSIS_UUID = "u123";
  78. private static final String OTHER_ANALYSIS_UUID = "u369";
  79. private static final Measure SOME_MEASURE = Measure.newMeasureBuilder().create("some value");
  80. private static final String SOME_DATA = "some data";
  81. private ReportMetricValidator reportMetricValidator = mock(ReportMetricValidator.class);
  82. private DbClient dbClient = dbTester.getDbClient();
  83. private MetricRepository metricRepository = mock(MetricRepository.class);
  84. private MeasureRepositoryImpl underTest = new MeasureRepositoryImpl(dbClient, reportReader, metricRepository, reportMetricValidator);
  85. private DbClient mockedDbClient = mock(DbClient.class);
  86. private BatchReportReader mockBatchReportReader = mock(BatchReportReader.class);
  87. private MeasureRepositoryImpl underTestWithMock = new MeasureRepositoryImpl(mockedDbClient, mockBatchReportReader, metricRepository, reportMetricValidator);
  88. private DbSession dbSession = dbTester.getSession();
  89. @Before
  90. public void setUp() {
  91. when(metric1.getKey()).thenReturn(METRIC_KEY_1);
  92. when(metric1.getType()).thenReturn(Metric.MetricType.STRING);
  93. when(metric2.getKey()).thenReturn(METRIC_KEY_2);
  94. when(metric2.getType()).thenReturn(Metric.MetricType.STRING);
  95. // references to metrics are consistent with DB by design
  96. when(metricRepository.getByKey(METRIC_KEY_1)).thenReturn(metric1);
  97. when(metricRepository.getByKey(METRIC_KEY_2)).thenReturn(metric2);
  98. }
  99. @Test
  100. public void getBaseMeasure_throws_NPE_and_does_not_open_session_if_component_is_null() {
  101. try {
  102. underTestWithMock.getBaseMeasure(null, metric1);
  103. fail("an NPE should have been raised");
  104. } catch (NullPointerException e) {
  105. verifyZeroInteractions(mockedDbClient);
  106. }
  107. }
  108. @Test
  109. public void getBaseMeasure_throws_NPE_and_does_not_open_session_if_metric_is_null() {
  110. try {
  111. underTestWithMock.getBaseMeasure(FILE_COMPONENT, null);
  112. fail("an NPE should have been raised");
  113. } catch (NullPointerException e) {
  114. verifyZeroInteractions(mockedDbClient);
  115. }
  116. }
  117. @Test
  118. public void getBaseMeasure_returns_absent_if_measure_does_not_exist_in_DB() {
  119. Optional<Measure> res = underTest.getBaseMeasure(FILE_COMPONENT, metric1);
  120. assertThat(res).isNotPresent();
  121. }
  122. @Test
  123. public void getBaseMeasure_returns_Measure_if_measure_of_last_snapshot_only_in_DB() {
  124. ComponentDto project = dbTester.components().insertPrivateProject();
  125. dbTester.components().insertComponent(newFileDto(project).setUuid(FILE_COMPONENT.getUuid()));
  126. SnapshotDto lastAnalysis = dbTester.components().insertSnapshot(project, t -> t.setLast(true));
  127. SnapshotDto oldAnalysis = dbTester.components().insertSnapshot(project, t -> t.setLast(false));
  128. MetricDto metric1 = dbTester.measures().insertMetric(t -> t.setValueType(org.sonar.api.measures.Metric.ValueType.STRING.name()));
  129. MetricDto metric2 = dbTester.measures().insertMetric(t -> t.setValueType(org.sonar.api.measures.Metric.ValueType.STRING.name()));
  130. dbClient.measureDao().insert(dbSession, createMeasureDto(metric1.getUuid(), FILE_COMPONENT.getUuid(), lastAnalysis.getUuid()));
  131. dbClient.measureDao().insert(dbSession, createMeasureDto(metric1.getUuid(), FILE_COMPONENT.getUuid(), oldAnalysis.getUuid()));
  132. dbSession.commit();
  133. // metric 1 is associated to snapshot with "last=true"
  134. assertThat(underTest.getBaseMeasure(FILE_COMPONENT, metricOf(metric1)).get().getStringValue())
  135. .isEqualTo(SOME_DATA);
  136. // metric 2 is associated to snapshot with "last=false" => not retrieved
  137. assertThat(underTest.getBaseMeasure(FILE_COMPONENT, metricOf(metric2))).isNotPresent();
  138. }
  139. private Metric metricOf(MetricDto metricDto) {
  140. Metric res = mock(Metric.class);
  141. when(res.getKey()).thenReturn(metricDto.getKey());
  142. when(res.getUuid()).thenReturn(metricDto.getUuid());
  143. when(res.getType()).thenReturn(Metric.MetricType.valueOf(metricDto.getValueType()));
  144. return res;
  145. }
  146. @Test
  147. public void add_throws_NPE_if_Component_argument_is_null() {
  148. assertThatThrownBy(() -> underTest.add(null, metric1, SOME_MEASURE))
  149. .isInstanceOf(NullPointerException.class);
  150. }
  151. @Test
  152. public void add_throws_NPE_if_Component_metric_is_null() {
  153. assertThatThrownBy(() -> underTest.add(FILE_COMPONENT, null, SOME_MEASURE))
  154. .isInstanceOf(NullPointerException.class);
  155. }
  156. @Test
  157. public void add_throws_NPE_if_Component_measure_is_null() {
  158. assertThatThrownBy(() -> underTest.add(FILE_COMPONENT, metric1, null))
  159. .isInstanceOf(NullPointerException.class);
  160. }
  161. @Test
  162. public void add_throws_UOE_if_measure_already_exists() {
  163. assertThatThrownBy(() -> {
  164. underTest.add(FILE_COMPONENT, metric1, SOME_MEASURE);
  165. underTest.add(FILE_COMPONENT, metric1, SOME_MEASURE);
  166. })
  167. .isInstanceOf(UnsupportedOperationException.class);
  168. }
  169. @Test
  170. public void update_throws_NPE_if_Component_argument_is_null() {
  171. }
  172. @Test
  173. public void update_throws_NPE_if_Component_metric_is_null() {
  174. assertThatThrownBy(() -> underTest.update(FILE_COMPONENT, null, SOME_MEASURE))
  175. .isInstanceOf(NullPointerException.class);
  176. }
  177. @Test
  178. public void update_throws_NPE_if_Component_measure_is_null() {
  179. assertThatThrownBy(() -> underTest.update(FILE_COMPONENT, metric1, null))
  180. .isInstanceOf(NullPointerException.class);
  181. }
  182. @Test
  183. public void update_throws_UOE_if_measure_does_not_exists() {
  184. assertThatThrownBy(() -> underTest.update(FILE_COMPONENT, metric1, SOME_MEASURE))
  185. .isInstanceOf(UnsupportedOperationException.class);
  186. }
  187. private static final List<Measure> MEASURES = ImmutableList.of(
  188. Measure.newMeasureBuilder().create(1),
  189. Measure.newMeasureBuilder().create(1L),
  190. Measure.newMeasureBuilder().create(1d, 1),
  191. Measure.newMeasureBuilder().create(true),
  192. Measure.newMeasureBuilder().create(false),
  193. Measure.newMeasureBuilder().create("sds"),
  194. Measure.newMeasureBuilder().create(Measure.Level.OK),
  195. Measure.newMeasureBuilder().createNoValue());
  196. @DataProvider
  197. public static Object[][] measures() {
  198. return from(MEASURES).transform(new Function<Measure, Object[]>() {
  199. @Nullable
  200. @Override
  201. public Object[] apply(Measure input) {
  202. return new Measure[] {input};
  203. }
  204. }).toArray(Object[].class);
  205. }
  206. @Test
  207. public void add_accepts_NO_VALUE_as_measure_arg() {
  208. for (Metric.MetricType metricType : Metric.MetricType.values()) {
  209. underTest.add(FILE_COMPONENT, new MetricImpl("1", "key" + metricType, "name" + metricType, metricType), Measure.newMeasureBuilder().createNoValue());
  210. }
  211. }
  212. @Test
  213. @UseDataProvider("measures")
  214. public void update_throws_IAE_if_valueType_of_Measure_is_not_the_same_as_the_Metric_valueType_unless_NO_VALUE(Measure measure) {
  215. for (Metric.MetricType metricType : Metric.MetricType.values()) {
  216. if (metricType.getValueType() == measure.getValueType() || measure.getValueType() == Measure.ValueType.NO_VALUE) {
  217. continue;
  218. }
  219. try {
  220. final MetricImpl metric = new MetricImpl("1", "key" + metricType, "name" + metricType, metricType);
  221. underTest.add(FILE_COMPONENT, metric, getSomeMeasureByValueType(metricType));
  222. underTest.update(FILE_COMPONENT, metric, measure);
  223. fail("An IllegalArgumentException should have been raised");
  224. } catch (IllegalArgumentException e) {
  225. assertThat(e).hasMessage(format(
  226. "Measure's ValueType (%s) is not consistent with the Metric's ValueType (%s)",
  227. measure.getValueType(), metricType.getValueType()));
  228. }
  229. }
  230. }
  231. @Test
  232. public void update_accepts_NO_VALUE_as_measure_arg() {
  233. for (Metric.MetricType metricType : Metric.MetricType.values()) {
  234. MetricImpl metric = new MetricImpl("1", "key" + metricType, "name" + metricType, metricType);
  235. underTest.add(FILE_COMPONENT, metric, getSomeMeasureByValueType(metricType));
  236. underTest.update(FILE_COMPONENT, metric, Measure.newMeasureBuilder().createNoValue());
  237. }
  238. }
  239. private Measure getSomeMeasureByValueType(final Metric.MetricType metricType) {
  240. return MEASURES.stream().filter(input -> input.getValueType() == metricType.getValueType()).findFirst().get();
  241. }
  242. @Test
  243. public void update_supports_updating_to_the_same_value() {
  244. underTest.add(FILE_COMPONENT, metric1, SOME_MEASURE);
  245. underTest.update(FILE_COMPONENT, metric1, SOME_MEASURE);
  246. }
  247. @Test
  248. public void update_updates_the_stored_value() {
  249. Measure newMeasure = Measure.updatedMeasureBuilder(SOME_MEASURE).create();
  250. underTest.add(FILE_COMPONENT, metric1, SOME_MEASURE);
  251. underTest.update(FILE_COMPONENT, metric1, newMeasure);
  252. assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric1).get()).isSameAs(newMeasure);
  253. }
  254. @Test
  255. public void getRawMeasure_throws_NPE_without_reading_batch_report_if_component_arg_is_null() {
  256. try {
  257. underTestWithMock.getRawMeasure(null, metric1);
  258. fail("an NPE should have been raised");
  259. } catch (NullPointerException e) {
  260. verifyNoMoreInteractions(mockBatchReportReader);
  261. }
  262. }
  263. @Test
  264. public void getRawMeasure_throws_NPE_without_reading_batch_report_if_metric_arg_is_null() {
  265. try {
  266. underTestWithMock.getRawMeasure(FILE_COMPONENT, null);
  267. fail("an NPE should have been raised");
  268. } catch (NullPointerException e) {
  269. verifyNoMoreInteractions(mockBatchReportReader);
  270. }
  271. }
  272. @Test
  273. public void getRawMeasure_returns_measure_added_through_add_method() {
  274. underTest.add(FILE_COMPONENT, metric1, SOME_MEASURE);
  275. Optional<Measure> res = underTest.getRawMeasure(FILE_COMPONENT, metric1);
  276. assertThat(res).isPresent();
  277. assertThat(res.get()).isSameAs(SOME_MEASURE);
  278. // make sure we really match on the specified component and metric
  279. assertThat(underTest.getRawMeasure(OTHER_COMPONENT, metric1)).isNotPresent();
  280. assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric2)).isNotPresent();
  281. }
  282. @Test
  283. public void getRawMeasure_returns_measure_from_batch_if_not_added_through_add_method() {
  284. String value = "trololo";
  285. when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
  286. reportReader.putMeasures(FILE_COMPONENT.getReportAttributes().getRef(), ImmutableList.of(
  287. ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue(StringValue.newBuilder().setValue(value)).build()));
  288. Optional<Measure> res = underTest.getRawMeasure(FILE_COMPONENT, metric1);
  289. assertThat(res).isPresent();
  290. assertThat(res.get().getStringValue()).isEqualTo(value);
  291. // make sure we really match on the specified component and metric
  292. assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric2)).isNotPresent();
  293. assertThat(underTest.getRawMeasure(OTHER_COMPONENT, metric1)).isNotPresent();
  294. }
  295. @Test
  296. public void getRawMeasure_returns_only_validate_measure_from_batch_if_not_added_through_add_method() {
  297. when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
  298. when(reportMetricValidator.validate(METRIC_KEY_2)).thenReturn(false);
  299. reportReader.putMeasures(FILE_COMPONENT.getReportAttributes().getRef(), ImmutableList.of(
  300. ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue(StringValue.newBuilder().setValue("value1")).build(),
  301. ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_2).setStringValue(StringValue.newBuilder().setValue("value2")).build()));
  302. assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric1)).isPresent();
  303. assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric2)).isNotPresent();
  304. }
  305. @Test
  306. public void getRawMeasure_retrieves_added_measure_over_batch_measure() {
  307. when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
  308. reportReader.putMeasures(FILE_COMPONENT.getReportAttributes().getRef(), ImmutableList.of(
  309. ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue(StringValue.newBuilder().setValue("some value")).build()));
  310. Measure addedMeasure = SOME_MEASURE;
  311. underTest.add(FILE_COMPONENT, metric1, addedMeasure);
  312. Optional<Measure> res = underTest.getRawMeasure(FILE_COMPONENT, metric1);
  313. assertThat(res).isPresent();
  314. assertThat(res.get()).isSameAs(addedMeasure);
  315. }
  316. @Test
  317. public void getRawMeasure_retrieves_measure_from_batch_and_caches_it_locally_so_that_it_can_be_updated() {
  318. when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
  319. reportReader.putMeasures(FILE_COMPONENT.getReportAttributes().getRef(), ImmutableList.of(
  320. ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue(StringValue.newBuilder().setValue("some value")).build()));
  321. Optional<Measure> measure = underTest.getRawMeasure(FILE_COMPONENT, metric1);
  322. underTest.update(FILE_COMPONENT, metric1, Measure.updatedMeasureBuilder(measure.get()).create());
  323. }
  324. @Test
  325. public void getRawMeasures_returns_added_measures_over_batch_measures() {
  326. when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
  327. when(reportMetricValidator.validate(METRIC_KEY_2)).thenReturn(true);
  328. ScannerReport.Measure batchMeasure1 = ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue(StringValue.newBuilder().setValue("some value")).build();
  329. ScannerReport.Measure batchMeasure2 = ScannerReport.Measure.newBuilder().setMetricKey(METRIC_KEY_2).setStringValue(StringValue.newBuilder().setValue("some value")).build();
  330. reportReader.putMeasures(FILE_COMPONENT.getReportAttributes().getRef(), ImmutableList.of(batchMeasure1, batchMeasure2));
  331. Measure addedMeasure = SOME_MEASURE;
  332. underTest.add(FILE_COMPONENT, metric1, addedMeasure);
  333. Map<String, Measure> rawMeasures = underTest.getRawMeasures(FILE_COMPONENT);
  334. assertThat(rawMeasures.keySet()).hasSize(2);
  335. assertThat(rawMeasures).containsEntry(METRIC_KEY_1, addedMeasure);
  336. assertThat(rawMeasures.get(METRIC_KEY_2)).extracting(Measure::getStringValue).isEqualTo("some value");
  337. }
  338. private static MeasureDto createMeasureDto(String metricUuid, String componentUuid, String analysisUuid) {
  339. return new MeasureDto()
  340. .setComponentUuid(componentUuid)
  341. .setAnalysisUuid(analysisUuid)
  342. .setData(SOME_DATA)
  343. .setMetricUuid(metricUuid);
  344. }
  345. }