You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SearchActionFacetsTest.java 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2020 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.server.issue.ws;
  21. import com.google.common.collect.ImmutableMap;
  22. import java.time.Clock;
  23. import java.util.Map;
  24. import java.util.Random;
  25. import java.util.stream.IntStream;
  26. import org.junit.Rule;
  27. import org.junit.Test;
  28. import org.junit.rules.ExpectedException;
  29. import org.sonar.api.issue.Issue;
  30. import org.sonar.api.resources.Languages;
  31. import org.sonar.api.rules.RuleType;
  32. import org.sonar.api.server.ws.WebService;
  33. import org.sonar.api.utils.Durations;
  34. import org.sonar.api.utils.System2;
  35. import org.sonar.db.DbTester;
  36. import org.sonar.db.component.ComponentDto;
  37. import org.sonar.db.organization.OrganizationDto;
  38. import org.sonar.db.rule.RuleDefinitionDto;
  39. import org.sonar.db.user.UserDto;
  40. import org.sonar.server.es.EsTester;
  41. import org.sonar.server.es.StartupIndexer;
  42. import org.sonar.server.issue.AvatarResolverImpl;
  43. import org.sonar.server.issue.TextRangeResponseFormatter;
  44. import org.sonar.server.issue.TransitionService;
  45. import org.sonar.server.issue.index.IssueIndex;
  46. import org.sonar.server.issue.index.IssueIndexer;
  47. import org.sonar.server.issue.index.IssueIteratorFactory;
  48. import org.sonar.server.issue.index.IssueQueryFactory;
  49. import org.sonar.server.permission.index.PermissionIndexer;
  50. import org.sonar.server.permission.index.WebAuthorizationTypeSupport;
  51. import org.sonar.server.tester.UserSessionRule;
  52. import org.sonar.server.ws.WsActionTester;
  53. import org.sonarqube.ws.Common;
  54. import org.sonarqube.ws.Common.FacetValue;
  55. import org.sonarqube.ws.Issues.SearchWsResponse;
  56. import static com.google.common.collect.ImmutableMap.of;
  57. import static java.util.stream.Collectors.toMap;
  58. import static org.assertj.core.api.Assertions.assertThat;
  59. import static org.assertj.core.groups.Tuple.tuple;
  60. import static org.sonar.api.server.ws.WebService.Param.FACETS;
  61. import static org.sonar.db.component.ComponentTesting.newDirectory;
  62. import static org.sonar.db.component.ComponentTesting.newFileDto;
  63. import static org.sonar.db.component.ComponentTesting.newModuleDto;
  64. import static org.sonar.server.tester.UserSessionRule.standalone;
  65. import static org.sonarqube.ws.client.issue.IssuesWsParameters.FACET_MODE_EFFORT;
  66. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENT_KEYS;
  67. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENT_UUIDS;
  68. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_FILE_UUIDS;
  69. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_MODULE_UUIDS;
  70. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ORGANIZATION;
  71. import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECTS;
  72. public class SearchActionFacetsTest {
  73. private static final String[] ISSUE_STATUSES = Issue.STATUSES.stream().filter(s -> !Issue.STATUS_TO_REVIEW.equals(s)).filter(s -> !Issue.STATUS_REVIEWED.equals(s))
  74. .toArray(String[]::new);
  75. @Rule
  76. public UserSessionRule userSession = standalone();
  77. @Rule
  78. public DbTester db = DbTester.create();
  79. @Rule
  80. public EsTester es = EsTester.create();
  81. @Rule
  82. public ExpectedException expectedException = ExpectedException.none();
  83. private IssueIndex issueIndex = new IssueIndex(es.client(), System2.INSTANCE, userSession, new WebAuthorizationTypeSupport(userSession));
  84. private IssueIndexer issueIndexer = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient()));
  85. private StartupIndexer permissionIndexer = new PermissionIndexer(db.getDbClient(), es.client(), issueIndexer);
  86. private IssueQueryFactory issueQueryFactory = new IssueQueryFactory(db.getDbClient(), Clock.systemUTC(), userSession);
  87. private SearchResponseLoader searchResponseLoader = new SearchResponseLoader(userSession, db.getDbClient(), new TransitionService(userSession, null));
  88. private Languages languages = new Languages();
  89. private UserResponseFormatter userFormatter = new UserResponseFormatter(new AvatarResolverImpl());
  90. private SearchResponseFormat searchResponseFormat = new SearchResponseFormat(new Durations(), languages, new TextRangeResponseFormatter(), userFormatter);
  91. private WsActionTester ws = new WsActionTester(
  92. new SearchAction(userSession, issueIndex, issueQueryFactory, searchResponseLoader, searchResponseFormat, System2.INSTANCE, db.getDbClient()));
  93. @Test
  94. public void display_all_facets() {
  95. ComponentDto project = db.components().insertPublicProject();
  96. ComponentDto module = db.components().insertComponent(newModuleDto(project));
  97. ComponentDto file = db.components().insertComponent(newFileDto(module));
  98. RuleDefinitionDto rule = db.rules().insertIssueRule();
  99. UserDto user = db.users().insertUser();
  100. db.issues().insertIssue(rule, project, file, i -> i
  101. .setSeverity("MAJOR")
  102. .setStatus("OPEN")
  103. .setType(RuleType.CODE_SMELL)
  104. .setEffort(10L)
  105. .setAssigneeUuid(user.getUuid()));
  106. indexPermissions();
  107. indexIssues();
  108. SearchWsResponse response = ws.newRequest()
  109. .setParam(PARAM_COMPONENT_KEYS, project.getKey())
  110. .setParam(FACETS, "severities,statuses,resolutions,rules,types,languages,projects,moduleUuids,fileUuids,assignees")
  111. .executeProtobuf(SearchWsResponse.class);
  112. Map<String, Number> expectedStatuses = ImmutableMap.<String, Number>builder().put("OPEN", 1L).put("CONFIRMED", 0L)
  113. .put("REOPENED", 0L).put("RESOLVED", 0L).put("CLOSED", 0L).build();
  114. assertThat(response.getFacets().getFacetsList())
  115. .extracting(Common.Facet::getProperty, facet -> facet.getValuesList().stream().collect(toMap(FacetValue::getVal, FacetValue::getCount)))
  116. .containsExactlyInAnyOrder(
  117. tuple("severities", of("INFO", 0L, "MINOR", 0L, "MAJOR", 1L, "CRITICAL", 0L, "BLOCKER", 0L)),
  118. tuple("statuses", expectedStatuses),
  119. tuple("resolutions", of("", 1L, "FALSE-POSITIVE", 0L, "FIXED", 0L, "REMOVED", 0L, "WONTFIX", 0L)),
  120. tuple("rules", of(rule.getKey().toString(), 1L)),
  121. tuple("types", of("CODE_SMELL", 1L, "BUG", 0L, "VULNERABILITY", 0L)),
  122. tuple("languages", of(rule.getLanguage(), 1L)),
  123. tuple("projects", of(project.getKey(), 1L)),
  124. tuple("moduleUuids", of(module.uuid(), 1L)),
  125. tuple("fileUuids", of(file.uuid(), 1L)),
  126. tuple("assignees", of("", 0L, user.getLogin(), 1L)));
  127. }
  128. @Test
  129. public void display_facets_in_effort_mode() {
  130. ComponentDto project = db.components().insertPublicProject();
  131. ComponentDto file = db.components().insertComponent(newFileDto(project));
  132. RuleDefinitionDto rule = db.rules().insertIssueRule();
  133. db.issues().insertIssue(rule, project, file, i -> i
  134. .setSeverity("MAJOR")
  135. .setStatus("OPEN")
  136. .setType(RuleType.CODE_SMELL)
  137. .setEffort(10L)
  138. .setAssigneeUuid(null));
  139. indexPermissions();
  140. indexIssues();
  141. SearchWsResponse response = ws.newRequest()
  142. .setParam(PARAM_COMPONENT_KEYS, project.getKey())
  143. .setParam(FACETS, "severities,statuses,resolutions,rules,types,languages,projects,fileUuids,assignees")
  144. .setParam("facetMode", FACET_MODE_EFFORT)
  145. .executeProtobuf(SearchWsResponse.class);
  146. Map<String, Number> expectedStatuses = ImmutableMap.<String, Number>builder().put("OPEN", 10L).put("CONFIRMED", 0L)
  147. .put("REOPENED", 0L).put("RESOLVED", 0L).put("CLOSED", 0L).build();
  148. assertThat(response.getFacets().getFacetsList())
  149. .extracting(Common.Facet::getProperty, facet -> facet.getValuesList().stream().collect(toMap(FacetValue::getVal, FacetValue::getCount)))
  150. .containsExactlyInAnyOrder(
  151. tuple("severities", of("INFO", 0L, "MINOR", 0L, "MAJOR", 10L, "CRITICAL", 0L, "BLOCKER", 0L)),
  152. tuple("statuses", expectedStatuses),
  153. tuple("resolutions", of("", 10L, "FALSE-POSITIVE", 0L, "FIXED", 0L, "REMOVED", 0L, "WONTFIX", 0L)),
  154. tuple("rules", of(rule.getKey().toString(), 10L)),
  155. tuple("types", of("CODE_SMELL", 10L, "BUG", 0L, "VULNERABILITY", 0L)),
  156. tuple("languages", of(rule.getLanguage(), 10L)),
  157. tuple("projects", of(project.getKey(), 10L)),
  158. tuple("fileUuids", of(file.uuid(), 10L)),
  159. tuple("assignees", of("", 10L)));
  160. }
  161. @Test
  162. public void display_projects_facet() {
  163. ComponentDto project = db.components().insertPublicProject();
  164. ComponentDto file = db.components().insertComponent(newFileDto(project));
  165. RuleDefinitionDto rule = db.rules().insertIssueRule();
  166. db.issues().insertIssue(rule, project, file);
  167. indexPermissions();
  168. indexIssues();
  169. SearchWsResponse response = ws.newRequest()
  170. .setParam(PARAM_PROJECTS, project.getKey())
  171. .setParam(WebService.Param.FACETS, "projects")
  172. .executeProtobuf(SearchWsResponse.class);
  173. assertThat(response.getFacets().getFacetsList())
  174. .extracting(Common.Facet::getProperty, facet -> facet.getValuesList().stream().collect(toMap(FacetValue::getVal, FacetValue::getCount)))
  175. .containsExactlyInAnyOrder(tuple("projects", of(project.getKey(), 1L)));
  176. }
  177. @Test
  178. public void projects_facet_is_sticky() {
  179. OrganizationDto organization1 = db.organizations().insert();
  180. OrganizationDto organization2 = db.organizations().insert();
  181. OrganizationDto organization3 = db.organizations().insert();
  182. ComponentDto project1 = db.components().insertPublicProject(organization1);
  183. ComponentDto project2 = db.components().insertPublicProject(organization2);
  184. ComponentDto project3 = db.components().insertPublicProject(organization3);
  185. ComponentDto file1 = db.components().insertComponent(newFileDto(project1));
  186. ComponentDto file2 = db.components().insertComponent(newFileDto(project2));
  187. ComponentDto file3 = db.components().insertComponent(newFileDto(project3));
  188. RuleDefinitionDto rule = db.rules().insertIssueRule();
  189. db.issues().insertIssue(rule, project1, file1);
  190. db.issues().insertIssue(rule, project2, file2);
  191. db.issues().insertIssue(rule, project3, file3);
  192. indexPermissions();
  193. indexIssues();
  194. SearchWsResponse response = ws.newRequest()
  195. .setParam(PARAM_PROJECTS, project1.getKey())
  196. .setParam(WebService.Param.FACETS, "projects")
  197. .executeProtobuf(SearchWsResponse.class);
  198. assertThat(response.getFacets().getFacetsList())
  199. .extracting(Common.Facet::getProperty, facet -> facet.getValuesList().stream().collect(toMap(FacetValue::getVal, FacetValue::getCount)))
  200. .containsExactlyInAnyOrder(tuple("projects", of(project1.getKey(), 1L, project2.getKey(), 1L, project3.getKey(), 1L)));
  201. }
  202. @Test
  203. public void display_moduleUuids_facet_using_project() {
  204. ComponentDto project = db.components().insertPublicProject();
  205. ComponentDto module = db.components().insertComponent(newModuleDto(project));
  206. ComponentDto subModule1 = db.components().insertComponent(newModuleDto(module));
  207. ComponentDto subModule2 = db.components().insertComponent(newModuleDto(module));
  208. ComponentDto subModule3 = db.components().insertComponent(newModuleDto(module));
  209. ComponentDto file1 = db.components().insertComponent(newFileDto(subModule1));
  210. ComponentDto file2 = db.components().insertComponent(newFileDto(subModule2));
  211. RuleDefinitionDto rule = db.rules().insertIssueRule();
  212. db.issues().insertIssue(rule, project, file1);
  213. db.issues().insertIssue(rule, project, file2);
  214. indexPermissions();
  215. indexIssues();
  216. SearchWsResponse response = ws.newRequest()
  217. .setParam(PARAM_PROJECTS, project.getKey())
  218. .setParam(PARAM_COMPONENT_UUIDS, module.uuid())
  219. .setParam(PARAM_MODULE_UUIDS, subModule1.uuid())
  220. .setParam(WebService.Param.FACETS, "moduleUuids")
  221. .executeProtobuf(SearchWsResponse.class);
  222. assertThat(response.getFacets().getFacetsList())
  223. .extracting(Common.Facet::getProperty, facet -> facet.getValuesList().stream().collect(toMap(FacetValue::getVal, FacetValue::getCount)))
  224. .containsExactlyInAnyOrder(tuple("moduleUuids", of(subModule1.uuid(), 1L, subModule2.uuid(), 1L)));
  225. }
  226. @Test
  227. public void display_module_facet_using_organization() {
  228. OrganizationDto organization = db.organizations().insert();
  229. ComponentDto project = db.components().insertPublicProject(organization);
  230. ComponentDto module = db.components().insertComponent(newModuleDto(project));
  231. ComponentDto subModule1 = db.components().insertComponent(newModuleDto(module));
  232. ComponentDto subModule2 = db.components().insertComponent(newModuleDto(module));
  233. ComponentDto subModule3 = db.components().insertComponent(newModuleDto(module));
  234. ComponentDto file1 = db.components().insertComponent(newFileDto(subModule1));
  235. ComponentDto file2 = db.components().insertComponent(newFileDto(subModule2));
  236. RuleDefinitionDto rule = db.rules().insertIssueRule();
  237. db.issues().insertIssue(rule, project, file1);
  238. db.issues().insertIssue(rule, project, file2);
  239. indexPermissions();
  240. indexIssues();
  241. SearchWsResponse response = ws.newRequest()
  242. .setParam(PARAM_ORGANIZATION, organization.getKey())
  243. .setParam(PARAM_COMPONENT_UUIDS, module.uuid())
  244. .setParam(PARAM_MODULE_UUIDS, subModule1.uuid() + "," + subModule2.uuid())
  245. .setParam(WebService.Param.FACETS, "moduleUuids")
  246. .executeProtobuf(SearchWsResponse.class);
  247. assertThat(response.getFacets().getFacetsList())
  248. .extracting(Common.Facet::getProperty, facet -> facet.getValuesList().stream().collect(toMap(FacetValue::getVal, FacetValue::getCount)))
  249. .containsExactlyInAnyOrder(tuple("moduleUuids", of(subModule1.uuid(), 1L, subModule2.uuid(), 1L)));
  250. }
  251. @Test
  252. public void fail_to_display_module_facet_when_no_organization_or_project_is_set() {
  253. ComponentDto project = db.components().insertPublicProject();
  254. ComponentDto module = db.components().insertComponent(newModuleDto(project));
  255. ComponentDto file = db.components().insertComponent(newFileDto(module, null));
  256. RuleDefinitionDto rule = db.rules().insertIssueRule();
  257. db.issues().insertIssue(rule, project, file);
  258. indexPermissions();
  259. indexIssues();
  260. expectedException.expect(IllegalArgumentException.class);
  261. expectedException.expectMessage("Facet(s) 'moduleUuids' require to also filter by project or organization");
  262. ws.newRequest()
  263. .setParam(PARAM_COMPONENT_UUIDS, module.uuid())
  264. .setParam(WebService.Param.FACETS, "moduleUuids")
  265. .execute();
  266. }
  267. @Test
  268. public void display_directory_facet_using_project() {
  269. ComponentDto project = db.components().insertPublicProject();
  270. ComponentDto directory = db.components().insertComponent(newDirectory(project, "src/main/java/dir"));
  271. ComponentDto file = db.components().insertComponent(newFileDto(project, directory));
  272. RuleDefinitionDto rule = db.rules().insertIssueRule();
  273. db.issues().insertIssue(rule, project, file);
  274. indexPermissions();
  275. indexIssues();
  276. SearchWsResponse response = ws.newRequest()
  277. .setParam("resolved", "false")
  278. .setParam(PARAM_COMPONENT_KEYS, project.getKey())
  279. .setParam(WebService.Param.FACETS, "directories")
  280. .executeProtobuf(SearchWsResponse.class);
  281. assertThat(response.getFacets().getFacetsList())
  282. .extracting(Common.Facet::getProperty, facet -> facet.getValuesList().stream().collect(toMap(FacetValue::getVal, FacetValue::getCount)))
  283. .containsExactlyInAnyOrder(tuple("directories", of(directory.path(), 1L)));
  284. }
  285. @Test
  286. public void display_directory_facet_using_organization() {
  287. OrganizationDto organization = db.organizations().insert();
  288. ComponentDto project = db.components().insertPublicProject(organization);
  289. ComponentDto directory = db.components().insertComponent(newDirectory(project, "src/main/java/dir"));
  290. ComponentDto file = db.components().insertComponent(newFileDto(project, directory));
  291. RuleDefinitionDto rule = db.rules().insertIssueRule();
  292. db.issues().insertIssue(rule, project, file);
  293. indexPermissions();
  294. indexIssues();
  295. SearchWsResponse response = ws.newRequest()
  296. .setParam("resolved", "false")
  297. .setParam(PARAM_ORGANIZATION, organization.getKey())
  298. .setParam(WebService.Param.FACETS, "directories")
  299. .executeProtobuf(SearchWsResponse.class);
  300. assertThat(response.getFacets().getFacetsList())
  301. .extracting(Common.Facet::getProperty, facet -> facet.getValuesList().stream().collect(toMap(FacetValue::getVal, FacetValue::getCount)))
  302. .containsExactlyInAnyOrder(tuple("directories", of(directory.path(), 1L)));
  303. }
  304. @Test
  305. public void fail_to_display_directory_facet_when_no_organization_or_project_is_set() {
  306. ComponentDto project = db.components().insertPublicProject();
  307. ComponentDto directory = db.components().insertComponent(newDirectory(project, "src"));
  308. ComponentDto file = db.components().insertComponent(newFileDto(project, directory));
  309. RuleDefinitionDto rule = db.rules().insertIssueRule();
  310. db.issues().insertIssue(rule, project, file);
  311. indexPermissions();
  312. indexIssues();
  313. expectedException.expect(IllegalArgumentException.class);
  314. expectedException.expectMessage("Facet(s) 'directories' require to also filter by project or organization");
  315. ws.newRequest()
  316. .setParam(WebService.Param.FACETS, "directories")
  317. .execute();
  318. }
  319. @Test
  320. public void display_fileUuids_facet_with_project() {
  321. OrganizationDto organization = db.organizations().insert();
  322. ComponentDto project = db.components().insertPublicProject(organization);
  323. ComponentDto file1 = db.components().insertComponent(newFileDto(project));
  324. ComponentDto file2 = db.components().insertComponent(newFileDto(project));
  325. ComponentDto file3 = db.components().insertComponent(newFileDto(project));
  326. RuleDefinitionDto rule = db.rules().insertIssueRule();
  327. db.issues().insertIssue(rule, project, file1);
  328. db.issues().insertIssue(rule, project, file2);
  329. indexPermissions();
  330. indexIssues();
  331. SearchWsResponse response = ws.newRequest()
  332. .setParam(PARAM_COMPONENT_KEYS, project.getKey())
  333. .setParam(PARAM_FILE_UUIDS, file1.uuid())
  334. .setParam(WebService.Param.FACETS, "fileUuids")
  335. .executeProtobuf(SearchWsResponse.class);
  336. assertThat(response.getFacets().getFacetsList())
  337. .extracting(Common.Facet::getProperty, facet -> facet.getValuesList().stream().collect(toMap(FacetValue::getVal, FacetValue::getCount)))
  338. .containsExactlyInAnyOrder(tuple("fileUuids", of(file1.uuid(), 1L, file2.uuid(), 1L)));
  339. }
  340. @Test
  341. public void display_fileUuids_facet_with_organization() {
  342. OrganizationDto organization = db.organizations().insert();
  343. ComponentDto project = db.components().insertPublicProject(organization);
  344. ComponentDto file1 = db.components().insertComponent(newFileDto(project));
  345. ComponentDto file2 = db.components().insertComponent(newFileDto(project));
  346. ComponentDto file3 = db.components().insertComponent(newFileDto(project));
  347. RuleDefinitionDto rule = db.rules().insertIssueRule();
  348. db.issues().insertIssue(rule, project, file1);
  349. db.issues().insertIssue(rule, project, file2);
  350. indexPermissions();
  351. indexIssues();
  352. SearchWsResponse response = ws.newRequest()
  353. .setParam(PARAM_ORGANIZATION, organization.getKey())
  354. .setParam(PARAM_FILE_UUIDS, file1.uuid())
  355. .setParam(WebService.Param.FACETS, "fileUuids")
  356. .executeProtobuf(SearchWsResponse.class);
  357. assertThat(response.getFacets().getFacetsList())
  358. .extracting(Common.Facet::getProperty, facet -> facet.getValuesList().stream().collect(toMap(FacetValue::getVal, FacetValue::getCount)))
  359. .containsExactlyInAnyOrder(tuple("fileUuids", of(file1.uuid(), 1L, file2.uuid(), 1L)));
  360. }
  361. @Test
  362. public void fail_to_display_fileUuids_facet_when_no_organization_or_project_is_set() {
  363. ComponentDto project = db.components().insertPublicProject();
  364. ComponentDto file = db.components().insertComponent(newFileDto(project));
  365. RuleDefinitionDto rule = db.rules().insertIssueRule();
  366. db.issues().insertIssue(rule, project, file);
  367. indexPermissions();
  368. indexIssues();
  369. expectedException.expect(IllegalArgumentException.class);
  370. expectedException.expectMessage("Facet(s) 'fileUuids' require to also filter by project or organization");
  371. ws.newRequest()
  372. .setParam(PARAM_FILE_UUIDS, file.uuid())
  373. .setParam(WebService.Param.FACETS, "fileUuids")
  374. .execute();
  375. }
  376. @Test
  377. public void check_facets_max_size_for_issues() {
  378. ComponentDto project = db.components().insertPublicProject();
  379. Random random = new Random();
  380. IntStream.rangeClosed(1, 110)
  381. .forEach(index -> {
  382. UserDto user = db.users().insertUser();
  383. ComponentDto module = db.components().insertComponent(newModuleDto(project));
  384. ComponentDto directory = db.components().insertComponent(newDirectory(module, "dir" + index));
  385. ComponentDto file = db.components().insertComponent(newFileDto(directory));
  386. RuleDefinitionDto rule = db.rules().insertIssueRule();
  387. db.issues().insertIssue(rule, project, file, i -> i.setAssigneeUuid(user.getUuid())
  388. .setStatus(ISSUE_STATUSES[random.nextInt(ISSUE_STATUSES.length)])
  389. .setType(rule.getType()));
  390. });
  391. // insert some hotspots which should be filtered by default
  392. IntStream.rangeClosed(1, 30)
  393. .forEach(index -> {
  394. UserDto user = db.users().insertUser();
  395. ComponentDto module = db.components().insertComponent(newModuleDto(project));
  396. ComponentDto directory = db.components().insertComponent(newDirectory(module, "dir" + index));
  397. ComponentDto file = db.components().insertComponent(newFileDto(directory));
  398. db.issues().insertHotspot(project, file, i -> i.setAssigneeUuid(user.getUuid())
  399. .setStatus(random.nextBoolean() ? Issue.STATUS_TO_REVIEW : Issue.STATUS_REVIEWED));
  400. });
  401. indexPermissions();
  402. indexIssues();
  403. SearchWsResponse response = ws.newRequest()
  404. .setParam(PARAM_COMPONENT_KEYS, project.getKey())
  405. .setParam(FACETS, "fileUuids,directories,moduleUuids,statuses,resolutions,severities,types,rules,languages,assignees")
  406. .executeProtobuf(SearchWsResponse.class);
  407. assertThat(response.getFacets().getFacetsList())
  408. .extracting(Common.Facet::getProperty, Common.Facet::getValuesCount)
  409. .containsExactlyInAnyOrder(
  410. tuple("fileUuids", 100),
  411. tuple("directories", 100),
  412. tuple("moduleUuids", 100),
  413. tuple("rules", 100),
  414. tuple("languages", 100),
  415. // Assignees contains one additional element : it's the empty string that will return number of unassigned issues
  416. tuple("assignees", 101),
  417. // Following facets returned fixed number of elements
  418. tuple("statuses", 5),
  419. tuple("resolutions", 5),
  420. tuple("severities", 5),
  421. tuple("types", 3));
  422. }
  423. @Test
  424. public void check_projects_facet_max_size() {
  425. RuleDefinitionDto rule = db.rules().insertIssueRule();
  426. IntStream.rangeClosed(1, 110)
  427. .forEach(i -> {
  428. ComponentDto project = db.components().insertPublicProject();
  429. db.issues().insertIssue(rule, project, project);
  430. });
  431. indexPermissions();
  432. indexIssues();
  433. SearchWsResponse response = ws.newRequest()
  434. .setParam(FACETS, "projects")
  435. .executeProtobuf(SearchWsResponse.class);
  436. assertThat(response.getPaging().getTotal()).isEqualTo(110);
  437. assertThat(response.getFacets().getFacets(0).getValuesCount()).isEqualTo(100);
  438. }
  439. @Test
  440. public void display_zero_valued_facets_for_selected_items_having_no_issue() {
  441. ComponentDto project1 = db.components().insertPublicProject();
  442. ComponentDto module1 = db.components().insertComponent(newModuleDto(project1));
  443. ComponentDto module2 = db.components().insertComponent(newModuleDto(project1));
  444. ComponentDto project2 = db.components().insertPublicProject();
  445. ComponentDto file1 = db.components().insertComponent(newFileDto(module1));
  446. ComponentDto file2 = db.components().insertComponent(newFileDto(module1));
  447. RuleDefinitionDto rule1 = db.rules().insertIssueRule();
  448. RuleDefinitionDto rule2 = db.rules().insertIssueRule();
  449. UserDto user1 = db.users().insertUser();
  450. UserDto user2 = db.users().insertUser();
  451. db.issues().insertIssue(rule1, project1, file1, i -> i
  452. .setSeverity("MAJOR")
  453. .setStatus("OPEN")
  454. .setResolution(null)
  455. .setType(RuleType.CODE_SMELL)
  456. .setEffort(10L)
  457. .setAssigneeUuid(user1.getUuid()));
  458. indexPermissions();
  459. indexIssues();
  460. SearchWsResponse response = ws.newRequest()
  461. .setParam(PARAM_PROJECTS, project1.getKey() + "," + project2.getKey())
  462. .setParam(PARAM_MODULE_UUIDS, module1.uuid() + "," + module2.uuid())
  463. .setParam(PARAM_FILE_UUIDS, file1.uuid() + "," + file2.uuid())
  464. .setParam("rules", rule1.getKey().toString() + "," + rule2.getKey().toString())
  465. .setParam("severities", "MAJOR,MINOR")
  466. .setParam("languages", rule1.getLanguage() + "," + rule2.getLanguage())
  467. .setParam("assignees", user1.getLogin() + "," + user2.getLogin())
  468. .setParam(FACETS, "severities,statuses,resolutions,rules,types,languages,projects,moduleUuids,fileUuids,assignees")
  469. .executeProtobuf(SearchWsResponse.class);
  470. Map<String, Number> expectedStatuses = ImmutableMap.<String, Number>builder().put("OPEN", 1L).put("CONFIRMED", 0L)
  471. .put("REOPENED", 0L).put("RESOLVED", 0L).put("CLOSED", 0L).build();
  472. assertThat(response.getFacets().getFacetsList())
  473. .extracting(Common.Facet::getProperty, facet -> facet.getValuesList().stream().collect(toMap(FacetValue::getVal, FacetValue::getCount)))
  474. .containsExactlyInAnyOrder(
  475. tuple("severities", of("INFO", 0L, "MINOR", 0L, "MAJOR", 1L, "CRITICAL", 0L, "BLOCKER", 0L)),
  476. tuple("statuses", expectedStatuses),
  477. tuple("resolutions", of("", 1L, "FALSE-POSITIVE", 0L, "FIXED", 0L, "REMOVED", 0L, "WONTFIX", 0L)),
  478. tuple("rules", of(rule1.getKey().toString(), 1L, rule2.getKey().toString(), 0L)),
  479. tuple("types", of("CODE_SMELL", 1L, "BUG", 0L, "VULNERABILITY", 0L)),
  480. tuple("languages", of(rule1.getLanguage(), 1L, rule2.getLanguage(), 0L)),
  481. tuple("projects", of(project1.getKey(), 1L, project2.getKey(), 0L)),
  482. tuple("moduleUuids", of(module1.uuid(), 1L, module2.uuid(), 0L)),
  483. tuple("fileUuids", of(file1.uuid(), 1L, file2.uuid(), 0L)),
  484. tuple("assignees", of("", 0L, user1.getLogin(), 1L, user2.getLogin(), 0L)));
  485. }
  486. @Test
  487. public void assignedToMe_facet_must_escape_login_of_authenticated_user() {
  488. // login looks like an invalid regexp
  489. UserDto user = db.users().insertUser(u -> u.setLogin("foo["));
  490. userSession.logIn(user);
  491. // should not fail
  492. SearchWsResponse response = ws.newRequest()
  493. .setParam(FACETS, "assigned_to_me")
  494. .executeProtobuf(SearchWsResponse.class);
  495. assertThat(response.getFacets().getFacetsList())
  496. .extracting(Common.Facet::getProperty, facet -> facet.getValuesList().stream().collect(toMap(FacetValue::getVal, FacetValue::getCount)))
  497. .containsExactlyInAnyOrder(
  498. tuple("assigned_to_me", of("foo[", 0L)));
  499. }
  500. @Test
  501. public void assigned_to_me_facet_is_sticky_relative_to_assignees() {
  502. ComponentDto project = db.components().insertPublicProject();
  503. indexPermissions();
  504. ComponentDto file = db.components().insertComponent(newFileDto(project));
  505. RuleDefinitionDto rule = db.rules().insertIssueRule();
  506. UserDto john = db.users().insertUser();
  507. UserDto alice = db.users().insertUser();
  508. db.issues().insertIssue(rule, project, file, i -> i.setAssigneeUuid(john.getUuid()));
  509. db.issues().insertIssue(rule, project, file, i -> i.setAssigneeUuid(alice.getUuid()));
  510. db.issues().insertIssue(rule, project, file, i -> i.setAssigneeUuid(null));
  511. indexIssues();
  512. userSession.logIn(john);
  513. SearchWsResponse response = ws.newRequest()
  514. .setParam("resolved", "false")
  515. .setParam("assignees", alice.getLogin())
  516. .setParam(FACETS, "assignees,assigned_to_me")
  517. .executeProtobuf(SearchWsResponse.class);
  518. assertThat(response.getFacets().getFacetsList())
  519. .extracting(Common.Facet::getProperty, facet -> facet.getValuesList().stream().collect(toMap(FacetValue::getVal, FacetValue::getCount)))
  520. .containsExactlyInAnyOrder(
  521. tuple("assignees", of(john.getLogin(), 1L, alice.getLogin(), 1L, "", 1L)),
  522. tuple("assigned_to_me", of(john.getLogin(), 1L)));
  523. }
  524. private void indexPermissions() {
  525. permissionIndexer.indexOnStartup(permissionIndexer.getIndexTypes());
  526. }
  527. private void indexIssues() {
  528. issueIndexer.indexOnStartup(issueIndexer.getIndexTypes());
  529. }
  530. }