]> source.dussan.org Git - sonarqube.git/blob
4790e657094af259d88748d4ec48c68c05a188a7
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2020 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.measure.ws;
21
22 import com.google.common.base.Joiner;
23 import java.util.List;
24 import java.util.stream.IntStream;
25 import org.junit.Rule;
26 import org.junit.Test;
27 import org.junit.rules.ExpectedException;
28 import org.sonar.api.measures.CoreMetrics;
29 import org.sonar.api.measures.Metric;
30 import org.sonar.api.server.ws.WebService.Param;
31 import org.sonar.api.utils.System2;
32 import org.sonar.api.web.UserRole;
33 import org.sonar.core.util.stream.MoreCollectors;
34 import org.sonar.db.DbClient;
35 import org.sonar.db.DbSession;
36 import org.sonar.db.DbTester;
37 import org.sonar.db.component.ComponentDto;
38 import org.sonar.db.component.ResourceTypesRule;
39 import org.sonar.db.component.SnapshotDto;
40 import org.sonar.db.measure.LiveMeasureDto;
41 import org.sonar.db.metric.MetricDto;
42 import org.sonar.db.metric.MetricTesting;
43 import org.sonar.db.organization.OrganizationDto;
44 import org.sonar.server.component.ComponentFinder;
45 import org.sonar.server.exceptions.BadRequestException;
46 import org.sonar.server.exceptions.ForbiddenException;
47 import org.sonar.server.exceptions.NotFoundException;
48 import org.sonar.server.l18n.I18nRule;
49 import org.sonar.server.tester.UserSessionRule;
50 import org.sonar.server.ws.WsActionTester;
51 import org.sonarqube.ws.Common;
52 import org.sonarqube.ws.Measures.ComponentTreeWsResponse;
53 import org.sonarqube.ws.Measures.PeriodValue;
54
55 import static java.lang.Double.parseDouble;
56 import static java.lang.String.format;
57 import static java.util.Collections.singletonList;
58 import static org.assertj.core.api.Assertions.assertThat;
59 import static org.assertj.core.api.Assertions.tuple;
60 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING_KEY;
61 import static org.sonar.api.measures.Metric.ValueType.DISTRIB;
62 import static org.sonar.api.measures.Metric.ValueType.FLOAT;
63 import static org.sonar.api.measures.Metric.ValueType.INT;
64 import static org.sonar.api.measures.Metric.ValueType.RATING;
65 import static org.sonar.api.resources.Qualifiers.APP;
66 import static org.sonar.api.resources.Qualifiers.DIRECTORY;
67 import static org.sonar.api.resources.Qualifiers.FILE;
68 import static org.sonar.api.resources.Qualifiers.PROJECT;
69 import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE;
70 import static org.sonar.api.server.ws.WebService.Param.SORT;
71 import static org.sonar.api.utils.DateUtils.parseDateTime;
72 import static org.sonar.api.web.UserRole.USER;
73 import static org.sonar.db.component.BranchType.PULL_REQUEST;
74 import static org.sonar.db.component.ComponentTesting.newDirectory;
75 import static org.sonar.db.component.ComponentTesting.newFileDto;
76 import static org.sonar.db.component.ComponentTesting.newProjectCopy;
77 import static org.sonar.db.component.SnapshotTesting.newAnalysis;
78 import static org.sonar.server.component.ws.MeasuresWsParameters.ADDITIONAL_PERIOD;
79 import static org.sonar.server.component.ws.MeasuresWsParameters.DEPRECATED_ADDITIONAL_PERIODS;
80 import static org.sonar.server.component.ws.MeasuresWsParameters.DEPRECATED_PARAM_BASE_COMPONENT_ID;
81 import static org.sonar.server.component.ws.MeasuresWsParameters.DEPRECATED_PARAM_BASE_COMPONENT_KEY;
82 import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_ADDITIONAL_FIELDS;
83 import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_BRANCH;
84 import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_COMPONENT;
85 import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_KEYS;
86 import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_PERIOD_SORT;
87 import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_SORT;
88 import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_SORT_FILTER;
89 import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_PULL_REQUEST;
90 import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_QUALIFIERS;
91 import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_STRATEGY;
92 import static org.sonar.server.measure.ws.ComponentTreeAction.LEAVES_STRATEGY;
93 import static org.sonar.server.measure.ws.ComponentTreeAction.METRIC_PERIOD_SORT;
94 import static org.sonar.server.measure.ws.ComponentTreeAction.METRIC_SORT;
95 import static org.sonar.server.measure.ws.ComponentTreeAction.NAME_SORT;
96 import static org.sonar.server.measure.ws.ComponentTreeAction.WITH_MEASURES_ONLY_METRIC_SORT_FILTER;
97 import static org.sonar.test.JsonAssert.assertJson;
98 import static org.sonarqube.ws.Measures.Component;
99 import static org.sonarqube.ws.Measures.Measure;
100
101 public class ComponentTreeActionTest {
102   @Rule
103   public UserSessionRule userSession = UserSessionRule.standalone().logIn().setRoot();
104   @Rule
105   public ExpectedException expectedException = ExpectedException.none();
106   @Rule
107   public DbTester db = DbTester.create(System2.INSTANCE);
108
109   private I18nRule i18n = new I18nRule();
110   private ResourceTypesRule resourceTypes = new ResourceTypesRule()
111     .setRootQualifiers(PROJECT)
112     .setLeavesQualifiers(FILE, UNIT_TEST_FILE);
113   private DbClient dbClient = db.getDbClient();
114   private DbSession dbSession = db.getSession();
115
116   private WsActionTester ws = new WsActionTester(
117     new ComponentTreeAction(
118       dbClient, new ComponentFinder(dbClient, resourceTypes), userSession,
119       i18n, resourceTypes));
120
121   @Test
122   public void json_example() {
123     ComponentDto project = db.components().insertPrivateProject(p -> p.setDbKey("MY_PROJECT")
124       .setName("My Project"));
125     SnapshotDto analysis = db.components().insertSnapshot(project, s -> s.setPeriodDate(parseDateTime("2016-01-11T10:49:50+0100").getTime())
126       .setPeriodMode("previous_version")
127       .setPeriodParam("1.0-SNAPSHOT"));
128     ComponentDto file1 = db.components().insertComponent(newFileDto(project, null)
129       .setUuid("AVIwDXE-bJbJqrw6wFv5")
130       .setDbKey("com.sonarsource:java-markdown:src/main/java/com/sonarsource/markdown/impl/ElementImpl.java")
131       .setName("ElementImpl.java")
132       .setLanguage("java")
133       .setQualifier(FILE)
134       .setPath("src/main/java/com/sonarsource/markdown/impl/ElementImpl.java"));
135     ComponentDto file2 = db.components().insertComponent(newFileDto(project, null)
136       .setUuid("AVIwDXE_bJbJqrw6wFwJ")
137       .setDbKey("com.sonarsource:java-markdown:src/test/java/com/sonarsource/markdown/impl/ElementImplTest.java")
138       .setName("ElementImplTest.java")
139       .setLanguage("java")
140       .setQualifier(UNIT_TEST_FILE)
141       .setPath("src/test/java/com/sonarsource/markdown/impl/ElementImplTest.java"));
142     ComponentDto dir = db.components().insertComponent(newDirectory(project, "src/main/java/com/sonarsource/markdown/impl")
143       .setUuid("AVIwDXE-bJbJqrw6wFv8")
144       .setDbKey("com.sonarsource:java-markdown:src/main/java/com/sonarsource/markdown/impl")
145       .setQualifier(DIRECTORY));
146
147     MetricDto complexity = insertComplexityMetric();
148     db.measures().insertLiveMeasure(file1, complexity, m -> m.setValue(12.0d));
149     db.measures().insertLiveMeasure(dir, complexity, m -> m.setValue(35.0d).setVariation(0.0d));
150     db.measures().insertLiveMeasure(project, complexity, m -> m.setValue(42.0d));
151
152     MetricDto ncloc = insertNclocMetric();
153     db.measures().insertLiveMeasure(file1, ncloc, m -> m.setValue(114.0d));
154     db.measures().insertLiveMeasure(dir, ncloc, m -> m.setValue(217.0d).setVariation(0.0d));
155     db.measures().insertLiveMeasure(project, ncloc, m -> m.setValue(1984.0d));
156
157     MetricDto newViolations = insertNewViolationsMetric();
158     db.measures().insertLiveMeasure(file1, newViolations, m -> m.setVariation(25.0d));
159     db.measures().insertLiveMeasure(dir, newViolations, m -> m.setVariation(25.0d));
160     db.measures().insertLiveMeasure(project, newViolations, m -> m.setVariation(255.0d));
161
162     db.commit();
163
164     String response = ws.newRequest()
165       .setParam(PARAM_COMPONENT, project.getKey())
166       .setParam(PARAM_METRIC_KEYS, "ncloc, complexity, new_violations")
167       .setParam(PARAM_ADDITIONAL_FIELDS, "metrics,period,periods")
168       .execute()
169       .getInput();
170
171     assertJson(response).isSimilarTo(getClass().getResource("component_tree-example.json"));
172   }
173
174   @Test
175   public void empty_response() {
176     ComponentDto project = db.components().insertPrivateProject();
177
178     ComponentTreeWsResponse response = ws.newRequest()
179       .setParam(PARAM_COMPONENT, project.getKey())
180       .setParam(PARAM_METRIC_KEYS, "ncloc, complexity")
181       .executeProtobuf(ComponentTreeWsResponse.class);
182
183     assertThat(response.getBaseComponent().getKey()).isEqualTo(project.getKey());
184     assertThat(response.getComponentsList()).isEmpty();
185     assertThat(response.getMetrics().getMetricsList()).isEmpty();
186     assertThat(response.hasPeriod()).isFalse();
187     assertThat(response.getPeriods().getPeriodsList()).isEmpty();
188   }
189
190   @Test
191   public void load_measures_and_periods() {
192     ComponentDto project = db.components().insertPrivateProject();
193     SnapshotDto projectSnapshot = dbClient.snapshotDao().insert(dbSession,
194       newAnalysis(project)
195         .setPeriodDate(System.currentTimeMillis())
196         .setPeriodMode("last_version")
197         .setPeriodDate(System.currentTimeMillis()));
198     userSession.anonymous().addProjectPermission(UserRole.USER, project);
199     ComponentDto directory = newDirectory(project, "directory-uuid", "path/to/directory").setName("directory-1");
200     db.components().insertComponent(directory);
201     ComponentDto file = newFileDto(directory, null, "file-uuid").setName("file-1");
202     db.components().insertComponent(file);
203     MetricDto ncloc = insertNclocMetric();
204     MetricDto coverage = insertCoverageMetric();
205     db.commit();
206     db.measures().insertLiveMeasure(file, ncloc, m -> m.setValue(5.0d).setVariation(4.0d));
207     db.measures().insertLiveMeasure(file, coverage, m -> m.setValue(15.5d));
208     db.measures().insertLiveMeasure(directory, coverage, m -> m.setValue(15.5d));
209
210     ComponentTreeWsResponse response = ws.newRequest()
211       .setParam(PARAM_COMPONENT, project.getKey())
212       .setParam(PARAM_METRIC_KEYS, "ncloc,coverage")
213       .setParam(PARAM_ADDITIONAL_FIELDS, DEPRECATED_ADDITIONAL_PERIODS + "," + ADDITIONAL_PERIOD)
214       .executeProtobuf(ComponentTreeWsResponse.class);
215
216     assertThat(response.getComponentsList().get(0).getMeasuresList()).extracting("metric").containsOnly("coverage");
217     // file measures
218     List<Measure> fileMeasures = response.getComponentsList().get(1).getMeasuresList();
219     assertThat(fileMeasures).extracting("metric").containsOnly("ncloc", "coverage");
220     assertThat(fileMeasures).extracting("value").containsOnly("5", "15.5");
221     assertThat(response.getPeriod().getMode()).isEqualTo("last_version");
222     assertThat(response.getPeriods().getPeriodsList()).extracting("mode").containsOnly("last_version");
223   }
224
225   @Test
226   public void load_measures_with_best_value() {
227     ComponentDto project = db.components().insertPrivateProject();
228     SnapshotDto projectSnapshot = db.components().insertSnapshot(project);
229     userSession.anonymous().addProjectPermission(UserRole.USER, project);
230     ComponentDto directory = newDirectory(project, "directory-uuid", "path/to/directory").setName("directory-1");
231     db.components().insertComponent(directory);
232     ComponentDto file = newFileDto(directory, null, "file-uuid").setName("file-1");
233     db.components().insertComponent(file);
234     MetricDto coverage = insertCoverageMetric();
235     dbClient.metricDao().insert(dbSession, MetricTesting.newMetricDto()
236       .setKey("ncloc")
237       .setValueType(INT.name())
238       .setOptimizedBestValue(true)
239       .setBestValue(100d)
240       .setWorstValue(1000d));
241     dbClient.metricDao().insert(dbSession, newMetricDto()
242       .setKey("new_violations")
243       .setOptimizedBestValue(true)
244       .setBestValue(1984.0d)
245       .setValueType(INT.name()));
246     db.commit();
247     db.measures().insertLiveMeasure(file, coverage, m -> m.setValue(15.5d));
248     db.measures().insertLiveMeasure(directory, coverage, m -> m.setValue(42.0d));
249
250     ComponentTreeWsResponse response = ws.newRequest()
251       .setParam(PARAM_COMPONENT, project.getKey())
252       .setParam(PARAM_METRIC_KEYS, "ncloc,coverage,new_violations")
253       .setParam(PARAM_ADDITIONAL_FIELDS, "metrics")
254       .executeProtobuf(ComponentTreeWsResponse.class);
255
256     // directory measures
257     assertThat(response.getComponentsList().get(0).getMeasuresList()).extracting("metric").containsOnly("coverage");
258     // file measures
259     List<Measure> fileMeasures = response.getComponentsList().get(1).getMeasuresList();
260     assertThat(fileMeasures)
261       .extracting(Measure::getMetric, Measure::getValue, Measure::getBestValue, Measure::hasBestValue)
262       .containsExactlyInAnyOrder(tuple("ncloc", "100", true, true),
263         tuple("coverage", "15.5", false, false),
264         tuple("new_violations", "", false, false));
265
266     List<Common.Metric> metrics = response.getMetrics().getMetricsList();
267     assertThat(metrics).extracting("bestValue").contains("100", "");
268     assertThat(metrics).extracting("worstValue").contains("1000");
269   }
270
271   @Test
272   public void return_is_best_value_on_leak_measures() {
273     ComponentDto project = db.components().insertPrivateProject();
274     db.components().insertSnapshot(project);
275     userSession.anonymous().addProjectPermission(UserRole.USER, project);
276     ComponentDto file = newFileDto(project, null);
277     db.components().insertComponent(file);
278
279     MetricDto matchingBestValue = db.measures().insertMetric(m -> m
280       .setKey("new_lines")
281       .setValueType(INT.name())
282       .setBestValue(100d));
283     MetricDto doesNotMatchBestValue = db.measures().insertMetric(m -> m
284       .setKey("new_lines_2")
285       .setValueType(INT.name())
286       .setBestValue(100d));
287     MetricDto noBestValue = db.measures().insertMetric(m -> m
288       .setKey("new_violations")
289       .setValueType(INT.name())
290       .setBestValue(null));
291     db.measures().insertLiveMeasure(file, matchingBestValue, m -> m.setValue(null).setData((String) null).setVariation(100d));
292     db.measures().insertLiveMeasure(file, doesNotMatchBestValue, m -> m.setValue(null).setData((String) null).setVariation(10d));
293     db.measures().insertLiveMeasure(file, noBestValue, m -> m.setValue(null).setData((String) null).setVariation(42.0d));
294
295     ComponentTreeWsResponse response = ws.newRequest()
296       .setParam(PARAM_COMPONENT, project.getKey())
297       .setParam(PARAM_METRIC_KEYS, "new_lines,new_lines_2,new_violations")
298       .executeProtobuf(ComponentTreeWsResponse.class);
299
300     // file measures
301
302     // verify backward compatibility
303     List<Measure> fileMeasures = response.getComponentsList().get(0).getMeasuresList();
304     assertThat(fileMeasures)
305       .extracting(Measure::getMetric, m -> m.getPeriods().getPeriodsValueList())
306       .containsExactlyInAnyOrder(
307         tuple(matchingBestValue.getKey(), singletonList(PeriodValue.newBuilder().setIndex(1).setValue("100").setBestValue(true).build())),
308         tuple(doesNotMatchBestValue.getKey(), singletonList(PeriodValue.newBuilder().setIndex(1).setValue("10").setBestValue(false).build())),
309         tuple(noBestValue.getKey(), singletonList(PeriodValue.newBuilder().setIndex(1).setValue("42").build())));
310
311     assertThat(fileMeasures)
312       .extracting(Measure::getMetric, Measure::getPeriod)
313       .containsExactlyInAnyOrder(
314         tuple(matchingBestValue.getKey(), PeriodValue.newBuilder().setIndex(1).setValue("100").setBestValue(true).build()),
315         tuple(doesNotMatchBestValue.getKey(), PeriodValue.newBuilder().setIndex(1).setValue("10").setBestValue(false).build()),
316         tuple(noBestValue.getKey(), PeriodValue.newBuilder().setIndex(1).setValue("42").build()));
317   }
318
319   @Test
320   public void use_best_value_for_rating() {
321     ComponentDto project = db.components().insertPrivateProject();
322     userSession.anonymous().addProjectPermission(UserRole.USER, project);
323     SnapshotDto projectSnapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(project)
324       .setPeriodDate(parseDateTime("2016-01-11T10:49:50+0100").getTime())
325       .setPeriodMode("previous_version")
326       .setPeriodParam("1.0-SNAPSHOT"));
327     ComponentDto directory = newDirectory(project, "directory-uuid", "path/to/directory").setName("directory-1");
328     db.components().insertComponent(directory);
329     ComponentDto file = newFileDto(directory, null, "file-uuid").setName("file-1");
330     db.components().insertComponent(file);
331     MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDto()
332       .setKey(NEW_SECURITY_RATING_KEY)
333       .setOptimizedBestValue(true)
334       .setBestValue(1d)
335       .setValueType(RATING.name()));
336     db.commit();
337     db.measures().insertLiveMeasure(directory, metric, m -> m.setVariation(2d));
338
339     ComponentTreeWsResponse response = ws.newRequest()
340       .setParam(PARAM_COMPONENT, project.getKey())
341       .setParam(PARAM_METRIC_KEYS, NEW_SECURITY_RATING_KEY)
342       .setParam(PARAM_ADDITIONAL_FIELDS, "metrics")
343       .executeProtobuf(ComponentTreeWsResponse.class);
344
345     // directory
346     assertThat(response.getComponentsList().get(0).getMeasuresList().get(0).getPeriods().getPeriodsValue(0).getValue()).isEqualTo("2.0");
347     assertThat(response.getComponentsList().get(0).getMeasuresList().get(0).getPeriod().getValue()).isEqualTo("2.0");
348     // file measures
349     assertThat(response.getComponentsList().get(1).getMeasuresList().get(0).getPeriods().getPeriodsValue(0).getValue()).isEqualTo("1.0");
350     assertThat(response.getComponentsList().get(1).getMeasuresList().get(0).getPeriod().getValue()).isEqualTo("1.0");
351   }
352
353   @Test
354   public void load_measures_multi_sort_with_metric_key_and_paginated() {
355     ComponentDto project = db.components().insertPrivateProject();
356     SnapshotDto projectSnapshot = db.components().insertSnapshot(project);
357     ComponentDto file9 = db.components().insertComponent(newFileDto(project, null, "file-uuid-9").setName("file-1"));
358     ComponentDto file8 = db.components().insertComponent(newFileDto(project, null, "file-uuid-8").setName("file-1"));
359     ComponentDto file7 = db.components().insertComponent(newFileDto(project, null, "file-uuid-7").setName("file-1"));
360     ComponentDto file6 = db.components().insertComponent(newFileDto(project, null, "file-uuid-6").setName("file-1"));
361     ComponentDto file5 = db.components().insertComponent(newFileDto(project, null, "file-uuid-5").setName("file-1"));
362     ComponentDto file4 = db.components().insertComponent(newFileDto(project, null, "file-uuid-4").setName("file-1"));
363     ComponentDto file3 = db.components().insertComponent(newFileDto(project, null, "file-uuid-3").setName("file-1"));
364     ComponentDto file2 = db.components().insertComponent(newFileDto(project, null, "file-uuid-2").setName("file-1"));
365     ComponentDto file1 = db.components().insertComponent(newFileDto(project, null, "file-uuid-1").setName("file-1"));
366     MetricDto coverage = insertCoverageMetric();
367     db.commit();
368     db.measures().insertLiveMeasure(file1, coverage, m -> m.setValue(1.0d));
369     db.measures().insertLiveMeasure(file2, coverage, m -> m.setValue(2.0d));
370     db.measures().insertLiveMeasure(file3, coverage, m -> m.setValue(3.0d));
371     db.measures().insertLiveMeasure(file4, coverage, m -> m.setValue(4.0d));
372     db.measures().insertLiveMeasure(file5, coverage, m -> m.setValue(5.0d));
373     db.measures().insertLiveMeasure(file6, coverage, m -> m.setValue(6.0d));
374     db.measures().insertLiveMeasure(file7, coverage, m -> m.setValue(7.0d));
375     db.measures().insertLiveMeasure(file8, coverage, m -> m.setValue(8.0d));
376     db.measures().insertLiveMeasure(file9, coverage, m -> m.setValue(9.0d));
377
378     ComponentTreeWsResponse response = ws.newRequest()
379       .setParam(PARAM_COMPONENT, project.getKey())
380       .setParam(SORT, NAME_SORT + ", " + METRIC_SORT)
381       .setParam(PARAM_METRIC_SORT, "coverage")
382       .setParam(PARAM_METRIC_KEYS, "coverage")
383       .setParam(PARAM_STRATEGY, "leaves")
384       .setParam(PARAM_QUALIFIERS, "FIL,UTS")
385       .setParam(Param.PAGE, "2")
386       .setParam(Param.PAGE_SIZE, "3")
387       .executeProtobuf(ComponentTreeWsResponse.class);
388
389     assertThat(response.getComponentsList()).extracting("id").containsExactly("file-uuid-4", "file-uuid-5", "file-uuid-6");
390     assertThat(response.getPaging().getPageIndex()).isEqualTo(2);
391     assertThat(response.getPaging().getPageSize()).isEqualTo(3);
392     assertThat(response.getPaging().getTotal()).isEqualTo(9);
393   }
394
395   @Test
396   public void sort_by_metric_value() {
397     ComponentDto project = db.components().insertPrivateProject();
398     SnapshotDto projectSnapshot = db.components().insertSnapshot(project);
399     ComponentDto file4 = db.components().insertComponent(newFileDto(project, null, "file-uuid-4"));
400     ComponentDto file3 = db.components().insertComponent(newFileDto(project, null, "file-uuid-3"));
401     ComponentDto file1 = db.components().insertComponent(newFileDto(project, null, "file-uuid-1"));
402     ComponentDto file2 = db.components().insertComponent(newFileDto(project, null, "file-uuid-2"));
403     MetricDto ncloc = newMetricDto().setKey("ncloc").setValueType(INT.name()).setDirection(1);
404     dbClient.metricDao().insert(dbSession, ncloc);
405     db.commit();
406     db.measures().insertLiveMeasure(file1, ncloc, m -> m.setValue(1.0d));
407     db.measures().insertLiveMeasure(file2, ncloc, m -> m.setValue(2.0d));
408     db.measures().insertLiveMeasure(file3, ncloc, m -> m.setValue(3.0d));
409
410     ComponentTreeWsResponse response = ws.newRequest()
411       .setParam(PARAM_COMPONENT, project.getKey())
412       .setParam(SORT, METRIC_SORT)
413       .setParam(PARAM_METRIC_SORT, "ncloc")
414       .setParam(PARAM_METRIC_KEYS, "ncloc")
415       .executeProtobuf(ComponentTreeWsResponse.class);
416
417     assertThat(response.getComponentsList()).extracting("id").containsExactly("file-uuid-1", "file-uuid-2", "file-uuid-3", "file-uuid-4");
418     assertThat(response.getPaging().getTotal()).isEqualTo(4);
419   }
420
421   @Test
422   public void remove_components_without_measure_on_the_metric_sort() {
423     ComponentDto project = db.components().insertPrivateProject();
424     SnapshotDto projectSnapshot = db.components().insertSnapshot(project);
425     ComponentDto file1 = newFileDto(project, null, "file-uuid-1");
426     ComponentDto file2 = newFileDto(project, null, "file-uuid-2");
427     ComponentDto file3 = newFileDto(project, null, "file-uuid-3");
428     ComponentDto file4 = newFileDto(project, null, "file-uuid-4");
429     db.components().insertComponent(file1);
430     db.components().insertComponent(file2);
431     db.components().insertComponent(file3);
432     db.components().insertComponent(file4);
433     MetricDto ncloc = newMetricDto().setKey("ncloc").setValueType(INT.name()).setDirection(1);
434     dbClient.metricDao().insert(dbSession, ncloc);
435     db.measures().insertLiveMeasure(file1, ncloc, m -> m.setData((String) null).setValue(1.0d).setVariation(null));
436     db.measures().insertLiveMeasure(file2, ncloc, m -> m.setData((String) null).setValue(2.0d).setVariation(null));
437     db.measures().insertLiveMeasure(file3, ncloc, m -> m.setData((String) null).setValue(3.0d).setVariation(null));
438     // measure on period 1
439     db.measures().insertLiveMeasure(file4, ncloc, m -> m.setData((String) null).setValue(null).setVariation(4.0d));
440     db.commit();
441
442     ComponentTreeWsResponse response = ws.newRequest()
443       .setParam(PARAM_COMPONENT, project.getKey())
444       .setParam(SORT, METRIC_SORT)
445       .setParam(PARAM_METRIC_SORT, "ncloc")
446       .setParam(PARAM_METRIC_KEYS, "ncloc")
447       .setParam(PARAM_METRIC_SORT_FILTER, WITH_MEASURES_ONLY_METRIC_SORT_FILTER)
448       .executeProtobuf(ComponentTreeWsResponse.class);
449
450     assertThat(response.getComponentsList()).extracting("id")
451       .containsExactly(file1.uuid(), file2.uuid(), file3.uuid())
452       .doesNotContain(file4.uuid());
453     assertThat(response.getPaging().getTotal()).isEqualTo(3);
454   }
455
456   @Test
457   public void sort_by_metric_period() {
458     ComponentDto project = db.components().insertPrivateProject();
459     SnapshotDto projectSnapshot = db.components().insertSnapshot(project);
460     ComponentDto file3 = db.components().insertComponent(newFileDto(project, null, "file-uuid-3"));
461     ComponentDto file1 = db.components().insertComponent(newFileDto(project, null, "file-uuid-1"));
462     ComponentDto file2 = db.components().insertComponent(newFileDto(project, null, "file-uuid-2"));
463     MetricDto ncloc = newMetricDto().setKey("ncloc").setValueType(INT.name()).setDirection(1);
464     dbClient.metricDao().insert(dbSession, ncloc);
465     db.commit();
466     db.measures().insertLiveMeasure(file1, ncloc, m -> m.setVariation(1.0d));
467     db.measures().insertLiveMeasure(file2, ncloc, m -> m.setVariation(2.0d));
468     db.measures().insertLiveMeasure(file3, ncloc, m -> m.setVariation(3.0d));
469
470     ComponentTreeWsResponse response = ws.newRequest()
471       .setParam(PARAM_COMPONENT, project.getKey())
472       .setParam(SORT, METRIC_PERIOD_SORT)
473       .setParam(PARAM_METRIC_SORT, "ncloc")
474       .setParam(PARAM_METRIC_KEYS, "ncloc")
475       .setParam(PARAM_METRIC_PERIOD_SORT, "1")
476       .executeProtobuf(ComponentTreeWsResponse.class);
477
478     assertThat(response.getComponentsList()).extracting("id").containsExactly("file-uuid-1", "file-uuid-2", "file-uuid-3");
479   }
480
481   @Test
482   public void remove_components_without_measure_on_the_metric_period_sort() {
483     ComponentDto project = db.components().insertPrivateProject();
484     SnapshotDto projectSnapshot = db.components().insertSnapshot(project);
485     ComponentDto file4 = db.components().insertComponent(newFileDto(project, null, "file-uuid-4"));
486     ComponentDto file3 = db.components().insertComponent(newFileDto(project, null, "file-uuid-3"));
487     ComponentDto file2 = db.components().insertComponent(newFileDto(project, null, "file-uuid-2"));
488     ComponentDto file1 = db.components().insertComponent(newFileDto(project, null, "file-uuid-1"));
489     MetricDto ncloc = newMetricDto().setKey("new_ncloc").setValueType(INT.name()).setDirection(1);
490     dbClient.metricDao().insert(dbSession, ncloc);
491     db.measures().insertLiveMeasure(file1, ncloc, m -> m.setData((String) null).setValue(null).setVariation(1.0d));
492     db.measures().insertLiveMeasure(file2, ncloc, m -> m.setData((String) null).setValue(null).setVariation(2.0d));
493     db.measures().insertLiveMeasure(file3, ncloc, m -> m.setData((String) null).setValue(null).setVariation(3.0d));
494     // file 4 measure is on absolute value
495     db.measures().insertLiveMeasure(file4, ncloc, m -> m.setData((String) null).setValue(4.0d).setVariation(null));
496     db.commit();
497
498     ComponentTreeWsResponse response = ws.newRequest()
499       .setParam(PARAM_COMPONENT, project.getKey())
500       .setParam(SORT, METRIC_PERIOD_SORT + "," + NAME_SORT)
501       .setParam(PARAM_METRIC_SORT, "new_ncloc")
502       .setParam(PARAM_METRIC_KEYS, "new_ncloc")
503       .setParam(PARAM_METRIC_PERIOD_SORT, "1")
504       .setParam(PARAM_METRIC_SORT_FILTER, WITH_MEASURES_ONLY_METRIC_SORT_FILTER)
505       .executeProtobuf(ComponentTreeWsResponse.class);
506
507     assertThat(response.getComponentsList()).extracting("id")
508       .containsExactly("file-uuid-1", "file-uuid-2", "file-uuid-3")
509       .doesNotContain("file-uuid-4");
510   }
511
512   @Test
513   public void load_measures_when_no_leave_qualifier() {
514     resourceTypes.setLeavesQualifiers();
515     ComponentDto project = db.components().insertPrivateProject();
516     db.components().insertSnapshot(project);
517     db.components().insertComponent(newFileDto(project, null));
518     insertNclocMetric();
519
520     ComponentTreeWsResponse result = ws.newRequest()
521       .setParam(PARAM_COMPONENT, project.getKey())
522       .setParam(PARAM_STRATEGY, LEAVES_STRATEGY)
523       .setParam(PARAM_METRIC_KEYS, "ncloc")
524       .executeProtobuf(ComponentTreeWsResponse.class);
525
526     assertThat(result.getBaseComponent().getKey()).isEqualTo(project.getKey());
527     assertThat(result.getComponentsCount()).isZero();
528   }
529
530   @Test
531   public void branch() {
532     OrganizationDto organization = db.organizations().insert();
533     ComponentDto project = db.components().insertPrivateProject(organization);
534     ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
535     SnapshotDto analysis = db.components().insertSnapshot(branch);
536     ComponentDto file = db.components().insertComponent(newFileDto(branch));
537     MetricDto complexity = db.measures().insertMetric(m -> m.setValueType(INT.name()));
538     LiveMeasureDto measure = db.measures().insertLiveMeasure(file, complexity, m -> m.setValue(12.0d));
539
540     ComponentTreeWsResponse response = ws.newRequest()
541       .setParam(PARAM_COMPONENT, file.getKey())
542       .setParam(PARAM_BRANCH, file.getBranch())
543       .setParam(PARAM_METRIC_KEYS, complexity.getKey())
544       .executeProtobuf(ComponentTreeWsResponse.class);
545
546     assertThat(response.getBaseComponent()).extracting(Component::getKey, Component::getBranch)
547       .containsExactlyInAnyOrder(file.getKey(), file.getBranch());
548     assertThat(response.getBaseComponent().getMeasuresList())
549       .extracting(Measure::getMetric, m -> parseDouble(m.getValue()))
550       .containsExactlyInAnyOrder(tuple(complexity.getKey(), measure.getValue()));
551   }
552
553   @Test
554   public void pull_request() {
555     OrganizationDto organization = db.organizations().insert();
556     ComponentDto project = db.components().insertPrivateProject(organization);
557     ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("pr-123").setBranchType(PULL_REQUEST));
558     SnapshotDto analysis = db.components().insertSnapshot(branch);
559     ComponentDto file = db.components().insertComponent(newFileDto(branch));
560     MetricDto complexity = db.measures().insertMetric(m -> m.setValueType(INT.name()));
561     LiveMeasureDto measure = db.measures().insertLiveMeasure(file, complexity, m -> m.setValue(12.0d));
562
563     ComponentTreeWsResponse response = ws.newRequest()
564       .setParam(PARAM_COMPONENT, file.getKey())
565       .setParam(PARAM_PULL_REQUEST, "pr-123")
566       .setParam(PARAM_METRIC_KEYS, complexity.getKey())
567       .executeProtobuf(ComponentTreeWsResponse.class);
568
569     assertThat(response.getBaseComponent()).extracting(Component::getKey, Component::getPullRequest)
570       .containsExactlyInAnyOrder(file.getKey(), "pr-123");
571     assertThat(response.getBaseComponent().getMeasuresList())
572       .extracting(Measure::getMetric, m -> parseDouble(m.getValue()))
573       .containsExactlyInAnyOrder(tuple(complexity.getKey(), measure.getValue()));
574   }
575
576   @Test
577   public void fix_pull_request_new_issue_count_metrics() {
578     OrganizationDto organization = db.organizations().insert();
579     ComponentDto project = db.components().insertPrivateProject(organization);
580     ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("pr-123").setBranchType(PULL_REQUEST));
581     SnapshotDto analysis = db.components().insertSnapshot(branch);
582     ComponentDto file = db.components().insertComponent(newFileDto(branch));
583     MetricDto bug = db.measures().insertMetric(m -> m.setValueType(INT.name()).setKey(CoreMetrics.BUGS_KEY));
584     MetricDto newBug = db.measures().insertMetric(m -> m.setValueType(INT.name()).setKey(CoreMetrics.NEW_BUGS_KEY));
585
586     LiveMeasureDto measure = db.measures().insertLiveMeasure(file, bug, m -> m.setValue(12.0d));
587
588     ComponentTreeWsResponse response = ws.newRequest()
589       .setParam(PARAM_COMPONENT, file.getKey())
590       .setParam(PARAM_PULL_REQUEST, "pr-123")
591       .setParam(PARAM_METRIC_KEYS, newBug.getKey())
592       .executeProtobuf(ComponentTreeWsResponse.class);
593
594     assertThat(response.getBaseComponent()).extracting(Component::getKey, Component::getPullRequest)
595       .containsExactlyInAnyOrder(file.getKey(), "pr-123");
596     assertThat(response.getBaseComponent().getMeasuresList())
597       .extracting(Measure::getMetric, m -> parseDouble(m.getPeriods().getPeriodsValue(0).getValue()), Measure::getValue)
598       .containsExactlyInAnyOrder(tuple(newBug.getKey(), measure.getValue(), ""));
599
600     assertThat(response.getBaseComponent().getMeasuresList())
601       .extracting(Measure::getMetric, m -> parseDouble(m.getPeriod().getValue()), Measure::getValue)
602       .containsExactlyInAnyOrder(tuple(newBug.getKey(), measure.getValue(), ""));
603   }
604
605   @Test
606   public void new_issue_count_measures_are_not_transformed_if_they_dont_exist_in_pr() {
607     OrganizationDto organization = db.organizations().insert();
608     ComponentDto project = db.components().insertPrivateProject(organization);
609     ComponentDto pr = db.components().insertProjectBranch(project, b -> b.setKey("pr").setBranchType(PULL_REQUEST));
610     SnapshotDto analysis = db.components().insertSnapshot(pr);
611     ComponentDto file = db.components().insertComponent(newFileDto(pr));
612     MetricDto bug = db.measures().insertMetric(m -> m.setValueType(INT.name()).setKey(CoreMetrics.BUGS_KEY));
613     MetricDto newBug = db.measures().insertMetric(m -> m.setValueType(INT.name()).setKey(CoreMetrics.NEW_BUGS_KEY));
614
615     ComponentTreeWsResponse response = ws.newRequest()
616       .setParam(PARAM_COMPONENT, file.getKey())
617       .setParam(PARAM_PULL_REQUEST, "pr")
618       .setParam(PARAM_METRIC_KEYS, newBug.getKey() + "," + bug.getKey())
619       .executeProtobuf(ComponentTreeWsResponse.class);
620
621     assertThat(response.getBaseComponent()).extracting(Component::getKey, Component::getPullRequest)
622       .containsExactlyInAnyOrder(file.getKey(), "pr");
623     assertThat(response.getBaseComponent().getMeasuresList())
624       .isEmpty();
625   }
626
627   @Test
628   public void return_deprecated_id_in_the_response() {
629     ComponentDto project = db.components().insertPrivateProject();
630     SnapshotDto analysis = db.components().insertSnapshot(project);
631     ComponentDto file = db.components().insertComponent(newFileDto(project));
632     MetricDto ncloc = insertNclocMetric();
633     db.measures().insertLiveMeasure(file, ncloc, m -> m.setValue(2d));
634
635     ComponentTreeWsResponse response = ws.newRequest()
636       .setParam(PARAM_COMPONENT, project.getKey())
637       .setParam(PARAM_METRIC_KEYS, ncloc.getKey())
638       .executeProtobuf(ComponentTreeWsResponse.class);
639
640     assertThat(response.getBaseComponent().getId()).isEqualTo(project.uuid());
641     assertThat(response.getComponentsList()).extracting(Component::getId)
642       .containsExactlyInAnyOrder(file.uuid());
643   }
644
645   @Test
646   public void use_deprecated_base_component_id_parameter() {
647     ComponentDto project = db.components().insertPrivateProject();
648     userSession.addProjectPermission(USER, project);
649     insertNclocMetric();
650
651     ComponentTreeWsResponse response = ws.newRequest()
652       .setParam("baseComponentId", project.uuid())
653       .setParam(PARAM_METRIC_KEYS, "ncloc")
654       .executeProtobuf(ComponentTreeWsResponse.class);
655
656     assertThat(response.getBaseComponent().getKey()).isEqualTo(project.getKey());
657   }
658
659   @Test
660   public void use_deprecated_base_component_key_parameter() {
661     ComponentDto project = db.components().insertPrivateProject();
662     userSession.addProjectPermission(USER, project);
663     insertNclocMetric();
664
665     ComponentTreeWsResponse response = ws.newRequest()
666       .setParam("baseComponentKey", project.getKey())
667       .setParam(PARAM_METRIC_KEYS, "ncloc")
668       .executeProtobuf(ComponentTreeWsResponse.class);
669
670     assertThat(response.getBaseComponent().getKey()).isEqualTo(project.getKey());
671   }
672
673   @Test
674   public void metric_without_a_domain() {
675     ComponentDto project = db.components().insertPrivateProject();
676     SnapshotDto analysis = db.getDbClient().snapshotDao().insert(dbSession, newAnalysis(project));
677     MetricDto metricWithoutDomain = db.measures().insertMetric(m -> m
678       .setValueType(Metric.ValueType.INT.name())
679       .setDomain(null));
680     db.measures().insertLiveMeasure(project, metricWithoutDomain);
681
682     ComponentTreeWsResponse result = ws.newRequest()
683       .setParam(PARAM_COMPONENT, project.getKey())
684       .setParam(PARAM_METRIC_KEYS, metricWithoutDomain.getKey())
685       .setParam(PARAM_ADDITIONAL_FIELDS, "metrics")
686       .executeProtobuf(ComponentTreeWsResponse.class);
687
688     assertThat(result.getBaseComponent().getMeasures(0).getMetric()).isEqualTo(metricWithoutDomain.getKey());
689     Common.Metric responseMetric = result.getMetrics().getMetrics(0);
690     assertThat(responseMetric.getKey()).isEqualTo(metricWithoutDomain.getKey());
691     assertThat(responseMetric.hasDomain()).isFalse();
692   }
693
694   @Test
695   public void project_reference_from_portfolio() {
696     ComponentDto project = db.components().insertPrivateProject();
697     ComponentDto view = db.components().insertPrivatePortfolio(db.getDefaultOrganization());
698     SnapshotDto viewAnalysis = db.components().insertSnapshot(view);
699     ComponentDto projectCopy = db.components().insertComponent(newProjectCopy(project, view));
700     MetricDto ncloc = insertNclocMetric();
701     db.measures().insertLiveMeasure(projectCopy, ncloc, m -> m.setValue(5d));
702
703     ComponentTreeWsResponse result = ws.newRequest()
704       .setParam(PARAM_COMPONENT, view.getKey())
705       .setParam(PARAM_METRIC_KEYS, ncloc.getKey())
706       .executeProtobuf(ComponentTreeWsResponse.class);
707
708     assertThat(result.getComponentsList())
709       .extracting(Component::getKey, Component::getRefId, Component::getRefKey)
710       .containsExactlyInAnyOrder(tuple(projectCopy.getKey(), project.uuid(), project.getKey()));
711   }
712
713   @Test
714   public void project_branch_reference_from_application_branch() {
715     MetricDto ncloc = insertNclocMetric();
716     ComponentDto application = db.components().insertPublicProject(c -> c.setQualifier(APP).setDbKey("app-key"));
717     ComponentDto applicationBranch = db.components().insertProjectBranch(application, a -> a.setKey("app-branch"));
718     ComponentDto project = db.components().insertPrivateProject(p -> p.setDbKey("project-key"));
719     ComponentDto projectBranch = db.components().insertProjectBranch(project, b -> b.setKey("project-branch"));
720     ComponentDto techProjectBranch = db.components().insertComponent(newProjectCopy(projectBranch, applicationBranch)
721       .setDbKey(applicationBranch.getKey() + applicationBranch.getBranch() + projectBranch.getDbKey()));
722     SnapshotDto applicationBranchAnalysis = db.components().insertSnapshot(applicationBranch);
723     db.measures().insertLiveMeasure(applicationBranch, ncloc, m -> m.setValue(5d));
724     db.measures().insertLiveMeasure(techProjectBranch, ncloc, m -> m.setValue(1d));
725
726     ComponentTreeWsResponse result = ws.newRequest()
727       .setParam(PARAM_COMPONENT, applicationBranch.getKey())
728       .setParam(PARAM_BRANCH, applicationBranch.getBranch())
729       .setParam(PARAM_METRIC_KEYS, ncloc.getKey())
730       .executeProtobuf(ComponentTreeWsResponse.class);
731
732     assertThat(result.getBaseComponent())
733       .extracting(Component::getKey, Component::getBranch)
734       .containsExactlyInAnyOrder(applicationBranch.getKey(), applicationBranch.getBranch());
735     assertThat(result.getComponentsList())
736       .extracting(Component::getKey, Component::getBranch, Component::getRefId, Component::getRefKey)
737       .containsExactlyInAnyOrder(tuple(techProjectBranch.getKey(), projectBranch.getBranch(), projectBranch.uuid(), project.getKey()));
738   }
739
740   @Test
741   public void fail_when_metric_keys_parameter_is_empty() {
742     ComponentDto project = db.components().insertPrivateProject();
743     db.components().insertSnapshot(project);
744
745     expectedException.expect(BadRequestException.class);
746     expectedException.expectMessage("The 'metricKeys' parameter must contain at least one metric key");
747
748     ws.newRequest()
749       .setParam(PARAM_COMPONENT, project.getKey())
750       .setParam(PARAM_METRIC_KEYS, "")
751       .executeProtobuf(ComponentTreeWsResponse.class);
752   }
753
754   @Test
755   public void fail_when_a_metric_is_not_found() {
756     ComponentDto project = db.components().insertPrivateProject();
757     db.components().insertSnapshot(project);
758     insertNclocMetric();
759     insertNewViolationsMetric();
760     expectedException.expect(NotFoundException.class);
761     expectedException.expectMessage("The following metric keys are not found: unknown-metric, another-unknown-metric");
762
763     ws.newRequest()
764       .setParam(PARAM_COMPONENT, project.getKey())
765       .setParam(PARAM_METRIC_KEYS, "ncloc, new_violations, unknown-metric, another-unknown-metric").executeProtobuf(ComponentTreeWsResponse.class);
766   }
767
768   @Test
769   public void fail_when_using_DISTRIB_metrics() {
770     ComponentDto project = db.components().insertPrivateProject();
771     db.components().insertSnapshot(project);
772     dbClient.metricDao().insert(dbSession, newMetricDto().setKey("distrib1").setValueType(DISTRIB.name()));
773     dbClient.metricDao().insert(dbSession, newMetricDto().setKey("distrib2").setValueType(DISTRIB.name()));
774     db.commit();
775
776     expectedException.expect(IllegalArgumentException.class);
777     expectedException.expectMessage("Metrics distrib1, distrib2 can't be requested in this web service. Please use api/measures/component");
778
779     ws.newRequest()
780       .setParam(PARAM_COMPONENT, project.getKey())
781       .setParam(PARAM_METRIC_KEYS, "distrib1,distrib2")
782       .execute();
783   }
784
785   @Test
786   public void fail_when_using_DATA_metrics() {
787     ComponentDto project = db.components().insertPrivateProject();
788     db.components().insertSnapshot(project);
789
790     dbClient.metricDao().insert(dbSession, newMetricDto().setKey("data1").setValueType(DISTRIB.name()));
791     dbClient.metricDao().insert(dbSession, newMetricDto().setKey("data2").setValueType(DISTRIB.name()));
792     db.commit();
793
794     expectedException.expect(IllegalArgumentException.class);
795     expectedException.expectMessage("Metrics data1, data2 can't be requested in this web service. Please use api/measures/component");
796
797     ws.newRequest()
798       .setParam(PARAM_COMPONENT, project.getKey())
799       .setParam(PARAM_METRIC_KEYS, "data1,data2")
800       .execute();
801   }
802
803   @Test
804   public void fail_when_setting_more_than_15_metric_keys() {
805     ComponentDto project = db.components().insertPrivateProject();
806     db.components().insertSnapshot(project);
807     List<String> metrics = IntStream.range(0, 20)
808       .mapToObj(i -> "metric" + i)
809       .collect(MoreCollectors.toList());
810     db.commit();
811
812     expectedException.expect(IllegalArgumentException.class);
813     expectedException.expectMessage("'metricKeys' can contains only 15 values, got 20");
814
815     ws.newRequest()
816       .setParam(PARAM_COMPONENT, project.getKey())
817       .setParam(PARAM_METRIC_KEYS, Joiner.on(",").join(metrics))
818       .execute();
819   }
820
821   @Test
822   public void fail_when_search_query_have_less_than_3_characters() {
823     ComponentDto project = db.components().insertPrivateProject();
824     db.components().insertSnapshot(project);
825     insertNclocMetric();
826     insertNewViolationsMetric();
827     expectedException.expect(IllegalArgumentException.class);
828     expectedException.expectMessage("'q' length (2) is shorter than the minimum authorized (3)");
829
830     ws.newRequest()
831       .setParam(PARAM_COMPONENT, project.getKey())
832       .setParam(PARAM_METRIC_KEYS, "ncloc, new_violations")
833       .setParam(Param.TEXT_QUERY, "fi")
834       .executeProtobuf(ComponentTreeWsResponse.class);
835   }
836
837   @Test
838   public void fail_when_insufficient_privileges() {
839     userSession.logIn();
840     ComponentDto project = db.components().insertPrivateProject();
841     db.components().insertSnapshot(project);
842
843     expectedException.expect(ForbiddenException.class);
844
845     ws.newRequest()
846       .setParam(PARAM_COMPONENT, project.getKey())
847       .setParam(PARAM_METRIC_KEYS, "ncloc")
848       .executeProtobuf(ComponentTreeWsResponse.class);
849   }
850
851   @Test
852   public void fail_when_sort_by_metric_and_no_metric_sort_provided() {
853     ComponentDto project = db.components().insertPrivateProject();
854     db.components().insertSnapshot(project);
855     expectedException.expect(BadRequestException.class);
856     expectedException
857       .expectMessage("To sort by a metric, the 's' parameter must contain 'metric' or 'metricPeriod', and a metric key must be provided in the 'metricSort' parameter");
858
859     ws.newRequest()
860       .setParam(PARAM_COMPONENT, project.getKey())
861       .setParam(PARAM_METRIC_KEYS, "ncloc")
862       // PARAM_METRIC_SORT is not set
863       .setParam(SORT, METRIC_SORT)
864       .executeProtobuf(ComponentTreeWsResponse.class);
865   }
866
867   @Test
868   public void fail_when_sort_by_metric_and_not_in_the_list_of_metric_keys() {
869     ComponentDto project = db.components().insertPrivateProject();
870     db.components().insertSnapshot(project);
871
872     expectedException.expect(BadRequestException.class);
873     expectedException.expectMessage("To sort by the 'complexity' metric, it must be in the list of metric keys in the 'metricKeys' parameter");
874
875     ws.newRequest()
876       .setParam(PARAM_COMPONENT, project.getKey())
877       .setParam(PARAM_METRIC_KEYS, "ncloc,violations")
878       .setParam(PARAM_METRIC_SORT, "complexity")
879       .setParam(SORT, METRIC_SORT)
880       .executeProtobuf(ComponentTreeWsResponse.class);
881   }
882
883   @Test
884   public void fail_when_sort_by_metric_period_and_no_metric_period_sort_provided() {
885     ComponentDto project = db.components().insertPrivateProject();
886     db.components().insertSnapshot(project);
887
888     expectedException.expect(BadRequestException.class);
889     expectedException.expectMessage("To sort by a metric period, the 's' parameter must contain 'metricPeriod' and the 'metricPeriodSort' must be provided.");
890
891     ws.newRequest()
892       .setParam(PARAM_COMPONENT, project.getKey())
893       .setParam(PARAM_METRIC_KEYS, "ncloc")
894       .setParam(PARAM_METRIC_SORT, "ncloc")
895       // PARAM_METRIC_PERIOD_SORT_IS_NOT_SET
896       .setParam(SORT, METRIC_PERIOD_SORT)
897       .executeProtobuf(ComponentTreeWsResponse.class);
898   }
899
900   @Test
901   public void fail_when_paging_parameter_is_too_big() {
902     ComponentDto project = db.components().insertPrivateProject();
903     db.components().insertSnapshot(project);
904     insertNclocMetric();
905
906     expectedException.expect(IllegalArgumentException.class);
907     expectedException.expectMessage("'ps' value (2540) must be less than 500");
908
909     ws.newRequest()
910       .setParam(PARAM_COMPONENT, project.getKey())
911       .setParam(PARAM_METRIC_KEYS, "ncloc")
912       .setParam(Param.PAGE_SIZE, "2540")
913       .execute();
914   }
915
916   @Test
917   public void fail_when_with_measures_only_and_no_metric_sort() {
918     ComponentDto project = db.components().insertPrivateProject();
919     db.components().insertSnapshot(project);
920     insertNclocMetric();
921
922     expectedException.expect(BadRequestException.class);
923     expectedException
924       .expectMessage("To filter components based on the sort metric, the 's' parameter must contain 'metric' or 'metricPeriod' and the 'metricSort' parameter must be provided");
925
926     ws.newRequest()
927       .setParam(PARAM_COMPONENT, project.getKey())
928       .setParam(PARAM_METRIC_KEYS, "ncloc")
929       .setParam(PARAM_METRIC_SORT_FILTER, WITH_MEASURES_ONLY_METRIC_SORT_FILTER)
930       .executeProtobuf(ComponentTreeWsResponse.class);
931   }
932
933   @Test
934   public void fail_when_component_does_not_exist() {
935     insertNclocMetric();
936
937     expectedException.expect(NotFoundException.class);
938     expectedException.expectMessage("Component key 'project-key' not found");
939
940     ws.newRequest()
941       .setParam(DEPRECATED_PARAM_BASE_COMPONENT_KEY, "project-key")
942       .setParam(PARAM_METRIC_KEYS, "ncloc")
943       .execute();
944   }
945
946   @Test
947   public void fail_when_component_is_removed() {
948     ComponentDto project = db.components().insertPrivateProject();
949     db.components().insertSnapshot(project);
950     ComponentDto file = db.components().insertComponent(newFileDto(project).setDbKey("file-key").setEnabled(false));
951     userSession.anonymous().addProjectPermission(UserRole.USER, project);
952     insertNclocMetric();
953
954     expectedException.expect(NotFoundException.class);
955     expectedException.expectMessage(format("Component key '%s' not found", file.getKey()));
956
957     ws.newRequest()
958       .setParam(PARAM_COMPONENT, file.getKey())
959       .setParam(PARAM_METRIC_KEYS, "ncloc")
960       .execute();
961   }
962
963   @Test
964   public void fail_if_branch_does_not_exist() {
965     ComponentDto project = db.components().insertPrivateProject();
966     ComponentDto file = db.components().insertComponent(newFileDto(project));
967     userSession.addProjectPermission(UserRole.USER, project);
968     db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
969
970     expectedException.expect(NotFoundException.class);
971     expectedException.expectMessage(String.format("Component '%s' on branch '%s' not found", file.getKey(), "another_branch"));
972
973     ws.newRequest()
974       .setParam(PARAM_COMPONENT, file.getKey())
975       .setParam(PARAM_BRANCH, "another_branch")
976       .setParam(PARAM_METRIC_KEYS, "ncloc")
977       .execute();
978   }
979
980   @Test
981   public void fail_when_using_branch_db_key() throws Exception {
982     OrganizationDto organization = db.organizations().insert();
983     ComponentDto project = db.components().insertPrivateProject(organization);
984     userSession.logIn().addProjectPermission(UserRole.USER, project);
985     ComponentDto branch = db.components().insertProjectBranch(project);
986     insertNclocMetric();
987
988     expectedException.expect(NotFoundException.class);
989     expectedException.expectMessage(format("Component key '%s' not found", branch.getDbKey()));
990
991     ws.newRequest()
992       .setParam(PARAM_COMPONENT, branch.getDbKey())
993       .setParam(PARAM_METRIC_KEYS, "ncloc")
994       .execute();
995   }
996
997   @Test
998   public void fail_when_using_branch_uuid() {
999     OrganizationDto organization = db.organizations().insert();
1000     ComponentDto project = db.components().insertPrivateProject(organization);
1001     userSession.logIn().addProjectPermission(UserRole.USER, project);
1002     ComponentDto branch = db.components().insertProjectBranch(project);
1003     insertNclocMetric();
1004
1005     expectedException.expect(NotFoundException.class);
1006     expectedException.expectMessage(format("Component id '%s' not found", branch.uuid()));
1007
1008     ws.newRequest()
1009       .setParam(DEPRECATED_PARAM_BASE_COMPONENT_ID, branch.uuid())
1010       .setParam(PARAM_METRIC_KEYS, "ncloc")
1011       .execute();
1012   }
1013
1014   private static MetricDto newMetricDto() {
1015     return MetricTesting.newMetricDto()
1016       .setWorstValue(null)
1017       .setBestValue(null)
1018       .setOptimizedBestValue(false)
1019       .setUserManaged(false);
1020   }
1021
1022   private MetricDto insertNewViolationsMetric() {
1023     MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDto()
1024       .setKey("new_violations")
1025       .setShortName("New issues")
1026       .setDescription("New Issues")
1027       .setDomain("Issues")
1028       .setValueType("INT")
1029       .setDirection(-1)
1030       .setQualitative(true)
1031       .setHidden(false)
1032       .setUserManaged(false)
1033       .setOptimizedBestValue(true)
1034       .setBestValue(0.0d));
1035     db.commit();
1036     return metric;
1037   }
1038
1039   private MetricDto insertNclocMetric() {
1040     MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDto()
1041       .setKey("ncloc")
1042       .setShortName("Lines of code")
1043       .setDescription("Non Commenting Lines of Code")
1044       .setDomain("Size")
1045       .setValueType(INT.name())
1046       .setDirection(-1)
1047       .setQualitative(false)
1048       .setHidden(false)
1049       .setUserManaged(false));
1050     db.commit();
1051     return metric;
1052   }
1053
1054   private MetricDto insertComplexityMetric() {
1055     MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDto()
1056       .setKey("complexity")
1057       .setShortName("Complexity")
1058       .setDescription("Cyclomatic complexity")
1059       .setDomain("Complexity")
1060       .setValueType(INT.name())
1061       .setDirection(-1)
1062       .setQualitative(false)
1063       .setHidden(false)
1064       .setUserManaged(false));
1065     db.commit();
1066     return metric;
1067   }
1068
1069   private MetricDto insertCoverageMetric() {
1070     MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDto()
1071       .setKey("coverage")
1072       .setShortName("Coverage")
1073       .setDescription("Code Coverage")
1074       .setDomain("Coverage")
1075       .setValueType(FLOAT.name())
1076       .setDirection(1)
1077       .setQualitative(false)
1078       .setHidden(false)
1079       .setUserManaged(false));
1080     db.commit();
1081     return metric;
1082   }
1083 }