3 * Copyright (C) 2009-2017 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.junit.rules.ExpectedException;
28 import org.sonar.api.config.MapSettings;
29 import org.sonar.api.resources.Qualifiers;
30 import org.sonar.db.component.ComponentDto;
31 import org.sonar.db.organization.OrganizationDto;
32 import org.sonar.db.organization.OrganizationTesting;
33 import org.sonar.server.es.EsTester;
34 import org.sonar.server.es.Facets;
35 import org.sonar.server.es.SearchOptions;
36 import org.sonar.server.permission.index.AuthorizationTypeSupport;
37 import org.sonar.server.permission.index.PermissionIndexerDao;
38 import org.sonar.server.permission.index.PermissionIndexerTester;
39 import org.sonar.server.tester.UserSessionRule;
41 import static com.google.common.base.Preconditions.checkArgument;
42 import static com.google.common.collect.Lists.newArrayList;
43 import static java.util.Arrays.stream;
44 import static org.assertj.core.api.Assertions.assertThat;
45 import static org.assertj.core.api.Assertions.entry;
46 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
47 import static org.sonar.server.component.ws.FilterParser.Operator.GT;
48 import static org.sonar.server.component.ws.FilterParser.Operator.LT;
49 import static org.sonar.server.measure.index.ProjectMeasuresIndexDefinition.INDEX_TYPE_PROJECT_MEASURES;
50 import static org.sonar.server.measure.index.ProjectMeasuresQuery.MetricCriterion;
52 public class ProjectMeasuresIndexTextSearchTest {
54 private static final String NCLOC = "ncloc";
56 private static final OrganizationDto ORG = OrganizationTesting.newOrganizationDto();
59 public EsTester es = new EsTester(new ProjectMeasuresIndexDefinition(new MapSettings()));
62 public ExpectedException expectedException = ExpectedException.none();
65 public UserSessionRule userSession = UserSessionRule.standalone();
67 private ProjectMeasuresIndexer projectMeasureIndexer = new ProjectMeasuresIndexer(null, es.client());
68 private PermissionIndexerTester authorizationIndexerTester = new PermissionIndexerTester(es, projectMeasureIndexer);
69 private ProjectMeasuresIndex underTest = new ProjectMeasuresIndex(es.client(), new AuthorizationTypeSupport(userSession));
72 public void match_exact_case_insensitive_name() {
74 newDoc(newPrivateProjectDto(ORG).setUuid("struts").setName("Apache Struts")),
75 newDoc(newPrivateProjectDto(ORG).setUuid("sonarqube").setName("SonarQube")));
77 assertTextQueryResults("Apache Struts", "struts");
78 assertTextQueryResults("APACHE STRUTS", "struts");
79 assertTextQueryResults("APACHE struTS", "struts");
83 public void match_from_sub_name() {
84 index(newDoc(newPrivateProjectDto(ORG).setUuid("struts").setName("Apache Struts")));
86 assertTextQueryResults("truts", "struts");
87 assertTextQueryResults("pache", "struts");
88 assertTextQueryResults("apach", "struts");
89 assertTextQueryResults("che stru", "struts");
93 public void match_name_with_dot() {
94 index(newDoc(newPrivateProjectDto(ORG).setUuid("struts").setName("Apache.Struts")));
96 assertTextQueryResults("apache struts", "struts");
100 public void match_partial_name() {
101 index(newDoc(newPrivateProjectDto(ORG).setUuid("struts").setName("XstrutsxXjavax")));
103 assertTextQueryResults("struts java", "struts");
107 public void match_partial_name_prefix_word1() {
108 index(newDoc(newPrivateProjectDto(ORG).setUuid("struts").setName("MyStruts.java")));
110 assertTextQueryResults("struts java", "struts");
114 public void match_partial_name_suffix_word1() {
115 index(newDoc(newPrivateProjectDto(ORG).setUuid("struts").setName("StrutsObject.java")));
117 assertTextQueryResults("struts java", "struts");
121 public void match_partial_name_prefix_word2() {
122 index(newDoc(newPrivateProjectDto(ORG).setUuid("struts").setName("MyStruts.xjava")));
124 assertTextQueryResults("struts java", "struts");
128 public void match_partial_name_suffix_word2() {
129 index(newDoc(newPrivateProjectDto(ORG).setUuid("struts").setName("MyStrutsObject.xjavax")));
131 assertTextQueryResults("struts java", "struts");
135 public void match_subset_of_document_terms() {
136 index(newDoc(newPrivateProjectDto(ORG).setUuid("struts").setName("Some.Struts.Project.java.old")));
138 assertTextQueryResults("struts java", "struts");
142 public void match_partial_match_prefix_and_suffix_everywhere() {
143 index(newDoc(newPrivateProjectDto(ORG).setUuid("struts").setName("MyStruts.javax")));
145 assertTextQueryResults("struts java", "struts");
149 public void ignore_empty_words() {
150 index(newDoc(newPrivateProjectDto(ORG).setUuid("struts").setName("Struts")));
152 assertTextQueryResults(" struts \n \n\n", "struts");
156 public void match_name_from_prefix() {
157 index(newDoc(newPrivateProjectDto(ORG).setUuid("struts").setName("Apache Struts")));
159 assertTextQueryResults("apach", "struts");
160 assertTextQueryResults("ApA", "struts");
161 assertTextQueryResults("AP", "struts");
165 public void match_name_from_two_words() {
166 index(newDoc(newPrivateProjectDto(ORG).setUuid("project").setName("ApacheStrutsFoundation")));
168 assertTextQueryResults("apache struts", "project");
169 assertTextQueryResults("struts apache", "project");
170 // Only one word is matching
171 assertNoResults("apache plugin");
172 assertNoResults("project struts");
176 public void match_long_name() {
178 newDoc(newPrivateProjectDto(ORG).setUuid("project1").setName("LongNameLongNameLongNameLongNameSonarQube")),
179 newDoc(newPrivateProjectDto(ORG).setUuid("project2").setName("LongNameLongNameLongNameLongNameSonarQubeX")));
181 assertTextQueryResults("LongNameLongNameLongNameLongNameSonarQube", "project1", "project2");
185 public void match_name_with_two_characters() {
186 index(newDoc(newPrivateProjectDto(ORG).setUuid("struts").setName("Apache Struts")));
188 assertTextQueryResults("st", "struts");
189 assertTextQueryResults("tr", "struts");
193 public void match_exact_case_insensitive_key() {
195 newDoc(newPrivateProjectDto(ORG).setUuid("project1").setName("Windows").setKey("project1")),
196 newDoc(newPrivateProjectDto(ORG).setUuid("project2").setName("apachee").setKey("project2")));
198 assertTextQueryResults("project1", "project1");
199 assertTextQueryResults("PROJECT1", "project1");
200 assertTextQueryResults("pRoJecT1", "project1");
204 public void match_key_with_dot() {
206 newDoc(newPrivateProjectDto(ORG).setUuid("sonarqube").setName("SonarQube").setKey("org.sonarqube")),
207 newDoc(newPrivateProjectDto(ORG).setUuid("sq").setName("SQ").setKey("sonarqube")));
209 assertTextQueryResults("org.sonarqube", "sonarqube");
210 assertNoResults("orgsonarqube");
211 assertNoResults("org-sonarqube");
212 assertNoResults("org:sonarqube");
213 assertNoResults("org sonarqube");
217 public void match_key_with_dash() {
219 newDoc(newPrivateProjectDto(ORG).setUuid("sonarqube").setName("SonarQube").setKey("org-sonarqube")),
220 newDoc(newPrivateProjectDto(ORG).setUuid("sq").setName("SQ").setKey("sonarqube")));
222 assertTextQueryResults("org-sonarqube", "sonarqube");
223 assertNoResults("orgsonarqube");
224 assertNoResults("org.sonarqube");
225 assertNoResults("org:sonarqube");
226 assertNoResults("org sonarqube");
230 public void match_key_with_colon() {
232 newDoc(newPrivateProjectDto(ORG).setUuid("sonarqube").setName("SonarQube").setKey("org:sonarqube")),
233 newDoc(newPrivateProjectDto(ORG).setUuid("sq").setName("SQ").setKey("sonarqube")));
235 assertTextQueryResults("org:sonarqube", "sonarqube");
236 assertNoResults("orgsonarqube");
237 assertNoResults("org-sonarqube");
238 assertNoResults("org_sonarqube");
239 assertNoResults("org sonarqube");
243 public void match_key_having_all_special_characters() {
244 index(newDoc(newPrivateProjectDto(ORG).setUuid("sonarqube").setName("SonarQube").setKey("org.sonarqube:sonar-sérvèr_ç")));
246 assertTextQueryResults("org.sonarqube:sonar-sérvèr_ç", "sonarqube");
250 public void does_not_match_partial_key() {
251 index(newDoc(newPrivateProjectDto(ORG).setUuid("project").setName("some name").setKey("theKey")));
253 assertNoResults("theke");
254 assertNoResults("hekey");
258 public void facets_take_into_account_text_search() {
260 // docs with ncloc<1K
261 newDoc(newPrivateProjectDto(ORG).setName("Windows").setKey("project1"), NCLOC, 0d),
262 newDoc(newPrivateProjectDto(ORG).setName("apachee").setKey("project2"), NCLOC, 999d),
263 // docs with ncloc>=1K and ncloc<10K
264 newDoc(newPrivateProjectDto(ORG).setName("Apache").setKey("project3"), NCLOC, 1_000d),
265 // docs with ncloc>=100K and ncloc<500K
266 newDoc(newPrivateProjectDto(ORG).setName("Apache Foundation").setKey("project4"), NCLOC, 100_000d));
268 assertNclocFacet(new ProjectMeasuresQuery().setQueryText("apache"), 1L, 1L, 0L, 1L, 0L);
269 assertNclocFacet(new ProjectMeasuresQuery().setQueryText("PAch"), 1L, 1L, 0L, 1L, 0L);
270 assertNclocFacet(new ProjectMeasuresQuery().setQueryText("apache foundation"), 0L, 0L, 0L, 1L, 0L);
271 assertNclocFacet(new ProjectMeasuresQuery().setQueryText("project3"), 0L, 1L, 0L, 0L, 0L);
272 assertNclocFacet(new ProjectMeasuresQuery().setQueryText("project"), 0L, 0L, 0L, 0L, 0L);
276 public void filter_by_metric_take_into_account_text_search() {
278 newDoc(newPrivateProjectDto(ORG).setUuid("project1").setName("Windows").setKey("project1"), NCLOC, 30_000d),
279 newDoc(newPrivateProjectDto(ORG).setUuid("project2").setName("apachee").setKey("project2"), NCLOC, 40_000d),
280 newDoc(newPrivateProjectDto(ORG).setUuid("project3").setName("Apache").setKey("project3"), NCLOC, 50_000d),
281 newDoc(newPrivateProjectDto(ORG).setUuid("project4").setName("Apache").setKey("project4"), NCLOC, 60_000d));
283 assertResults(new ProjectMeasuresQuery().setQueryText("apache").addMetricCriterion(MetricCriterion.create(NCLOC, GT, 20_000d)), "project3", "project4", "project2");
284 assertResults(new ProjectMeasuresQuery().setQueryText("apache").addMetricCriterion(MetricCriterion.create(NCLOC, LT, 55_000d)), "project3", "project2");
285 assertResults(new ProjectMeasuresQuery().setQueryText("PAC").addMetricCriterion(MetricCriterion.create(NCLOC, LT, 55_000d)), "project3", "project2");
286 assertResults(new ProjectMeasuresQuery().setQueryText("apachee").addMetricCriterion(MetricCriterion.create(NCLOC, GT, 30_000d)), "project2");
287 assertResults(new ProjectMeasuresQuery().setQueryText("unknown").addMetricCriterion(MetricCriterion.create(NCLOC, GT, 20_000d)));
290 private void index(ProjectMeasuresDoc... docs) {
291 es.putDocuments(INDEX_TYPE_PROJECT_MEASURES, docs);
292 stream(docs).forEach(doc -> {
293 PermissionIndexerDao.Dto access = new PermissionIndexerDao.Dto(doc.getId(), System.currentTimeMillis(), Qualifiers.PROJECT);
294 access.allowAnyone();
295 authorizationIndexerTester.allow(access);
299 private static ProjectMeasuresDoc newDoc(ComponentDto project) {
300 return new ProjectMeasuresDoc()
301 .setOrganizationUuid(project.getOrganizationUuid())
302 .setId(project.uuid())
303 .setKey(project.key())
304 .setName(project.name());
307 private static ProjectMeasuresDoc newDoc(ComponentDto project, String metric1, Object value1) {
308 return newDoc(project).setMeasures(newArrayList(newMeasure(metric1, value1)));
311 private static Map<String, Object> newMeasure(String key, Object value) {
312 return ImmutableMap.of("key", key, "value", value);
315 private void assertResults(ProjectMeasuresQuery query, String... expectedProjectUuids) {
316 List<String> result = underTest.search(query, new SearchOptions()).getIds();
317 assertThat(result).containsExactly(expectedProjectUuids);
320 private void assertTextQueryResults(String queryText, String... expectedProjectUuids) {
321 assertResults(new ProjectMeasuresQuery().setQueryText(queryText), expectedProjectUuids);
324 private void assertNoResults(String queryText) {
325 assertTextQueryResults(queryText);
328 private void assertNclocFacet(ProjectMeasuresQuery query, Long... facetExpectedValues) {
329 checkArgument(facetExpectedValues.length == 5, "5 facet values is required");
330 Facets facets = underTest.search(query, new SearchOptions().addFacets(NCLOC)).getFacets();
331 assertThat(facets.get(NCLOC)).containsExactly(
332 entry("*-1000.0", facetExpectedValues[0]),
333 entry("1000.0-10000.0", facetExpectedValues[1]),
334 entry("10000.0-100000.0", facetExpectedValues[2]),
335 entry("100000.0-500000.0", facetExpectedValues[3]),
336 entry("500000.0-*", facetExpectedValues[4]));