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 27KB

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