]> source.dussan.org Git - sonarqube.git/blob
0f32005b93bd9d2fb4544e9f85e023ee0f57b422
[sonarqube.git] /
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.component.ws;
21
22 import com.google.common.base.Joiner;
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.Arrays;
27 import java.util.Date;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Optional;
31 import java.util.Set;
32 import java.util.function.Consumer;
33 import java.util.stream.IntStream;
34 import java.util.stream.Stream;
35 import javax.annotation.Nullable;
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.measures.Metric;
41 import org.sonar.api.resources.Qualifiers;
42 import org.sonar.api.server.ws.WebService;
43 import org.sonar.api.server.ws.WebService.Param;
44 import org.sonar.api.utils.System2;
45 import org.sonar.core.platform.EditionProvider.Edition;
46 import org.sonar.core.platform.PlatformEditionProvider;
47 import org.sonar.db.DbClient;
48 import org.sonar.db.DbSession;
49 import org.sonar.db.DbTester;
50 import org.sonar.db.component.ComponentDto;
51 import org.sonar.db.measure.LiveMeasureDto;
52 import org.sonar.db.metric.MetricDto;
53 import org.sonar.db.project.ProjectDto;
54 import org.sonar.db.property.PropertyDto;
55 import org.sonar.server.component.ws.SearchProjectsAction.RequestBuilder;
56 import org.sonar.server.component.ws.SearchProjectsAction.SearchProjectsRequest;
57 import org.sonar.server.es.EsTester;
58 import org.sonar.server.issue.index.IssueIndexSyncProgressChecker;
59 import org.sonar.server.measure.index.ProjectMeasuresIndex;
60 import org.sonar.server.measure.index.ProjectMeasuresIndexer;
61 import org.sonar.server.permission.index.PermissionIndexerTester;
62 import org.sonar.server.permission.index.WebAuthorizationTypeSupport;
63 import org.sonar.server.qualitygate.ProjectsInWarning;
64 import org.sonar.server.tester.UserSessionRule;
65 import org.sonar.server.ws.TestRequest;
66 import org.sonar.server.ws.WsActionTester;
67 import org.sonarqube.ws.Common;
68 import org.sonarqube.ws.Components.Component;
69 import org.sonarqube.ws.Components.SearchProjectsWsResponse;
70
71 import static java.util.Arrays.asList;
72 import static java.util.Collections.singletonList;
73 import static java.util.Optional.ofNullable;
74 import static org.assertj.core.api.Assertions.assertThat;
75 import static org.assertj.core.api.Assertions.assertThatThrownBy;
76 import static org.assertj.core.api.Assertions.tuple;
77 import static org.mockito.Mockito.mock;
78 import static org.mockito.Mockito.when;
79 import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
80 import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_DENSITY_KEY;
81 import static org.sonar.api.measures.CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION_KEY;
82 import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_DENSITY_KEY;
83 import static org.sonar.api.measures.CoreMetrics.NEW_LINES_KEY;
84 import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY;
85 import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_RATING_KEY;
86 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING_KEY;
87 import static org.sonar.api.measures.CoreMetrics.RELIABILITY_RATING_KEY;
88 import static org.sonar.api.measures.CoreMetrics.SECURITY_RATING_KEY;
89 import static org.sonar.api.measures.CoreMetrics.SQALE_RATING_KEY;
90 import static org.sonar.api.measures.Metric.ValueType.DATA;
91 import static org.sonar.api.measures.Metric.ValueType.INT;
92 import static org.sonar.api.measures.Metric.ValueType.LEVEL;
93 import static org.sonar.api.server.ws.WebService.Param.ASCENDING;
94 import static org.sonar.api.server.ws.WebService.Param.FACETS;
95 import static org.sonar.api.server.ws.WebService.Param.FIELDS;
96 import static org.sonar.api.server.ws.WebService.Param.PAGE;
97 import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
98 import static org.sonar.api.server.ws.WebService.Param.SORT;
99 import static org.sonar.api.utils.DateUtils.formatDateTime;
100 import static org.sonar.core.util.stream.MoreCollectors.toList;
101 import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
102 import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_002;
103 import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_003;
104 import static org.sonar.test.JsonAssert.assertJson;
105 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_FILTER;
106 import static org.sonarqube.ws.client.project.ProjectsWsParameters.FILTER_LANGUAGES;
107 import static org.sonarqube.ws.client.project.ProjectsWsParameters.FILTER_QUALIFIER;
108 import static org.sonarqube.ws.client.project.ProjectsWsParameters.FILTER_TAGS;
109
110 @RunWith(DataProviderRunner.class)
111 public class SearchProjectsActionTest {
112
113   private static final String NCLOC = "ncloc";
114   private static final String COVERAGE = "coverage";
115   private static final String NEW_COVERAGE = "new_coverage";
116   private static final String LEAK_PROJECTS_KEY = "leak_projects";
117   private static final String QUALITY_GATE_STATUS = "alert_status";
118   private static final String ANALYSIS_DATE = "analysisDate";
119
120   @Rule
121   public final UserSessionRule userSession = UserSessionRule.standalone();
122   @Rule
123   public final EsTester es = EsTester.create();
124   @Rule
125   public final DbTester db = DbTester.create(System2.INSTANCE);
126
127   @DataProvider
128   public static Object[][] rating_metric_keys() {
129     return new Object[][]{{SQALE_RATING_KEY}, {RELIABILITY_RATING_KEY}, {SECURITY_RATING_KEY}};
130   }
131
132   @DataProvider
133   public static Object[][] new_rating_metric_keys() {
134     return new Object[][]{{NEW_MAINTAINABILITY_RATING_KEY}, {NEW_RELIABILITY_RATING_KEY}, {NEW_SECURITY_RATING_KEY}};
135   }
136
137   @DataProvider
138   public static Object[][] component_qualifiers_for_valid_editions() {
139     return new Object[][]{
140       {new String[]{Qualifiers.PROJECT}, Edition.COMMUNITY},
141       {new String[]{Qualifiers.APP, Qualifiers.PROJECT}, Edition.DEVELOPER},
142       {new String[]{Qualifiers.APP, Qualifiers.PROJECT}, Edition.ENTERPRISE},
143       {new String[]{Qualifiers.APP, Qualifiers.PROJECT}, Edition.DATACENTER},
144     };
145   }
146
147   @DataProvider
148   public static Object[][] community_or_developer_edition() {
149     return new Object[][]{
150       {Edition.COMMUNITY},
151       {Edition.DEVELOPER},
152     };
153   }
154
155   @DataProvider
156   public static Object[][] enterprise_or_datacenter_edition() {
157     return new Object[][]{
158       {Edition.ENTERPRISE},
159       {Edition.DATACENTER},
160     };
161   }
162
163   private DbClient dbClient = db.getDbClient();
164   private DbSession dbSession = db.getSession();
165
166   private PlatformEditionProvider editionProviderMock = mock(PlatformEditionProvider.class);
167   private PermissionIndexerTester authorizationIndexerTester = new PermissionIndexerTester(es, new ProjectMeasuresIndexer(dbClient, es.client()));
168   private ProjectMeasuresIndex index = new ProjectMeasuresIndex(es.client(), new WebAuthorizationTypeSupport(userSession), System2.INSTANCE);
169   private ProjectMeasuresIndexer projectMeasuresIndexer = new ProjectMeasuresIndexer(db.getDbClient(), es.client());
170   private ProjectsInWarning projectsInWarning = new ProjectsInWarning();
171
172   private WsActionTester ws = new WsActionTester(new SearchProjectsAction(dbClient, index, userSession, projectsInWarning, editionProviderMock,
173     new IssueIndexSyncProgressChecker(db.getDbClient())));
174
175   private RequestBuilder request = SearchProjectsRequest.builder();
176
177   @Before
178   public void setUp() {
179     projectsInWarning.update(0L);
180   }
181
182   @Test
183   public void verify_definition() {
184     WebService.Action def = ws.getDef();
185
186     assertThat(def.key()).isEqualTo("search_projects");
187     assertThat(def.since()).isEqualTo("6.2");
188     assertThat(def.isInternal()).isTrue();
189     assertThat(def.isPost()).isFalse();
190     assertThat(def.responseExampleAsString()).isNotEmpty();
191     assertThat(def.params().stream().map(Param::key).collect(toList())).containsOnly("filter", "facets", "s", "asc", "ps", "p", "f");
192     assertThat(def.changelog()).hasSize(2);
193
194     Param sort = def.param("s");
195     assertThat(sort.defaultValue()).isEqualTo("name");
196     assertThat(sort.possibleValues()).containsExactlyInAnyOrder(
197       "coverage",
198       "reliability_rating",
199       "duplicated_lines_density",
200       "ncloc_language_distribution",
201       "lines",
202       "new_lines",
203       "security_rating",
204       "security_review_rating",
205       "new_security_review_rating",
206       "security_hotspots_reviewed",
207       "new_security_hotspots_reviewed",
208       "new_reliability_rating",
209       "new_coverage",
210       "new_security_rating",
211       "sqale_rating",
212       "new_duplicated_lines_density",
213       "alert_status",
214       "ncloc",
215       "new_maintainability_rating",
216       "name",
217       "analysisDate");
218
219     Param asc = def.param("asc");
220     assertThat(asc.defaultValue()).isEqualTo("true");
221     assertThat(asc.possibleValues()).containsOnly("true", "false", "yes", "no");
222
223     Param f = def.param("f");
224     assertThat(f.defaultValue()).isNull();
225     assertThat(f.possibleValues()).containsOnly("_all", "analysisDate", "leakPeriodDate");
226
227     Param facets = def.param("facets");
228     assertThat(facets.defaultValue()).isNull();
229     assertThat(facets.possibleValues()).containsOnly("ncloc", "duplicated_lines_density", "coverage", "sqale_rating", "reliability_rating", "security_rating", "alert_status",
230       "languages", "tags", "qualifier", "new_reliability_rating", "new_security_rating", "new_maintainability_rating", "new_coverage", "new_duplicated_lines_density", "new_lines",
231       "security_review_rating", "security_hotspots_reviewed", "new_security_hotspots_reviewed", "new_security_review_rating");
232   }
233
234   @Test
235   public void json_example() {
236     userSession.logIn();
237     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(COVERAGE).setValueType("PERCENT"));
238     ComponentDto project1 = insertProject(
239       c -> c.setKey(KEY_PROJECT_EXAMPLE_001).setName("My Project 1"),
240       p -> p.setTagsString("finance, java"),
241       new Measure(coverage, c -> c.setValue(80d)));
242
243     db.components().insertProjectBranch(db.components().getProjectDto(project1), branchDto -> branchDto.setNeedIssueSync(true));
244
245     ComponentDto project2 = insertProject(
246       c -> c.setKey(KEY_PROJECT_EXAMPLE_002).setName("My Project 2"),
247       new Measure(coverage, c -> c.setValue(90d)));
248     ComponentDto project3 = insertProject(
249       c -> c.setKey(KEY_PROJECT_EXAMPLE_003).setName("My Project 3"),
250       p -> p.setTagsString("sales, offshore, java"),
251       new Measure(coverage, c -> c.setValue(20d)));
252     addFavourite(project1);
253     index();
254
255     String jsonResult = ws.newRequest()
256       .setParam(FACETS, COVERAGE)
257       .setParam(FIELDS, "_all")
258       .execute().getInput();
259
260     assertJson(jsonResult).ignoreFields("id").isSimilarTo(ws.getDef().responseExampleAsString());
261     assertJson(ws.getDef().responseExampleAsString()).ignoreFields("id").isSimilarTo(jsonResult);
262
263     SearchProjectsWsResponse protobufResult = ws.newRequest()
264       .setParam(FACETS, COVERAGE)
265       .executeProtobuf(SearchProjectsWsResponse.class);
266
267     assertThat(protobufResult.getComponentsList()).extracting(Component::getKey)
268       .containsExactly(project1.getKey(), project2.getKey(), project3.getKey());
269   }
270
271   @Test
272   public void order_by_name_case_insensitive() {
273     userSession.logIn();
274     insertProject(c -> c.setName("Maven"));
275     insertProject(c -> c.setName("Apache"));
276     insertProject(c -> c.setName("guava"));
277     index();
278
279     SearchProjectsWsResponse result = call(request);
280
281     assertThat(result.getComponentsList()).extracting(Component::getName)
282       .containsExactly("Apache", "guava", "Maven");
283   }
284
285   @Test
286   public void paginate_result() {
287     userSession.logIn();
288     IntStream.rangeClosed(1, 9).forEach(i -> insertProject(c -> c.setName("PROJECT-" + i)));
289     index();
290
291     SearchProjectsWsResponse result = call(request.setPage(2).setPageSize(3));
292
293     assertThat(result.getPaging().getPageIndex()).isEqualTo(2);
294     assertThat(result.getPaging().getPageSize()).isEqualTo(3);
295     assertThat(result.getPaging().getTotal()).isEqualTo(9);
296     assertThat(result.getComponentsCount()).isEqualTo(3);
297     assertThat(result.getComponentsList())
298       .extracting(Component::getName)
299       .containsExactly("PROJECT-4", "PROJECT-5", "PROJECT-6");
300   }
301
302   @Test
303   public void empty_result() {
304     userSession.logIn();
305
306     SearchProjectsWsResponse result = call(request);
307
308     assertThat(result.getComponentsCount()).isZero();
309     Common.Paging paging = result.getPaging();
310     assertThat(paging.getPageIndex()).isOne();
311     assertThat(paging.getPageSize()).isEqualTo(100);
312     assertThat(paging.getTotal()).isZero();
313   }
314
315   @Test
316   public void filter_projects_with_query() {
317     userSession.logIn();
318     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(COVERAGE).setValueType(INT.name()));
319     MetricDto ncloc = db.measures().insertMetric(c -> c.setKey(NCLOC).setValueType(INT.name()));
320     ComponentDto project1 = insertProject(
321       new Measure(coverage, c -> c.setValue(81d)),
322       new Measure(ncloc, c -> c.setValue(10_000d)));
323     ComponentDto project2 = insertProject(
324       new Measure(coverage, c -> c.setValue(80d)),
325       new Measure(ncloc, c -> c.setValue(10_000d)));
326     ComponentDto project3 = insertProject(
327       new Measure(coverage, c -> c.setValue(80d)),
328       new Measure(ncloc, c -> c.setValue(10_001d)));
329     index();
330
331     SearchProjectsWsResponse result = call(request.setFilter("coverage <= 80 and ncloc <= 10000"));
332
333     assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactly(project2.getKey());
334   }
335
336   @Test
337   public void filter_projects_by_quality_gate() {
338     userSession.logIn();
339     MetricDto qualityGateStatus = db.measures().insertMetric(c -> c.setKey(QUALITY_GATE_STATUS).setValueType(LEVEL.name()));
340     ComponentDto project1 = insertProject(new Measure(qualityGateStatus, c -> c.setValue(null).setData("OK")));
341     ComponentDto project2 = insertProject(new Measure(qualityGateStatus, c -> c.setValue(null).setData("OK")));
342     ComponentDto project3 = insertProject(new Measure(qualityGateStatus, c -> c.setValue(null).setData("ERROR")));
343     index();
344
345     SearchProjectsWsResponse result = call(request.setFilter("alert_status = OK"));
346
347     assertThat(result.getComponentsList())
348       .extracting(Component::getKey)
349       .containsExactlyInAnyOrder(project1.getKey(), project2.getKey());
350   }
351
352   @Test
353   public void filter_projects_by_languages() {
354     userSession.logIn();
355     MetricDto nclocMetric = db.measures().insertMetric(c -> c.setKey(NCLOC).setValueType(INT.name()));
356     MetricDto languagesDistributionMetric = db.measures().insertMetric(c -> c.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY).setValueType("DATA"));
357     ComponentDto project1 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("<null>=2;java=6;xoo=18")));
358     db.measures().insertLiveMeasure(project1, nclocMetric, m -> m.setValue(26d));
359     ComponentDto project2 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("java=3;xoo=9")));
360     db.measures().insertLiveMeasure(project2, nclocMetric, m -> m.setValue(12d));
361     ComponentDto project3 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("xoo=1")));
362     db.measures().insertLiveMeasure(project3, nclocMetric, m -> m.setValue(1d));
363     ComponentDto project4 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("<null>=1;java=5;xoo=13")));
364     db.measures().insertLiveMeasure(project4, nclocMetric, m -> m.setValue(19d));
365     index();
366
367     SearchProjectsWsResponse result = call(request.setFilter("languages IN (java, js, <null>)"));
368
369     assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactlyInAnyOrder(project1.getKey(), project2.getKey(), project4.getKey());
370   }
371
372   @Test
373   @UseDataProvider("rating_metric_keys")
374   public void filter_projects_by_rating(String metricKey) {
375     userSession.logIn();
376     MetricDto ratingMetric = db.measures().insertMetric(c -> c.setKey(metricKey).setValueType(INT.name()));
377     ComponentDto project1 = insertProject(new Measure(ratingMetric, c -> c.setValue(1d)));
378     ComponentDto project2 = insertProject(new Measure(ratingMetric, c -> c.setValue(2d)));
379     ComponentDto project3 = insertProject(new Measure(ratingMetric, c -> c.setValue(3d)));
380     index();
381
382     SearchProjectsWsResponse result = call(request.setFilter(metricKey + " = 2"));
383
384     assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactly(project2.getKey());
385   }
386
387   @Test
388   @UseDataProvider("new_rating_metric_keys")
389   public void filter_projects_by_new_rating(String newMetricKey) {
390     userSession.logIn();
391     MetricDto ratingMetric = db.measures().insertMetric(c -> c.setKey(newMetricKey).setValueType(INT.name()));
392     insertProject(new Measure(ratingMetric, c -> c.setValue(1d)));
393     ComponentDto project2 = insertProject(new Measure(ratingMetric, c -> c.setValue(2d)));
394     insertProject(new Measure(ratingMetric, c -> c.setValue(3d)));
395     index();
396
397     SearchProjectsWsResponse result = call(request.setFilter(newMetricKey + " = 2"));
398
399     assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactly(project2.getKey());
400   }
401
402   @Test
403   public void filter_projects_by_tags() {
404     userSession.logIn();
405     ComponentDto project1 = insertProject(defaults(), p -> p.setTags(asList("finance", "platform")));
406     insertProject(defaults(), p -> p.setTags(singletonList("marketing")));
407     ComponentDto project3 = insertProject(defaults(), p -> p.setTags(singletonList("offshore")));
408     index();
409
410     SearchProjectsWsResponse result = call(request.setFilter("tags in (finance, offshore)"));
411
412     assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactlyInAnyOrder(project1.getKey(), project3.getKey());
413   }
414
415   @Test
416   public void filter_projects_by_coverage() {
417     userSession.logIn();
418     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(COVERAGE).setValueType("PERCENT"));
419     ComponentDto project1 = insertProject(new Measure(coverage, c -> c.setValue(80d)));
420     ComponentDto project2 = insertProject(new Measure(coverage, c -> c.setValue(85d)));
421     ComponentDto project3 = insertProject(new Measure(coverage, c -> c.setValue(10d)));
422     index();
423
424     SearchProjectsWsResponse result = call(request.setFilter("coverage <= 80"));
425
426     assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactlyInAnyOrder(project1.getKey(), project3.getKey());
427   }
428
429   @Test
430   public void filter_projects_by_new_coverage() {
431     userSession.logIn();
432     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(NEW_COVERAGE).setValueType("PERCENT"));
433     ComponentDto project1 = insertProject(new Measure(coverage, c -> c.setValue(80d)));
434     ComponentDto project2 = insertProject(new Measure(coverage, c -> c.setValue(85d)));
435     ComponentDto project3 = insertProject(new Measure(coverage, c -> c.setValue(10d)));
436     index();
437
438     SearchProjectsWsResponse result = call(request.setFilter("new_coverage <= 80"));
439
440     assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactlyInAnyOrder(project1.getKey(), project3.getKey());
441   }
442
443   @Test
444   public void filter_projects_by_duplications() {
445     userSession.logIn();
446     MetricDto duplications = db.measures().insertMetric(c -> c.setKey(DUPLICATED_LINES_DENSITY_KEY).setValueType("PERCENT"));
447     ComponentDto project1 = insertProject(new Measure(duplications, c -> c.setValue(80d)));
448     ComponentDto project2 = insertProject(new Measure(duplications, c -> c.setValue(85d)));
449     ComponentDto project3 = insertProject(new Measure(duplications, c -> c.setValue(10d)));
450     index();
451
452     SearchProjectsWsResponse result = call(request.setFilter("duplicated_lines_density <= 80"));
453
454     assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactlyInAnyOrder(project1.getKey(), project3.getKey());
455   }
456
457   @Test
458   public void filter_projects_by_no_duplication() {
459     userSession.logIn();
460     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(COVERAGE).setValueType("PERCENT"));
461     MetricDto duplications = db.measures().insertMetric(c -> c.setKey(DUPLICATED_LINES_DENSITY_KEY).setValueType("PERCENT"));
462     ComponentDto project1 = insertProject(new Measure(coverage, c -> c.setValue(10d)));
463     ComponentDto project2 = insertProject(new Measure(duplications, c -> c.setValue(0d)));
464     ComponentDto project3 = insertProject(new Measure(duplications, c -> c.setValue(79d)));
465     index();
466
467     SearchProjectsWsResponse result = call(request.setFilter("duplicated_lines_density = NO_DATA"));
468
469     assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactlyInAnyOrder(project1.getKey());
470   }
471
472   @Test
473   public void filter_projects_by_no_duplication_should_not_return_projects_with_duplication() {
474     userSession.logIn();
475     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(COVERAGE).setValueType("PERCENT"));
476     MetricDto duplications = db.measures().insertMetric(c -> c.setKey(DUPLICATED_LINES_DENSITY_KEY).setValueType("PERCENT"));
477     insertProject(new Measure(duplications, c -> c.setValue(10d)), new Measure(coverage, c -> c.setValue(50d)));
478     index();
479
480     SearchProjectsWsResponse result = call(request.setFilter("duplicated_lines_density = NO_DATA"));
481
482     assertThat(result.getComponentsList()).extracting(Component::getKey).isEmpty();
483   }
484
485   @Test
486   public void filter_projects_by_new_duplications() {
487     userSession.logIn();
488     MetricDto newDuplications = db.measures().insertMetric(c -> c.setKey(NEW_DUPLICATED_LINES_DENSITY_KEY).setValueType("PERCENT"));
489     ComponentDto project1 = insertProject(new Measure(newDuplications, c -> c.setValue(80d)));
490     ComponentDto project2 = insertProject(new Measure(newDuplications, c -> c.setValue(85d)));
491     ComponentDto project3 = insertProject(new Measure(newDuplications, c -> c.setValue(10d)));
492     index();
493
494     SearchProjectsWsResponse result = call(request.setFilter("new_duplicated_lines_density <= 80"));
495
496     assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactlyInAnyOrder(project1.getKey(), project3.getKey());
497   }
498
499   @Test
500   public void filter_projects_by_ncloc() {
501     userSession.logIn();
502     MetricDto ncloc = db.measures().insertMetric(c -> c.setKey(NCLOC).setValueType(INT.name()));
503     ComponentDto project1 = insertProject(new Measure(ncloc, c -> c.setValue(80d)));
504     ComponentDto project2 = insertProject(new Measure(ncloc, c -> c.setValue(85d)));
505     ComponentDto project3 = insertProject(new Measure(ncloc, c -> c.setValue(10d)));
506     index();
507
508     SearchProjectsWsResponse result = call(request.setFilter("ncloc <= 80"));
509
510     assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactlyInAnyOrder(project1.getKey(), project3.getKey());
511   }
512
513   @Test
514   public void filter_projects_by_new_lines() {
515     userSession.logIn();
516     MetricDto newLines = db.measures().insertMetric(c -> c.setKey(NEW_LINES_KEY).setValueType(INT.name()));
517     ComponentDto project1 = insertProject(new Measure(newLines, c -> c.setValue(80d)));
518     ComponentDto project2 = insertProject(new Measure(newLines, c -> c.setValue(85d)));
519     ComponentDto project3 = insertProject(new Measure(newLines, c -> c.setValue(10d)));
520     index();
521
522     SearchProjectsWsResponse result = call(request.setFilter("new_lines <= 80"));
523
524     assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactlyInAnyOrder(project1.getKey(), project3.getKey());
525   }
526
527   @Test
528   public void filter_projects_by_text_query() {
529     userSession.logIn();
530     insertProject(c -> c.setKey("sonar-java").setName("Sonar Java"));
531     insertProject(c -> c.setKey("sonar-groovy").setName("Sonar Groovy"));
532     insertProject(c -> c.setKey("sonar-markdown").setName("Sonar Markdown"));
533     insertProject(c -> c.setKey("sonarqube").setName("Sonar Qube"));
534     index();
535
536     assertThat(call(request.setFilter("query = \"Groovy\"")).getComponentsList()).extracting(Component::getName).containsOnly("Sonar Groovy");
537     assertThat(call(request.setFilter("query = \"oNar\"")).getComponentsList()).extracting(Component::getName).containsOnly("Sonar Java", "Sonar Groovy", "Sonar Markdown",
538       "Sonar Qube");
539     assertThat(call(request.setFilter("query = \"sonar-java\"")).getComponentsList()).extracting(Component::getName).containsOnly("Sonar Java");
540   }
541
542   @Test
543   public void filter_projects_on_favorites() {
544     userSession.logIn();
545     ComponentDto javaProject = insertProject();
546     ComponentDto markDownProject = insertProject();
547     ComponentDto sonarQubeProject = insertProject();
548     Stream.of(javaProject, markDownProject).forEach(this::addFavourite);
549     index();
550
551     SearchProjectsWsResponse result = call(request.setFilter("isFavorite"));
552
553     assertThat(result.getComponentsCount()).isEqualTo(2);
554     assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactly(javaProject.getKey(), markDownProject.getKey());
555   }
556
557   @Test
558   public void does_not_fail_on_orphan_favorite() {
559     userSession.logIn();
560     ComponentDto javaProject = insertProject();
561     ComponentDto markDownProject = insertProject();
562     ComponentDto sonarQubeProject = insertProject();
563     Stream.of(javaProject, markDownProject).forEach(this::addFavourite);
564     index();
565
566     addFavourite(null, null, null, null);
567
568     SearchProjectsWsResponse result = call(request.setFilter("isFavorite"));
569
570     assertThat(result.getComponentsCount()).isEqualTo(2);
571     assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactly(javaProject.getKey(), markDownProject.getKey());
572   }
573
574   @Test
575   public void filtering_on_favorites_returns_empty_results_if_not_logged_in() {
576     userSession.anonymous();
577     ComponentDto javaProject = insertProject();
578     ComponentDto markDownProject = insertProject();
579     ComponentDto sonarQubeProject = insertProject();
580     Stream.of(javaProject, markDownProject).forEach(this::addFavourite);
581     index();
582
583     SearchProjectsWsResponse result = call(request.setFilter("isFavorite"));
584
585     assertThat(result.getComponentsCount()).isZero();
586   }
587
588   @Test
589   @UseDataProvider("component_qualifiers_for_valid_editions")
590   public void default_filter_projects_and_apps_by_editions(String[] qualifiers, Edition edition) {
591     when(editionProviderMock.get()).thenReturn(Optional.of(edition));
592     userSession.logIn();
593     ComponentDto portfolio1 = insertPortfolio();
594     ComponentDto portfolio2 = insertPortfolio();
595
596     ComponentDto application1 = insertApplication();
597     ComponentDto application2 = insertApplication();
598     ComponentDto application3 = insertApplication();
599
600     ComponentDto project1 = insertProject();
601     ComponentDto project2 = insertProject();
602     ComponentDto project3 = insertProject();
603     index();
604
605     SearchProjectsWsResponse result = call(request);
606
607     assertThat(result.getComponentsCount()).isEqualTo(
608       Stream.of(application1, application2, application3, project1, project2, project3)
609         .filter(c -> Stream.of(qualifiers).anyMatch(s -> s.equals(c.qualifier())))
610         .count());
611
612     assertThat(result.getComponentsList()).extracting(Component::getKey)
613       .containsExactly(
614         Stream.of(application1, application2, application3, project1, project2, project3)
615           .filter(c -> Stream.of(qualifiers).anyMatch(s -> s.equals(c.qualifier())))
616           .map(ComponentDto::getKey)
617           .toArray(String[]::new));
618   }
619
620   @Test
621   public void should_return_projects_only_when_no_edition() {
622     when(editionProviderMock.get()).thenReturn(Optional.empty());
623     userSession.logIn();
624
625     ComponentDto portfolio1 = insertPortfolio();
626     ComponentDto portfolio2 = insertPortfolio();
627
628     insertApplication();
629     insertApplication();
630     insertApplication();
631
632     ComponentDto project1 = insertProject();
633     ComponentDto project2 = insertProject();
634     ComponentDto project3 = insertProject();
635     index();
636
637     SearchProjectsWsResponse result = call(request);
638
639     assertThat(result.getComponentsCount()).isEqualTo(3);
640
641     assertThat(result.getComponentsList()).extracting(Component::getKey)
642       .containsExactly(Stream.of(project1, project2, project3).map(ComponentDto::getKey).toArray(String[]::new));
643   }
644
645   @Test
646   @UseDataProvider("enterprise_or_datacenter_edition")
647   public void filter_projects_and_apps_by_APP_qualifier_when_ee_dc(Edition edition) {
648     when(editionProviderMock.get()).thenReturn(Optional.of(edition));
649     userSession.logIn();
650     ComponentDto application1 = insertApplication();
651     ComponentDto application2 = insertApplication();
652     ComponentDto application3 = insertApplication();
653
654     insertProject();
655     insertProject();
656     insertProject();
657     index();
658
659     SearchProjectsWsResponse result = call(request.setFilter("qualifier = APP"));
660
661     assertThat(result.getComponentsCount())
662       .isEqualTo(3);
663
664     assertThat(result.getComponentsList()).extracting(Component::getKey)
665       .containsExactly(
666         Stream.of(application1, application2, application3)
667           .map(ComponentDto::getKey)
668           .toArray(String[]::new));
669   }
670
671   @Test
672   @UseDataProvider("enterprise_or_datacenter_edition")
673   public void filter_projects_and_apps_by_TRK_qualifier_when_ee_or_dc(Edition edition) {
674     when(editionProviderMock.get()).thenReturn(Optional.of(edition));
675     userSession.logIn();
676
677     insertApplication();
678     insertApplication();
679     insertApplication();
680
681     ComponentDto project1 = insertProject();
682     ComponentDto project2 = insertProject();
683     ComponentDto project3 = insertProject();
684     index();
685
686     SearchProjectsWsResponse result = call(request.setFilter("qualifier = TRK"));
687
688     assertThat(result.getComponentsCount())
689       .isEqualTo(3);
690
691     assertThat(result.getComponentsList()).extracting(Component::getKey)
692       .containsExactly(
693         Stream.of(project1, project2, project3)
694           .map(ComponentDto::getKey)
695           .toArray(String[]::new));
696   }
697
698   @Test
699   @UseDataProvider("community_or_developer_edition")
700   public void fail_when_qualifier_filter_by_APP_set_when_ce_or_de(Edition edition) {
701     when(editionProviderMock.get()).thenReturn(Optional.of(edition));
702     userSession.logIn();
703
704     assertThatThrownBy(() -> call(request.setFilter("qualifiers = APP")))
705       .isInstanceOf(IllegalArgumentException.class);
706   }
707
708   @Test
709   @UseDataProvider("enterprise_or_datacenter_edition")
710   public void fail_when_qualifier_filter_invalid_when_ee_or_dc(Edition edition) {
711     when(editionProviderMock.get()).thenReturn(Optional.of(edition));
712     userSession.logIn();
713
714     assertThatThrownBy(() -> call(request.setFilter("qualifiers = BLA")))
715       .isInstanceOf(IllegalArgumentException.class);
716   }
717
718   @Test
719   public void do_not_return_isFavorite_if_anonymous_user() {
720     userSession.anonymous();
721     insertProject();
722     index();
723
724     SearchProjectsWsResponse result = call(request);
725
726     assertThat(result.getComponentsList()).extracting(Component::hasIsFavorite).containsExactlyInAnyOrder(false);
727   }
728
729   @Test
730   public void return_nloc_facet() {
731     userSession.logIn();
732     MetricDto ncloc = db.measures().insertMetric(c -> c.setKey(NCLOC).setValueType(INT.name()));
733     insertProject(new Measure(ncloc, c -> c.setValue(5d)));
734     insertProject(new Measure(ncloc, c -> c.setValue(5d)));
735     insertProject(new Measure(ncloc, c -> c.setValue(10_000d)));
736     insertProject(new Measure(ncloc, c -> c.setValue(500_001d)));
737     index();
738
739     SearchProjectsWsResponse result = call(request.setFacets(singletonList(NCLOC)));
740
741     Common.Facet facet = result.getFacets().getFacetsList().stream()
742       .filter(oneFacet -> NCLOC.equals(oneFacet.getProperty()))
743       .findFirst().orElseThrow(IllegalStateException::new);
744     assertThat(facet.getValuesList())
745       .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
746       .containsExactly(
747         tuple("*-1000.0", 2L),
748         tuple("1000.0-10000.0", 0L),
749         tuple("10000.0-100000.0", 1L),
750         tuple("100000.0-500000.0", 0L),
751         tuple("500000.0-*", 1L));
752   }
753
754   @Test
755   public void return_new_lines_facet() {
756     userSession.logIn();
757     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(NEW_LINES_KEY).setValueType(INT.name()));
758     insertProject(new Measure(coverage, c -> c.setValue(100d)));
759     insertProject(new Measure(coverage, c -> c.setValue(15_000d)));
760     insertProject(new Measure(coverage, c -> c.setValue(50_000d)));
761     index();
762
763     SearchProjectsWsResponse result = call(request.setFacets(singletonList(NEW_LINES_KEY)));
764
765     Common.Facet facet = result.getFacets().getFacetsList().stream()
766       .filter(oneFacet -> NEW_LINES_KEY.equals(oneFacet.getProperty()))
767       .findFirst().orElseThrow(IllegalStateException::new);
768     assertThat(facet.getValuesList())
769       .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
770       .containsExactly(
771         tuple("*-1000.0", 1L),
772         tuple("1000.0-10000.0", 0L),
773         tuple("10000.0-100000.0", 2L),
774         tuple("100000.0-500000.0", 0L),
775         tuple("500000.0-*", 0L));
776   }
777
778   @Test
779   public void return_languages_facet() {
780     userSession.logIn();
781     MetricDto nclocMetric = db.measures().insertMetric(c -> c.setKey(NCLOC).setValueType(INT.name()));
782     MetricDto languagesDistributionMetric = db.measures().insertMetric(c -> c.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY).setValueType("DATA"));
783     ComponentDto project1 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("<null>=2;java=6;xoo=18")));
784     db.measures().insertLiveMeasure(project1, nclocMetric, m -> m.setValue(26d));
785     ComponentDto project2 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("java=5;xoo=19")));
786     db.measures().insertLiveMeasure(project2, nclocMetric, m -> m.setValue(24d));
787     ComponentDto project3 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("xoo=1")));
788     db.measures().insertLiveMeasure(project3, nclocMetric, m -> m.setValue(1d));
789     ComponentDto project4 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("<null>=1;java=3;xoo=8")));
790     db.measures().insertLiveMeasure(project4, nclocMetric, m -> m.setValue(12d));
791     index();
792
793     SearchProjectsWsResponse result = call(request.setFacets(singletonList(FILTER_LANGUAGES)));
794
795     Common.Facet facet = result.getFacets().getFacetsList().stream()
796       .filter(oneFacet -> FILTER_LANGUAGES.equals(oneFacet.getProperty()))
797       .findFirst().orElseThrow(IllegalStateException::new);
798     assertThat(facet.getValuesList())
799       .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
800       .containsExactly(
801         tuple("xoo", 4L),
802         tuple("java", 3L),
803         tuple("<null>", 2L));
804   }
805
806   @Test
807   public void return_languages_facet_with_language_having_no_project_if_language_is_in_filter() {
808     userSession.logIn();
809     MetricDto languagesDistributionMetric = db.measures().insertMetric(c -> c.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY).setValueType("DATA"));
810     MetricDto nclocMetric = db.measures().insertMetric(c -> c.setKey(NCLOC).setValueType(INT.name()));
811     ComponentDto project1 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("<null>=2;java=6")));
812     db.measures().insertLiveMeasure(project1, nclocMetric, m -> m.setValue(8d));
813     ComponentDto project2 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("java=5")));
814     db.measures().insertLiveMeasure(project2, nclocMetric, m -> m.setValue(5d));
815     index();
816
817     SearchProjectsWsResponse result = call(request.setFilter("languages = xoo").setFacets(singletonList(FILTER_LANGUAGES)));
818
819     Common.Facet facet = result.getFacets().getFacetsList().stream()
820       .filter(oneFacet -> FILTER_LANGUAGES.equals(oneFacet.getProperty()))
821       .findFirst().orElseThrow(IllegalStateException::new);
822     assertThat(facet.getValuesList())
823       .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
824       .containsOnly(
825         tuple("xoo", 0L),
826         tuple("java", 2L),
827         tuple("<null>", 1L));
828   }
829
830   @Test
831   public void return_tags_facet() {
832     userSession.logIn();
833     insertProject(defaults(), p -> p.setTags(asList("finance", "platform")));
834     insertProject(defaults(), p -> p.setTags(singletonList("offshore")));
835     insertProject(defaults(), p -> p.setTags(singletonList("offshore")));
836     index();
837
838     SearchProjectsWsResponse result = call(request.setFacets(singletonList(FILTER_TAGS)));
839
840     Common.Facet facet = result.getFacets().getFacetsList().stream()
841       .filter(oneFacet -> FILTER_TAGS.equals(oneFacet.getProperty()))
842       .findFirst().orElseThrow(IllegalStateException::new);
843     assertThat(facet.getValuesList())
844       .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
845       .containsExactly(
846         tuple("offshore", 2L),
847         tuple("finance", 1L),
848         tuple("platform", 1L));
849   }
850
851   @Test
852   public void return_tags_facet_with_tags_having_no_project_if_tags_is_in_filter() {
853     userSession.logIn();
854     insertProject(defaults(), p -> p.setTags(asList("finance", "platform")));
855     insertProject(defaults(), p -> p.setTags(singletonList("offshore")));
856     insertProject(defaults(), p -> p.setTags(singletonList("offshore")));
857     index();
858
859     SearchProjectsWsResponse result = call(request.setFilter("tags = marketing").setFacets(singletonList(FILTER_TAGS)));
860
861     Common.Facet facet = result.getFacets().getFacetsList().stream()
862       .filter(oneFacet -> FILTER_TAGS.equals(oneFacet.getProperty()))
863       .findFirst().orElseThrow(IllegalStateException::new);
864     assertThat(facet.getValuesList())
865       .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
866       .containsExactly(
867         tuple("offshore", 2L),
868         tuple("finance", 1L),
869         tuple("platform", 1L),
870         tuple("marketing", 0L));
871   }
872
873   @Test
874   public void return_qualifiers_facet() {
875     when(editionProviderMock.get()).thenReturn(Optional.of(Edition.ENTERPRISE));
876     userSession.logIn();
877     ComponentDto application1 = insertApplication();
878     ComponentDto application2 = insertApplication();
879     ComponentDto application3 = insertApplication();
880     ComponentDto application4 = insertApplication();
881
882     ComponentDto project1 = insertProject();
883     ComponentDto project2 = insertProject();
884     ComponentDto project3 = insertProject();
885     index();
886
887     SearchProjectsWsResponse result = call(request.setFacets(singletonList(FILTER_QUALIFIER)));
888
889     Common.Facet facet = result.getFacets().getFacetsList().stream()
890       .filter(oneFacet -> FILTER_QUALIFIER.equals(oneFacet.getProperty()))
891       .findFirst().orElseThrow(IllegalStateException::new);
892     assertThat(facet.getValuesList())
893       .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
894       .containsExactly(
895         tuple("APP", 4L),
896         tuple("TRK", 3L));
897   }
898
899   @Test
900   public void return_qualifiers_facet_with_qualifiers_having_no_project_if_qualifiers_is_in_filter() {
901     when(editionProviderMock.get()).thenReturn(Optional.of(Edition.ENTERPRISE));
902     userSession.logIn();
903     ComponentDto application1 = insertApplication();
904     ComponentDto application2 = insertApplication();
905     ComponentDto application3 = insertApplication();
906     ComponentDto application4 = insertApplication();
907     index();
908
909     SearchProjectsWsResponse result = call(request.setFilter("qualifier = APP").setFacets(singletonList(FILTER_QUALIFIER)));
910
911     Common.Facet facet = result.getFacets().getFacetsList().stream()
912       .filter(oneFacet -> FILTER_QUALIFIER.equals(oneFacet.getProperty()))
913       .findFirst().orElseThrow(IllegalStateException::new);
914     assertThat(facet.getValuesList())
915       .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
916       .containsExactly(
917         tuple("APP", 4L),
918         tuple("TRK", 0L));
919   }
920
921   @Test
922   @UseDataProvider("rating_metric_keys")
923   public void return_rating_facet(String ratingMetricKey) {
924     userSession.logIn();
925     MetricDto ratingMetric = db.measures().insertMetric(c -> c.setKey(ratingMetricKey).setValueType("RATING"));
926     insertProject(new Measure(ratingMetric, c -> c.setValue(1d)));
927     insertProject(new Measure(ratingMetric, c -> c.setValue(1d)));
928     insertProject(new Measure(ratingMetric, c -> c.setValue(3d)));
929     insertProject(new Measure(ratingMetric, c -> c.setValue(5d)));
930     index();
931
932     SearchProjectsWsResponse result = call(request.setFacets(singletonList(ratingMetricKey)));
933
934     Common.Facet facet = result.getFacets().getFacetsList().stream()
935       .filter(oneFacet -> ratingMetricKey.equals(oneFacet.getProperty()))
936       .findFirst().orElseThrow(IllegalStateException::new);
937     assertThat(facet.getValuesList())
938       .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
939       .containsExactly(
940         tuple("1", 2L),
941         tuple("2", 0L),
942         tuple("3", 1L),
943         tuple("4", 0L),
944         tuple("5", 1L));
945   }
946
947   @Test
948   @UseDataProvider("new_rating_metric_keys")
949   public void return_new_rating_facet(String newRatingMetricKey) {
950     userSession.logIn();
951     MetricDto newRatingMetric = db.measures().insertMetric(c -> c.setKey(newRatingMetricKey).setValueType("RATING"));
952     insertProject(new Measure(newRatingMetric, c -> c.setValue(1d)));
953     insertProject(new Measure(newRatingMetric, c -> c.setValue(1d)));
954     insertProject(new Measure(newRatingMetric, c -> c.setValue(3d)));
955     insertProject(new Measure(newRatingMetric, c -> c.setValue(5d)));
956     index();
957
958     SearchProjectsWsResponse result = call(request.setFacets(singletonList(newRatingMetricKey)));
959
960     Common.Facet facet = result.getFacets().getFacetsList().stream()
961       .filter(oneFacet -> newRatingMetricKey.equals(oneFacet.getProperty()))
962       .findFirst().orElseThrow(IllegalStateException::new);
963     assertThat(facet.getValuesList())
964       .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
965       .containsExactly(
966         tuple("1", 2L),
967         tuple("2", 0L),
968         tuple("3", 1L),
969         tuple("4", 0L),
970         tuple("5", 1L));
971   }
972
973   @Test
974   public void return_coverage_facet() {
975     userSession.logIn();
976     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(COVERAGE).setValueType("PERCENT"));
977     insertProject();
978     insertProject(new Measure(coverage, c -> c.setValue(80d)));
979     insertProject(new Measure(coverage, c -> c.setValue(85d)));
980     insertProject(new Measure(coverage, c -> c.setValue(10d)));
981     index();
982
983     SearchProjectsWsResponse result = call(request.setFacets(singletonList(COVERAGE)));
984
985     Common.Facet facet = result.getFacets().getFacetsList().stream()
986       .filter(oneFacet -> COVERAGE.equals(oneFacet.getProperty()))
987       .findFirst().orElseThrow(IllegalStateException::new);
988     assertThat(facet.getValuesList())
989       .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
990       .containsOnly(
991         tuple("NO_DATA", 1L),
992         tuple("*-30.0", 1L),
993         tuple("30.0-50.0", 0L),
994         tuple("50.0-70.0", 0L),
995         tuple("70.0-80.0", 0L),
996         tuple("80.0-*", 2L));
997   }
998
999   @Test
1000   public void return_new_coverage_facet() {
1001     userSession.logIn();
1002     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(NEW_COVERAGE).setValueType("PERCENT"));
1003     insertProject();
1004     insertProject(new Measure(coverage, c -> c.setValue(80d)));
1005     insertProject(new Measure(coverage, c -> c.setValue(85d)));
1006     insertProject(new Measure(coverage, c -> c.setValue(10d)));
1007     index();
1008
1009     SearchProjectsWsResponse result = call(request.setFacets(singletonList(NEW_COVERAGE)));
1010
1011     Common.Facet facet = result.getFacets().getFacetsList().stream()
1012       .filter(oneFacet -> NEW_COVERAGE.equals(oneFacet.getProperty()))
1013       .findFirst().orElseThrow(IllegalStateException::new);
1014     assertThat(facet.getValuesList())
1015       .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
1016       .containsOnly(
1017         tuple("NO_DATA", 1L),
1018         tuple("*-30.0", 1L),
1019         tuple("30.0-50.0", 0L),
1020         tuple("50.0-70.0", 0L),
1021         tuple("70.0-80.0", 0L),
1022         tuple("80.0-*", 2L));
1023   }
1024
1025   @Test
1026   public void return_duplications_facet() {
1027     userSession.logIn();
1028     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(DUPLICATED_LINES_DENSITY_KEY).setValueType("PERCENT"));
1029     insertProject(new Measure(coverage, c -> c.setValue(10d)));
1030     insertProject(new Measure(coverage, c -> c.setValue(15d)));
1031     insertProject(new Measure(coverage, c -> c.setValue(5d)));
1032     insertProject();
1033     index();
1034
1035     SearchProjectsWsResponse result = call(request.setFacets(singletonList(DUPLICATED_LINES_DENSITY_KEY)));
1036
1037     Common.Facet facet = result.getFacets().getFacetsList().stream()
1038       .filter(oneFacet -> DUPLICATED_LINES_DENSITY_KEY.equals(oneFacet.getProperty()))
1039       .findFirst().orElseThrow(IllegalStateException::new);
1040     assertThat(facet.getValuesList())
1041       .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
1042       .containsOnly(
1043         tuple("NO_DATA", 1L),
1044         tuple("*-3.0", 0L),
1045         tuple("3.0-5.0", 0L),
1046         tuple("5.0-10.0", 1L),
1047         tuple("10.0-20.0", 2L),
1048         tuple("20.0-*", 0L));
1049   }
1050
1051   @Test
1052   public void return_new_duplications_facet() {
1053     userSession.logIn();
1054     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(NEW_DUPLICATED_LINES_DENSITY_KEY).setValueType("PERCENT"));
1055     insertProject();
1056     insertProject(new Measure(coverage, c -> c.setValue(10d)));
1057     insertProject(new Measure(coverage, c -> c.setValue(15d)));
1058     insertProject(new Measure(coverage, c -> c.setValue(5d)));
1059     index();
1060
1061     SearchProjectsWsResponse result = call(request.setFacets(singletonList(NEW_DUPLICATED_LINES_DENSITY_KEY)));
1062
1063     Common.Facet facet = result.getFacets().getFacetsList().stream()
1064       .filter(oneFacet -> NEW_DUPLICATED_LINES_DENSITY_KEY.equals(oneFacet.getProperty()))
1065       .findFirst().orElseThrow(IllegalStateException::new);
1066     assertThat(facet.getValuesList())
1067       .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
1068       .containsOnly(
1069         tuple("NO_DATA", 1L),
1070         tuple("*-3.0", 0L),
1071         tuple("3.0-5.0", 0L),
1072         tuple("5.0-10.0", 1L),
1073         tuple("10.0-20.0", 2L),
1074         tuple("20.0-*", 0L));
1075   }
1076
1077   @Test
1078   public void return_quality_gate_facet() {
1079     userSession.logIn();
1080     MetricDto qualityGateStatus = db.measures().insertMetric(c -> c.setKey(ALERT_STATUS_KEY).setValueType(LEVEL.name()));
1081     insertProject(new Measure(qualityGateStatus, c -> c.setData(Metric.Level.ERROR.name()).setValue(null)));
1082     insertProject(new Measure(qualityGateStatus, c -> c.setData(Metric.Level.ERROR.name()).setValue(null)));
1083     insertProject(new Measure(qualityGateStatus, c -> c.setData(Metric.Level.WARN.name()).setValue(null)));
1084     insertProject(new Measure(qualityGateStatus, c -> c.setData(Metric.Level.OK.name()).setValue(null)));
1085     projectsInWarning.update(1L);
1086     index();
1087
1088     SearchProjectsWsResponse result = call(request.setFacets(singletonList(ALERT_STATUS_KEY)));
1089
1090     Common.Facet facet = result.getFacets().getFacetsList().stream()
1091       .filter(oneFacet -> ALERT_STATUS_KEY.equals(oneFacet.getProperty()))
1092       .findFirst().orElseThrow(IllegalStateException::new);
1093     assertThat(facet.getValuesList())
1094       .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
1095       .containsOnly(
1096         tuple("OK", 1L),
1097         tuple("ERROR", 2L),
1098         tuple("WARN", 1L));
1099   }
1100
1101   @Test
1102   public void return_quality_gate_facet_without_warning_when_no_projects_in_warning() {
1103     userSession.logIn();
1104     MetricDto qualityGateStatus = db.measures().insertMetric(c -> c.setKey(ALERT_STATUS_KEY).setValueType(LEVEL.name()));
1105     insertProject(new Measure(qualityGateStatus, c -> c.setData(Metric.Level.ERROR.name()).setValue(null)));
1106     insertProject(new Measure(qualityGateStatus, c -> c.setData(Metric.Level.ERROR.name()).setValue(null)));
1107     insertProject(new Measure(qualityGateStatus, c -> c.setData(Metric.Level.OK.name()).setValue(null)));
1108     projectsInWarning.update(0L);
1109     index();
1110
1111     SearchProjectsWsResponse result = call(request.setFacets(singletonList(ALERT_STATUS_KEY)));
1112
1113     Common.Facet facet = result.getFacets().getFacetsList().stream()
1114       .filter(oneFacet -> ALERT_STATUS_KEY.equals(oneFacet.getProperty()))
1115       .findFirst().orElseThrow(IllegalStateException::new);
1116     assertThat(facet.getValuesList())
1117       .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
1118       .containsOnly(
1119         tuple("OK", 1L),
1120         tuple("ERROR", 2L));
1121   }
1122
1123   @Test
1124   public void default_sort_is_by_ascending_name() {
1125     userSession.logIn();
1126     insertProject(c -> c.setName("Sonar Java"));
1127     insertProject(c -> c.setName("Sonar Groovy"));
1128     insertProject(c -> c.setName("Sonar Markdown"));
1129     insertProject(c -> c.setName("Sonar Qube"));
1130     index();
1131
1132     SearchProjectsWsResponse result = call(request);
1133
1134     assertThat(result.getComponentsList()).extracting(Component::getName).containsExactly("Sonar Groovy", "Sonar Java", "Sonar Markdown", "Sonar Qube");
1135   }
1136
1137   @Test
1138   public void sort_by_name() {
1139     userSession.logIn();
1140     insertProject(c -> c.setName("Sonar Java"));
1141     insertProject(c -> c.setName("Sonar Groovy"));
1142     insertProject(c -> c.setName("Sonar Markdown"));
1143     insertProject(c -> c.setName("Sonar Qube"));
1144     index();
1145
1146     assertThat(call(request.setSort("name").setAsc(true)).getComponentsList()).extracting(Component::getName)
1147       .containsExactly("Sonar Groovy", "Sonar Java", "Sonar Markdown", "Sonar Qube");
1148     assertThat(call(request.setSort("name").setAsc(false)).getComponentsList()).extracting(Component::getName)
1149       .containsExactly("Sonar Qube", "Sonar Markdown", "Sonar Java", "Sonar Groovy");
1150   }
1151
1152   @Test
1153   public void sort_by_coverage_then_by_name() {
1154     userSession.logIn();
1155     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(COVERAGE).setValueType(INT.name()));
1156     ComponentDto project1 = insertProject(c -> c.setName("Sonar Java"), new Measure(coverage, c -> c.setValue(81d)));
1157     ComponentDto project2 = insertProject(c -> c.setName("Sonar Groovy"), new Measure(coverage, c -> c.setValue(81d)));
1158     ComponentDto project3 = insertProject(c -> c.setName("Sonar Markdown"), new Measure(coverage, c -> c.setValue(80d)));
1159     ComponentDto project4 = insertProject(c -> c.setName("Sonar Qube"), new Measure(coverage, c -> c.setValue(80d)));
1160     index();
1161
1162     assertThat(call(request.setSort(COVERAGE).setAsc(true)).getComponentsList()).extracting(Component::getKey)
1163       .containsExactly(project3.getKey(), project4.getKey(), project2.getKey(), project1.getKey());
1164     assertThat(call(request.setSort(COVERAGE).setAsc(false)).getComponentsList()).extracting(Component::getKey)
1165       .containsExactly(project2.getKey(), project1.getKey(), project3.getKey(), project4.getKey());
1166   }
1167
1168   @Test
1169   public void sort_by_quality_gate_then_by_name() {
1170     userSession.logIn();
1171     MetricDto qualityGateStatus = db.measures().insertMetric(c -> c.setKey(QUALITY_GATE_STATUS).setValueType(LEVEL.name()));
1172     ComponentDto project1 = insertProject(c -> c.setName("Sonar Java"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("ERROR")));
1173     ComponentDto project2 = insertProject(c -> c.setName("Sonar Groovy"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("ERROR")));
1174     ComponentDto project3 = insertProject(c -> c.setName("Sonar Markdown"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("OK")));
1175     ComponentDto project4 = insertProject(c -> c.setName("Sonar Qube"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("OK")));
1176     index();
1177
1178     assertThat(call(request.setSort(QUALITY_GATE_STATUS).setAsc(true)).getComponentsList()).extracting(Component::getKey)
1179       .containsExactly(project3.getKey(), project4.getKey(), project2.getKey(), project1.getKey());
1180     assertThat(call(request.setSort(QUALITY_GATE_STATUS).setAsc(false)).getComponentsList()).extracting(Component::getKey)
1181       .containsExactly(project2.getKey(), project1.getKey(), project3.getKey(), project4.getKey());
1182   }
1183
1184   @Test
1185   public void sort_by_last_analysis_date() {
1186     userSession.logIn();
1187     ComponentDto project1 = db.components().insertPublicProject(p -> p.setKey("project1"));
1188     authorizationIndexerTester.allowOnlyAnyone(project1);
1189     ComponentDto project2 = db.components().insertPublicProject(p -> p.setKey("project2"));
1190     db.components().insertSnapshot(project2, snapshot -> snapshot.setCreatedAt(40_000_000_000L).setLast(true));
1191     authorizationIndexerTester.allowOnlyAnyone(project2);
1192     ComponentDto project3 = db.components().insertPublicProject(p -> p.setKey("project3"));
1193     db.components().insertSnapshot(project3, snapshot -> snapshot.setCreatedAt(20_000_000_000L).setLast(true));
1194     authorizationIndexerTester.allowOnlyAnyone(project3);
1195     ComponentDto project4 = db.components().insertPublicProject(p -> p.setKey("project4"));
1196     db.components().insertSnapshot(project4, snapshot -> snapshot.setCreatedAt(10_000_000_000L).setLast(false));
1197     db.components().insertSnapshot(project4, snapshot -> snapshot.setCreatedAt(30_000_000_000L).setLast(true));
1198     authorizationIndexerTester.allowOnlyAnyone(project4);
1199     index();
1200
1201     assertThat(call(request.setSort(ANALYSIS_DATE).setAsc(true)).getComponentsList()).extracting(Component::getKey)
1202       .containsExactly(project3.getKey(), project4.getKey(), project2.getKey(), project1.getKey());
1203
1204     assertThat(call(request.setSort(ANALYSIS_DATE).setAsc(false)).getComponentsList()).extracting(Component::getKey)
1205       .containsExactly(project2.getKey(), project4.getKey(), project3.getKey(), project1.getKey());
1206   }
1207
1208   @Test
1209   public void return_last_analysis_date() {
1210     userSession.logIn();
1211     ComponentDto project1 = db.components().insertPublicProject();
1212     db.components().insertSnapshot(project1, snapshot -> snapshot.setCreatedAt(10_000_000_000L).setLast(false));
1213     db.components().insertSnapshot(project1, snapshot -> snapshot.setCreatedAt(20_000_000_000L).setLast(true));
1214     authorizationIndexerTester.allowOnlyAnyone(project1);
1215     ComponentDto project2 = db.components().insertPublicProject();
1216     db.components().insertSnapshot(project2, snapshot -> snapshot.setCreatedAt(30_000_000_000L).setLast(true));
1217     authorizationIndexerTester.allowOnlyAnyone(project2);
1218     // No snapshot on project 3
1219     ComponentDto project3 = db.components().insertPublicProject();
1220     authorizationIndexerTester.allowOnlyAnyone(project3);
1221     index();
1222
1223     SearchProjectsWsResponse result = call(request.setAdditionalFields(singletonList("analysisDate")));
1224
1225     assertThat(result.getComponentsList()).extracting(Component::getKey, Component::hasAnalysisDate, Component::getAnalysisDate)
1226       .containsOnly(
1227         tuple(project1.getKey(), true, formatDateTime(new Date(20_000_000_000L))),
1228         tuple(project2.getKey(), true, formatDateTime(new Date(30_000_000_000L))),
1229         tuple(project3.getKey(), false, ""));
1230   }
1231
1232   @Test
1233   public void return_leak_period_date() {
1234     when(editionProviderMock.get()).thenReturn(Optional.of(Edition.ENTERPRISE));
1235     userSession.logIn();
1236     ComponentDto project1 = db.components().insertPublicProject();
1237     db.components().insertSnapshot(project1, snapshot -> snapshot.setPeriodDate(10_000_000_000L));
1238     authorizationIndexerTester.allowOnlyAnyone(project1);
1239     // No leak period
1240     ComponentDto project2 = db.components().insertPublicProject();
1241     db.components().insertSnapshot(project2, snapshot -> snapshot.setPeriodDate(null));
1242     authorizationIndexerTester.allowOnlyAnyone(project2);
1243     // No snapshot on project 3
1244     ComponentDto project3 = db.components().insertPublicProject();
1245     authorizationIndexerTester.allowOnlyAnyone(project3);
1246
1247     MetricDto leakProjects = db.measures().insertMetric(c -> c.setKey(LEAK_PROJECTS_KEY).setValueType(DATA.name()));
1248     ComponentDto application1 = insertApplication(
1249       new Measure(leakProjects, c -> c.setData("{\"leakProjects\":[{\"id\": 1, \"leak\":20000000000}, {\"id\": 2, \"leak\":10000000000}]}")));
1250     db.components().insertSnapshot(application1);
1251
1252     authorizationIndexerTester.allowOnlyAnyone(application1);
1253     index();
1254
1255     SearchProjectsWsResponse result = call(request.setAdditionalFields(singletonList("leakPeriodDate")));
1256
1257     assertThat(result.getComponentsList()).extracting(Component::getKey, Component::hasLeakPeriodDate, Component::getLeakPeriodDate)
1258       .containsOnly(
1259         tuple(project1.getKey(), true, formatDateTime(new Date(10_000_000_000L))),
1260         tuple(project2.getKey(), false, ""),
1261         tuple(project3.getKey(), false, ""),
1262         tuple(application1.getKey(), true, formatDateTime(new Date(10_000_000_000L))));
1263   }
1264
1265   @Test
1266   public void return_visibility_flag() {
1267     userSession.logIn();
1268     ComponentDto privateProject = db.components().insertPublicProject();
1269     authorizationIndexerTester.allowOnlyAnyone(privateProject);
1270     ComponentDto publicProject = db.components().insertPrivateProject();
1271     authorizationIndexerTester.allowOnlyAnyone(publicProject);
1272     index();
1273
1274     SearchProjectsWsResponse result = call(request);
1275
1276     assertThat(result.getComponentsList()).extracting(Component::getKey, Component::getVisibility)
1277       .containsExactly(
1278         tuple(privateProject.getKey(), privateProject.isPrivate() ? "private" : "public"),
1279         tuple(publicProject.getKey(), publicProject.isPrivate() ? "private" : "public"));
1280   }
1281
1282   @Test
1283   public void does_not_return_branches() {
1284     ComponentDto project = db.components().insertPublicProject();
1285     authorizationIndexerTester.allowOnlyAnyone(project);
1286     ComponentDto branch = db.components().insertProjectBranch(project);
1287     index();
1288
1289     SearchProjectsWsResponse result = call(request);
1290
1291     assertThat(result.getComponentsList()).extracting(Component::getKey)
1292       .containsExactlyInAnyOrder(project.getKey());
1293   }
1294
1295   @Test
1296   public void use_deprecated_warning_quality_gate_in_filter() {
1297     userSession.logIn();
1298     MetricDto qualityGateStatus = db.measures().insertMetric(c -> c.setKey(QUALITY_GATE_STATUS).setValueType(LEVEL.name()));
1299     ComponentDto project1 = insertProject(c -> c.setName("Sonar Java"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("ERROR")));
1300     ComponentDto project2 = insertProject(c -> c.setName("Sonar Groovy"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("WARN")));
1301     ComponentDto project3 = insertProject(c -> c.setName("Sonar Markdown"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("WARN")));
1302     ComponentDto project4 = insertProject(c -> c.setName("Sonar Qube"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("OK")));
1303     index();
1304
1305     List<Component> projects = call(request
1306       .setFilter("alert_status = WARN"))
1307       .getComponentsList();
1308
1309     assertThat(projects)
1310       .extracting(Component::getKey)
1311       .containsExactly(project2.getKey(), project3.getKey());
1312   }
1313
1314   @Test
1315   public void fail_when_filter_metrics_are_unknown() {
1316     userSession.logIn();
1317
1318     request.setFilter("debt > 80");
1319
1320     assertThatThrownBy(() -> call(request))
1321       .isInstanceOf(IllegalArgumentException.class)
1322       .hasMessage("Following metrics are not supported: 'debt'");
1323   }
1324
1325   @Test
1326   public void fail_when_sort_metrics_are_unknown() {
1327     userSession.logIn();
1328
1329     request.setSort("debt");
1330
1331     assertThatThrownBy(() -> call(request))
1332       .isInstanceOf(IllegalArgumentException.class)
1333       .hasMessageContaining("Value of parameter 's' (debt) must be one of: [");
1334   }
1335
1336   @Test
1337   public void fail_if_page_size_greater_than_500() {
1338     userSession.logIn();
1339     request.setPageSize(501);
1340     assertThatThrownBy(() -> call(request))
1341       .isInstanceOf(IllegalArgumentException.class);
1342   }
1343
1344   private SearchProjectsWsResponse call(RequestBuilder requestBuilder) {
1345     SearchProjectsRequest wsRequest = requestBuilder.build();
1346     TestRequest httpRequest = ws.newRequest();
1347     ofNullable(wsRequest.getFilter()).ifPresent(filter -> httpRequest.setParam(PARAM_FILTER, filter));
1348     ofNullable(wsRequest.getSort()).ifPresent(sort -> httpRequest.setParam(SORT, sort));
1349     ofNullable(wsRequest.getAsc()).ifPresent(asc -> httpRequest.setParam(ASCENDING, Boolean.toString(asc)));
1350     httpRequest.setParam(PAGE, String.valueOf(wsRequest.getPage()));
1351     httpRequest.setParam(PAGE_SIZE, String.valueOf(wsRequest.getPageSize()));
1352     httpRequest.setParam(FACETS, Joiner.on(",").join(wsRequest.getFacets()));
1353     httpRequest.setParam(FIELDS, Joiner.on(",").join(wsRequest.getAdditionalFields()));
1354     return httpRequest.executeProtobuf(SearchProjectsWsResponse.class);
1355   }
1356
1357   private void addFavourite(ComponentDto project) {
1358     addFavourite(project.uuid(), project.getKey(),
1359       project.name(), project.qualifier());
1360   }
1361
1362   private void addFavourite(@Nullable String componentUuid, @Nullable String componentKey,
1363     @Nullable String componentName, @Nullable String qualifier) {
1364     dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto().setKey("favourite")
1365         .setComponentUuid(componentUuid).setUserUuid(userSession.getUuid()), userSession.getLogin(), componentKey,
1366       componentName, qualifier);
1367     dbSession.commit();
1368   }
1369
1370   private ComponentDto insertProject(Measure... measures) {
1371     return insertProject(defaults(), defaults(), measures);
1372   }
1373
1374   private ComponentDto insertProject(Consumer<ComponentDto> componentConsumer, Measure... measures) {
1375     return insertProject(componentConsumer, defaults(), measures);
1376   }
1377
1378   private ComponentDto insertProject(Consumer<ComponentDto> componentConsumer, Consumer<ProjectDto> projectConsumer,
1379     Measure... measures) {
1380     ComponentDto project = db.components().insertPublicProject(componentConsumer, projectConsumer);
1381     Arrays.stream(measures).forEach(m -> db.measures().insertLiveMeasure(project, m.metric, m.consumer));
1382     return project;
1383   }
1384
1385   private ComponentDto insertApplication(Measure... measures) {
1386     return insertApplication(defaults(), measures);
1387   }
1388
1389   private ComponentDto insertApplication(Consumer<ComponentDto> componentConsumer, Measure... measures) {
1390     ComponentDto application = db.components().insertPublicApplication(componentConsumer);
1391     Arrays.stream(measures).forEach(m -> db.measures().insertLiveMeasure(application, m.metric, m.consumer));
1392     return application;
1393   }
1394
1395   private void index() {
1396     projectMeasuresIndexer.indexAll();
1397     Set<ComponentDto> roots = dbClient.componentDao().selectComponentsByQualifiers(db.getSession(),
1398       new HashSet<>(asList(Qualifiers.PROJECT, Qualifiers.VIEW, Qualifiers.APP)));
1399     authorizationIndexerTester.allowOnlyAnyone(roots.toArray(new ComponentDto[0]));
1400   }
1401
1402   private ComponentDto insertPortfolio() {
1403     return db.components().insertPublicPortfolio();
1404   }
1405
1406   private static class Measure {
1407     private final MetricDto metric;
1408     private final Consumer<LiveMeasureDto> consumer;
1409
1410     public Measure(MetricDto metric, Consumer<LiveMeasureDto> consumer) {
1411       this.metric = metric;
1412       this.consumer = consumer;
1413     }
1414   }
1415
1416   private static <T> Consumer<T> defaults() {
1417     return t -> {
1418     };
1419   }
1420 }