3 * Copyright (C) 2009-2021 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.component.ws;
22 import java.util.List;
23 import java.util.stream.Collectors;
24 import java.util.stream.Stream;
25 import javax.annotation.Nullable;
26 import org.junit.Before;
27 import org.junit.Rule;
28 import org.junit.Test;
29 import org.sonar.api.resources.Qualifiers;
30 import org.sonar.api.server.ws.Change;
31 import org.sonar.api.server.ws.WebService;
32 import org.sonar.api.utils.System2;
33 import org.sonar.core.util.stream.MoreCollectors;
34 import org.sonar.db.DbTester;
35 import org.sonar.db.component.ComponentDto;
36 import org.sonar.db.component.ComponentTesting;
37 import org.sonar.db.component.ResourceTypesRule;
38 import org.sonar.server.component.index.ComponentIndex;
39 import org.sonar.server.component.index.ComponentIndexer;
40 import org.sonar.server.es.EsTester;
41 import org.sonar.server.favorite.FavoriteFinder;
42 import org.sonar.server.permission.index.PermissionIndexerTester;
43 import org.sonar.server.permission.index.WebAuthorizationTypeSupport;
44 import org.sonar.server.tester.UserSessionRule;
45 import org.sonar.server.ws.TestRequest;
46 import org.sonar.server.ws.TestResponse;
47 import org.sonar.server.ws.WsActionTester;
48 import org.sonarqube.ws.Components.SuggestionsWsResponse;
49 import org.sonarqube.ws.Components.SuggestionsWsResponse.Category;
50 import org.sonarqube.ws.Components.SuggestionsWsResponse.Suggestion;
51 import org.sonarqube.ws.MediaTypes;
53 import static java.util.Arrays.asList;
54 import static java.util.Collections.singletonList;
55 import static java.util.Optional.ofNullable;
56 import static java.util.stream.Collectors.joining;
57 import static java.util.stream.IntStream.range;
58 import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
59 import static org.assertj.core.api.Assertions.assertThat;
60 import static org.assertj.core.groups.Tuple.tuple;
61 import static org.mockito.Mockito.doReturn;
62 import static org.mockito.Mockito.mock;
63 import static org.sonar.api.resources.Qualifiers.APP;
64 import static org.sonar.api.resources.Qualifiers.FILE;
65 import static org.sonar.api.resources.Qualifiers.MODULE;
66 import static org.sonar.api.resources.Qualifiers.PROJECT;
67 import static org.sonar.api.resources.Qualifiers.SUBVIEW;
68 import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE;
69 import static org.sonar.api.resources.Qualifiers.VIEW;
70 import static org.sonar.api.web.UserRole.USER;
71 import static org.sonar.db.component.ComponentTesting.newModuleDto;
72 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
73 import static org.sonar.db.component.ComponentTesting.newPublicProjectDto;
74 import static org.sonar.server.component.ws.SuggestionsAction.PARAM_MORE;
75 import static org.sonar.server.component.ws.SuggestionsAction.PARAM_QUERY;
76 import static org.sonar.server.component.ws.SuggestionsAction.PARAM_RECENTLY_BROWSED;
77 import static org.sonar.server.component.ws.SuggestionsAction.SHORT_INPUT_WARNING;
78 import static org.sonar.test.JsonAssert.assertJson;
80 public class SuggestionsActionTest {
81 private static final String[] SUGGESTION_QUALIFIERS = Stream.of(SuggestionCategory.values())
82 .map(SuggestionCategory::getQualifier)
83 .collect(MoreCollectors.toList()).toArray(new String[0]);
86 public final DbTester db = DbTester.create(System2.INSTANCE);
88 public final EsTester es = EsTester.create();
90 public final UserSessionRule userSessionRule = UserSessionRule.standalone();
91 public final ResourceTypesRule resourceTypes = new ResourceTypesRule();
93 private final ComponentIndexer componentIndexer = new ComponentIndexer(db.getDbClient(), es.client());
94 private final FavoriteFinder favoriteFinder = mock(FavoriteFinder.class);
95 private final ComponentIndex index = new ComponentIndex(es.client(), new WebAuthorizationTypeSupport(userSessionRule), System2.INSTANCE);
96 private final SuggestionsAction underTest = new SuggestionsAction(db.getDbClient(), index, favoriteFinder, userSessionRule, resourceTypes);
97 private final PermissionIndexerTester authorizationIndexerTester = new PermissionIndexerTester(es, componentIndexer);
98 private final WsActionTester ws = new WsActionTester(underTest);
101 public void setUp() {
102 resourceTypes.setAllQualifiers(SUGGESTION_QUALIFIERS);
106 public void define_suggestions_action() {
107 WebService.Action action = ws.getDef();
108 assertThat(action).isNotNull();
109 assertThat(action.isInternal()).isTrue();
110 assertThat(action.isPost()).isFalse();
111 assertThat(action.handler()).isNotNull();
112 assertThat(action.responseExampleAsString()).isNotEmpty();
113 assertThat(action.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder(
116 PARAM_RECENTLY_BROWSED);
117 assertThat(action.changelog()).extracting(Change::getVersion, Change::getDescription).containsExactlyInAnyOrder(
118 tuple("8.4", "The use of 'DIR', 'FIL','UTS' as values for parameter 'more' is no longer supported"),
119 tuple("7.6", "The use of 'BRC' as value for parameter 'more' is deprecated"));
121 WebService.Param recentlyBrowsed = action.param(PARAM_RECENTLY_BROWSED);
122 assertThat(recentlyBrowsed.since()).isEqualTo("6.4");
123 assertThat(recentlyBrowsed.exampleValue()).isNotEmpty();
124 assertThat(recentlyBrowsed.description()).isNotEmpty();
125 assertThat(recentlyBrowsed.isRequired()).isFalse();
127 WebService.Param query = action.param(PARAM_QUERY);
128 assertThat(query.exampleValue()).isNotEmpty();
129 assertThat(query.description()).isNotEmpty();
130 assertThat(query.isRequired()).isFalse();
134 public void test_example_json_response() {
135 ComponentDto project1 = db.components().insertPublicProject(p -> p.setDbKey("org.sonarsource:sonarqube").setName("SonarSource :: SonarQube"));
136 ComponentDto project2 = db.components().insertPublicProject(p -> p.setDbKey("org.sonarsource:sonarlint").setName("SonarSource :: SonarLint"));
137 componentIndexer.indexAll();
138 authorizationIndexerTester.allowOnlyAnyone(project1);
139 authorizationIndexerTester.allowOnlyAnyone(project2);
141 TestResponse wsResponse = ws.newRequest()
142 .setParam(PARAM_QUERY, "Sonar")
143 .setParam(PARAM_RECENTLY_BROWSED, project1.getDbKey())
145 .setMediaType(MediaTypes.JSON)
148 assertJson(ws.getDef().responseExampleAsString()).isSimilarTo(wsResponse.getInput());
152 public void suggestions_without_query_should_contain_recently_browsed() {
153 ComponentDto project = db.components().insertComponent(newPrivateProjectDto());
155 componentIndexer.indexAll();
156 userSessionRule.addProjectPermission(USER, project);
158 SuggestionsWsResponse response = ws.newRequest()
160 .setParam(PARAM_RECENTLY_BROWSED, project.getDbKey())
161 .executeProtobuf(SuggestionsWsResponse.class);
163 // assert match in qualifier "TRK"
164 assertThat(response.getResultsList())
165 .filteredOn(q -> q.getItemsCount() > 0)
166 .extracting(Category::getQ)
167 .containsExactly(PROJECT);
169 // assert correct id to be found
170 assertThat(response.getResultsList())
171 .flatExtracting(Category::getItemsList)
172 .extracting(Suggestion::getKey, Suggestion::getIsRecentlyBrowsed)
173 .containsExactly(tuple(project.getDbKey(), true));
177 public void suggestions_without_query_should_contain_recently_browsed_public_project() {
178 ComponentDto project = db.components().insertComponent(newPublicProjectDto());
180 componentIndexer.indexAll();
182 SuggestionsWsResponse response = ws.newRequest()
184 .setParam(PARAM_RECENTLY_BROWSED, project.getDbKey())
185 .executeProtobuf(SuggestionsWsResponse.class);
187 // assert match in qualifier "TRK"
188 assertThat(response.getResultsList())
189 .filteredOn(q -> q.getItemsCount() > 0)
190 .extracting(Category::getQ)
191 .containsExactly(PROJECT);
193 // assert correct id to be found
194 assertThat(response.getResultsList())
195 .flatExtracting(Category::getItemsList)
196 .extracting(Suggestion::getKey, Suggestion::getIsRecentlyBrowsed)
197 .containsExactly(tuple(project.getDbKey(), true));
201 public void suggestions_without_query_should_not_contain_recently_browsed_without_permission() {
202 ComponentDto project = db.components().insertComponent(newPrivateProjectDto());
204 componentIndexer.indexAll();
206 SuggestionsWsResponse response = ws.newRequest()
208 .setParam(PARAM_RECENTLY_BROWSED, project.getDbKey())
209 .executeProtobuf(SuggestionsWsResponse.class);
211 assertThat(response.getResultsList())
212 .flatExtracting(Category::getItemsList)
217 public void suggestions_without_query_should_contain_favorites() {
218 ComponentDto project = db.components().insertComponent(newPrivateProjectDto());
219 doReturn(singletonList(project)).when(favoriteFinder).list();
221 componentIndexer.indexAll();
222 userSessionRule.addProjectPermission(USER, project);
224 SuggestionsWsResponse response = ws.newRequest()
226 .executeProtobuf(SuggestionsWsResponse.class);
228 // assert match in qualifier "TRK"
229 assertThat(response.getResultsList())
230 .filteredOn(q -> q.getItemsCount() > 0)
231 .extracting(Category::getQ)
232 .containsExactly(PROJECT);
234 // assert correct id to be found
235 assertThat(response.getResultsList())
236 .flatExtracting(Category::getItemsList)
237 .extracting(Suggestion::getKey, Suggestion::getIsFavorite)
238 .containsExactly(tuple(project.getDbKey(), true));
242 public void suggestions_without_query_should_not_contain_favorites_without_permission() {
243 ComponentDto project = db.components().insertComponent(newPrivateProjectDto());
244 doReturn(singletonList(project)).when(favoriteFinder).list();
246 componentIndexer.indexAll();
248 SuggestionsWsResponse response = ws.newRequest()
250 .executeProtobuf(SuggestionsWsResponse.class);
252 assertThat(response.getResultsList())
253 .flatExtracting(Category::getItemsList)
258 public void suggestions_without_query_should_contain_recently_browsed_favorites() {
259 ComponentDto project = db.components().insertComponent(newPrivateProjectDto());
260 doReturn(singletonList(project)).when(favoriteFinder).list();
262 componentIndexer.indexAll();
263 userSessionRule.addProjectPermission(USER, project);
265 SuggestionsWsResponse response = ws.newRequest()
267 .setParam(PARAM_RECENTLY_BROWSED, project.getDbKey())
268 .executeProtobuf(SuggestionsWsResponse.class);
270 // assert match in qualifier "TRK"
271 assertThat(response.getResultsList())
272 .filteredOn(q -> q.getItemsCount() > 0)
273 .extracting(Category::getQ)
274 .containsExactly(Qualifiers.PROJECT);
276 // assert correct id to be found
277 assertThat(response.getResultsList())
278 .flatExtracting(Category::getItemsList)
279 .extracting(Suggestion::getKey, Suggestion::getIsFavorite, Suggestion::getIsRecentlyBrowsed)
280 .containsExactly(tuple(project.getDbKey(), true, true));
284 public void suggestions_without_query_should_not_contain_matches_that_are_neither_favorites_nor_recently_browsed() {
285 ComponentDto project = db.components().insertComponent(newPrivateProjectDto());
287 componentIndexer.indexAll();
288 userSessionRule.addProjectPermission(USER, project);
290 SuggestionsWsResponse response = ws.newRequest()
292 .executeProtobuf(SuggestionsWsResponse.class);
294 // assert match in qualifier "TRK"
295 assertThat(response.getResultsList())
296 .filteredOn(q -> q.getItemsCount() > 0)
297 .extracting(Category::getQ)
302 public void suggestions_without_query_should_order_results() {
303 ComponentDto project1 = db.components().insertComponent(newPrivateProjectDto().setName("Alpha"));
304 ComponentDto project2 = db.components().insertComponent(newPrivateProjectDto().setName("Bravo"));
305 ComponentDto project3 = db.components().insertComponent(newPrivateProjectDto().setName("Charlie"));
306 ComponentDto project4 = db.components().insertComponent(newPrivateProjectDto().setName("Delta"));
307 doReturn(asList(project4, project2)).when(favoriteFinder).list();
309 componentIndexer.indexAll();
310 userSessionRule.addProjectPermission(USER, project1);
311 userSessionRule.addProjectPermission(USER, project2);
312 userSessionRule.addProjectPermission(USER, project3);
313 userSessionRule.addProjectPermission(USER, project4);
315 SuggestionsWsResponse response = ws.newRequest()
317 .setParam(PARAM_RECENTLY_BROWSED, Stream.of(project3, project1).map(ComponentDto::getDbKey).collect(joining(",")))
318 .executeProtobuf(SuggestionsWsResponse.class);
320 // assert order of keys
321 assertThat(response.getResultsList())
322 .flatExtracting(Category::getItemsList)
323 .extracting(Suggestion::getName, Suggestion::getIsFavorite, Suggestion::getIsRecentlyBrowsed)
325 tuple("Bravo", true, false),
326 tuple("Delta", true, false),
327 tuple("Alpha", false, true),
328 tuple("Charlie", false, true));
332 public void suggestions_without_query_should_return_empty_qualifiers() {
333 ComponentDto project = db.components().insertComponent(newPrivateProjectDto());
334 componentIndexer.indexOnAnalysis(project.projectUuid());
335 userSessionRule.addProjectPermission(USER, project);
337 SuggestionsWsResponse response = ws.newRequest()
339 .setParam(PARAM_RECENTLY_BROWSED, project.getDbKey())
340 .executeProtobuf(SuggestionsWsResponse.class);
342 assertThat(response.getResultsList())
343 .extracting(Category::getQ, Category::getItemsCount)
344 .containsExactlyInAnyOrder(tuple("VW", 0), tuple("APP", 0), tuple("SVW", 0), tuple("TRK", 1))
345 .doesNotContain(tuple("BRC", 0), tuple("FIL", 0), tuple("UTS", 0));
349 public void suggestions_should_filter_allowed_qualifiers() {
350 resourceTypes.setAllQualifiers(PROJECT, MODULE, FILE, UNIT_TEST_FILE);
351 ComponentDto project = db.components().insertComponent(newPrivateProjectDto());
352 componentIndexer.indexOnAnalysis(project.projectUuid());
353 userSessionRule.addProjectPermission(USER, project);
355 SuggestionsWsResponse response = ws.newRequest()
357 .setParam(PARAM_RECENTLY_BROWSED, project.getDbKey())
358 .executeProtobuf(SuggestionsWsResponse.class);
360 assertThat(response.getResultsList())
361 .extracting(Category::getQ)
362 .containsExactlyInAnyOrder(PROJECT).doesNotContain(MODULE, FILE, UNIT_TEST_FILE);
366 public void exact_match_in_one_qualifier() {
367 ComponentDto project = db.components().insertComponent(newPrivateProjectDto());
369 componentIndexer.indexAll();
370 authorizationIndexerTester.allowOnlyAnyone(project);
372 SuggestionsWsResponse response = ws.newRequest()
374 .setParam(PARAM_QUERY, project.getDbKey())
375 .executeProtobuf(SuggestionsWsResponse.class);
377 // assert match in qualifier "TRK"
378 assertThat(response.getResultsList())
379 .filteredOn(q -> q.getItemsCount() > 0)
380 .extracting(Category::getQ)
381 .containsExactly(PROJECT);
383 // assert correct id to be found
384 assertThat(response.getResultsList())
385 .flatExtracting(Category::getItemsList)
386 .extracting(Suggestion::getKey)
387 .containsExactly(project.getDbKey());
391 public void should_not_return_suggestion_on_non_existing_project() {
392 ComponentDto project = db.components().insertComponent(newPrivateProjectDto());
394 componentIndexer.indexAll();
395 authorizationIndexerTester.allowOnlyAnyone(project);
397 db.getDbClient().componentDao().delete(db.getSession(), project.uuid());
400 SuggestionsWsResponse response = ws.newRequest()
402 .setParam(PARAM_QUERY, project.getDbKey())
403 .executeProtobuf(SuggestionsWsResponse.class);
405 // assert match in qualifier "TRK"
406 assertThat(response.getResultsList())
407 .filteredOn(q -> q.getItemsCount() > 0)
412 public void must_not_search_if_no_valid_tokens_are_provided() {
413 ComponentDto project = db.components().insertComponent(newPrivateProjectDto().setName("SonarQube"));
415 componentIndexer.indexAll();
416 authorizationIndexerTester.allowOnlyAnyone(project);
418 SuggestionsWsResponse response = ws.newRequest()
420 .setParam(PARAM_QUERY, "S o")
421 .executeProtobuf(SuggestionsWsResponse.class);
423 assertThat(response.getResultsList()).filteredOn(q -> q.getItemsCount() > 0).isEmpty();
424 assertThat(response.getWarning()).contains(SHORT_INPUT_WARNING);
428 public void should_warn_about_short_inputs() {
429 SuggestionsWsResponse response = ws.newRequest()
431 .setParam(PARAM_QUERY, "validLongToken x")
432 .executeProtobuf(SuggestionsWsResponse.class);
434 assertThat(response.getWarning()).contains(SHORT_INPUT_WARNING);
438 public void should_warn_about_short_inputs_but_return_results_based_on_other_terms() {
439 ComponentDto project = db.components().insertComponent(newPrivateProjectDto().setName("SonarQube"));
441 componentIndexer.indexAll();
442 authorizationIndexerTester.allowOnlyAnyone(project);
444 SuggestionsWsResponse response = ws.newRequest()
446 .setParam(PARAM_QUERY, "Sonar Q")
447 .executeProtobuf(SuggestionsWsResponse.class);
449 assertThat(response.getResultsList())
450 .flatExtracting(Category::getItemsList)
451 .extracting(Suggestion::getKey)
452 .contains(project.getDbKey());
453 assertThat(response.getWarning()).contains(SHORT_INPUT_WARNING);
457 public void should_contain_component_names() {
458 ComponentDto project1 = db.components().insertComponent(newPrivateProjectDto().setName("Project1"));
459 componentIndexer.indexOnAnalysis(project1.projectUuid());
460 authorizationIndexerTester.allowOnlyAnyone(project1);
462 SuggestionsWsResponse response = ws.newRequest()
464 .setParam(PARAM_QUERY, "Project")
465 .executeProtobuf(SuggestionsWsResponse.class);
467 assertThat(response.getResultsList())
468 .flatExtracting(Category::getItemsList)
469 .extracting(Suggestion::getKey, Suggestion::getName)
470 .containsExactlyInAnyOrder(tuple(project1.getDbKey(), project1.name()));
474 public void should_not_return_modules() {
475 ComponentDto project = db.components().insertComponent(newPrivateProjectDto().setName("ProjectWithModules"));
476 db.components().insertComponent(newModuleDto(project).setName("Module1"));
477 db.components().insertComponent(newModuleDto(project).setName("Module2"));
478 componentIndexer.indexOnAnalysis(project.projectUuid());
479 authorizationIndexerTester.allowOnlyAnyone(project);
481 SuggestionsWsResponse response = ws.newRequest()
483 .setParam(PARAM_QUERY, "Module")
484 .executeProtobuf(SuggestionsWsResponse.class);
486 assertThat(response.getResultsList())
487 .flatExtracting(Category::getItemsList)
488 .extracting(Suggestion::getKey)
489 .containsOnly(project.getDbKey());
493 public void should_mark_recently_browsed_items() {
494 ComponentDto project = db.components().insertComponent(newPrivateProjectDto().setName("ProjectModule"));
495 ComponentDto module1 = newModuleDto(project).setName("Module1");
496 db.components().insertComponent(module1);
497 ComponentDto module2 = newModuleDto(project).setName("Module2");
498 db.components().insertComponent(module2);
499 componentIndexer.indexOnAnalysis(project.projectUuid());
500 authorizationIndexerTester.allowOnlyAnyone(project);
502 SuggestionsWsResponse response = ws.newRequest()
504 .setParam(PARAM_QUERY, "Module")
505 .setParam(PARAM_RECENTLY_BROWSED, Stream.of(module1.getDbKey(), project.getDbKey()).collect(joining(",")))
506 .executeProtobuf(SuggestionsWsResponse.class);
508 assertThat(response.getResultsList())
509 .flatExtracting(Category::getItemsList)
510 .extracting(Suggestion::getIsRecentlyBrowsed)
511 .containsExactly(true);
515 public void should_mark_favorite_items() {
516 ComponentDto favouriteProject = db.components().insertComponent(newPrivateProjectDto().setName("Project1"));
517 ComponentDto nonFavouriteProject = db.components().insertComponent(newPublicProjectDto().setName("Project2"));
519 doReturn(singletonList(favouriteProject)).when(favoriteFinder).list();
520 componentIndexer.indexOnAnalysis(favouriteProject.projectUuid());
521 componentIndexer.indexOnAnalysis(nonFavouriteProject.projectUuid());
522 authorizationIndexerTester.allowOnlyAnyone(favouriteProject, nonFavouriteProject);
524 SuggestionsWsResponse response = ws.newRequest()
526 .setParam(PARAM_QUERY, "Project")
527 .executeProtobuf(SuggestionsWsResponse.class);
529 assertThat(response.getResultsList())
530 .flatExtracting(Category::getItemsList)
531 .extracting(Suggestion::getKey, Suggestion::getIsFavorite)
532 .containsExactly(tuple(favouriteProject.getDbKey(), true), tuple(nonFavouriteProject.getDbKey(), false));
536 public void should_return_empty_qualifiers() {
537 ComponentDto project = db.components().insertComponent(newPrivateProjectDto());
538 componentIndexer.indexOnAnalysis(project.projectUuid());
539 authorizationIndexerTester.allowOnlyAnyone(project);
541 SuggestionsWsResponse response = ws.newRequest()
543 .setParam(PARAM_QUERY, project.name())
544 .executeProtobuf(SuggestionsWsResponse.class);
546 assertThat(response.getResultsList())
547 .extracting(Category::getQ, Category::getItemsCount)
548 .containsExactlyInAnyOrder(tuple("VW", 0), tuple("SVW", 0), tuple("APP", 0), tuple("TRK", 1));
552 public void should_only_provide_project_for_certain_qualifiers() {
553 String query = randomAlphabetic(10);
555 ComponentDto app = db.components().insertPublicApplication(v -> v.setName(query));
556 ComponentDto view = db.components().insertPublicPortfolio(v -> v.setName(query));
557 ComponentDto subView = db.components().insertComponent(ComponentTesting.newSubView(view).setName(query));
558 ComponentDto project = db.components().insertPrivateProject(p -> p.setName(query));
559 ComponentDto module = db.components().insertComponent(ComponentTesting.newModuleDto(project).setName(query));
560 ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(module).setName(query));
561 ComponentDto test = db.components().insertComponent(ComponentTesting.newFileDto(module).setName(query).setQualifier(UNIT_TEST_FILE));
562 componentIndexer.indexAll();
563 authorizationIndexerTester.allowOnlyAnyone(project);
564 authorizationIndexerTester.allowOnlyAnyone(view);
565 authorizationIndexerTester.allowOnlyAnyone(app);
567 SuggestionsWsResponse response = ws.newRequest()
569 .setParam(PARAM_QUERY, project.name())
570 .executeProtobuf(SuggestionsWsResponse.class);
572 assertThat(response.getResultsList())
573 .extracting(Category::getQ, c -> c.getItemsList().stream().map(Suggestion::hasProject).findFirst().orElse(null))
574 .containsExactlyInAnyOrder(
575 tuple(SuggestionCategory.APP.getName(), false),
576 tuple(SuggestionCategory.VIEW.getName(), false),
577 tuple(SuggestionCategory.SUBVIEW.getName(), false),
578 tuple(SuggestionCategory.PROJECT.getName(), false));
582 public void does_not_return_branches() {
583 ComponentDto project = db.components().insertPublicProject();
584 authorizationIndexerTester.allowOnlyAnyone(project);
585 ComponentDto branch = db.components().insertProjectBranch(project);
586 componentIndexer.indexAll();
587 authorizationIndexerTester.allowOnlyAnyone(project);
589 SuggestionsWsResponse response = ws.newRequest()
591 .setParam(PARAM_QUERY, project.name())
592 .executeProtobuf(SuggestionsWsResponse.class);
594 assertThat(response.getResultsList())
595 .filteredOn(c -> "TRK".equals(c.getQ()))
596 .extracting(Category::getItemsList)
601 public void should_not_propose_to_show_more_results_if_0_projects_are_found() {
602 check_proposal_to_show_more_results(0, 0, 0L, null, true);
606 public void should_not_propose_to_show_more_results_if_0_projects_are_found_and_no_search_query_is_provided() {
607 check_proposal_to_show_more_results(0, 0, 0L, null, false);
611 public void should_not_propose_to_show_more_results_if_5_projects_are_found() {
612 check_proposal_to_show_more_results(5, 5, 0L, null, true);
616 public void should_not_propose_to_show_more_results_if_5_projects_are_found_and_no_search_query_is_provided() {
617 check_proposal_to_show_more_results(5, 5, 0L, null, false);
621 public void should_not_propose_to_show_more_results_if_6_projects_are_found() {
622 check_proposal_to_show_more_results(6, 6, 0L, null, true);
626 public void should_not_propose_to_show_more_results_if_6_projects_are_found_and_no_search_query_is_provided() {
627 check_proposal_to_show_more_results(6, 6, 0L, null, false);
631 public void should_propose_to_show_more_results_if_7_projects_are_found() {
632 check_proposal_to_show_more_results(7, 6, 1L, null, true);
636 public void should_propose_to_show_more_results_if_7_projects_are_found_and_no_search_query_is_provided() {
637 check_proposal_to_show_more_results(7, 6, 1L, null, false);
641 public void show_more_results_if_requested_and_5_projects_are_found() {
642 check_proposal_to_show_more_results(5, 0, 0L, SuggestionCategory.PROJECT, true);
646 public void show_more_results_if_requested_and_5_projects_are_found_and_no_search_query_is_provided() {
647 check_proposal_to_show_more_results(5, 0, 0L, SuggestionCategory.PROJECT, false);
651 public void show_more_results_if_requested_and_6_projects_are_found() {
652 check_proposal_to_show_more_results(6, 0, 0L, SuggestionCategory.PROJECT, true);
656 public void show_more_results_if_requested_and_6_projects_are_found_and_no_search_query_is_provided() {
657 check_proposal_to_show_more_results(6, 0, 0L, SuggestionCategory.PROJECT, false);
661 public void show_more_results_if_requested_and_7_projects_are_found() {
662 check_proposal_to_show_more_results(7, 1, 0L, SuggestionCategory.PROJECT, true);
666 public void show_more_results_if_requested_and_7_projects_are_found_and_no_search_query_is_provided() {
667 check_proposal_to_show_more_results(7, 1, 0L, SuggestionCategory.PROJECT, false);
671 public void show_more_results_if_requested_and_26_projects_are_found() {
672 check_proposal_to_show_more_results(26, 20, 0L, SuggestionCategory.PROJECT, true);
676 public void show_more_results_if_requested_and_26_projects_are_found_and_no_search_query_is_provided() {
677 check_proposal_to_show_more_results(26, 20, 0L, SuggestionCategory.PROJECT, false);
681 public void show_more_results_if_requested_and_27_projects_are_found() {
682 check_proposal_to_show_more_results(27, 20, 1L, SuggestionCategory.PROJECT, true);
686 public void show_more_results_if_requested_and_27_projects_are_found_and_no_search_query_is_provided() {
687 check_proposal_to_show_more_results(27, 20, 1L, SuggestionCategory.PROJECT, false);
691 public void show_more_results_filter_out_if_non_allowed_qualifiers() {
692 resourceTypes.setAllQualifiers(APP, VIEW, SUBVIEW);
694 check_proposal_to_show_more_results(10, 0, 0L, SuggestionCategory.PROJECT, true);
697 private void check_proposal_to_show_more_results(int numberOfProjects, int expectedNumberOfResults, long expectedNumberOfMoreResults, @Nullable SuggestionCategory more,
699 String namePrefix = "MyProject";
701 List<ComponentDto> projects = range(0, numberOfProjects)
702 .mapToObj(i -> db.components().insertComponent(newPublicProjectDto().setName(namePrefix + i)))
703 .collect(Collectors.toList());
705 componentIndexer.indexAll();
706 projects.forEach(authorizationIndexerTester::allowOnlyAnyone);
708 TestRequest request = ws.newRequest()
711 request.setParam(PARAM_QUERY, namePrefix);
713 doReturn(projects).when(favoriteFinder).list();
715 ofNullable(more).ifPresent(c -> request.setParam(PARAM_MORE, c.getName()));
716 SuggestionsWsResponse response = request
717 .executeProtobuf(SuggestionsWsResponse.class);
719 // include limited number of results in the response
720 assertThat(response.getResultsList())
721 .flatExtracting(Category::getItemsList)
722 .hasSize(expectedNumberOfResults);
724 // indicate, that there are more results
725 if (expectedNumberOfResults == 0 && expectedNumberOfMoreResults == 0) {
726 assertThat(response.getResultsList())
727 .filteredOn(q -> q.getItemsCount() > 0)
730 assertThat(response.getResultsList())
731 .filteredOn(c -> "TRK".equals(c.getQ()))
732 .extracting(Category::getMore)
733 .containsExactly(expectedNumberOfMoreResults);
734 response.getResultsList().stream()
735 .filter(c -> !"TRK".equals(c.getQ()))
736 .map(Category::getMore)
737 .forEach(m -> assertThat(m).isEqualTo(0L));