]> source.dussan.org Git - sonarqube.git/blob
dbcedbbc490b0f9ca8aa1f5ec854cde945d0cf70
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.server.measure.index;
21
22 import com.google.common.collect.ImmutableMap;
23 import com.google.common.collect.ImmutableSet;
24 import com.google.common.collect.Sets;
25 import com.tngtech.java.junit.dataprovider.DataProvider;
26 import com.tngtech.java.junit.dataprovider.DataProviderRunner;
27 import com.tngtech.java.junit.dataprovider.UseDataProvider;
28 import java.util.Arrays;
29 import java.util.LinkedHashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.stream.IntStream;
33 import org.junit.Rule;
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 import org.sonar.api.utils.System2;
37 import org.sonar.db.component.ComponentDto;
38 import org.sonar.db.component.ComponentTesting;
39 import org.sonar.db.user.GroupDto;
40 import org.sonar.db.user.UserDto;
41 import org.sonar.server.es.EsTester;
42 import org.sonar.server.es.Facets;
43 import org.sonar.server.es.SearchIdResult;
44 import org.sonar.server.es.SearchOptions;
45 import org.sonar.server.measure.index.ProjectMeasuresQuery.MetricCriterion;
46 import org.sonar.server.permission.index.IndexPermissions;
47 import org.sonar.server.permission.index.PermissionIndexerTester;
48 import org.sonar.server.permission.index.WebAuthorizationTypeSupport;
49 import org.sonar.server.tester.UserSessionRule;
50
51 import static com.google.common.collect.Lists.newArrayList;
52 import static com.google.common.collect.Sets.newHashSet;
53 import static java.util.Arrays.asList;
54 import static java.util.Arrays.stream;
55 import static java.util.Collections.singletonList;
56 import static java.util.stream.Collectors.toList;
57 import static org.assertj.core.api.Assertions.assertThat;
58 import static org.assertj.core.api.Assertions.assertThatThrownBy;
59 import static org.assertj.core.api.Assertions.entry;
60 import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
61 import static org.sonar.api.measures.CoreMetrics.COVERAGE_KEY;
62 import static org.sonar.api.measures.Metric.Level.ERROR;
63 import static org.sonar.api.measures.Metric.Level.OK;
64 import static org.sonar.api.measures.Metric.Level.WARN;
65 import static org.sonar.api.resources.Qualifiers.APP;
66 import static org.sonar.api.resources.Qualifiers.PROJECT;
67 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
68 import static org.sonar.db.user.GroupTesting.newGroupDto;
69 import static org.sonar.db.user.UserTesting.newUserDto;
70 import static org.sonar.server.measure.index.ProjectMeasuresIndexDefinition.FIELD_TAGS;
71 import static org.sonar.server.measure.index.ProjectMeasuresIndexDefinition.TYPE_PROJECT_MEASURES;
72 import static org.sonar.server.measure.index.ProjectMeasuresQuery.Operator;
73 import static org.sonarqube.ws.client.project.ProjectsWsParameters.FILTER_QUALIFIER;
74
75 @RunWith(DataProviderRunner.class)
76 public class ProjectMeasuresIndexTest {
77
78   private static final String MAINTAINABILITY_RATING = "sqale_rating";
79   private static final String NEW_MAINTAINABILITY_RATING_KEY = "new_maintainability_rating";
80   private static final String RELIABILITY_RATING = "reliability_rating";
81   private static final String NEW_RELIABILITY_RATING = "new_reliability_rating";
82   private static final String SECURITY_RATING = "security_rating";
83   private static final String NEW_SECURITY_RATING = "new_security_rating";
84   private static final String SECURITY_REVIEW_RATING = "security_review_rating";
85   private static final String NEW_SECURITY_REVIEW_RATING = "new_security_review_rating";
86   private static final String SECURITY_HOTSPOTS_REVIEWED = "security_hotspots_reviewed";
87   private static final String NEW_SECURITY_HOTSPOTS_REVIEWED = "new_security_hotspots_reviewed";
88   private static final String COVERAGE = "coverage";
89   private static final String NEW_COVERAGE = "new_coverage";
90   private static final String DUPLICATION = "duplicated_lines_density";
91   private static final String NEW_DUPLICATION = "new_duplicated_lines_density";
92   private static final String NCLOC = "ncloc";
93   private static final String NEW_LINES = "new_lines";
94   private static final String LANGUAGES = "languages";
95
96   private static final ComponentDto PROJECT1 = ComponentTesting.newPrivateProjectDto().setUuid("Project-1").setName("Project 1").setKey("key-1");
97   private static final ComponentDto PROJECT2 = ComponentTesting.newPrivateProjectDto().setUuid("Project-2").setName("Project 2").setKey("key-2");
98   private static final ComponentDto PROJECT3 = ComponentTesting.newPrivateProjectDto().setUuid("Project-3").setName("Project 3").setKey("key-3");
99   private static final ComponentDto APP1 = ComponentTesting.newApplication().setUuid("App-1").setName("App 1").setKey("app-key-1");
100   private static final ComponentDto APP2 = ComponentTesting.newApplication().setUuid("App-2").setName("App 2").setKey("app-key-2");
101   private static final ComponentDto APP3 = ComponentTesting.newApplication().setUuid("App-3").setName("App 3").setKey("app-key-3");
102   private static final UserDto USER1 = newUserDto();
103   private static final UserDto USER2 = newUserDto();
104   private static final GroupDto GROUP1 = newGroupDto();
105   private static final GroupDto GROUP2 = newGroupDto();
106
107   @Rule
108   public EsTester es = EsTester.create();
109   @Rule
110   public UserSessionRule userSession = UserSessionRule.standalone();
111
112   @DataProvider
113   public static Object[][] rating_metric_keys() {
114     return new Object[][] {{MAINTAINABILITY_RATING}, {NEW_MAINTAINABILITY_RATING_KEY}, {RELIABILITY_RATING}, {NEW_RELIABILITY_RATING}, {SECURITY_RATING}, {NEW_SECURITY_RATING},
115       {SECURITY_REVIEW_RATING}, {NEW_SECURITY_REVIEW_RATING}};
116   }
117
118   private ProjectMeasuresIndexer projectMeasureIndexer = new ProjectMeasuresIndexer(null, es.client());
119   private PermissionIndexerTester authorizationIndexer = new PermissionIndexerTester(es, projectMeasureIndexer);
120   private ProjectMeasuresIndex underTest = new ProjectMeasuresIndex(es.client(), new WebAuthorizationTypeSupport(userSession), System2.INSTANCE);
121
122   @Test
123   public void return_empty_if_no_projects() {
124     assertNoResults(new ProjectMeasuresQuery());
125   }
126
127   @Test
128   public void default_sort_is_by_ascending_case_insensitive_name_then_by_key() {
129     ComponentDto windows = ComponentTesting.newPrivateProjectDto().setUuid("windows").setName("Windows").setKey("project1");
130     ComponentDto apachee = ComponentTesting.newPrivateProjectDto().setUuid("apachee").setName("apachee").setKey("project2");
131     ComponentDto apache1 = ComponentTesting.newPrivateProjectDto().setUuid("apache-1").setName("Apache").setKey("project3");
132     ComponentDto apache2 = ComponentTesting.newPrivateProjectDto().setUuid("apache-2").setName("Apache").setKey("project4");
133     index(newDoc(windows), newDoc(apachee), newDoc(apache1), newDoc(apache2));
134
135     assertResults(new ProjectMeasuresQuery(), apache1, apache2, apachee, windows);
136   }
137
138   @Test
139   public void sort_by_insensitive_name() {
140     ComponentDto windows = ComponentTesting.newPrivateProjectDto().setUuid("windows").setName("Windows");
141     ComponentDto apachee = ComponentTesting.newPrivateProjectDto().setUuid("apachee").setName("apachee");
142     ComponentDto apache = ComponentTesting.newPrivateProjectDto().setUuid("apache").setName("Apache");
143     index(newDoc(windows), newDoc(apachee), newDoc(apache));
144
145     assertResults(new ProjectMeasuresQuery().setSort("name").setAsc(true), apache, apachee, windows);
146     assertResults(new ProjectMeasuresQuery().setSort("name").setAsc(false), windows, apachee, apache);
147   }
148
149   @Test
150   public void sort_by_ncloc() {
151     index(
152       newDoc(PROJECT1, NCLOC, 15_000d),
153       newDoc(PROJECT2, NCLOC, 30_000d),
154       newDoc(PROJECT3, NCLOC, 1_000d));
155
156     assertResults(new ProjectMeasuresQuery().setSort("ncloc").setAsc(true), PROJECT3, PROJECT1, PROJECT2);
157     assertResults(new ProjectMeasuresQuery().setSort("ncloc").setAsc(false), PROJECT2, PROJECT1, PROJECT3);
158   }
159
160   @Test
161   public void sort_by_a_metric_then_by_name_then_by_key() {
162     ComponentDto windows = ComponentTesting.newPrivateProjectDto().setUuid("windows").setName("Windows").setKey("project1");
163     ComponentDto apachee = ComponentTesting.newPrivateProjectDto().setUuid("apachee").setName("apachee").setKey("project2");
164     ComponentDto apache1 = ComponentTesting.newPrivateProjectDto().setUuid("apache-1").setName("Apache").setKey("project3");
165     ComponentDto apache2 = ComponentTesting.newPrivateProjectDto().setUuid("apache-2").setName("Apache").setKey("project4");
166     index(
167       newDoc(windows, NCLOC, 10_000d),
168       newDoc(apachee, NCLOC, 5_000d),
169       newDoc(apache1, NCLOC, 5_000d),
170       newDoc(apache2, NCLOC, 5_000d));
171
172     assertResults(new ProjectMeasuresQuery().setSort("ncloc").setAsc(true), apache1, apache2, apachee, windows);
173     assertResults(new ProjectMeasuresQuery().setSort("ncloc").setAsc(false), windows, apache1, apache2, apachee);
174   }
175
176   @Test
177   public void sort_by_quality_gate_status() {
178     ComponentDto project4 = ComponentTesting.newPrivateProjectDto().setUuid("Project-4").setName("Project 4").setKey("key-4");
179     index(
180       newDoc(PROJECT1).setQualityGateStatus(OK.name()),
181       newDoc(PROJECT2).setQualityGateStatus(ERROR.name()),
182       newDoc(project4).setQualityGateStatus(OK.name()));
183
184     assertResults(new ProjectMeasuresQuery().setSort("alert_status").setAsc(true), PROJECT1, project4, PROJECT2);
185     assertResults(new ProjectMeasuresQuery().setSort("alert_status").setAsc(false), PROJECT2, PROJECT1, project4);
186   }
187
188   @Test
189   public void sort_by_quality_gate_status_then_by_name_then_by_key() {
190     ComponentDto windows = ComponentTesting.newPrivateProjectDto().setUuid("windows").setName("Windows").setKey("project1");
191     ComponentDto apachee = ComponentTesting.newPrivateProjectDto().setUuid("apachee").setName("apachee").setKey("project2");
192     ComponentDto apache1 = ComponentTesting.newPrivateProjectDto().setUuid("apache-1").setName("Apache").setKey("project3");
193     ComponentDto apache2 = ComponentTesting.newPrivateProjectDto().setUuid("apache-2").setName("Apache").setKey("project4");
194     index(
195       newDoc(windows).setQualityGateStatus(ERROR.name()),
196       newDoc(apachee).setQualityGateStatus(OK.name()),
197       newDoc(apache1).setQualityGateStatus(OK.name()),
198       newDoc(apache2).setQualityGateStatus(OK.name()));
199
200     assertResults(new ProjectMeasuresQuery().setSort("alert_status").setAsc(true), apache1, apache2, apachee, windows);
201     assertResults(new ProjectMeasuresQuery().setSort("alert_status").setAsc(false), windows, apache1, apache2, apachee);
202   }
203
204   @Test
205   public void paginate_results() {
206     IntStream.rangeClosed(1, 9)
207       .forEach(i -> index(newDoc(newPrivateProjectDto("P" + i))));
208
209     SearchIdResult<String> result = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().setPage(2, 3));
210
211     assertThat(result.getUuids()).containsExactly("P4", "P5", "P6");
212     assertThat(result.getTotal()).isEqualTo(9);
213   }
214
215   @Test
216   public void filter_with_lower_than() {
217     index(
218       newDoc(PROJECT1, COVERAGE, 79d, NCLOC, 10_000d),
219       newDoc(PROJECT2, COVERAGE, 80d, NCLOC, 10_000d),
220       newDoc(PROJECT3, COVERAGE, 81d, NCLOC, 10_000d));
221
222     ProjectMeasuresQuery query = new ProjectMeasuresQuery()
223       .addMetricCriterion(MetricCriterion.create(COVERAGE, Operator.LT, 80d));
224
225     assertResults(query, PROJECT1);
226   }
227
228   @Test
229   public void filter_with_lower_than_or_equals() {
230     index(
231       newDoc(PROJECT1, COVERAGE, 79d, NCLOC, 10_000d),
232       newDoc(PROJECT2, COVERAGE, 80d, NCLOC, 10_000d),
233       newDoc(PROJECT3, COVERAGE, 81d, NCLOC, 10_000d));
234
235     ProjectMeasuresQuery query = new ProjectMeasuresQuery()
236       .addMetricCriterion(MetricCriterion.create(COVERAGE, Operator.LTE, 80d));
237
238     assertResults(query, PROJECT1, PROJECT2);
239   }
240
241   @Test
242   public void filter_with_greater_than() {
243     index(
244       newDoc(PROJECT1, COVERAGE, 80d, NCLOC, 30_000d),
245       newDoc(PROJECT2, COVERAGE, 80d, NCLOC, 30_001d),
246       newDoc(PROJECT3, COVERAGE, 80d, NCLOC, 30_001d));
247
248     ProjectMeasuresQuery query = new ProjectMeasuresQuery().addMetricCriterion(MetricCriterion.create(NCLOC, Operator.GT, 30_000d));
249     assertResults(query, PROJECT2, PROJECT3);
250
251     query = new ProjectMeasuresQuery().addMetricCriterion(MetricCriterion.create(NCLOC, Operator.GT, 100_000d));
252     assertNoResults(query);
253   }
254
255   @Test
256   public void filter_with_greater_than_or_equals() {
257     index(
258       newDoc(PROJECT1, COVERAGE, 80d, NCLOC, 30_000d),
259       newDoc(PROJECT2, COVERAGE, 80d, NCLOC, 30_001d),
260       newDoc(PROJECT3, COVERAGE, 80d, NCLOC, 30_001d));
261
262     ProjectMeasuresQuery query = new ProjectMeasuresQuery().addMetricCriterion(MetricCriterion.create(NCLOC, Operator.GTE, 30_001d));
263     assertResults(query, PROJECT2, PROJECT3);
264
265     query = new ProjectMeasuresQuery().addMetricCriterion(MetricCriterion.create(NCLOC, Operator.GTE, 100_000d));
266     assertNoResults(query);
267   }
268
269   @Test
270   public void filter_with_equals() {
271     index(
272       newDoc(PROJECT1, COVERAGE, 79d, NCLOC, 10_000d),
273       newDoc(PROJECT2, COVERAGE, 80d, NCLOC, 10_000d),
274       newDoc(PROJECT3, COVERAGE, 81d, NCLOC, 10_000d));
275
276     ProjectMeasuresQuery query = new ProjectMeasuresQuery()
277       .addMetricCriterion(MetricCriterion.create(COVERAGE, Operator.EQ, 80d));
278
279     assertResults(query, PROJECT2);
280   }
281
282   @Test
283   public void filter_on_no_data_with_several_projects() {
284     index(
285       newDoc(PROJECT1, NCLOC, 1d),
286       newDoc(PROJECT2, DUPLICATION, 80d));
287
288     ProjectMeasuresQuery query = new ProjectMeasuresQuery()
289       .addMetricCriterion(MetricCriterion.createNoData(DUPLICATION));
290
291     assertResults(query, PROJECT1);
292   }
293
294   @Test
295   public void filter_on_no_data_should_not_return_projects_with_data_and_other_measures() {
296     ComponentDto project = ComponentTesting.newPrivateProjectDto();
297     index(newDoc(project, DUPLICATION, 80d, NCLOC, 1d));
298
299     ProjectMeasuresQuery query = new ProjectMeasuresQuery().addMetricCriterion(MetricCriterion.createNoData(DUPLICATION));
300
301     assertNoResults(query);
302   }
303
304   @Test
305   public void filter_on_no_data_should_not_return_projects_with_data() {
306     ComponentDto project = ComponentTesting.newPrivateProjectDto();
307     index(newDoc(project, DUPLICATION, 80d));
308
309     ProjectMeasuresQuery query = new ProjectMeasuresQuery().addMetricCriterion(MetricCriterion.createNoData(DUPLICATION));
310
311     assertNoResults(query);
312   }
313
314   @Test
315   public void filter_on_no_data_should_return_projects_with_no_data() {
316     ComponentDto project = ComponentTesting.newPrivateProjectDto();
317     index(newDoc(project, NCLOC, 1d));
318
319     ProjectMeasuresQuery query = new ProjectMeasuresQuery().addMetricCriterion(MetricCriterion.createNoData(DUPLICATION));
320
321     assertResults(query, project);
322   }
323
324   @Test
325   public void filter_on_several_metrics() {
326     index(
327       newDoc(PROJECT1, COVERAGE, 81d, NCLOC, 10_001d),
328       newDoc(PROJECT2, COVERAGE, 80d, NCLOC, 10_001d),
329       newDoc(PROJECT3, COVERAGE, 79d, NCLOC, 10_000d));
330
331     ProjectMeasuresQuery esQuery = new ProjectMeasuresQuery()
332       .addMetricCriterion(MetricCriterion.create(COVERAGE, Operator.LTE, 80d))
333       .addMetricCriterion(MetricCriterion.create(NCLOC, Operator.GT, 10_000d))
334       .addMetricCriterion(MetricCriterion.create(NCLOC, Operator.LT, 11_000d));
335     assertResults(esQuery, PROJECT2);
336   }
337
338   @Test
339   public void facet_security_hotspots_reviewed() {
340     index(
341       // 2 docs with no measure
342       newDocWithNoMeasure(),
343       newDocWithNoMeasure(),
344       // 3 docs < 30%
345       newDoc(SECURITY_HOTSPOTS_REVIEWED, 29),
346       newDoc(SECURITY_HOTSPOTS_REVIEWED, 28),
347       newDoc(SECURITY_HOTSPOTS_REVIEWED, 0),
348       // 2 docs with >=30% and <50%
349       newDoc(SECURITY_HOTSPOTS_REVIEWED, 30),
350       newDoc(SECURITY_HOTSPOTS_REVIEWED, 49),
351       // 4 docs with >=50% and <70%
352       newDoc(SECURITY_HOTSPOTS_REVIEWED, 50),
353       newDoc(SECURITY_HOTSPOTS_REVIEWED, 60),
354       newDoc(SECURITY_HOTSPOTS_REVIEWED, 61),
355       newDoc(SECURITY_HOTSPOTS_REVIEWED, 69),
356       // 2 docs with >=70% and <80%
357       newDoc(SECURITY_HOTSPOTS_REVIEWED, 70),
358       newDoc(SECURITY_HOTSPOTS_REVIEWED, 79),
359       // 5 docs with >= 80%
360       newDoc(SECURITY_HOTSPOTS_REVIEWED, 80),
361       newDoc(SECURITY_HOTSPOTS_REVIEWED, 90),
362       newDoc(SECURITY_HOTSPOTS_REVIEWED, 93),
363       newDoc(SECURITY_HOTSPOTS_REVIEWED, 99),
364       newDoc(SECURITY_HOTSPOTS_REVIEWED, 100));
365
366     Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(SECURITY_HOTSPOTS_REVIEWED)).getFacets();
367     assertThat(facets.get(SECURITY_HOTSPOTS_REVIEWED)).containsExactly(
368       entry("*-30.0", 3L),
369       entry("30.0-50.0", 2L),
370       entry("50.0-70.0", 4L),
371       entry("70.0-80.0", 2L),
372       entry("80.0-*", 5L));
373   }
374
375   @Test
376   public void facet_new_security_hotspots_reviewed() {
377     index(
378       // 2 docs with no measure
379       newDocWithNoMeasure(),
380       newDocWithNoMeasure(),
381       // 3 docs < 30%
382       newDoc(NEW_SECURITY_HOTSPOTS_REVIEWED, 29),
383       newDoc(NEW_SECURITY_HOTSPOTS_REVIEWED, 28),
384       newDoc(NEW_SECURITY_HOTSPOTS_REVIEWED, 0),
385       // 2 docs with >=30% and <50%
386       newDoc(NEW_SECURITY_HOTSPOTS_REVIEWED, 30),
387       newDoc(NEW_SECURITY_HOTSPOTS_REVIEWED, 49),
388       // 4 docs with >=50% and <70%
389       newDoc(NEW_SECURITY_HOTSPOTS_REVIEWED, 50),
390       newDoc(NEW_SECURITY_HOTSPOTS_REVIEWED, 60),
391       newDoc(NEW_SECURITY_HOTSPOTS_REVIEWED, 61),
392       newDoc(NEW_SECURITY_HOTSPOTS_REVIEWED, 69),
393       // 2 docs with >=70% and <80%
394       newDoc(NEW_SECURITY_HOTSPOTS_REVIEWED, 70),
395       newDoc(NEW_SECURITY_HOTSPOTS_REVIEWED, 79),
396       // 5 docs with >= 80%
397       newDoc(NEW_SECURITY_HOTSPOTS_REVIEWED, 80),
398       newDoc(NEW_SECURITY_HOTSPOTS_REVIEWED, 90),
399       newDoc(NEW_SECURITY_HOTSPOTS_REVIEWED, 93),
400       newDoc(NEW_SECURITY_HOTSPOTS_REVIEWED, 99),
401       newDoc(NEW_SECURITY_HOTSPOTS_REVIEWED, 100));
402
403     Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(NEW_SECURITY_HOTSPOTS_REVIEWED)).getFacets();
404     assertThat(facets.get(NEW_SECURITY_HOTSPOTS_REVIEWED)).containsExactly(
405       entry("*-30.0", 3L),
406       entry("30.0-50.0", 2L),
407       entry("50.0-70.0", 4L),
408       entry("70.0-80.0", 2L),
409       entry("80.0-*", 5L));
410   }
411
412   @Test
413   public void filter_on_quality_gate_status() {
414     index(
415       newDoc(PROJECT1).setQualityGateStatus(OK.name()),
416       newDoc(PROJECT2).setQualityGateStatus(OK.name()),
417       newDoc(PROJECT3).setQualityGateStatus(ERROR.name()));
418
419     ProjectMeasuresQuery query = new ProjectMeasuresQuery().setQualityGateStatus(OK);
420     assertResults(query, PROJECT1, PROJECT2);
421   }
422
423   @Test
424   public void filter_on_languages() {
425     ComponentDto project4 = ComponentTesting.newPrivateProjectDto().setUuid("Project-4").setName("Project 4").setKey("key-4");
426     index(
427       newDoc(PROJECT1).setLanguages(singletonList("java")),
428       newDoc(PROJECT2).setLanguages(singletonList("xoo")),
429       newDoc(PROJECT3).setLanguages(singletonList("xoo")),
430       newDoc(project4).setLanguages(asList("<null>", "java", "xoo")));
431
432     assertResults(new ProjectMeasuresQuery().setLanguages(newHashSet("java", "xoo")), PROJECT1, PROJECT2, PROJECT3, project4);
433     assertResults(new ProjectMeasuresQuery().setLanguages(newHashSet("java")), PROJECT1, project4);
434     assertResults(new ProjectMeasuresQuery().setLanguages(newHashSet("unknown")));
435   }
436
437   @Test
438   public void filter_on_query_text() {
439     ComponentDto windows = ComponentTesting.newPrivateProjectDto().setUuid("windows").setName("Windows").setKey("project1");
440     ComponentDto apachee = ComponentTesting.newPrivateProjectDto().setUuid("apachee").setName("apachee").setKey("project2");
441     ComponentDto apache1 = ComponentTesting.newPrivateProjectDto().setUuid("apache-1").setName("Apache").setKey("project3");
442     ComponentDto apache2 = ComponentTesting.newPrivateProjectDto().setUuid("apache-2").setName("Apache").setKey("project4");
443     index(newDoc(windows), newDoc(apachee), newDoc(apache1), newDoc(apache2));
444
445     assertResults(new ProjectMeasuresQuery().setQueryText("windows"), windows);
446     assertResults(new ProjectMeasuresQuery().setQueryText("project2"), apachee);
447     assertResults(new ProjectMeasuresQuery().setQueryText("pAch"), apache1, apache2, apachee);
448   }
449
450   @Test
451   public void filter_on_ids() {
452     index(
453       newDoc(PROJECT1),
454       newDoc(PROJECT2),
455       newDoc(PROJECT3));
456
457     ProjectMeasuresQuery query = new ProjectMeasuresQuery().setProjectUuids(newHashSet(PROJECT1.uuid(), PROJECT3.uuid()));
458     assertResults(query, PROJECT1, PROJECT3);
459   }
460
461   @Test
462   public void filter_on_tags() {
463     index(
464       newDoc(PROJECT1).setTags(newArrayList("finance", "platform")),
465       newDoc(PROJECT2).setTags(newArrayList("marketing", "platform")),
466       newDoc(PROJECT3).setTags(newArrayList("finance", "language")));
467
468     assertResults(new ProjectMeasuresQuery().setTags(newHashSet("finance")), PROJECT1, PROJECT3);
469     assertResults(new ProjectMeasuresQuery().setTags(newHashSet("finance", "language")), PROJECT1, PROJECT3);
470     assertResults(new ProjectMeasuresQuery().setTags(newHashSet("finance", "marketing")), PROJECT1, PROJECT2, PROJECT3);
471     assertResults(new ProjectMeasuresQuery().setTags(newHashSet("marketing")), PROJECT2);
472     assertNoResults(new ProjectMeasuresQuery().setTags(newHashSet("tag 42")));
473   }
474
475   @Test
476   public void filter_on_qualifier() {
477     index(newDoc(PROJECT1), newDoc(PROJECT2), newDoc(PROJECT3),
478       newDoc(APP1), newDoc(APP2), newDoc(APP3));
479
480     assertResults(new ProjectMeasuresQuery(),
481       APP1, APP2, APP3, PROJECT1, PROJECT2, PROJECT3);
482
483     assertResults(new ProjectMeasuresQuery().setQualifiers(Sets.newHashSet(PROJECT, APP)),
484       APP1, APP2, APP3, PROJECT1, PROJECT2, PROJECT3);
485
486     assertResults(new ProjectMeasuresQuery().setQualifiers(Sets.newHashSet(PROJECT)),
487       PROJECT1, PROJECT2, PROJECT3);
488
489     assertResults(new ProjectMeasuresQuery().setQualifiers(Sets.newHashSet(APP)),
490       APP1, APP2, APP3);
491   }
492
493   @Test
494   public void return_correct_number_of_total_if_exceeds_index_max_results() {
495     index(IntStream.range(0, 12_000)
496       .mapToObj(operand -> newDoc(ComponentTesting.newPrivateProjectDto()))
497       .toArray(ProjectMeasuresDoc[]::new));
498
499     ProjectMeasuresQuery query = new ProjectMeasuresQuery();
500     SearchIdResult<String> result = underTest.search(query, new SearchOptions());
501     assertThat(result.getTotal()).isEqualTo(12_000);
502   }
503
504   @Test
505   public void return_only_projects_and_applications_authorized_for_user() {
506     indexForUser(USER1, newDoc(PROJECT1), newDoc(PROJECT2),
507       newDoc(APP1), newDoc(APP2));
508     indexForUser(USER2, newDoc(PROJECT3), newDoc(APP3));
509
510     userSession.logIn(USER1);
511     assertResults(new ProjectMeasuresQuery(), APP1, APP2, PROJECT1, PROJECT2);
512   }
513
514   @Test
515   public void return_only_projects_and_applications_authorized_for_user_groups() {
516     indexForGroup(GROUP1, newDoc(PROJECT1), newDoc(PROJECT2),
517       newDoc(APP1), newDoc(APP2));
518     indexForGroup(GROUP2, newDoc(PROJECT3));
519
520     userSession.logIn().setGroups(GROUP1);
521     assertResults(new ProjectMeasuresQuery(), APP1, APP2, PROJECT1, PROJECT2);
522   }
523
524   @Test
525   public void return_only_projects_and_applications_authorized_for_user_and_groups() {
526     indexForUser(USER1, newDoc(PROJECT1), newDoc(PROJECT2),
527       newDoc(APP1), newDoc(APP2));
528     indexForGroup(GROUP1, newDoc(PROJECT3));
529
530     userSession.logIn(USER1).setGroups(GROUP1);
531     assertResults(new ProjectMeasuresQuery(), APP1, APP2, PROJECT1, PROJECT2, PROJECT3);
532   }
533
534   @Test
535   public void anonymous_user_can_only_access_projects_and_applications_authorized_for_anyone() {
536     index(newDoc(PROJECT1), newDoc(APP1));
537     indexForUser(USER1, newDoc(PROJECT2), newDoc(APP2));
538
539     userSession.anonymous();
540     assertResults(new ProjectMeasuresQuery(), APP1, PROJECT1);
541   }
542
543   @Test
544   public void return_all_projects_and_applications_when_setIgnoreAuthorization_is_true() {
545     indexForUser(USER1, newDoc(PROJECT1), newDoc(PROJECT2), newDoc(APP1), newDoc(APP2));
546     indexForUser(USER2, newDoc(PROJECT3), newDoc(APP3));
547     userSession.logIn(USER1);
548
549     assertResults(new ProjectMeasuresQuery().setIgnoreAuthorization(false), APP1, APP2, PROJECT1, PROJECT2);
550     assertResults(new ProjectMeasuresQuery().setIgnoreAuthorization(true), APP1, APP2, APP3, PROJECT1, PROJECT2, PROJECT3);
551   }
552
553   @Test
554   public void does_not_return_facet_when_no_facets_in_options() {
555     index(
556       newDoc(PROJECT1, NCLOC, 10d, COVERAGE_KEY, 30d, MAINTAINABILITY_RATING, 3d)
557         .setQualityGateStatus(OK.name()));
558
559     Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions()).getFacets();
560
561     assertThat(facets.getAll()).isEmpty();
562   }
563
564   @Test
565   public void facet_ncloc() {
566     index(
567       // 3 docs with ncloc<1K
568       newDoc(NCLOC, 0d),
569       newDoc(NCLOC, 0d),
570       newDoc(NCLOC, 999d),
571       // 2 docs with ncloc>=1K and ncloc<10K
572       newDoc(NCLOC, 1_000d),
573       newDoc(NCLOC, 9_999d),
574       // 4 docs with ncloc>=10K and ncloc<100K
575       newDoc(NCLOC, 10_000d),
576       newDoc(NCLOC, 10_000d),
577       newDoc(NCLOC, 11_000d),
578       newDoc(NCLOC, 99_000d),
579       // 2 docs with ncloc>=100K and ncloc<500K
580       newDoc(NCLOC, 100_000d),
581       newDoc(NCLOC, 499_000d),
582       // 5 docs with ncloc>= 500K
583       newDoc(NCLOC, 500_000d),
584       newDoc(NCLOC, 100_000_000d),
585       newDoc(NCLOC, 500_000d),
586       newDoc(NCLOC, 1_000_000d),
587       newDoc(NCLOC, 100_000_000_000d));
588
589     Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(NCLOC)).getFacets();
590
591     assertThat(facets.get(NCLOC)).containsExactly(
592       entry("*-1000.0", 3L),
593       entry("1000.0-10000.0", 2L),
594       entry("10000.0-100000.0", 4L),
595       entry("100000.0-500000.0", 2L),
596       entry("500000.0-*", 5L));
597   }
598
599   @Test
600   public void facet_ncloc_is_sticky() {
601     index(
602       // 1 docs with ncloc<1K
603       newDoc(NCLOC, 999d, COVERAGE, 0d, DUPLICATION, 0d),
604       // 2 docs with ncloc>=1K and ncloc<10K
605       newDoc(NCLOC, 1_000d, COVERAGE, 10d, DUPLICATION, 0d),
606       newDoc(NCLOC, 9_999d, COVERAGE, 20d, DUPLICATION, 0d),
607       // 3 docs with ncloc>=10K and ncloc<100K
608       newDoc(NCLOC, 10_000d, COVERAGE, 31d, DUPLICATION, 0d),
609       newDoc(NCLOC, 11_000d, COVERAGE, 40d, DUPLICATION, 0d),
610       newDoc(NCLOC, 99_000d, COVERAGE, 50d, DUPLICATION, 0d),
611       // 2 docs with ncloc>=100K and ncloc<500K
612       newDoc(NCLOC, 100_000d, COVERAGE, 71d, DUPLICATION, 0d),
613       newDoc(NCLOC, 499_000d, COVERAGE, 80d, DUPLICATION, 0d),
614       // 1 docs with ncloc>= 500K
615       newDoc(NCLOC, 501_000d, COVERAGE, 81d, DUPLICATION, 20d));
616
617     Facets facets = underTest.search(new ProjectMeasuresQuery()
618       .addMetricCriterion(MetricCriterion.create(NCLOC, Operator.LT, 10_000d))
619       .addMetricCriterion(MetricCriterion.create(DUPLICATION, Operator.LT, 10d)),
620       new SearchOptions().addFacets(NCLOC, COVERAGE)).getFacets();
621
622     // Sticky facet on ncloc does not take into account ncloc filter
623     assertThat(facets.get(NCLOC)).containsExactly(
624       entry("*-1000.0", 1L),
625       entry("1000.0-10000.0", 2L),
626       entry("10000.0-100000.0", 3L),
627       entry("100000.0-500000.0", 2L),
628       entry("500000.0-*", 0L));
629     // But facet on coverage does well take into into filters
630     assertThat(facets.get(COVERAGE)).containsOnly(
631       entry("NO_DATA", 0L),
632       entry("*-30.0", 3L),
633       entry("30.0-50.0", 0L),
634       entry("50.0-70.0", 0L),
635       entry("70.0-80.0", 0L),
636       entry("80.0-*", 0L));
637   }
638
639   @Test
640   public void facet_ncloc_contains_only_projects_authorized_for_user() {
641     // User can see these projects
642     indexForUser(USER1,
643       // docs with ncloc<1K
644       newDoc(NCLOC, 0d),
645       newDoc(NCLOC, 100d),
646       newDoc(NCLOC, 999d),
647       // docs with ncloc>=1K and ncloc<10K
648       newDoc(NCLOC, 1_000d),
649       newDoc(NCLOC, 9_999d));
650
651     // User cannot see these projects
652     indexForUser(USER2,
653       // doc with ncloc>=10K and ncloc<100K
654       newDoc(NCLOC, 11_000d),
655       // doc with ncloc>=100K and ncloc<500K
656       newDoc(NCLOC, 499_000d),
657       // doc with ncloc>= 500K
658       newDoc(NCLOC, 501_000d));
659
660     userSession.logIn(USER1);
661     Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(NCLOC)).getFacets();
662
663     assertThat(facets.get(NCLOC)).containsExactly(
664       entry("*-1000.0", 3L),
665       entry("1000.0-10000.0", 2L),
666       entry("10000.0-100000.0", 0L),
667       entry("100000.0-500000.0", 0L),
668       entry("500000.0-*", 0L));
669   }
670
671   @Test
672   public void facet_new_lines() {
673     index(
674       // 3 docs with ncloc<1K
675       newDoc(NEW_LINES, 0d),
676       newDoc(NEW_LINES, 0d),
677       newDoc(NEW_LINES, 999d),
678       // 2 docs with ncloc>=1K and ncloc<10K
679       newDoc(NEW_LINES, 1_000d),
680       newDoc(NEW_LINES, 9_999d),
681       // 4 docs with ncloc>=10K and ncloc<100K
682       newDoc(NEW_LINES, 10_000d),
683       newDoc(NEW_LINES, 10_000d),
684       newDoc(NEW_LINES, 11_000d),
685       newDoc(NEW_LINES, 99_000d),
686       // 2 docs with ncloc>=100K and ncloc<500K
687       newDoc(NEW_LINES, 100_000d),
688       newDoc(NEW_LINES, 499_000d),
689       // 5 docs with ncloc>= 500K
690       newDoc(NEW_LINES, 500_000d),
691       newDoc(NEW_LINES, 100_000_000d),
692       newDoc(NEW_LINES, 500_000d),
693       newDoc(NEW_LINES, 1_000_000d),
694       newDoc(NEW_LINES, 100_000_000_000d));
695
696     Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(NEW_LINES)).getFacets();
697
698     assertThat(facets.get(NEW_LINES)).containsExactly(
699       entry("*-1000.0", 3L),
700       entry("1000.0-10000.0", 2L),
701       entry("10000.0-100000.0", 4L),
702       entry("100000.0-500000.0", 2L),
703       entry("500000.0-*", 5L));
704   }
705
706   @Test
707   public void facet_coverage() {
708     index(
709       // 1 doc with no coverage
710       newDocWithNoMeasure(),
711       // 3 docs with coverage<30%
712       newDoc(COVERAGE, 0d),
713       newDoc(COVERAGE, 0d),
714       newDoc(COVERAGE, 29d),
715       // 2 docs with coverage>=30% and coverage<50%
716       newDoc(COVERAGE, 30d),
717       newDoc(COVERAGE, 49d),
718       // 4 docs with coverage>=50% and coverage<70%
719       newDoc(COVERAGE, 50d),
720       newDoc(COVERAGE, 60d),
721       newDoc(COVERAGE, 60d),
722       newDoc(COVERAGE, 69d),
723       // 2 docs with coverage>=70% and coverage<80%
724       newDoc(COVERAGE, 70d),
725       newDoc(COVERAGE, 79d),
726       // 5 docs with coverage>= 80%
727       newDoc(COVERAGE, 80d),
728       newDoc(COVERAGE, 80d),
729       newDoc(COVERAGE, 90d),
730       newDoc(COVERAGE, 90.5d),
731       newDoc(COVERAGE, 100d));
732
733     Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(COVERAGE)).getFacets();
734
735     assertThat(facets.get(COVERAGE)).containsOnly(
736       entry("NO_DATA", 1L),
737       entry("*-30.0", 3L),
738       entry("30.0-50.0", 2L),
739       entry("50.0-70.0", 4L),
740       entry("70.0-80.0", 2L),
741       entry("80.0-*", 5L));
742   }
743
744   @Test
745   public void facet_coverage_is_sticky() {
746     index(
747       // docs with no coverage
748       newDoc(NCLOC, 999d, DUPLICATION, 0d),
749       newDoc(NCLOC, 999d, DUPLICATION, 1d),
750       newDoc(NCLOC, 999d, DUPLICATION, 20d),
751       // docs with coverage<30%
752       newDoc(NCLOC, 999d, COVERAGE, 0d, DUPLICATION, 0d),
753       newDoc(NCLOC, 1_000d, COVERAGE, 10d, DUPLICATION, 0d),
754       newDoc(NCLOC, 9_999d, COVERAGE, 20d, DUPLICATION, 0d),
755       // docs with coverage>=30% and coverage<50%
756       newDoc(NCLOC, 10_000d, COVERAGE, 31d, DUPLICATION, 0d),
757       newDoc(NCLOC, 11_000d, COVERAGE, 40d, DUPLICATION, 0d),
758       // docs with coverage>=50% and coverage<70%
759       newDoc(NCLOC, 99_000d, COVERAGE, 50d, DUPLICATION, 0d),
760       // docs with coverage>=70% and coverage<80%
761       newDoc(NCLOC, 100_000d, COVERAGE, 71d, DUPLICATION, 0d),
762       // docs with coverage>= 80%
763       newDoc(NCLOC, 499_000d, COVERAGE, 80d, DUPLICATION, 15d),
764       newDoc(NCLOC, 501_000d, COVERAGE, 810d, DUPLICATION, 20d));
765
766     Facets facets = underTest.search(new ProjectMeasuresQuery()
767       .addMetricCriterion(MetricCriterion.create(COVERAGE, Operator.LT, 30d))
768       .addMetricCriterion(MetricCriterion.create(DUPLICATION, Operator.LT, 10d)),
769       new SearchOptions().addFacets(COVERAGE, NCLOC)).getFacets();
770
771     // Sticky facet on coverage does not take into account coverage filter
772     assertThat(facets.get(COVERAGE)).containsExactly(
773       entry("NO_DATA", 2L),
774       entry("*-30.0", 3L),
775       entry("30.0-50.0", 2L),
776       entry("50.0-70.0", 1L),
777       entry("70.0-80.0", 1L),
778       entry("80.0-*", 0L));
779     // But facet on ncloc does well take into into filters
780     assertThat(facets.get(NCLOC)).containsExactly(
781       entry("*-1000.0", 1L),
782       entry("1000.0-10000.0", 2L),
783       entry("10000.0-100000.0", 0L),
784       entry("100000.0-500000.0", 0L),
785       entry("500000.0-*", 0L));
786   }
787
788   @Test
789   public void facet_coverage_contains_only_projects_authorized_for_user() {
790     // User can see these projects
791     indexForUser(USER1,
792       // 1 doc with no coverage
793       newDocWithNoMeasure(),
794       // docs with coverage<30%
795       newDoc(COVERAGE, 0d),
796       newDoc(COVERAGE, 0d),
797       newDoc(COVERAGE, 29d),
798       // docs with coverage>=30% and coverage<50%
799       newDoc(COVERAGE, 30d),
800       newDoc(COVERAGE, 49d));
801
802     // User cannot see these projects
803     indexForUser(USER2,
804       // 2 docs with no coverage
805       newDocWithNoMeasure(),
806       newDocWithNoMeasure(),
807       // docs with coverage>=50% and coverage<70%
808       newDoc(COVERAGE, 50d),
809       // docs with coverage>=70% and coverage<80%
810       newDoc(COVERAGE, 70d),
811       // docs with coverage>= 80%
812       newDoc(COVERAGE, 80d));
813
814     userSession.logIn(USER1);
815     Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(COVERAGE)).getFacets();
816
817     assertThat(facets.get(COVERAGE)).containsExactly(
818       entry("NO_DATA", 1L),
819       entry("*-30.0", 3L),
820       entry("30.0-50.0", 2L),
821       entry("50.0-70.0", 0L),
822       entry("70.0-80.0", 0L),
823       entry("80.0-*", 0L));
824   }
825
826   @Test
827   public void facet_new_coverage() {
828     index(
829       // 1 doc with no coverage
830       newDocWithNoMeasure(),
831       // 3 docs with coverage<30%
832       newDoc(NEW_COVERAGE, 0d),
833       newDoc(NEW_COVERAGE, 0d),
834       newDoc(NEW_COVERAGE, 29d),
835       // 2 docs with coverage>=30% and coverage<50%
836       newDoc(NEW_COVERAGE, 30d),
837       newDoc(NEW_COVERAGE, 49d),
838       // 4 docs with coverage>=50% and coverage<70%
839       newDoc(NEW_COVERAGE, 50d),
840       newDoc(NEW_COVERAGE, 60d),
841       newDoc(NEW_COVERAGE, 60d),
842       newDoc(NEW_COVERAGE, 69d),
843       // 2 docs with coverage>=70% and coverage<80%
844       newDoc(NEW_COVERAGE, 70d),
845       newDoc(NEW_COVERAGE, 79d),
846       // 5 docs with coverage>= 80%
847       newDoc(NEW_COVERAGE, 80d),
848       newDoc(NEW_COVERAGE, 80d),
849       newDoc(NEW_COVERAGE, 90d),
850       newDoc(NEW_COVERAGE, 90.5d),
851       newDoc(NEW_COVERAGE, 100d));
852
853     Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(NEW_COVERAGE)).getFacets();
854
855     assertThat(facets.get(NEW_COVERAGE)).containsOnly(
856       entry("NO_DATA", 1L),
857       entry("*-30.0", 3L),
858       entry("30.0-50.0", 2L),
859       entry("50.0-70.0", 4L),
860       entry("70.0-80.0", 2L),
861       entry("80.0-*", 5L));
862   }
863
864   @Test
865   public void facet_duplicated_lines_density() {
866     index(
867       // 1 doc with no duplication
868       newDocWithNoMeasure(),
869       // 3 docs with duplication<3%
870       newDoc(DUPLICATION, 0d),
871       newDoc(DUPLICATION, 0d),
872       newDoc(DUPLICATION, 2.9d),
873       // 2 docs with duplication>=3% and duplication<5%
874       newDoc(DUPLICATION, 3d),
875       newDoc(DUPLICATION, 4.9d),
876       // 4 docs with duplication>=5% and duplication<10%
877       newDoc(DUPLICATION, 5d),
878       newDoc(DUPLICATION, 6d),
879       newDoc(DUPLICATION, 6d),
880       newDoc(DUPLICATION, 9.9d),
881       // 2 docs with duplication>=10% and duplication<20%
882       newDoc(DUPLICATION, 10d),
883       newDoc(DUPLICATION, 19.9d),
884       // 5 docs with duplication>= 20%
885       newDoc(DUPLICATION, 20d),
886       newDoc(DUPLICATION, 20d),
887       newDoc(DUPLICATION, 50d),
888       newDoc(DUPLICATION, 80d),
889       newDoc(DUPLICATION, 100d));
890
891     Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(DUPLICATION)).getFacets();
892
893     assertThat(facets.get(DUPLICATION)).containsOnly(
894       entry("NO_DATA", 1L),
895       entry("*-3.0", 3L),
896       entry("3.0-5.0", 2L),
897       entry("5.0-10.0", 4L),
898       entry("10.0-20.0", 2L),
899       entry("20.0-*", 5L));
900   }
901
902   @Test
903   public void facet_duplicated_lines_density_is_sticky() {
904     index(
905       // docs with no duplication
906       newDoc(NCLOC, 50_001d, COVERAGE, 29d),
907       // docs with duplication<3%
908       newDoc(DUPLICATION, 0d, NCLOC, 999d, COVERAGE, 0d),
909       // docs with duplication>=3% and duplication<5%
910       newDoc(DUPLICATION, 3d, NCLOC, 5000d, COVERAGE, 0d),
911       newDoc(DUPLICATION, 4.9d, NCLOC, 6000d, COVERAGE, 0d),
912       // docs with duplication>=5% and duplication<10%
913       newDoc(DUPLICATION, 5d, NCLOC, 11000d, COVERAGE, 0d),
914       // docs with duplication>=10% and duplication<20%
915       newDoc(DUPLICATION, 10d, NCLOC, 120000d, COVERAGE, 10d),
916       newDoc(DUPLICATION, 19.9d, NCLOC, 130000d, COVERAGE, 20d),
917       // docs with duplication>= 20%
918       newDoc(DUPLICATION, 20d, NCLOC, 1000000d, COVERAGE, 40d));
919
920     Facets facets = underTest.search(new ProjectMeasuresQuery()
921       .addMetricCriterion(MetricCriterion.create(DUPLICATION, Operator.LT, 10d))
922       .addMetricCriterion(MetricCriterion.create(COVERAGE, Operator.LT, 30d)),
923       new SearchOptions().addFacets(DUPLICATION, NCLOC)).getFacets();
924
925     // Sticky facet on duplication does not take into account duplication filter
926     assertThat(facets.get(DUPLICATION)).containsOnly(
927       entry("NO_DATA", 1L),
928       entry("*-3.0", 1L),
929       entry("3.0-5.0", 2L),
930       entry("5.0-10.0", 1L),
931       entry("10.0-20.0", 2L),
932       entry("20.0-*", 0L));
933     // But facet on ncloc does well take into into filters
934     assertThat(facets.get(NCLOC)).containsOnly(
935       entry("*-1000.0", 1L),
936       entry("1000.0-10000.0", 2L),
937       entry("10000.0-100000.0", 1L),
938       entry("100000.0-500000.0", 0L),
939       entry("500000.0-*", 0L));
940   }
941
942   @Test
943   public void facet_duplicated_lines_density_contains_only_projects_authorized_for_user() {
944     // User can see these projects
945     indexForUser(USER1,
946       // docs with no duplication
947       newDocWithNoMeasure(),
948       // docs with duplication<3%
949       newDoc(DUPLICATION, 0d),
950       newDoc(DUPLICATION, 0d),
951       newDoc(DUPLICATION, 2.9d),
952       // docs with duplication>=3% and duplication<5%
953       newDoc(DUPLICATION, 3d),
954       newDoc(DUPLICATION, 4.9d));
955
956     // User cannot see these projects
957     indexForUser(USER2,
958       // docs with no duplication
959       newDocWithNoMeasure(),
960       newDocWithNoMeasure(),
961       // docs with duplication>=5% and duplication<10%
962       newDoc(DUPLICATION, 5d),
963       // docs with duplication>=10% and duplication<20%
964       newDoc(DUPLICATION, 10d),
965       // docs with duplication>= 20%
966       newDoc(DUPLICATION, 20d));
967
968     userSession.logIn(USER1);
969     Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(DUPLICATION)).getFacets();
970
971     assertThat(facets.get(DUPLICATION)).containsOnly(
972       entry("NO_DATA", 1L),
973       entry("*-3.0", 3L),
974       entry("3.0-5.0", 2L),
975       entry("5.0-10.0", 0L),
976       entry("10.0-20.0", 0L),
977       entry("20.0-*", 0L));
978   }
979
980   @Test
981   public void facet_new_duplicated_lines_density() {
982     index(
983       // 2 docs with no measure
984       newDocWithNoMeasure(),
985       newDocWithNoMeasure(),
986       // 3 docs with duplication<3%
987       newDoc(NEW_DUPLICATION, 0d),
988       newDoc(NEW_DUPLICATION, 0d),
989       newDoc(NEW_DUPLICATION, 2.9d),
990       // 2 docs with duplication>=3% and duplication<5%
991       newDoc(NEW_DUPLICATION, 3d),
992       newDoc(NEW_DUPLICATION, 4.9d),
993       // 4 docs with duplication>=5% and duplication<10%
994       newDoc(NEW_DUPLICATION, 5d),
995       newDoc(NEW_DUPLICATION, 6d),
996       newDoc(NEW_DUPLICATION, 6d),
997       newDoc(NEW_DUPLICATION, 9.9d),
998       // 2 docs with duplication>=10% and duplication<20%
999       newDoc(NEW_DUPLICATION, 10d),
1000       newDoc(NEW_DUPLICATION, 19.9d),
1001       // 5 docs with duplication>= 20%
1002       newDoc(NEW_DUPLICATION, 20d),
1003       newDoc(NEW_DUPLICATION, 20d),
1004       newDoc(NEW_DUPLICATION, 50d),
1005       newDoc(NEW_DUPLICATION, 80d),
1006       newDoc(NEW_DUPLICATION, 100d));
1007
1008     Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(NEW_DUPLICATION)).getFacets();
1009
1010     assertThat(facets.get(NEW_DUPLICATION)).containsExactly(
1011       entry("NO_DATA", 2L),
1012       entry("*-3.0", 3L),
1013       entry("3.0-5.0", 2L),
1014       entry("5.0-10.0", 4L),
1015       entry("10.0-20.0", 2L),
1016       entry("20.0-*", 5L));
1017   }
1018
1019   @Test
1020   @UseDataProvider("rating_metric_keys")
1021   public void facet_on_rating(String metricKey) {
1022     index(
1023       // 3 docs with rating A
1024       newDoc(metricKey, 1d),
1025       newDoc(metricKey, 1d),
1026       newDoc(metricKey, 1d),
1027       // 2 docs with rating B
1028       newDoc(metricKey, 2d),
1029       newDoc(metricKey, 2d),
1030       // 4 docs with rating C
1031       newDoc(metricKey, 3d),
1032       newDoc(metricKey, 3d),
1033       newDoc(metricKey, 3d),
1034       newDoc(metricKey, 3d),
1035       // 2 docs with rating D
1036       newDoc(metricKey, 4d),
1037       newDoc(metricKey, 4d),
1038       // 5 docs with rating E
1039       newDoc(metricKey, 5d),
1040       newDoc(metricKey, 5d),
1041       newDoc(metricKey, 5d),
1042       newDoc(metricKey, 5d),
1043       newDoc(metricKey, 5d));
1044
1045     Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(metricKey)).getFacets();
1046
1047     assertThat(facets.get(metricKey)).containsExactly(
1048       entry("1", 3L),
1049       entry("2", 2L),
1050       entry("3", 4L),
1051       entry("4", 2L),
1052       entry("5", 5L));
1053   }
1054
1055   @Test
1056   @UseDataProvider("rating_metric_keys")
1057   public void facet_on_rating_is_sticky(String metricKey) {
1058     index(
1059       // docs with rating A
1060       newDoc(metricKey, 1d, NCLOC, 100d, COVERAGE, 0d),
1061       newDoc(metricKey, 1d, NCLOC, 200d, COVERAGE, 0d),
1062       newDoc(metricKey, 1d, NCLOC, 999d, COVERAGE, 0d),
1063       // docs with rating B
1064       newDoc(metricKey, 2d, NCLOC, 2000d, COVERAGE, 0d),
1065       newDoc(metricKey, 2d, NCLOC, 5000d, COVERAGE, 0d),
1066       // docs with rating C
1067       newDoc(metricKey, 3d, NCLOC, 20000d, COVERAGE, 0d),
1068       newDoc(metricKey, 3d, NCLOC, 30000d, COVERAGE, 0d),
1069       newDoc(metricKey, 3d, NCLOC, 40000d, COVERAGE, 0d),
1070       newDoc(metricKey, 3d, NCLOC, 50000d, COVERAGE, 0d),
1071       // docs with rating D
1072       newDoc(metricKey, 4d, NCLOC, 120000d, COVERAGE, 0d),
1073       // docs with rating E
1074       newDoc(metricKey, 5d, NCLOC, 600000d, COVERAGE, 40d),
1075       newDoc(metricKey, 5d, NCLOC, 700000d, COVERAGE, 50d),
1076       newDoc(metricKey, 5d, NCLOC, 800000d, COVERAGE, 60d));
1077
1078     Facets facets = underTest.search(new ProjectMeasuresQuery()
1079       .addMetricCriterion(MetricCriterion.create(metricKey, Operator.LT, 3d))
1080       .addMetricCriterion(MetricCriterion.create(COVERAGE, Operator.LT, 30d)),
1081       new SearchOptions().addFacets(metricKey, NCLOC)).getFacets();
1082
1083     // Sticky facet on maintainability rating does not take into account maintainability rating filter
1084     assertThat(facets.get(metricKey)).containsExactly(
1085       entry("1", 3L),
1086       entry("2", 2L),
1087       entry("3", 4L),
1088       entry("4", 1L),
1089       entry("5", 0L));
1090     // But facet on ncloc does well take into into filters
1091     assertThat(facets.get(NCLOC)).containsExactly(
1092       entry("*-1000.0", 3L),
1093       entry("1000.0-10000.0", 2L),
1094       entry("10000.0-100000.0", 0L),
1095       entry("100000.0-500000.0", 0L),
1096       entry("500000.0-*", 0L));
1097   }
1098
1099   @Test
1100   @UseDataProvider("rating_metric_keys")
1101   public void facet_on_rating_contains_only_projects_authorized_for_user(String metricKey) {
1102     // User can see these projects
1103     indexForUser(USER1,
1104       // 3 docs with rating A
1105       newDoc(metricKey, 1d),
1106       newDoc(metricKey, 1d),
1107       newDoc(metricKey, 1d),
1108       // 2 docs with rating B
1109       newDoc(metricKey, 2d),
1110       newDoc(metricKey, 2d));
1111
1112     // User cannot see these projects
1113     indexForUser(USER2,
1114       // docs with rating C
1115       newDoc(metricKey, 3d),
1116       // docs with rating D
1117       newDoc(metricKey, 4d),
1118       // docs with rating E
1119       newDoc(metricKey, 5d));
1120
1121     userSession.logIn(USER1);
1122     Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(metricKey)).getFacets();
1123
1124     assertThat(facets.get(metricKey)).containsExactly(
1125       entry("1", 3L),
1126       entry("2", 2L),
1127       entry("3", 0L),
1128       entry("4", 0L),
1129       entry("5", 0L));
1130   }
1131
1132   @Test
1133   public void facet_quality_gate() {
1134     index(
1135       // 2 docs with QG OK
1136       newDoc().setQualityGateStatus(OK.name()),
1137       newDoc().setQualityGateStatus(OK.name()),
1138       // 4 docs with QG ERROR
1139       newDoc().setQualityGateStatus(ERROR.name()),
1140       newDoc().setQualityGateStatus(ERROR.name()),
1141       newDoc().setQualityGateStatus(ERROR.name()),
1142       newDoc().setQualityGateStatus(ERROR.name()));
1143
1144     LinkedHashMap<String, Long> result = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(ALERT_STATUS_KEY)).getFacets().get(ALERT_STATUS_KEY);
1145
1146     assertThat(result).containsOnly(
1147       entry(ERROR.name(), 4L),
1148       entry(OK.name(), 2L),
1149       entry(WARN.name(), 0L));
1150   }
1151
1152   @Test
1153   public void facet_quality_gate_is_sticky() {
1154     index(
1155       // 2 docs with QG OK
1156       newDoc(NCLOC, 10d, COVERAGE, 0d).setQualityGateStatus(OK.name()),
1157       newDoc(NCLOC, 10d, COVERAGE, 0d).setQualityGateStatus(OK.name()),
1158       // 4 docs with QG ERROR
1159       newDoc(NCLOC, 100d, COVERAGE, 0d).setQualityGateStatus(ERROR.name()),
1160       newDoc(NCLOC, 5000d, COVERAGE, 40d).setQualityGateStatus(ERROR.name()),
1161       newDoc(NCLOC, 12000d, COVERAGE, 50d).setQualityGateStatus(ERROR.name()),
1162       newDoc(NCLOC, 13000d, COVERAGE, 60d).setQualityGateStatus(ERROR.name()));
1163
1164     Facets facets = underTest.search(new ProjectMeasuresQuery()
1165       .setQualityGateStatus(ERROR)
1166       .addMetricCriterion(MetricCriterion.create(COVERAGE, Operator.LT, 55d)),
1167       new SearchOptions().addFacets(ALERT_STATUS_KEY, NCLOC)).getFacets();
1168
1169     // Sticky facet on quality gate does not take into account quality gate filter
1170     assertThat(facets.get(ALERT_STATUS_KEY)).containsOnly(
1171       entry(OK.name(), 2L),
1172       entry(ERROR.name(), 3L),
1173       entry(WARN.name(), 0L));
1174     // But facet on ncloc does well take into into filters
1175     assertThat(facets.get(NCLOC)).containsExactly(
1176       entry("*-1000.0", 1L),
1177       entry("1000.0-10000.0", 1L),
1178       entry("10000.0-100000.0", 1L),
1179       entry("100000.0-500000.0", 0L),
1180       entry("500000.0-*", 0L));
1181   }
1182
1183   @Test
1184   public void facet_quality_gate_contains_only_projects_authorized_for_user() {
1185     // User can see these projects
1186     indexForUser(USER1,
1187       // 2 docs with QG OK
1188       newDoc().setQualityGateStatus(OK.name()),
1189       newDoc().setQualityGateStatus(OK.name()));
1190
1191     // User cannot see these projects
1192     indexForUser(USER2,
1193       // 4 docs with QG ERROR
1194       newDoc().setQualityGateStatus(ERROR.name()),
1195       newDoc().setQualityGateStatus(ERROR.name()),
1196       newDoc().setQualityGateStatus(ERROR.name()),
1197       newDoc().setQualityGateStatus(ERROR.name()));
1198
1199     userSession.logIn(USER1);
1200     LinkedHashMap<String, Long> result = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(ALERT_STATUS_KEY)).getFacets().get(ALERT_STATUS_KEY);
1201
1202     assertThat(result).containsOnly(
1203       entry(ERROR.name(), 0L),
1204       entry(OK.name(), 2L),
1205       entry(WARN.name(), 0L));
1206   }
1207
1208   @Test
1209   public void facet_quality_gate_using_deprecated_warning() {
1210     index(
1211       // 2 docs with QG OK
1212       newDoc().setQualityGateStatus(OK.name()),
1213       newDoc().setQualityGateStatus(OK.name()),
1214       // 3 docs with QG WARN
1215       newDoc().setQualityGateStatus(WARN.name()),
1216       newDoc().setQualityGateStatus(WARN.name()),
1217       newDoc().setQualityGateStatus(WARN.name()),
1218       // 4 docs with QG ERROR
1219       newDoc().setQualityGateStatus(ERROR.name()),
1220       newDoc().setQualityGateStatus(ERROR.name()),
1221       newDoc().setQualityGateStatus(ERROR.name()),
1222       newDoc().setQualityGateStatus(ERROR.name()));
1223
1224     LinkedHashMap<String, Long> result = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(ALERT_STATUS_KEY)).getFacets().get(ALERT_STATUS_KEY);
1225
1226     assertThat(result).containsOnly(
1227       entry(ERROR.name(), 4L),
1228       entry(WARN.name(), 3L),
1229       entry(OK.name(), 2L));
1230   }
1231
1232   @Test
1233   public void facet_quality_gate_does_not_return_deprecated_warning_when_set_ignore_warning_is_true() {
1234     index(
1235       // 2 docs with QG OK
1236       newDoc().setQualityGateStatus(OK.name()),
1237       newDoc().setQualityGateStatus(OK.name()),
1238       // 4 docs with QG ERROR
1239       newDoc().setQualityGateStatus(ERROR.name()),
1240       newDoc().setQualityGateStatus(ERROR.name()),
1241       newDoc().setQualityGateStatus(ERROR.name()),
1242       newDoc().setQualityGateStatus(ERROR.name()));
1243
1244     assertThat(underTest.search(new ProjectMeasuresQuery().setIgnoreWarning(true), new SearchOptions().addFacets(ALERT_STATUS_KEY)).getFacets().get(ALERT_STATUS_KEY)).containsOnly(
1245       entry(ERROR.name(), 4L),
1246       entry(OK.name(), 2L));
1247     assertThat(underTest.search(new ProjectMeasuresQuery().setIgnoreWarning(false), new SearchOptions().addFacets(ALERT_STATUS_KEY)).getFacets().get(ALERT_STATUS_KEY))
1248       .containsOnly(
1249         entry(ERROR.name(), 4L),
1250         entry(WARN.name(), 0L),
1251         entry(OK.name(), 2L));
1252   }
1253
1254   @Test
1255   public void facet_languages() {
1256     index(
1257       newDoc().setLanguages(singletonList("java")),
1258       newDoc().setLanguages(singletonList("java")),
1259       newDoc().setLanguages(singletonList("xoo")),
1260       newDoc().setLanguages(singletonList("xml")),
1261       newDoc().setLanguages(asList("<null>", "java")),
1262       newDoc().setLanguages(asList("<null>", "java", "xoo")));
1263
1264     Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(LANGUAGES)).getFacets();
1265
1266     assertThat(facets.get(LANGUAGES)).containsOnly(
1267       entry("<null>", 2L),
1268       entry("java", 4L),
1269       entry("xoo", 2L),
1270       entry("xml", 1L));
1271   }
1272
1273   @Test
1274   public void facet_languages_is_limited_to_10_languages() {
1275     index(
1276       newDoc().setLanguages(asList("<null>", "java", "xoo", "css", "cpp")),
1277       newDoc().setLanguages(asList("xml", "php", "python", "perl", "ruby")),
1278       newDoc().setLanguages(asList("js", "scala")));
1279
1280     Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(LANGUAGES)).getFacets();
1281
1282     assertThat(facets.get(LANGUAGES)).hasSize(10);
1283   }
1284
1285   @Test
1286   public void facet_languages_is_sticky() {
1287     index(
1288       newDoc(NCLOC, 10d).setLanguages(singletonList("java")),
1289       newDoc(NCLOC, 10d).setLanguages(singletonList("java")),
1290       newDoc(NCLOC, 10d).setLanguages(singletonList("xoo")),
1291       newDoc(NCLOC, 100d).setLanguages(singletonList("xml")),
1292       newDoc(NCLOC, 100d).setLanguages(asList("<null>", "java")),
1293       newDoc(NCLOC, 5000d).setLanguages(asList("<null>", "java", "xoo")));
1294
1295     Facets facets = underTest.search(
1296       new ProjectMeasuresQuery().setLanguages(ImmutableSet.of("java")),
1297       new SearchOptions().addFacets(LANGUAGES, NCLOC)).getFacets();
1298
1299     // Sticky facet on language does not take into account language filter
1300     assertThat(facets.get(LANGUAGES)).containsOnly(
1301       entry("<null>", 2L),
1302       entry("java", 4L),
1303       entry("xoo", 2L),
1304       entry("xml", 1L));
1305     // But facet on ncloc does well take account into filters
1306     assertThat(facets.get(NCLOC)).containsExactly(
1307       entry("*-1000.0", 3L),
1308       entry("1000.0-10000.0", 1L),
1309       entry("10000.0-100000.0", 0L),
1310       entry("100000.0-500000.0", 0L),
1311       entry("500000.0-*", 0L));
1312   }
1313
1314   @Test
1315   public void facet_languages_returns_more_than_10_languages_when_languages_filter_contains_value_not_in_top_10() {
1316     index(
1317       newDoc().setLanguages(asList("<null>", "java", "xoo", "css", "cpp")),
1318       newDoc().setLanguages(asList("xml", "php", "python", "perl", "ruby")),
1319       newDoc().setLanguages(asList("js", "scala")));
1320
1321     Facets facets = underTest.search(new ProjectMeasuresQuery().setLanguages(ImmutableSet.of("xoo", "xml")), new SearchOptions().addFacets(LANGUAGES)).getFacets();
1322
1323     assertThat(facets.get(LANGUAGES)).containsOnly(
1324       entry("<null>", 1L),
1325       entry("cpp", 1L),
1326       entry("css", 1L),
1327       entry("java", 1L),
1328       entry("js", 1L),
1329       entry("perl", 1L),
1330       entry("php", 1L),
1331       entry("python", 1L),
1332       entry("ruby", 1L),
1333       entry("scala", 1L),
1334       entry("xoo", 1L),
1335       entry("xml", 1L));
1336   }
1337
1338   @Test
1339   public void facet_languages_contains_only_projects_authorized_for_user() {
1340     // User can see these projects
1341     indexForUser(USER1,
1342       newDoc().setLanguages(singletonList("java")),
1343       newDoc().setLanguages(asList("java", "xoo")));
1344
1345     // User cannot see these projects
1346     indexForUser(USER2,
1347       newDoc().setLanguages(singletonList("java")),
1348       newDoc().setLanguages(asList("java", "xoo")));
1349
1350     userSession.logIn(USER1);
1351     LinkedHashMap<String, Long> result = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(LANGUAGES)).getFacets().get(LANGUAGES);
1352
1353     assertThat(result).containsOnly(
1354       entry("java", 2L),
1355       entry("xoo", 1L));
1356   }
1357
1358   @Test
1359   public void facet_qualifier() {
1360     index(
1361       // 2 docs with qualifier APP
1362       newDoc().setQualifier(APP),
1363       newDoc().setQualifier(APP),
1364       // 4 docs with qualifier TRK
1365       newDoc().setQualifier(PROJECT),
1366       newDoc().setQualifier(PROJECT),
1367       newDoc().setQualifier(PROJECT),
1368       newDoc().setQualifier(PROJECT));
1369
1370     LinkedHashMap<String, Long> result = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(FILTER_QUALIFIER)).getFacets().get(FILTER_QUALIFIER);
1371
1372     assertThat(result).containsOnly(
1373       entry(APP, 2L),
1374       entry(PROJECT, 4L));
1375   }
1376
1377   @Test
1378   public void facet_qualifier_is_sticky() {
1379     index(
1380       // 2 docs with qualifier APP
1381       newDoc(NCLOC, 10d, COVERAGE, 0d).setQualifier(APP),
1382       newDoc(NCLOC, 10d, COVERAGE, 0d).setQualifier(APP),
1383       // 4 docs with qualifier TRK
1384       newDoc(NCLOC, 100d, COVERAGE, 0d).setQualifier(PROJECT),
1385       newDoc(NCLOC, 5000d, COVERAGE, 40d).setQualifier(PROJECT),
1386       newDoc(NCLOC, 12000d, COVERAGE, 50d).setQualifier(PROJECT),
1387       newDoc(NCLOC, 13000d, COVERAGE, 60d).setQualifier(PROJECT));
1388
1389     Facets facets = underTest.search(new ProjectMeasuresQuery()
1390       .setQualifiers(Sets.newHashSet(PROJECT))
1391       .addMetricCriterion(MetricCriterion.create(COVERAGE, Operator.LT, 55d)),
1392       new SearchOptions().addFacets(FILTER_QUALIFIER, NCLOC)).getFacets();
1393
1394     // Sticky facet on qualifier does not take into account qualifier filter
1395     assertThat(facets.get(FILTER_QUALIFIER)).containsOnly(
1396       entry(APP, 2L),
1397       entry(PROJECT, 3L));
1398     // But facet on ncloc does well take into into filters
1399     assertThat(facets.get(NCLOC)).containsExactly(
1400       entry("*-1000.0", 1L),
1401       entry("1000.0-10000.0", 1L),
1402       entry("10000.0-100000.0", 1L),
1403       entry("100000.0-500000.0", 0L),
1404       entry("500000.0-*", 0L));
1405   }
1406
1407   @Test
1408   public void facet_qualifier_contains_only_app_and_projects_authorized_for_user() {
1409     // User can see these projects
1410     indexForUser(USER1,
1411       // 3 docs with qualifier APP, PROJECT
1412       newDoc().setQualifier(APP),
1413       newDoc().setQualifier(APP),
1414       newDoc().setQualifier(PROJECT));
1415
1416     // User cannot see these projects
1417     indexForUser(USER2,
1418       // 4 docs with qualifier PROJECT
1419       newDoc().setQualifier(PROJECT),
1420       newDoc().setQualifier(PROJECT),
1421       newDoc().setQualifier(PROJECT),
1422       newDoc().setQualifier(PROJECT));
1423
1424     userSession.logIn(USER1);
1425     LinkedHashMap<String, Long> result = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(FILTER_QUALIFIER)).getFacets().get(FILTER_QUALIFIER);
1426
1427     assertThat(result).containsOnly(
1428       entry(APP, 2L),
1429       entry(PROJECT, 1L));
1430   }
1431
1432   @Test
1433   public void facet_tags() {
1434     index(
1435       newDoc().setTags(newArrayList("finance", "offshore", "java")),
1436       newDoc().setTags(newArrayList("finance", "javascript")),
1437       newDoc().setTags(newArrayList("marketing", "finance")),
1438       newDoc().setTags(newArrayList("marketing", "offshore")),
1439       newDoc().setTags(newArrayList("finance", "marketing")),
1440       newDoc().setTags(newArrayList("finance")));
1441
1442     Map<String, Long> result = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(FIELD_TAGS)).getFacets().get(FIELD_TAGS);
1443
1444     assertThat(result).containsOnly(
1445       entry("finance", 5L),
1446       entry("marketing", 3L),
1447       entry("offshore", 2L),
1448       entry("java", 1L),
1449       entry("javascript", 1L));
1450   }
1451
1452   @Test
1453   public void facet_tags_is_sticky() {
1454     index(
1455       newDoc().setTags(newArrayList("finance")).setQualityGateStatus(OK.name()),
1456       newDoc().setTags(newArrayList("finance")).setQualityGateStatus(ERROR.name()),
1457       newDoc().setTags(newArrayList("cpp")).setQualityGateStatus(ERROR.name()));
1458
1459     Facets facets = underTest.search(
1460       new ProjectMeasuresQuery().setTags(newHashSet("cpp")),
1461       new SearchOptions().addFacets(FIELD_TAGS).addFacets(ALERT_STATUS_KEY))
1462       .getFacets();
1463
1464     assertThat(facets.get(FIELD_TAGS)).containsOnly(
1465       entry("finance", 2L),
1466       entry("cpp", 1L));
1467     assertThat(facets.get(ALERT_STATUS_KEY)).containsOnly(
1468       entry(OK.name(), 0L),
1469       entry(ERROR.name(), 1L),
1470       entry(WARN.name(), 0L));
1471   }
1472
1473   @Test
1474   public void facet_tags_returns_10_elements_by_default() {
1475     index(
1476       newDoc().setTags(newArrayList("finance1", "finance2", "finance3", "finance4", "finance5", "finance6", "finance7", "finance8", "finance9", "finance10")),
1477       newDoc().setTags(newArrayList("finance1", "finance2", "finance3", "finance4", "finance5", "finance6", "finance7", "finance8", "finance9", "finance10")),
1478       newDoc().setTags(newArrayList("solo")));
1479
1480     Map<String, Long> result = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(FIELD_TAGS)).getFacets().get(FIELD_TAGS);
1481
1482     assertThat(result).hasSize(10).containsOnlyKeys("finance1", "finance2", "finance3", "finance4", "finance5", "finance6", "finance7", "finance8", "finance9", "finance10");
1483   }
1484
1485   @Test
1486   public void facet_tags_returns_more_than_10_tags_when_tags_filter_contains_value_not_in_top_10() {
1487     index(
1488       newDoc().setTags(newArrayList("finance1", "finance2", "finance3", "finance4", "finance5", "finance6", "finance7", "finance8", "finance9", "finance10")),
1489       newDoc().setTags(newArrayList("finance1", "finance2", "finance3", "finance4", "finance5", "finance6", "finance7", "finance8", "finance9", "finance10")),
1490       newDoc().setTags(newArrayList("solo", "solo2")));
1491
1492     Map<String, Long> result = underTest.search(new ProjectMeasuresQuery().setTags(ImmutableSet.of("solo", "solo2")), new SearchOptions().addFacets(FIELD_TAGS)).getFacets()
1493       .get(FIELD_TAGS);
1494
1495     assertThat(result).hasSize(12).containsOnlyKeys("finance1", "finance2", "finance3", "finance4", "finance5", "finance6", "finance7", "finance8", "finance9", "finance10", "solo",
1496       "solo2");
1497   }
1498
1499   @Test
1500   public void search_tags() {
1501     index(
1502       newDoc().setTags(newArrayList("finance", "offshore", "java")),
1503       newDoc().setTags(newArrayList("official", "javascript")),
1504       newDoc().setTags(newArrayList("marketing", "official")),
1505       newDoc().setTags(newArrayList("marketing", "Madhoff")),
1506       newDoc().setTags(newArrayList("finance", "offshore")),
1507       newDoc().setTags(newArrayList("offshore")));
1508
1509     List<String> result = underTest.searchTags("off", 1,10);
1510
1511     assertThat(result).containsOnly("offshore", "official", "Madhoff");
1512   }
1513
1514   @Test
1515   public void search_tags_return_all_tags() {
1516     index(
1517       newDoc().setTags(newArrayList("finance", "offshore", "java")),
1518       newDoc().setTags(newArrayList("official", "javascript")),
1519       newDoc().setTags(newArrayList("marketing", "official")),
1520       newDoc().setTags(newArrayList("marketing", "Madhoff")),
1521       newDoc().setTags(newArrayList("finance", "offshore")),
1522       newDoc().setTags(newArrayList("offshore")));
1523
1524     List<String> result = underTest.searchTags(null, 1, 10);
1525
1526     assertThat(result).containsOnly("offshore", "official", "Madhoff", "finance", "marketing", "java", "javascript");
1527   }
1528
1529   @Test
1530   public void search_tags_in_lexical_order() {
1531     index(
1532       newDoc().setTags(newArrayList("finance", "offshore", "java")),
1533       newDoc().setTags(newArrayList("official", "javascript")),
1534       newDoc().setTags(newArrayList("marketing", "official")),
1535       newDoc().setTags(newArrayList("marketing", "Madhoff")),
1536       newDoc().setTags(newArrayList("finance", "offshore")),
1537       newDoc().setTags(newArrayList("offshore")));
1538
1539     List<String> result = underTest.searchTags(null, 1,10);
1540
1541     assertThat(result).containsExactly("Madhoff", "finance", "java", "javascript", "marketing", "official", "offshore");
1542   }
1543
1544   @Test
1545   public void search_tags_follows_paging() {
1546     index(
1547       newDoc().setTags(newArrayList("finance", "offshore", "java")),
1548       newDoc().setTags(newArrayList("official", "javascript")),
1549       newDoc().setTags(newArrayList("marketing", "official")),
1550       newDoc().setTags(newArrayList("marketing", "Madhoff")),
1551       newDoc().setTags(newArrayList("finance", "offshore")),
1552       newDoc().setTags(newArrayList("offshore")));
1553
1554     List<String> result = underTest.searchTags(null, 1,3);
1555     assertThat(result).containsExactly("Madhoff", "finance", "java");
1556
1557     result = underTest.searchTags(null, 2,3);
1558     assertThat(result).containsExactly("javascript", "marketing", "official");
1559
1560     result = underTest.searchTags(null, 3,3);
1561     assertThat(result).containsExactly("offshore");
1562
1563     result = underTest.searchTags(null, 3,4);
1564     assertThat(result).isEmpty();
1565   }
1566
1567   @Test
1568   public void search_tags_returns_nothing_if_page_too_large() {
1569     index(
1570       newDoc().setTags(newArrayList("finance", "offshore", "java")),
1571       newDoc().setTags(newArrayList("official", "javascript")),
1572       newDoc().setTags(newArrayList("marketing", "official")),
1573       newDoc().setTags(newArrayList("marketing", "Madhoff")),
1574       newDoc().setTags(newArrayList("finance", "offshore")),
1575       newDoc().setTags(newArrayList("offshore")));
1576
1577     List<String> result = underTest.searchTags(null, 10,2);
1578
1579     assertThat(result).isEmpty();
1580   }
1581
1582   @Test
1583   public void search_tags_only_of_authorized_projects() {
1584     indexForUser(USER1,
1585       newDoc(PROJECT1).setTags(singletonList("finance")),
1586       newDoc(PROJECT2).setTags(singletonList("marketing")));
1587     indexForUser(USER2,
1588       newDoc(PROJECT3).setTags(singletonList("offshore")));
1589
1590     userSession.logIn(USER1);
1591
1592     List<String> result = underTest.searchTags(null, 1,10);
1593
1594     assertThat(result).containsOnly("finance", "marketing");
1595   }
1596
1597   @Test
1598   public void search_tags_with_no_tags() {
1599     List<String> result = underTest.searchTags("whatever", 1,10);
1600
1601     assertThat(result).isEmpty();
1602   }
1603
1604   @Test
1605   public void search_tags_with_page_size_at_0() {
1606     index(newDoc().setTags(newArrayList("offshore")));
1607
1608     List<String> result = underTest.searchTags(null, 1,0);
1609
1610     assertThat(result).isEmpty();
1611   }
1612
1613   @Test
1614   public void search_statistics() {
1615     es.putDocuments(TYPE_PROJECT_MEASURES,
1616       newDoc("lines", 10, "coverage", 80)
1617         .setLanguages(Arrays.asList("java", "cs", "js"))
1618         .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", 200, "cs", 250, "js", 50)),
1619       newDoc("lines", 20, "coverage", 80)
1620         .setLanguages(Arrays.asList("java", "python", "kotlin"))
1621         .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", 300, "python", 100, "kotlin", 404)));
1622
1623     ProjectMeasuresStatistics result = underTest.searchSupportStatistics();
1624
1625     assertThat(result.getProjectCount()).isEqualTo(2);
1626     assertThat(result.getProjectCountByLanguage()).containsOnly(
1627       entry("java", 2L), entry("cs", 1L), entry("js", 1L), entry("python", 1L), entry("kotlin", 1L));
1628     assertThat(result.getNclocByLanguage()).containsOnly(
1629       entry("java", 500L), entry("cs", 250L), entry("js", 50L), entry("python", 100L), entry("kotlin", 404L));
1630   }
1631
1632   @Test
1633   public void search_statistics_for_large_instances() {
1634     int nbProjects = 25000;
1635     int javaLocByProjects = 100;
1636     int jsLocByProjects = 900;
1637     int csLocByProjects = 2;
1638
1639     ProjectMeasuresDoc[] documents = IntStream.range(0, nbProjects).mapToObj(i ->
1640       newDoc("lines", 10, "coverage", 80)
1641         .setLanguages(asList("java", "cs", "js"))
1642         .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", javaLocByProjects, "cs", csLocByProjects, "js", jsLocByProjects))).toArray(ProjectMeasuresDoc[]::new);
1643
1644     es.putDocuments(TYPE_PROJECT_MEASURES, documents);
1645
1646     ProjectMeasuresStatistics result = underTest.searchSupportStatistics();
1647
1648     assertThat(result.getProjectCount()).isEqualTo(nbProjects);
1649     assertThat(result.getProjectCountByLanguage())
1650       .hasSize(3)
1651       .containsEntry("java", (long) nbProjects)
1652       .containsEntry("cs", (long) nbProjects)
1653       .containsEntry("js", (long) nbProjects);
1654
1655     assertThat(result.getNclocByLanguage())
1656       .hasSize(3)
1657       .containsEntry("java",(long) nbProjects * javaLocByProjects)
1658       .containsEntry("cs",(long) nbProjects * csLocByProjects)
1659       .containsEntry("js",(long) nbProjects * jsLocByProjects);
1660   }
1661
1662   @Test
1663   public void search_statistics_should_ignore_applications() {
1664     es.putDocuments(TYPE_PROJECT_MEASURES,
1665       // insert projects
1666       newDoc(ComponentTesting.newPrivateProjectDto(), "lines", 10, "coverage", 80)
1667         .setLanguages(Arrays.asList("java", "cs", "js"))
1668         .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", 200, "cs", 250, "js", 50)),
1669       newDoc(ComponentTesting.newPrivateProjectDto(), "lines", 20, "coverage", 80)
1670         .setLanguages(Arrays.asList("java", "python", "kotlin"))
1671         .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", 300, "python", 100, "kotlin", 404)),
1672
1673       // insert applications
1674       newDoc(ComponentTesting.newApplication(), "lines", 1000, "coverage", 70)
1675         .setLanguages(Arrays.asList("java", "python", "kotlin"))
1676         .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", 300, "python", 100, "kotlin", 404)),
1677       newDoc(ComponentTesting.newApplication(), "lines", 20, "coverage", 80)
1678         .setLanguages(Arrays.asList("java", "python", "kotlin"))
1679         .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", 300, "python", 100, "kotlin", 404)));
1680
1681     ProjectMeasuresStatistics result = underTest.searchSupportStatistics();
1682
1683     assertThat(result.getProjectCount()).isEqualTo(2);
1684     assertThat(result.getProjectCountByLanguage()).containsOnly(
1685       entry("java", 2L), entry("cs", 1L), entry("js", 1L), entry("python", 1L), entry("kotlin", 1L));
1686     assertThat(result.getNclocByLanguage()).containsOnly(
1687       entry("java", 500L), entry("cs", 250L), entry("js", 50L), entry("python", 100L), entry("kotlin", 404L));
1688   }
1689
1690   @Test
1691   public void search_statistics_should_count_0_if_no_projects() {
1692     es.putDocuments(TYPE_PROJECT_MEASURES,
1693       // insert applications
1694       newDoc(ComponentTesting.newApplication(), "lines", 1000, "coverage", 70)
1695         .setLanguages(Arrays.asList("java", "python", "kotlin"))
1696         .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", 300, "python", 100, "kotlin", 404)),
1697       newDoc(ComponentTesting.newApplication(), "lines", 20, "coverage", 80)
1698         .setLanguages(Arrays.asList("java", "python", "kotlin"))
1699         .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", 300, "python", 100, "kotlin", 404)));
1700
1701     ProjectMeasuresStatistics result = underTest.searchSupportStatistics();
1702
1703     assertThat(result.getProjectCount()).isZero();
1704     assertThat(result.getProjectCountByLanguage()).isEmpty();
1705   }
1706
1707   @Test
1708   public void fail_if_page_size_greater_than_100() {
1709     assertThatThrownBy(() -> underTest.searchTags("whatever", 1, 101))
1710       .isInstanceOf(IllegalArgumentException.class)
1711       .hasMessageContaining("Page size must be lower than or equals to 100");
1712   }
1713
1714   @Test
1715   public void fail_if_page_greater_than_20() {
1716     assertThatThrownBy(() -> underTest.searchTags("whatever", 21, 100))
1717       .isInstanceOf(IllegalArgumentException.class)
1718       .hasMessageContaining("Page must be between 0 and 20");
1719   }
1720
1721   private void index(ProjectMeasuresDoc... docs) {
1722     es.putDocuments(TYPE_PROJECT_MEASURES, docs);
1723     authorizationIndexer.allow(stream(docs).map(doc -> new IndexPermissions(doc.getId(), PROJECT).allowAnyone()).collect(toList()));
1724   }
1725
1726   private void indexForUser(UserDto user, ProjectMeasuresDoc... docs) {
1727     es.putDocuments(TYPE_PROJECT_MEASURES, docs);
1728     authorizationIndexer.allow(stream(docs).map(doc -> new IndexPermissions(doc.getId(), PROJECT).addUserUuid(user.getUuid())).collect(toList()));
1729   }
1730
1731   private void indexForGroup(GroupDto group, ProjectMeasuresDoc... docs) {
1732     es.putDocuments(TYPE_PROJECT_MEASURES, docs);
1733     authorizationIndexer.allow(stream(docs).map(doc -> new IndexPermissions(doc.getId(), PROJECT).addGroupUuid(group.getUuid())).collect(toList()));
1734   }
1735
1736   private static ProjectMeasuresDoc newDoc(ComponentDto project) {
1737     return new ProjectMeasuresDoc()
1738       .setId(project.uuid())
1739       .setKey(project.getKey())
1740       .setName(project.name())
1741       .setQualifier(project.qualifier());
1742   }
1743
1744   private static ProjectMeasuresDoc newDoc() {
1745     return newDoc(ComponentTesting.newPrivateProjectDto());
1746   }
1747
1748   private static ProjectMeasuresDoc newDoc(ComponentDto project, String metric1, Object value1) {
1749     return newDoc(project).setMeasures(newArrayList(newMeasure(metric1, value1)));
1750   }
1751
1752   private static ProjectMeasuresDoc newDoc(ComponentDto project, String metric1, Object value1, String metric2, Object value2) {
1753     return newDoc(project).setMeasures(newArrayList(newMeasure(metric1, value1), newMeasure(metric2, value2)));
1754   }
1755
1756   private static ProjectMeasuresDoc newDoc(ComponentDto project, String metric1, Object value1, String metric2, Object value2, String metric3, Object value3) {
1757     return newDoc(project).setMeasures(newArrayList(newMeasure(metric1, value1), newMeasure(metric2, value2), newMeasure(metric3, value3)));
1758   }
1759
1760   private static Map<String, Object> newMeasure(String key, Object value) {
1761     return ImmutableMap.of("key", key, "value", value);
1762   }
1763
1764   private static ProjectMeasuresDoc newDocWithNoMeasure() {
1765     return newDoc(ComponentTesting.newPrivateProjectDto());
1766   }
1767
1768   private static ProjectMeasuresDoc newDoc(String metric1, Object value1) {
1769     return newDoc(ComponentTesting.newPrivateProjectDto(), metric1, value1);
1770   }
1771
1772   private static ProjectMeasuresDoc newDoc(String metric1, Object value1, String metric2, Object value2) {
1773     return newDoc(ComponentTesting.newPrivateProjectDto(), metric1, value1, metric2, value2);
1774   }
1775
1776   private static ProjectMeasuresDoc newDoc(String metric1, Object value1, String metric2, Object value2, String metric3, Object value3) {
1777     return newDoc(ComponentTesting.newPrivateProjectDto(), metric1, value1, metric2, value2, metric3, value3);
1778   }
1779
1780   private void assertResults(ProjectMeasuresQuery query, ComponentDto... expectedProjects) {
1781     List<String> result = underTest.search(query, new SearchOptions()).getUuids();
1782     assertThat(result).containsExactly(Arrays.stream(expectedProjects).map(ComponentDto::uuid).toArray(String[]::new));
1783   }
1784
1785   private void assertNoResults(ProjectMeasuresQuery query) {
1786     assertResults(query);
1787   }
1788 }