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