3 * Copyright (C) 2009-2022 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 package org.sonar.server.measure.index;
22 import com.google.common.collect.ImmutableMap;
23 import java.util.List;
25 import org.junit.Rule;
26 import org.junit.Test;
27 import org.sonar.api.utils.System2;
28 import org.sonar.db.component.ComponentDto;
29 import org.sonar.server.es.EsTester;
30 import org.sonar.server.es.Facets;
31 import org.sonar.server.es.SearchOptions;
32 import org.sonar.server.measure.index.ProjectMeasuresQuery.MetricCriterion;
33 import org.sonar.server.permission.index.IndexPermissions;
34 import org.sonar.server.permission.index.PermissionIndexerTester;
35 import org.sonar.server.permission.index.WebAuthorizationTypeSupport;
36 import org.sonar.server.tester.UserSessionRule;
38 import static com.google.common.base.Preconditions.checkArgument;
39 import static com.google.common.collect.Lists.newArrayList;
40 import static java.util.Arrays.stream;
41 import static java.util.stream.Collectors.toList;
42 import static org.assertj.core.api.Assertions.assertThat;
43 import static org.assertj.core.api.Assertions.entry;
44 import static org.sonar.api.resources.Qualifiers.PROJECT;
45 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
46 import static org.sonar.server.measure.index.ProjectMeasuresIndexDefinition.TYPE_PROJECT_MEASURES;
47 import static org.sonar.server.measure.index.ProjectMeasuresQuery.Operator.GT;
48 import static org.sonar.server.measure.index.ProjectMeasuresQuery.Operator.LT;
50 public class ProjectMeasuresIndexTextSearchTest {
52 private static final String NCLOC = "ncloc";
55 public EsTester es = EsTester.create();
57 public UserSessionRule userSession = UserSessionRule.standalone();
59 private ProjectMeasuresIndexer projectMeasureIndexer = new ProjectMeasuresIndexer(null, es.client());
60 private PermissionIndexerTester authorizationIndexer = new PermissionIndexerTester(es, projectMeasureIndexer);
61 private ProjectMeasuresIndex underTest = new ProjectMeasuresIndex(es.client(), new WebAuthorizationTypeSupport(userSession), System2.INSTANCE);
64 public void search_partial_text_from_project_key() {
66 newDoc(newPrivateProjectDto().setUuid("struts").setName("Apache Struts").setDbKey("org.apache.commons.structs")),
67 newDoc(newPrivateProjectDto().setUuid("sonarqube").setName("SonarQube").setDbKey("org.sonar.sonarqube")));
69 assertTextQueryResults("apache", "struts");
70 assertTextQueryResults("apache.comm", "struts");
71 assertTextQueryResults("org.apache.commons.structs", "struts");
72 assertTextQueryResults("org.", "struts", "sonarqube");
76 public void match_exact_case_insensitive_name() {
78 newDoc(newPrivateProjectDto().setUuid("struts").setName("Apache Struts")),
79 newDoc(newPrivateProjectDto().setUuid("sonarqube").setName("SonarQube")));
81 assertTextQueryResults("Apache Struts", "struts");
82 assertTextQueryResults("APACHE STRUTS", "struts");
83 assertTextQueryResults("APACHE struTS", "struts");
87 public void match_from_sub_name() {
88 index(newDoc(newPrivateProjectDto().setUuid("struts").setName("Apache Struts")));
90 assertTextQueryResults("truts", "struts");
91 assertTextQueryResults("pache", "struts");
92 assertTextQueryResults("apach", "struts");
93 assertTextQueryResults("che stru", "struts");
97 public void match_name_with_dot() {
98 index(newDoc(newPrivateProjectDto().setUuid("struts").setName("Apache.Struts")));
100 assertTextQueryResults("apache struts", "struts");
104 public void match_partial_name() {
105 index(newDoc(newPrivateProjectDto().setUuid("struts").setName("XstrutsxXjavax")));
107 assertTextQueryResults("struts java", "struts");
111 public void match_partial_name_prefix_word1() {
112 index(newDoc(newPrivateProjectDto().setUuid("struts").setName("MyStruts.java")));
114 assertTextQueryResults("struts java", "struts");
118 public void match_partial_name_suffix_word1() {
119 index(newDoc(newPrivateProjectDto().setUuid("struts").setName("StrutsObject.java")));
121 assertTextQueryResults("struts java", "struts");
125 public void match_partial_name_prefix_word2() {
126 index(newDoc(newPrivateProjectDto().setUuid("struts").setName("MyStruts.xjava")));
128 assertTextQueryResults("struts java", "struts");
132 public void match_partial_name_suffix_word2() {
133 index(newDoc(newPrivateProjectDto().setUuid("struts").setName("MyStrutsObject.xjavax")));
135 assertTextQueryResults("struts java", "struts");
139 public void match_subset_of_document_terms() {
140 index(newDoc(newPrivateProjectDto().setUuid("struts").setName("Some.Struts.Project.java.old")));
142 assertTextQueryResults("struts java", "struts");
146 public void match_partial_match_prefix_and_suffix_everywhere() {
147 index(newDoc(newPrivateProjectDto().setUuid("struts").setName("MyStruts.javax")));
149 assertTextQueryResults("struts java", "struts");
153 public void ignore_empty_words() {
154 index(newDoc(newPrivateProjectDto().setUuid("struts").setName("Struts")));
156 assertTextQueryResults(" struts \n \n\n", "struts");
160 public void match_name_from_prefix() {
161 index(newDoc(newPrivateProjectDto().setUuid("struts").setName("Apache Struts")));
163 assertTextQueryResults("apach", "struts");
164 assertTextQueryResults("ApA", "struts");
165 assertTextQueryResults("AP", "struts");
169 public void match_name_from_two_words() {
170 index(newDoc(newPrivateProjectDto().setUuid("project").setName("ApacheStrutsFoundation")));
172 assertTextQueryResults("apache struts", "project");
173 assertTextQueryResults("struts apache", "project");
174 // Only one word is matching
175 assertNoResults("apache plugin");
176 assertNoResults("project struts");
180 public void match_long_name() {
182 newDoc(newPrivateProjectDto().setUuid("project1").setName("LongNameLongNameLongNameLongNameSonarQube")),
183 newDoc(newPrivateProjectDto().setUuid("project2").setName("LongNameLongNameLongNameLongNameSonarQubeX")));
185 assertTextQueryResults("LongNameLongNameLongNameLongNameSonarQube", "project1", "project2");
189 public void match_name_with_two_characters() {
190 index(newDoc(newPrivateProjectDto().setUuid("struts").setName("Apache Struts")));
192 assertTextQueryResults("st", "struts");
193 assertTextQueryResults("tr", "struts");
197 public void match_exact_case_insensitive_key() {
199 newDoc(newPrivateProjectDto().setUuid("project1").setName("Windows").setDbKey("project1")),
200 newDoc(newPrivateProjectDto().setUuid("project2").setName("apachee").setDbKey("project2")));
202 assertTextQueryResults("project1", "project1");
203 assertTextQueryResults("PROJECT1", "project1");
204 assertTextQueryResults("pRoJecT1", "project1");
208 public void match_key_with_dot() {
210 newDoc(newPrivateProjectDto().setUuid("sonarqube").setName("SonarQube").setDbKey("org.sonarqube")),
211 newDoc(newPrivateProjectDto().setUuid("sq").setName("SQ").setDbKey("sonarqube")));
213 assertTextQueryResults("org.sonarqube", "sonarqube");
214 assertNoResults("orgsonarqube");
215 assertNoResults("org-sonarqube");
216 assertNoResults("org:sonarqube");
217 assertNoResults("org sonarqube");
221 public void match_key_with_dash() {
223 newDoc(newPrivateProjectDto().setUuid("sonarqube").setName("SonarQube").setDbKey("org-sonarqube")),
224 newDoc(newPrivateProjectDto().setUuid("sq").setName("SQ").setDbKey("sonarqube")));
226 assertTextQueryResults("org-sonarqube", "sonarqube");
227 assertNoResults("orgsonarqube");
228 assertNoResults("org.sonarqube");
229 assertNoResults("org:sonarqube");
230 assertNoResults("org sonarqube");
234 public void match_key_with_colon() {
236 newDoc(newPrivateProjectDto().setUuid("sonarqube").setName("SonarQube").setDbKey("org:sonarqube")),
237 newDoc(newPrivateProjectDto().setUuid("sq").setName("SQ").setDbKey("sonarqube")));
239 assertTextQueryResults("org:sonarqube", "sonarqube");
240 assertNoResults("orgsonarqube");
241 assertNoResults("org-sonarqube");
242 assertNoResults("org_sonarqube");
243 assertNoResults("org sonarqube");
247 public void match_key_having_all_special_characters() {
248 index(newDoc(newPrivateProjectDto().setUuid("sonarqube").setName("SonarQube").setDbKey("org.sonarqube:sonar-sérvèr_ç")));
250 assertTextQueryResults("org.sonarqube:sonar-sérvèr_ç", "sonarqube");
254 public void facets_take_into_account_text_search() {
256 // docs with ncloc<1K
257 newDoc(newPrivateProjectDto().setName("Windows").setDbKey("project1"), NCLOC, 0d),
258 newDoc(newPrivateProjectDto().setName("apachee").setDbKey("project2"), NCLOC, 999d),
259 // docs with ncloc>=1K and ncloc<10K
260 newDoc(newPrivateProjectDto().setName("Apache").setDbKey("project3"), NCLOC, 1_000d),
261 // docs with ncloc>=100K and ncloc<500K
262 newDoc(newPrivateProjectDto().setName("Apache Foundation").setDbKey("project4"), NCLOC, 100_000d));
264 assertNclocFacet(new ProjectMeasuresQuery().setQueryText("apache"), 1L, 1L, 0L, 1L, 0L);
265 assertNclocFacet(new ProjectMeasuresQuery().setQueryText("PAch"), 1L, 1L, 0L, 1L, 0L);
266 assertNclocFacet(new ProjectMeasuresQuery().setQueryText("apache foundation"), 0L, 0L, 0L, 1L, 0L);
267 assertNclocFacet(new ProjectMeasuresQuery().setQueryText("project3"), 0L, 1L, 0L, 0L, 0L);
268 assertNclocFacet(new ProjectMeasuresQuery().setQueryText("project"), 2L, 1L, 0L, 1L, 0L);
272 public void filter_by_metric_take_into_account_text_search() {
274 newDoc(newPrivateProjectDto().setUuid("project1").setName("Windows").setDbKey("project1"), NCLOC, 30_000d),
275 newDoc(newPrivateProjectDto().setUuid("project2").setName("apachee").setDbKey("project2"), NCLOC, 40_000d),
276 newDoc(newPrivateProjectDto().setUuid("project3").setName("Apache").setDbKey("project3"), NCLOC, 50_000d),
277 newDoc(newPrivateProjectDto().setUuid("project4").setName("Apache").setDbKey("project4"), NCLOC, 60_000d));
279 assertResults(new ProjectMeasuresQuery().setQueryText("apache").addMetricCriterion(MetricCriterion.create(NCLOC, GT, 20_000d)), "project3", "project4", "project2");
280 assertResults(new ProjectMeasuresQuery().setQueryText("apache").addMetricCriterion(MetricCriterion.create(NCLOC, LT, 55_000d)), "project3", "project2");
281 assertResults(new ProjectMeasuresQuery().setQueryText("PAC").addMetricCriterion(MetricCriterion.create(NCLOC, LT, 55_000d)), "project3", "project2");
282 assertResults(new ProjectMeasuresQuery().setQueryText("apachee").addMetricCriterion(MetricCriterion.create(NCLOC, GT, 30_000d)), "project2");
283 assertResults(new ProjectMeasuresQuery().setQueryText("unknown").addMetricCriterion(MetricCriterion.create(NCLOC, GT, 20_000d)));
286 private void index(ProjectMeasuresDoc... docs) {
287 es.putDocuments(TYPE_PROJECT_MEASURES, docs);
288 authorizationIndexer.allow(stream(docs).map(doc -> new IndexPermissions(doc.getId(), PROJECT).allowAnyone()).collect(toList()));
291 private static ProjectMeasuresDoc newDoc(ComponentDto project) {
292 return new ProjectMeasuresDoc()
293 .setId(project.uuid())
294 .setKey(project.getDbKey())
295 .setName(project.name())
296 .setQualifier(project.qualifier());
299 private static ProjectMeasuresDoc newDoc(ComponentDto project, String metric1, Object value1) {
300 return newDoc(project).setMeasures(newArrayList(newMeasure(metric1, value1)));
303 private static Map<String, Object> newMeasure(String key, Object value) {
304 return ImmutableMap.of("key", key, "value", value);
307 private void assertResults(ProjectMeasuresQuery query, String... expectedProjectUuids) {
308 List<String> result = underTest.search(query, new SearchOptions()).getUuids();
309 assertThat(result).containsExactly(expectedProjectUuids);
312 private void assertTextQueryResults(String queryText, String... expectedProjectUuids) {
313 assertResults(new ProjectMeasuresQuery().setQueryText(queryText), expectedProjectUuids);
316 private void assertNoResults(String queryText) {
317 assertTextQueryResults(queryText);
320 private void assertNclocFacet(ProjectMeasuresQuery query, Long... facetExpectedValues) {
321 checkArgument(facetExpectedValues.length == 5, "5 facet values is required");
322 Facets facets = underTest.search(query, new SearchOptions().addFacets(NCLOC)).getFacets();
323 assertThat(facets.get(NCLOC)).containsExactly(
324 entry("*-1000.0", facetExpectedValues[0]),
325 entry("1000.0-10000.0", facetExpectedValues[1]),
326 entry("10000.0-100000.0", facetExpectedValues[2]),
327 entry("100000.0-500000.0", facetExpectedValues[3]),
328 entry("500000.0-*", facetExpectedValues[4]));