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.

SearchActionTest.java 64KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481
  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.hotspot.ws;
  21. import com.google.common.collect.ImmutableSet;
  22. import com.google.common.collect.Ordering;
  23. import com.google.common.collect.Sets;
  24. import com.tngtech.java.junit.dataprovider.DataProvider;
  25. import com.tngtech.java.junit.dataprovider.DataProviderRunner;
  26. import com.tngtech.java.junit.dataprovider.UseDataProvider;
  27. import java.util.Arrays;
  28. import java.util.Collection;
  29. import java.util.Collections;
  30. import java.util.Comparator;
  31. import java.util.List;
  32. import java.util.Map;
  33. import java.util.Random;
  34. import java.util.Set;
  35. import java.util.function.Consumer;
  36. import java.util.stream.IntStream;
  37. import java.util.stream.Stream;
  38. import javax.annotation.Nullable;
  39. import org.junit.Rule;
  40. import org.junit.Test;
  41. import org.junit.runner.RunWith;
  42. import org.sonar.api.issue.Issue;
  43. import org.sonar.api.rules.RuleType;
  44. import org.sonar.api.utils.System2;
  45. import org.sonar.api.web.UserRole;
  46. import org.sonar.db.DbClient;
  47. import org.sonar.db.DbTester;
  48. import org.sonar.db.component.BranchType;
  49. import org.sonar.db.component.ComponentDto;
  50. import org.sonar.db.component.ComponentTesting;
  51. import org.sonar.db.issue.IssueDto;
  52. import org.sonar.db.organization.OrganizationDto;
  53. import org.sonar.db.rule.RuleDefinitionDto;
  54. import org.sonar.db.rule.RuleTesting;
  55. import org.sonar.server.es.EsTester;
  56. import org.sonar.server.exceptions.ForbiddenException;
  57. import org.sonar.server.exceptions.NotFoundException;
  58. import org.sonar.server.issue.index.IssueIndex;
  59. import org.sonar.server.issue.index.IssueIndexer;
  60. import org.sonar.server.issue.index.IssueIteratorFactory;
  61. import org.sonar.server.organization.TestDefaultOrganizationProvider;
  62. import org.sonar.server.permission.index.PermissionIndexer;
  63. import org.sonar.server.permission.index.WebAuthorizationTypeSupport;
  64. import org.sonar.server.security.SecurityStandards;
  65. import org.sonar.server.security.SecurityStandards.SQCategory;
  66. import org.sonar.server.tester.UserSessionRule;
  67. import org.sonar.server.view.index.ViewIndexer;
  68. import org.sonar.server.ws.TestRequest;
  69. import org.sonar.server.ws.WsActionTester;
  70. import org.sonarqube.ws.Hotspots;
  71. import org.sonarqube.ws.Hotspots.Component;
  72. import org.sonarqube.ws.Hotspots.SearchWsResponse;
  73. import static java.util.Collections.singleton;
  74. import static java.util.stream.Collectors.joining;
  75. import static java.util.stream.Collectors.toList;
  76. import static java.util.stream.Collectors.toSet;
  77. import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
  78. import static org.assertj.core.api.Assertions.assertThat;
  79. import static org.assertj.core.api.Assertions.assertThatThrownBy;
  80. import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
  81. import static org.sonar.api.issue.Issue.RESOLUTION_SAFE;
  82. import static org.sonar.api.issue.Issue.STATUSES;
  83. import static org.sonar.api.issue.Issue.STATUS_CLOSED;
  84. import static org.sonar.api.issue.Issue.STATUS_REVIEWED;
  85. import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
  86. import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
  87. import static org.sonar.api.utils.DateUtils.formatDateTime;
  88. import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
  89. import static org.sonar.db.component.ComponentTesting.newDirectory;
  90. import static org.sonar.db.component.ComponentTesting.newFileDto;
  91. import static org.sonar.db.issue.IssueTesting.newIssue;
  92. @RunWith(DataProviderRunner.class)
  93. public class SearchActionTest {
  94. private static final Random RANDOM = new Random();
  95. private static final int ONE_MINUTE = 60_000;
  96. @Rule
  97. public DbTester dbTester = DbTester.create(System2.INSTANCE);
  98. @Rule
  99. public EsTester es = EsTester.create();
  100. @Rule
  101. public UserSessionRule userSessionRule = UserSessionRule.standalone();
  102. private DbClient dbClient = dbTester.getDbClient();
  103. private TestDefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(dbTester);
  104. private IssueIndex issueIndex = new IssueIndex(es.client(), System2.INSTANCE, userSessionRule, new WebAuthorizationTypeSupport(userSessionRule));
  105. private IssueIndexer issueIndexer = new IssueIndexer(es.client(), dbClient, new IssueIteratorFactory(dbClient));
  106. private ViewIndexer viewIndexer = new ViewIndexer(dbClient, es.client());
  107. private PermissionIndexer permissionIndexer = new PermissionIndexer(dbClient, es.client(), issueIndexer);
  108. private HotspotWsResponseFormatter responseFormatter = new HotspotWsResponseFormatter(defaultOrganizationProvider);
  109. private SearchAction underTest = new SearchAction(dbClient, userSessionRule, issueIndex, responseFormatter);
  110. private WsActionTester actionTester = new WsActionTester(underTest);
  111. @Test
  112. public void verify_ws_def() {
  113. assertThat(actionTester.getDef().isInternal()).isTrue();
  114. assertThat(actionTester.getDef().param("onlyMine").isRequired()).isFalse();
  115. assertThat(actionTester.getDef().param("onlyMine").possibleValues())
  116. .containsExactlyInAnyOrder("yes", "no", "true", "false");
  117. }
  118. @Test
  119. public void fails_with_IAE_if_parameters_projectKey_and_hotspots_are_missing() {
  120. TestRequest request = actionTester.newRequest();
  121. assertThatThrownBy(request::execute)
  122. .isInstanceOf(IllegalArgumentException.class)
  123. .hasMessage("A value must be provided for either parameter 'projectKey' or parameter 'hotspots'");
  124. }
  125. @Test
  126. public void fail_with_IAE_if_parameter_branch_is_used_without_parameter_projectKey() {
  127. TestRequest request = actionTester.newRequest()
  128. .setParam("hotspots", randomAlphabetic(2))
  129. .setParam("branch", randomAlphabetic(1));
  130. assertThatThrownBy(request::execute)
  131. .isInstanceOf(IllegalArgumentException.class)
  132. .hasMessage("Parameter 'branch' must be used with parameter 'projectKey'");
  133. }
  134. @Test
  135. public void fail_with_IAE_if_parameter_pullRequest_is_used_without_parameter_projectKey() {
  136. TestRequest request = actionTester.newRequest()
  137. .setParam("hotspots", randomAlphabetic(2))
  138. .setParam("pullRequest", randomAlphabetic(1));
  139. assertThatThrownBy(request::execute)
  140. .isInstanceOf(IllegalArgumentException.class)
  141. .hasMessage("Parameter 'pullRequest' must be used with parameter 'projectKey'");
  142. }
  143. @Test
  144. public void fail_with_IAE_if_both_parameters_pullRequest_and_branch_are_provided() {
  145. TestRequest request = actionTester.newRequest()
  146. .setParam("projectKey", randomAlphabetic(2))
  147. .setParam("branch", randomAlphabetic(1))
  148. .setParam("pullRequest", randomAlphabetic(1));
  149. assertThatThrownBy(request::execute)
  150. .isInstanceOf(IllegalArgumentException.class)
  151. .hasMessage("Only one of parameters 'branch' and 'pullRequest' can be provided");
  152. }
  153. @Test
  154. @UseDataProvider("badStatuses")
  155. public void fails_with_IAE_if_status_parameter_is_neither_TO_REVIEW_or_REVIEWED(String badStatus) {
  156. TestRequest request = actionTester.newRequest()
  157. .setParam("projectKey", randomAlphabetic(13))
  158. .setParam("status", badStatus);
  159. assertThatThrownBy(request::execute)
  160. .isInstanceOf(IllegalArgumentException.class)
  161. .hasMessage("Value of parameter 'status' (" + badStatus + ") must be one of: [TO_REVIEW, REVIEWED]");
  162. }
  163. @DataProvider
  164. public static Object[][] badStatuses() {
  165. return Stream.concat(
  166. Issue.STATUSES.stream(),
  167. Stream.of(randomAlphabetic(3)))
  168. .filter(t -> !STATUS_REVIEWED.equals(t))
  169. .filter(t -> !STATUS_TO_REVIEW.equals(t))
  170. .map(t -> new Object[] {t})
  171. .toArray(Object[][]::new);
  172. }
  173. @Test
  174. @UseDataProvider("validStatusesAndResolutions")
  175. public void fail_with_IAE_if_parameter_status_is_specified_with_hotspots_parameter(String status, @Nullable String notUsed) {
  176. TestRequest request = actionTester.newRequest()
  177. .setParam("hotspots", randomAlphabetic(12))
  178. .setParam("status", status);
  179. assertThatThrownBy(request::execute)
  180. .isInstanceOf(IllegalArgumentException.class)
  181. .hasMessage("Parameter 'status' can't be used with parameter 'hotspots'");
  182. }
  183. @Test
  184. @UseDataProvider("badResolutions")
  185. public void fails_with_IAE_if_resolution_parameter_is_neither_FIXED_nor_SAFE(String badResolution) {
  186. TestRequest request = actionTester.newRequest()
  187. .setParam("projectKey", randomAlphabetic(13))
  188. .setParam("status", STATUS_TO_REVIEW)
  189. .setParam("resolution", badResolution);
  190. assertThatThrownBy(request::execute)
  191. .isInstanceOf(IllegalArgumentException.class)
  192. .hasMessage("Value of parameter 'resolution' (" + badResolution + ") must be one of: [FIXED, SAFE]");
  193. }
  194. @DataProvider
  195. public static Object[][] badResolutions() {
  196. return Stream.of(
  197. Issue.RESOLUTIONS.stream(),
  198. Issue.SECURITY_HOTSPOT_RESOLUTIONS.stream(),
  199. Stream.of(randomAlphabetic(4)))
  200. .flatMap(t -> t)
  201. .filter(t -> !RESOLUTION_FIXED.equals(t))
  202. .filter(t -> !RESOLUTION_SAFE.equals(t))
  203. .map(t -> new Object[] {t})
  204. .toArray(Object[][]::new);
  205. }
  206. @Test
  207. @UseDataProvider("fixedOrSafeResolution")
  208. public void fails_with_IAE_if_resolution_is_provided_with_status_TO_REVIEW(String resolution) {
  209. TestRequest request = actionTester.newRequest()
  210. .setParam("projectKey", randomAlphabetic(13))
  211. .setParam("status", STATUS_TO_REVIEW)
  212. .setParam("resolution", resolution);
  213. assertThatThrownBy(request::execute)
  214. .isInstanceOf(IllegalArgumentException.class)
  215. .hasMessage("Value '" + resolution + "' of parameter 'resolution' can only be provided if value of parameter 'status' is 'REVIEWED'");
  216. }
  217. @Test
  218. @UseDataProvider("fixedOrSafeResolution")
  219. public void fails_with_IAE_if_resolution_is_provided_with_hotspots_parameter(String resolution) {
  220. TestRequest request = actionTester.newRequest()
  221. .setParam("hotspots", randomAlphabetic(13))
  222. .setParam("resolution", resolution);
  223. assertThatThrownBy(request::execute)
  224. .isInstanceOf(IllegalArgumentException.class)
  225. .hasMessage("Parameter 'resolution' can't be used with parameter 'hotspots'");
  226. }
  227. @DataProvider
  228. public static Object[][] fixedOrSafeResolution() {
  229. return new Object[][] {
  230. {RESOLUTION_SAFE},
  231. {RESOLUTION_FIXED}
  232. };
  233. }
  234. @Test
  235. public void fails_with_NotFoundException_if_project_does_not_exist() {
  236. String key = randomAlphabetic(12);
  237. TestRequest request = actionTester.newRequest()
  238. .setParam("projectKey", key);
  239. assertThatThrownBy(request::execute)
  240. .isInstanceOf(NotFoundException.class)
  241. .hasMessage("Project '%s' not found", key);
  242. }
  243. @Test
  244. public void fails_with_NotFoundException_if_project_is_neither_a_project_nor_an_application() {
  245. ComponentDto project = dbTester.components().insertPrivateProject();
  246. ComponentDto directory = dbTester.components().insertComponent(ComponentTesting.newDirectory(project, "foo"));
  247. ComponentDto file = dbTester.components().insertComponent(ComponentTesting.newFileDto(project));
  248. ComponentDto portfolio = dbTester.components().insertPrivatePortfolio();
  249. TestRequest request = actionTester.newRequest();
  250. for (ComponentDto component : Arrays.asList(directory, file, portfolio)) {
  251. request.setParam("projectKey", component.getKey());
  252. assertThatThrownBy(request::execute)
  253. .isInstanceOf(NotFoundException.class)
  254. .hasMessage("Project '%s' not found", component.getKey());
  255. }
  256. }
  257. @Test
  258. public void fails_with_ForbiddenException_if_project_is_private_and_not_allowed() {
  259. ComponentDto project = dbTester.components().insertPrivateProject();
  260. userSessionRule.registerComponents(project);
  261. TestRequest request = newRequest(project);
  262. assertThatThrownBy(request::execute)
  263. .isInstanceOf(ForbiddenException.class)
  264. .hasMessage("Insufficient privileges");
  265. }
  266. @Test
  267. public void fails_with_ForbiddenException_if_application_is_private_and_not_allowed() {
  268. ComponentDto application = dbTester.components().insertPrivateApplication();
  269. userSessionRule.registerComponents(application);
  270. TestRequest request = newRequest(application);
  271. assertThatThrownBy(request::execute)
  272. .isInstanceOf(ForbiddenException.class)
  273. .hasMessage("Insufficient privileges");
  274. }
  275. @Test
  276. public void succeeds_on_public_project() {
  277. ComponentDto project = dbTester.components().insertPublicProject();
  278. userSessionRule.registerComponents(project);
  279. SearchWsResponse response = newRequest(project)
  280. .executeProtobuf(SearchWsResponse.class);
  281. assertThat(response.getHotspotsList()).isEmpty();
  282. assertThat(response.getComponentsList()).isEmpty();
  283. }
  284. @Test
  285. public void succeeds_on_public_application() {
  286. ComponentDto application = dbTester.components().insertPublicApplication();
  287. userSessionRule.registerComponents(application);
  288. SearchWsResponse response = newRequest(application)
  289. .executeProtobuf(SearchWsResponse.class);
  290. assertThat(response.getHotspotsList()).isEmpty();
  291. assertThat(response.getComponentsList()).isEmpty();
  292. }
  293. @Test
  294. public void succeeds_on_private_project_with_permission() {
  295. ComponentDto project = dbTester.components().insertPrivateProject();
  296. userSessionRule.registerComponents(project);
  297. userSessionRule.logIn().addProjectPermission(UserRole.USER, project);
  298. SearchWsResponse response = newRequest(project)
  299. .executeProtobuf(SearchWsResponse.class);
  300. assertThat(response.getHotspotsList()).isEmpty();
  301. assertThat(response.getComponentsList()).isEmpty();
  302. }
  303. @Test
  304. public void succeeds_on_private_application_with_permission() {
  305. ComponentDto application = dbTester.components().insertPrivateApplication();
  306. userSessionRule.registerComponents(application);
  307. userSessionRule.logIn().addProjectPermission(UserRole.USER, application);
  308. SearchWsResponse response = newRequest(application)
  309. .executeProtobuf(SearchWsResponse.class);
  310. assertThat(response.getHotspotsList()).isEmpty();
  311. assertThat(response.getComponentsList()).isEmpty();
  312. }
  313. @Test
  314. public void does_not_fail_if_rule_of_hotspot_does_not_exist_in_DB() {
  315. ComponentDto project = dbTester.components().insertPublicProject();
  316. userSessionRule.registerComponents(project);
  317. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  318. indexPermissions();
  319. IssueDto[] hotspots = IntStream.range(0, 1 + RANDOM.nextInt(10))
  320. .mapToObj(i -> {
  321. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  322. return insertHotspot(project, file, rule);
  323. })
  324. .toArray(IssueDto[]::new);
  325. indexIssues();
  326. IssueDto hotspotWithoutRule = hotspots[RANDOM.nextInt(hotspots.length)];
  327. dbTester.executeUpdateSql("delete from rules where id=" + hotspotWithoutRule.getRuleId());
  328. SearchWsResponse response = newRequest(project)
  329. .executeProtobuf(SearchWsResponse.class);
  330. assertThat(response.getHotspotsList())
  331. .extracting(Hotspots.SearchWsResponse.Hotspot::getKey)
  332. .containsOnly(Arrays.stream(hotspots)
  333. .filter(t -> !t.getKey().equals(hotspotWithoutRule.getKey()))
  334. .map(IssueDto::getKey)
  335. .toArray(String[]::new));
  336. }
  337. @Test
  338. public void returns_no_hotspot_component_nor_rule_when_project_has_no_hotspot() {
  339. ComponentDto project = dbTester.components().insertPublicProject();
  340. userSessionRule.registerComponents(project);
  341. indexPermissions();
  342. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  343. Arrays.stream(RuleType.values())
  344. .filter(t -> t != SECURITY_HOTSPOT)
  345. .forEach(ruleType -> {
  346. RuleDefinitionDto rule = newRule(ruleType);
  347. dbTester.issues().insert(rule, project, file, t -> t.setType(ruleType));
  348. });
  349. indexIssues();
  350. SearchWsResponse response = newRequest(project)
  351. .executeProtobuf(SearchWsResponse.class);
  352. assertThat(response.getHotspotsList()).isEmpty();
  353. }
  354. @Test
  355. public void returns_hotspot_components_when_project_has_hotspots() {
  356. ComponentDto project = dbTester.components().insertPublicProject();
  357. userSessionRule.registerComponents(project);
  358. indexPermissions();
  359. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  360. ComponentDto fileWithHotspot = dbTester.components().insertComponent(newFileDto(project));
  361. Arrays.stream(RuleType.values())
  362. .filter(t -> t != SECURITY_HOTSPOT)
  363. .forEach(ruleType -> {
  364. RuleDefinitionDto rule = newRule(ruleType);
  365. dbTester.issues().insert(rule, project, file, t -> t.setType(ruleType));
  366. });
  367. IssueDto[] hotspots = IntStream.range(0, 1 + RANDOM.nextInt(10))
  368. .mapToObj(i -> {
  369. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  370. return insertHotspot(project, fileWithHotspot, rule);
  371. })
  372. .toArray(IssueDto[]::new);
  373. indexIssues();
  374. SearchWsResponse response = newRequest(project)
  375. .executeProtobuf(SearchWsResponse.class);
  376. assertThat(response.getHotspotsList())
  377. .extracting(SearchWsResponse.Hotspot::getKey)
  378. .containsOnly(Arrays.stream(hotspots).map(IssueDto::getKey).toArray(String[]::new));
  379. assertThat(response.getComponentsList())
  380. .extracting(Component::getKey)
  381. .containsOnly(project.getKey(), fileWithHotspot.getKey());
  382. }
  383. @Test
  384. public void returns_single_component_when_all_hotspots_are_on_project() {
  385. ComponentDto project = dbTester.components().insertPublicProject();
  386. userSessionRule.registerComponents(project);
  387. indexPermissions();
  388. IssueDto[] hotspots = IntStream.range(0, 1 + RANDOM.nextInt(10))
  389. .mapToObj(i -> {
  390. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  391. return insertHotspot(project, project, rule);
  392. })
  393. .toArray(IssueDto[]::new);
  394. indexIssues();
  395. SearchWsResponse response = newRequest(project)
  396. .executeProtobuf(SearchWsResponse.class);
  397. assertThat(response.getHotspotsList())
  398. .extracting(Hotspots.SearchWsResponse.Hotspot::getKey)
  399. .containsOnly(Arrays.stream(hotspots).map(IssueDto::getKey).toArray(String[]::new));
  400. assertThat(response.getComponentsList())
  401. .extracting(Component::getKey)
  402. .containsOnly(project.getKey());
  403. }
  404. @Test
  405. public void returns_hotspots_of_specified_project() {
  406. ComponentDto project1 = dbTester.components().insertPublicProject();
  407. ComponentDto project2 = dbTester.components().insertPublicProject();
  408. userSessionRule.registerComponents(project1, project2);
  409. indexPermissions();
  410. ComponentDto file1 = dbTester.components().insertComponent(newFileDto(project1));
  411. ComponentDto file2 = dbTester.components().insertComponent(newFileDto(project2));
  412. IssueDto[] hotspots2 = IntStream.range(0, 1 + RANDOM.nextInt(10))
  413. .mapToObj(i -> {
  414. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  415. insertHotspot(project1, file1, rule);
  416. return insertHotspot(project2, file2, rule);
  417. })
  418. .toArray(IssueDto[]::new);
  419. indexIssues();
  420. SearchWsResponse responseProject1 = newRequest(project1)
  421. .executeProtobuf(SearchWsResponse.class);
  422. assertThat(responseProject1.getHotspotsList())
  423. .extracting(SearchWsResponse.Hotspot::getKey)
  424. .doesNotContainAnyElementsOf(Arrays.stream(hotspots2).map(IssueDto::getKey).collect(toList()));
  425. assertThat(responseProject1.getComponentsList())
  426. .extracting(Component::getKey)
  427. .containsOnly(project1.getKey(), file1.getKey());
  428. SearchWsResponse responseProject2 = newRequest(project2)
  429. .executeProtobuf(SearchWsResponse.class);
  430. assertThat(responseProject2.getHotspotsList())
  431. .extracting(SearchWsResponse.Hotspot::getKey)
  432. .containsOnly(Arrays.stream(hotspots2).map(IssueDto::getKey).toArray(String[]::new));
  433. assertThat(responseProject2.getComponentsList())
  434. .extracting(Component::getKey)
  435. .containsOnly(project2.getKey(), file2.getKey());
  436. }
  437. @Test
  438. public void returns_only_hotspots_to_review_or_reviewed_of_project() {
  439. ComponentDto project = dbTester.components().insertPublicProject();
  440. userSessionRule.registerComponents(project);
  441. indexPermissions();
  442. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  443. IssueDto[] hotspots = STATUSES.stream()
  444. .map(status -> {
  445. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  446. return insertHotspot(rule, project, file, t -> t.setStatus(status));
  447. })
  448. .toArray(IssueDto[]::new);
  449. indexIssues();
  450. String[] expectedKeys = Arrays.stream(hotspots)
  451. .filter(t -> STATUS_REVIEWED.equals(t.getStatus()) || STATUS_TO_REVIEW.equals(t.getStatus()))
  452. .map(IssueDto::getKey)
  453. .toArray(String[]::new);
  454. SearchWsResponse response = newRequest(project)
  455. .executeProtobuf(SearchWsResponse.class);
  456. assertThat(response.getHotspotsList())
  457. .extracting(SearchWsResponse.Hotspot::getKey)
  458. .containsOnly(expectedKeys);
  459. }
  460. @Test
  461. public void returns_hotspots_of_specified_application() {
  462. ComponentDto application1 = dbTester.components().insertPublicApplication();
  463. ComponentDto application2 = dbTester.components().insertPublicApplication();
  464. ComponentDto project1 = dbTester.components().insertPublicProject();
  465. ComponentDto project2 = dbTester.components().insertPublicProject();
  466. dbTester.components().insertComponent(ComponentTesting.newProjectCopy(project1, application1));
  467. dbTester.components().insertComponent(ComponentTesting.newProjectCopy(project2, application2));
  468. indexViews();
  469. userSessionRule.registerComponents(application1, application2, project1, project2);
  470. indexPermissions();
  471. ComponentDto file1 = dbTester.components().insertComponent(newFileDto(project1));
  472. ComponentDto file2 = dbTester.components().insertComponent(newFileDto(project2));
  473. IssueDto[] hotspots2 = IntStream.range(0, 1 + RANDOM.nextInt(10))
  474. .mapToObj(i -> {
  475. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  476. insertHotspot(project1, file1, rule);
  477. return insertHotspot(project2, file2, rule);
  478. })
  479. .toArray(IssueDto[]::new);
  480. indexIssues();
  481. SearchWsResponse responseApplication1 = newRequest(application1)
  482. .executeProtobuf(SearchWsResponse.class);
  483. assertThat(responseApplication1.getHotspotsList())
  484. .extracting(SearchWsResponse.Hotspot::getKey)
  485. .doesNotContainAnyElementsOf(Arrays.stream(hotspots2).map(IssueDto::getKey).collect(toList()));
  486. assertThat(responseApplication1.getComponentsList())
  487. .extracting(Component::getKey)
  488. .containsOnly(project1.getKey(), file1.getKey());
  489. SearchWsResponse responseApplication2 = newRequest(application2)
  490. .executeProtobuf(SearchWsResponse.class);
  491. assertThat(responseApplication2.getHotspotsList())
  492. .extracting(SearchWsResponse.Hotspot::getKey)
  493. .containsOnly(Arrays.stream(hotspots2).map(IssueDto::getKey).toArray(String[]::new));
  494. assertThat(responseApplication2.getComponentsList())
  495. .extracting(Component::getKey)
  496. .containsOnly(project2.getKey(), file2.getKey());
  497. }
  498. @Test
  499. public void returns_hotspots_of_specified_application_branch() {
  500. ComponentDto application = dbTester.components().insertPublicApplication();
  501. ComponentDto applicationBranch = dbTester.components().insertProjectBranch(application);
  502. ComponentDto project1 = dbTester.components().insertPublicProject();
  503. ComponentDto project2 = dbTester.components().insertPublicProject();
  504. dbTester.components().insertComponent(ComponentTesting.newProjectCopy(project1, application));
  505. dbTester.components().insertComponent(ComponentTesting.newProjectCopy(project2, applicationBranch));
  506. indexViews();
  507. userSessionRule.registerComponents(application, applicationBranch, project1, project2);
  508. indexPermissions();
  509. ComponentDto file1 = dbTester.components().insertComponent(newFileDto(project1));
  510. ComponentDto file2 = dbTester.components().insertComponent(newFileDto(project2));
  511. IssueDto[] hotspots2 = IntStream.range(0, 1 + RANDOM.nextInt(10))
  512. .mapToObj(i -> {
  513. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  514. insertHotspot(project1, file1, rule);
  515. return insertHotspot(project2, file2, rule);
  516. })
  517. .toArray(IssueDto[]::new);
  518. indexIssues();
  519. SearchWsResponse responseApplication = newRequest(application)
  520. .executeProtobuf(SearchWsResponse.class);
  521. assertThat(responseApplication.getHotspotsList())
  522. .extracting(SearchWsResponse.Hotspot::getKey)
  523. .doesNotContainAnyElementsOf(Arrays.stream(hotspots2).map(IssueDto::getKey).collect(toList()));
  524. assertThat(responseApplication.getComponentsList())
  525. .extracting(Component::getKey)
  526. .containsOnly(project1.getKey(), file1.getKey());
  527. SearchWsResponse responseApplicationBranch = newRequest(applicationBranch)
  528. .executeProtobuf(SearchWsResponse.class);
  529. assertThat(responseApplicationBranch.getHotspotsList())
  530. .extracting(SearchWsResponse.Hotspot::getKey)
  531. .containsOnly(Arrays.stream(hotspots2).map(IssueDto::getKey).toArray(String[]::new));
  532. assertThat(responseApplicationBranch.getComponentsList())
  533. .extracting(Component::getKey)
  534. .containsOnly(project2.getKey(), file2.getKey());
  535. }
  536. @Test
  537. public void returns_hotspot_of_branch_or_pullRequest() {
  538. ComponentDto project = dbTester.components().insertPublicProject();
  539. userSessionRule.registerComponents(project);
  540. indexPermissions();
  541. ComponentDto branch = dbTester.components().insertProjectBranch(project);
  542. ComponentDto pullRequest = dbTester.components().insertProjectBranch(project, t -> t.setBranchType(BranchType.PULL_REQUEST));
  543. ComponentDto fileProject = dbTester.components().insertComponent(newFileDto(project));
  544. ComponentDto fileBranch = dbTester.components().insertComponent(newFileDto(branch));
  545. ComponentDto filePR = dbTester.components().insertComponent(newFileDto(pullRequest));
  546. IssueDto[] hotspotProject = IntStream.range(0, 1 + RANDOM.nextInt(10))
  547. .mapToObj(i -> {
  548. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  549. return insertHotspot(project, fileProject, rule);
  550. })
  551. .toArray(IssueDto[]::new);
  552. IssueDto[] hotspotBranch = IntStream.range(0, 1 + RANDOM.nextInt(10))
  553. .mapToObj(i -> {
  554. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  555. return insertHotspot(branch, fileBranch, rule);
  556. })
  557. .toArray(IssueDto[]::new);
  558. IssueDto[] hotspotPR = IntStream.range(0, 1 + RANDOM.nextInt(10))
  559. .mapToObj(i -> {
  560. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  561. return insertHotspot(pullRequest, filePR, rule);
  562. })
  563. .toArray(IssueDto[]::new);
  564. indexIssues();
  565. SearchWsResponse responseProject = newRequest(project)
  566. .executeProtobuf(SearchWsResponse.class);
  567. SearchWsResponse responseBranch = newRequest(branch)
  568. .executeProtobuf(SearchWsResponse.class);
  569. SearchWsResponse responsePR = newRequest(pullRequest)
  570. .executeProtobuf(SearchWsResponse.class);
  571. assertThat(responseProject.getHotspotsList())
  572. .extracting(SearchWsResponse.Hotspot::getKey)
  573. .containsExactlyInAnyOrder(Arrays.stream(hotspotProject).map(IssueDto::getKey).toArray(String[]::new));
  574. assertThat(responseBranch.getHotspotsList())
  575. .extracting(SearchWsResponse.Hotspot::getKey)
  576. .containsExactlyInAnyOrder(Arrays.stream(hotspotBranch).map(IssueDto::getKey).toArray(String[]::new));
  577. assertThat(responsePR.getHotspotsList())
  578. .extracting(SearchWsResponse.Hotspot::getKey)
  579. .containsExactlyInAnyOrder(Arrays.stream(hotspotPR).map(IssueDto::getKey).toArray(String[]::new));
  580. }
  581. @Test
  582. @UseDataProvider("onlyMineParamValues")
  583. public void returns_hotspots_of_specified_project_assigned_to_current_user_if_only_mine_is_set(String onlyMineParameter, boolean shouldFilter) {
  584. ComponentDto project1 = dbTester.components().insertPublicProject();
  585. String assigneeUuid = this.userSessionRule.logIn().registerComponents(project1).getUuid();
  586. indexPermissions();
  587. ComponentDto file1 = dbTester.components().insertComponent(newFileDto(project1));
  588. IssueDto[] assigneeHotspots = IntStream.range(0, 1 + RANDOM.nextInt(10))
  589. .mapToObj(i -> {
  590. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  591. insertHotspot(rule, project1, file1, randomAlphabetic(5));
  592. return insertHotspot(rule, project1, file1, assigneeUuid);
  593. })
  594. .toArray(IssueDto[]::new);
  595. indexIssues();
  596. SearchWsResponse allHotspots = newRequest(project1)
  597. .executeProtobuf(SearchWsResponse.class);
  598. SearchWsResponse userHotspots = newRequest(project1, r -> r.setParam("onlyMine", onlyMineParameter))
  599. .executeProtobuf(SearchWsResponse.class);
  600. assertThat(allHotspots.getHotspotsList())
  601. .extracting(SearchWsResponse.Hotspot::getKey)
  602. .contains(Arrays.stream(assigneeHotspots).map(IssueDto::getKey).toArray(String[]::new))
  603. .hasSizeGreaterThan(assigneeHotspots.length);
  604. if (shouldFilter) {
  605. assertThat(userHotspots.getHotspotsList())
  606. .extracting(SearchWsResponse.Hotspot::getKey)
  607. .containsOnly(Arrays.stream(assigneeHotspots).map(IssueDto::getKey).toArray(String[]::new));
  608. } else {
  609. assertThat(userHotspots.getHotspotsList())
  610. .extracting(SearchWsResponse.Hotspot::getKey)
  611. .containsOnly(allHotspots.getHotspotsList().stream().map(SearchWsResponse.Hotspot::getKey).toArray(String[]::new));
  612. }
  613. }
  614. @DataProvider
  615. public static Object[][] onlyMineParamValues() {
  616. return new Object[][] {
  617. {"yes", true},
  618. {"true", true},
  619. {"no", false},
  620. {"false", false}
  621. };
  622. }
  623. @Test
  624. public void fail_if_hotspots_provided_with_onlyMine_param() {
  625. ComponentDto project = dbTester.components().insertPrivateProject();
  626. userSessionRule.registerComponents(project);
  627. userSessionRule.logIn().addProjectPermission(UserRole.USER, project);
  628. assertThatThrownBy(() -> actionTester.newRequest()
  629. .setParam("hotspots", IntStream.range(2, 10).mapToObj(String::valueOf).collect(joining(",")))
  630. .setParam("onlyMine", "true")
  631. .execute())
  632. .isInstanceOf(IllegalArgumentException.class)
  633. .hasMessage("Parameter 'onlyMine' can be used with parameter 'projectKey' only");
  634. }
  635. @Test
  636. public void fail_if_user_not_authenticated_with_onlyMine_param() {
  637. ComponentDto project = dbTester.components().insertPublicProject();
  638. userSessionRule.anonymous();
  639. assertThatThrownBy(() -> actionTester.newRequest()
  640. .setParam("projectKey", project.getKey())
  641. .setParam("onlyMine", "true")
  642. .execute())
  643. .isInstanceOf(IllegalArgumentException.class)
  644. .hasMessage("Parameter 'onlyMine' requires user to be logged in");
  645. }
  646. @Test
  647. public void returns_hotpots_with_any_status_if_no_status_nor_resolution_parameter() {
  648. ComponentDto project = dbTester.components().insertPublicProject();
  649. userSessionRule.registerComponents(project);
  650. indexPermissions();
  651. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  652. List<IssueDto> hotspots = insertRandomNumberOfHotspotsOfAllSupportedStatusesAndResolutions(project, file)
  653. .collect(toList());
  654. indexIssues();
  655. SearchWsResponse response = newRequest(project, null, null)
  656. .executeProtobuf(SearchWsResponse.class);
  657. assertThat(response.getHotspotsList())
  658. .extracting(SearchWsResponse.Hotspot::getKey)
  659. .containsExactlyInAnyOrder(hotspots.stream().map(IssueDto::getKey).toArray(String[]::new));
  660. }
  661. @Test
  662. public void returns_hotpots_reviewed_as_safe_and_fixed_if_status_is_REVIEWED_and_resolution_is_not_set() {
  663. ComponentDto project = dbTester.components().insertPublicProject();
  664. userSessionRule.registerComponents(project);
  665. indexPermissions();
  666. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  667. List<IssueDto> reviewedHotspots = insertRandomNumberOfHotspotsOfAllSupportedStatusesAndResolutions(project, file)
  668. .filter(t -> STATUS_REVIEWED.equals(t.getStatus()))
  669. .collect(toList());
  670. indexIssues();
  671. SearchWsResponse response = newRequest(project, STATUS_REVIEWED, null)
  672. .executeProtobuf(SearchWsResponse.class);
  673. assertThat(response.getHotspotsList())
  674. .extracting(SearchWsResponse.Hotspot::getKey)
  675. .containsExactlyInAnyOrder(reviewedHotspots.stream().map(IssueDto::getKey).toArray(String[]::new));
  676. }
  677. @Test
  678. public void returns_hotpots_reviewed_as_safe_if_status_is_REVIEWED_and_resolution_is_SAFE() {
  679. ComponentDto project = dbTester.components().insertPublicProject();
  680. userSessionRule.registerComponents(project);
  681. indexPermissions();
  682. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  683. List<IssueDto> safeHotspots = insertRandomNumberOfHotspotsOfAllSupportedStatusesAndResolutions(project, file)
  684. .filter(t -> STATUS_REVIEWED.equals(t.getStatus()) && RESOLUTION_SAFE.equals(t.getResolution()))
  685. .collect(toList());
  686. indexIssues();
  687. SearchWsResponse response = newRequest(project, STATUS_REVIEWED, RESOLUTION_SAFE)
  688. .executeProtobuf(SearchWsResponse.class);
  689. assertThat(response.getHotspotsList())
  690. .extracting(SearchWsResponse.Hotspot::getKey)
  691. .containsExactlyInAnyOrder(safeHotspots.stream().map(IssueDto::getKey).toArray(String[]::new));
  692. }
  693. @Test
  694. public void returns_hotpots_reviewed_as_fixed_if_status_is_REVIEWED_and_resolution_is_FIXED() {
  695. ComponentDto project = dbTester.components().insertPublicProject();
  696. userSessionRule.registerComponents(project);
  697. indexPermissions();
  698. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  699. List<IssueDto> fixedHotspots = insertRandomNumberOfHotspotsOfAllSupportedStatusesAndResolutions(project, file)
  700. .filter(t -> STATUS_REVIEWED.equals(t.getStatus()) && RESOLUTION_FIXED.equals(t.getResolution()))
  701. .collect(toList());
  702. indexIssues();
  703. SearchWsResponse response = newRequest(project, STATUS_REVIEWED, RESOLUTION_FIXED)
  704. .executeProtobuf(SearchWsResponse.class);
  705. assertThat(response.getHotspotsList())
  706. .extracting(SearchWsResponse.Hotspot::getKey)
  707. .containsExactlyInAnyOrder(fixedHotspots.stream().map(IssueDto::getKey).toArray(String[]::new));
  708. }
  709. @Test
  710. public void returns_only_unresolved_hotspots_when_status_is_TO_REVIEW() {
  711. ComponentDto project = dbTester.components().insertPublicProject();
  712. userSessionRule.registerComponents(project);
  713. indexPermissions();
  714. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  715. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  716. IssueDto unresolvedHotspot = insertHotspot(rule, project, file, t -> t.setResolution(null));
  717. // unrealistic case since a resolution must be set, but shows a limit of current implementation (resolution is enough)
  718. IssueDto badlyResolved = insertHotspot(rule, project, file, t -> t.setStatus(STATUS_TO_REVIEW).setResolution(randomAlphabetic(5)));
  719. IssueDto badlyReviewed = insertHotspot(rule, project, file, t -> t.setStatus(STATUS_REVIEWED).setResolution(null));
  720. IssueDto badlyClosedHotspot = insertHotspot(rule, project, file, t -> t.setStatus(STATUS_CLOSED).setResolution(null));
  721. indexIssues();
  722. SearchWsResponse response = newRequest(project, STATUS_TO_REVIEW, null)
  723. .executeProtobuf(SearchWsResponse.class);
  724. assertThat(response.getHotspotsList())
  725. .extracting(SearchWsResponse.Hotspot::getKey)
  726. .containsOnly(unresolvedHotspot.getKey());
  727. }
  728. private Stream<IssueDto> insertRandomNumberOfHotspotsOfAllSupportedStatusesAndResolutions(ComponentDto project, ComponentDto file) {
  729. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  730. List<IssueDto> hotspots = Arrays.stream(validStatusesAndResolutions())
  731. .flatMap(objects -> {
  732. String status = (String) objects[0];
  733. String resolution = (String) objects[1];
  734. return IntStream.range(0, 1 + RANDOM.nextInt(15))
  735. .mapToObj(i -> newIssue(rule, project, file)
  736. .setKee("hotspot_" + status + "_" + resolution + "_" + i)
  737. .setType(SECURITY_HOTSPOT)
  738. .setStatus(status)
  739. .setResolution(resolution));
  740. })
  741. .collect(toList());
  742. Collections.shuffle(hotspots);
  743. hotspots.forEach(t -> dbTester.issues().insertHotspot(t));
  744. return hotspots.stream();
  745. }
  746. @Test
  747. @UseDataProvider("validStatusesAndResolutions")
  748. public void returns_fields_of_hotspot(String status, @Nullable String resolution) {
  749. ComponentDto project = dbTester.components().insertPublicProject();
  750. userSessionRule.registerComponents(project);
  751. indexPermissions();
  752. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  753. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  754. IssueDto hotspot = insertHotspot(rule, project, file,
  755. t -> t
  756. .setStatus(randomAlphabetic(11))
  757. .setLine(RANDOM.nextInt(230))
  758. .setMessage(randomAlphabetic(10))
  759. .setAssigneeUuid(randomAlphabetic(9))
  760. .setAuthorLogin(randomAlphabetic(8))
  761. .setStatus(status)
  762. .setResolution(resolution));
  763. indexIssues();
  764. SearchWsResponse response = newRequest(project)
  765. .executeProtobuf(SearchWsResponse.class);
  766. assertThat(response.getHotspotsList()).hasSize(1);
  767. SearchWsResponse.Hotspot actual = response.getHotspots(0);
  768. assertThat(actual.getComponent()).isEqualTo(file.getKey());
  769. assertThat(actual.getProject()).isEqualTo(project.getKey());
  770. assertThat(actual.getStatus()).isEqualTo(status);
  771. if (resolution == null) {
  772. assertThat(actual.hasResolution()).isFalse();
  773. } else {
  774. assertThat(actual.getResolution()).isEqualTo(resolution);
  775. }
  776. assertThat(actual.getLine()).isEqualTo(hotspot.getLine());
  777. assertThat(actual.getMessage()).isEqualTo(hotspot.getMessage());
  778. assertThat(actual.getAssignee()).isEqualTo(hotspot.getAssigneeUuid());
  779. assertThat(actual.getAuthor()).isEqualTo(hotspot.getAuthorLogin());
  780. assertThat(actual.getCreationDate()).isEqualTo(formatDateTime(hotspot.getIssueCreationDate()));
  781. assertThat(actual.getUpdateDate()).isEqualTo(formatDateTime(hotspot.getIssueUpdateDate()));
  782. }
  783. @DataProvider
  784. public static Object[][] validStatusesAndResolutions() {
  785. return new Object[][] {
  786. {STATUS_TO_REVIEW, null},
  787. {STATUS_REVIEWED, RESOLUTION_FIXED},
  788. {STATUS_REVIEWED, RESOLUTION_SAFE},
  789. };
  790. }
  791. @Test
  792. @UseDataProvider("allSQCategories")
  793. public void returns_SQCategory_and_VulnerabilityProbability_of_rule(Set<String> securityStandards, SQCategory expected) {
  794. ComponentDto project = dbTester.components().insertPublicProject();
  795. userSessionRule.registerComponents(project);
  796. indexPermissions();
  797. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  798. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT, t -> t.setSecurityStandards(securityStandards));
  799. IssueDto hotspot = insertHotspot(project, file, rule);
  800. indexIssues();
  801. SearchWsResponse response = newRequest(project)
  802. .executeProtobuf(SearchWsResponse.class);
  803. assertThat(response.getHotspotsList()).hasSize(1);
  804. Hotspots.SearchWsResponse.Hotspot actual = response.getHotspots(0);
  805. assertThat(actual.getSecurityCategory()).isEqualTo(expected.getKey());
  806. assertThat(actual.getVulnerabilityProbability()).isEqualTo(expected.getVulnerability().name());
  807. }
  808. @DataProvider
  809. public static Object[][] allSQCategories() {
  810. Stream<Object[]> allCategoriesButOTHERS = SecurityStandards.CWES_BY_SQ_CATEGORY.entrySet()
  811. .stream()
  812. .map(t -> new Object[] {
  813. t.getValue().stream().map(c -> "cwe:" + c).collect(toSet()),
  814. t.getKey()
  815. });
  816. Stream<Object[]> sqCategoryOTHERS = Stream.of(
  817. new Object[] {Collections.emptySet(), SQCategory.OTHERS},
  818. new Object[] {ImmutableSet.of("foo", "donut", "acme"), SQCategory.OTHERS});
  819. return Stream.concat(allCategoriesButOTHERS, sqCategoryOTHERS).toArray(Object[][]::new);
  820. }
  821. @Test
  822. public void does_not_fail_when_hotspot_has_none_of_the_nullable_fields() {
  823. ComponentDto project = dbTester.components().insertPublicProject();
  824. userSessionRule.registerComponents(project);
  825. indexPermissions();
  826. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  827. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  828. insertHotspot(rule, project, file,
  829. t -> t.setResolution(null)
  830. .setLine(null)
  831. .setMessage(null)
  832. .setAssigneeUuid(null)
  833. .setAuthorLogin(null));
  834. indexIssues();
  835. SearchWsResponse response = newRequest(project)
  836. .executeProtobuf(SearchWsResponse.class);
  837. assertThat(response.getHotspotsList())
  838. .hasSize(1);
  839. SearchWsResponse.Hotspot actual = response.getHotspots(0);
  840. assertThat(actual.hasResolution()).isFalse();
  841. assertThat(actual.hasLine()).isFalse();
  842. assertThat(actual.getMessage()).isEmpty();
  843. assertThat(actual.hasAssignee()).isFalse();
  844. assertThat(actual.getAuthor()).isEmpty();
  845. }
  846. @Test
  847. public void returns_details_of_components() {
  848. ComponentDto project = dbTester.components().insertPublicProject();
  849. userSessionRule.registerComponents(project);
  850. indexPermissions();
  851. ComponentDto directory = dbTester.components().insertComponent(newDirectory(project, "donut/acme"));
  852. ComponentDto directory2 = dbTester.components().insertComponent(newDirectory(project, "foo/bar"));
  853. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  854. ComponentDto file2 = dbTester.components().insertComponent(newFileDto(project));
  855. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  856. IssueDto fileHotspot = insertHotspot(project, file, rule);
  857. IssueDto dirHotspot = insertHotspot(project, directory, rule);
  858. IssueDto projectHotspot = insertHotspot(project, project, rule);
  859. indexIssues();
  860. SearchWsResponse response = newRequest(project)
  861. .executeProtobuf(SearchWsResponse.class);
  862. assertThat(response.getHotspotsList())
  863. .extracting(SearchWsResponse.Hotspot::getKey)
  864. .containsOnly(fileHotspot.getKey(), dirHotspot.getKey(), projectHotspot.getKey());
  865. assertThat(response.getComponentsList()).hasSize(3);
  866. assertThat(response.getComponentsList())
  867. .extracting(Component::getOrganization)
  868. .containsOnly(defaultOrganizationProvider.get().getKey());
  869. assertThat(response.getComponentsList())
  870. .extracting(Component::getKey)
  871. .containsOnly(project.getKey(), directory.getKey(), file.getKey());
  872. Map<String, Component> componentByKey = response.getComponentsList().stream().collect(uniqueIndex(Component::getKey));
  873. Component actualProject = componentByKey.get(project.getKey());
  874. assertThat(actualProject.getQualifier()).isEqualTo(project.qualifier());
  875. assertThat(actualProject.getName()).isEqualTo(project.name());
  876. assertThat(actualProject.getLongName()).isEqualTo(project.longName());
  877. assertThat(actualProject.hasPath()).isFalse();
  878. assertThat(actualProject.hasBranch()).isFalse();
  879. assertThat(actualProject.hasPullRequest()).isFalse();
  880. Component actualDirectory = componentByKey.get(directory.getKey());
  881. assertThat(actualDirectory.getQualifier()).isEqualTo(directory.qualifier());
  882. assertThat(actualDirectory.getName()).isEqualTo(directory.name());
  883. assertThat(actualDirectory.getLongName()).isEqualTo(directory.longName());
  884. assertThat(actualDirectory.getPath()).isEqualTo(directory.path());
  885. assertThat(actualDirectory.hasBranch()).isFalse();
  886. assertThat(actualDirectory.hasPullRequest()).isFalse();
  887. Component actualFile = componentByKey.get(file.getKey());
  888. assertThat(actualFile.getQualifier()).isEqualTo(file.qualifier());
  889. assertThat(actualFile.getName()).isEqualTo(file.name());
  890. assertThat(actualFile.getLongName()).isEqualTo(file.longName());
  891. assertThat(actualFile.getPath()).isEqualTo(file.path());
  892. assertThat(actualFile.hasBranch()).isFalse();
  893. assertThat(actualFile.hasPullRequest()).isFalse();
  894. }
  895. @Test
  896. public void returns_branch_field_of_components_of_branch() {
  897. ComponentDto project = dbTester.components().insertPublicProject();
  898. ComponentDto branch = dbTester.components().insertProjectBranch(project);
  899. userSessionRule.registerComponents(project, branch);
  900. indexPermissions();
  901. ComponentDto directory = dbTester.components().insertComponent(newDirectory(branch, "donut/acme"));
  902. ComponentDto file = dbTester.components().insertComponent(newFileDto(branch));
  903. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  904. IssueDto fileHotspot = insertHotspot(branch, file, rule);
  905. IssueDto dirHotspot = insertHotspot(branch, directory, rule);
  906. IssueDto projectHotspot = insertHotspot(branch, branch, rule);
  907. indexIssues();
  908. SearchWsResponse response = newRequest(branch)
  909. .executeProtobuf(SearchWsResponse.class);
  910. assertThat(response.getHotspotsList())
  911. .extracting(SearchWsResponse.Hotspot::getKey)
  912. .containsOnly(fileHotspot.getKey(), dirHotspot.getKey(), projectHotspot.getKey());
  913. assertThat(response.getComponentsList())
  914. .extracting(Component::getKey)
  915. .containsOnly(project.getKey(), directory.getKey(), file.getKey());
  916. Map<String, Component> componentByKey = response.getComponentsList().stream().collect(uniqueIndex(Component::getKey));
  917. Component actualProject = componentByKey.get(project.getKey());
  918. assertThat(actualProject.getBranch()).isEqualTo(branch.getBranch());
  919. assertThat(actualProject.hasPullRequest()).isFalse();
  920. Component actualDirectory = componentByKey.get(directory.getKey());
  921. assertThat(actualDirectory.getBranch()).isEqualTo(branch.getBranch());
  922. assertThat(actualDirectory.hasPullRequest()).isFalse();
  923. Component actualFile = componentByKey.get(file.getKey());
  924. assertThat(actualFile.getBranch()).isEqualTo(branch.getBranch());
  925. assertThat(actualFile.hasPullRequest()).isFalse();
  926. }
  927. @Test
  928. public void returns_pullRequest_field_of_components_of_pullRequest() {
  929. ComponentDto project = dbTester.components().insertPublicProject();
  930. ComponentDto pullRequest = dbTester.components().insertProjectBranch(project, t -> t.setBranchType(BranchType.PULL_REQUEST));
  931. userSessionRule.registerComponents(project, pullRequest);
  932. indexPermissions();
  933. ComponentDto directory = dbTester.components().insertComponent(newDirectory(pullRequest, "donut/acme"));
  934. ComponentDto file = dbTester.components().insertComponent(newFileDto(pullRequest));
  935. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  936. IssueDto fileHotspot = insertHotspot(pullRequest, file, rule);
  937. IssueDto dirHotspot = insertHotspot(pullRequest, directory, rule);
  938. IssueDto projectHotspot = insertHotspot(pullRequest, pullRequest, rule);
  939. indexIssues();
  940. SearchWsResponse response = newRequest(pullRequest)
  941. .executeProtobuf(SearchWsResponse.class);
  942. assertThat(response.getHotspotsList())
  943. .extracting(SearchWsResponse.Hotspot::getKey)
  944. .containsOnly(fileHotspot.getKey(), dirHotspot.getKey(), projectHotspot.getKey());
  945. assertThat(response.getComponentsList())
  946. .extracting(Component::getKey)
  947. .containsOnly(project.getKey(), directory.getKey(), file.getKey());
  948. Map<String, Component> componentByKey = response.getComponentsList().stream().collect(uniqueIndex(Component::getKey));
  949. Component actualProject = componentByKey.get(project.getKey());
  950. assertThat(actualProject.hasBranch()).isFalse();
  951. assertThat(actualProject.getPullRequest()).isEqualTo(pullRequest.getPullRequest());
  952. Component actualDirectory = componentByKey.get(directory.getKey());
  953. assertThat(actualDirectory.hasBranch()).isFalse();
  954. assertThat(actualDirectory.getPullRequest()).isEqualTo(pullRequest.getPullRequest());
  955. Component actualFile = componentByKey.get(file.getKey());
  956. assertThat(actualFile.hasBranch()).isFalse();
  957. assertThat(actualFile.getPullRequest()).isEqualTo(pullRequest.getPullRequest());
  958. }
  959. @Test
  960. public void returns_hotspots_ordered_by_vulnerabilityProbability_score_then_rule_id() {
  961. ComponentDto project = dbTester.components().insertPublicProject();
  962. userSessionRule.registerComponents(project);
  963. indexPermissions();
  964. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  965. List<IssueDto> hotspots = Arrays.stream(SQCategory.values())
  966. .sorted(Ordering.from(Comparator.<SQCategory>comparingInt(t1 -> t1.getVulnerability().getScore()).reversed())
  967. .thenComparing(SQCategory::getKey))
  968. .flatMap(sqCategory -> {
  969. Set<String> cwes = SecurityStandards.CWES_BY_SQ_CATEGORY.get(sqCategory);
  970. Set<String> securityStandards = singleton("cwe:" + (cwes == null ? "unknown" : cwes.iterator().next()));
  971. RuleDefinitionDto rule1 = newRule(
  972. SECURITY_HOTSPOT,
  973. t -> t.setName("rule_" + sqCategory.name() + "_a").setSecurityStandards(securityStandards));
  974. RuleDefinitionDto rule2 = newRule(
  975. SECURITY_HOTSPOT,
  976. t -> t.setName("rule_" + sqCategory.name() + "_b").setSecurityStandards(securityStandards));
  977. return Stream.of(
  978. newHotspot(rule1, project, file).setKee(sqCategory + "_a"),
  979. newHotspot(rule2, project, file).setKee(sqCategory + "_b"));
  980. })
  981. .collect(toList());
  982. String[] expectedHotspotKeys = hotspots.stream().map(IssueDto::getKey).toArray(String[]::new);
  983. // insert hotspots in random order
  984. Collections.shuffle(hotspots);
  985. hotspots.forEach(dbTester.issues()::insertHotspot);
  986. indexIssues();
  987. SearchWsResponse response = newRequest(project)
  988. .executeProtobuf(SearchWsResponse.class);
  989. assertThat(response.getHotspotsList())
  990. .extracting(SearchWsResponse.Hotspot::getKey)
  991. .containsExactly(expectedHotspotKeys);
  992. }
  993. @Test
  994. public void returns_hotspots_ordered_by_file_path_then_line_then_key() {
  995. ComponentDto project = dbTester.components().insertPublicProject();
  996. userSessionRule.registerComponents(project);
  997. indexPermissions();
  998. ComponentDto file1 = dbTester.components().insertComponent(newFileDto(project).setPath("b/c/a"));
  999. ComponentDto file2 = dbTester.components().insertComponent(newFileDto(project).setPath("b/c/b"));
  1000. ComponentDto file3 = dbTester.components().insertComponent(newFileDto(project).setPath("a/a/d"));
  1001. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  1002. List<IssueDto> hotspots = Stream.of(
  1003. newHotspot(rule, project, file3).setLine(8),
  1004. newHotspot(rule, project, file3).setLine(10),
  1005. newHotspot(rule, project, file1).setLine(null),
  1006. newHotspot(rule, project, file1).setLine(9),
  1007. newHotspot(rule, project, file1).setLine(11).setKee("a"),
  1008. newHotspot(rule, project, file1).setLine(11).setKee("b"),
  1009. newHotspot(rule, project, file2).setLine(null),
  1010. newHotspot(rule, project, file2).setLine(2))
  1011. .collect(toList());
  1012. String[] expectedHotspotKeys = hotspots.stream().map(IssueDto::getKey).toArray(String[]::new);
  1013. // insert hotspots in random order
  1014. Collections.shuffle(hotspots);
  1015. hotspots.forEach(dbTester.issues()::insertHotspot);
  1016. indexIssues();
  1017. SearchWsResponse response = newRequest(project)
  1018. .executeProtobuf(SearchWsResponse.class);
  1019. assertThat(response.getHotspotsList())
  1020. .extracting(SearchWsResponse.Hotspot::getKey)
  1021. .containsExactly(expectedHotspotKeys);
  1022. }
  1023. @Test
  1024. public void returns_first_page_with_100_results_by_default() {
  1025. ComponentDto project = dbTester.components().insertPublicProject();
  1026. userSessionRule.registerComponents(project);
  1027. indexPermissions();
  1028. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1029. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  1030. int total = 436;
  1031. List<IssueDto> hotspots = IntStream.range(0, total)
  1032. .mapToObj(i -> dbTester.issues().insertHotspot(rule, project, file, t -> t.setLine(i)))
  1033. .collect(toList());
  1034. indexIssues();
  1035. TestRequest request = newRequest(project);
  1036. SearchWsResponse response = request.executeProtobuf(SearchWsResponse.class);
  1037. assertThat(response.getHotspotsList())
  1038. .extracting(SearchWsResponse.Hotspot::getKey)
  1039. .containsExactly(hotspots.stream().limit(100).map(IssueDto::getKey).toArray(String[]::new));
  1040. assertThat(response.getPaging().getTotal()).isEqualTo(hotspots.size());
  1041. assertThat(response.getPaging().getPageIndex()).isEqualTo(1);
  1042. assertThat(response.getPaging().getPageSize()).isEqualTo(100);
  1043. }
  1044. @Test
  1045. public void returns_specified_page_with_100_results_by_default() {
  1046. ComponentDto project = dbTester.components().insertPublicProject();
  1047. userSessionRule.registerComponents(project);
  1048. indexPermissions();
  1049. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1050. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  1051. verifyPaging(project, file, rule, 336, 100);
  1052. }
  1053. @Test
  1054. public void returns_specified_page_with_specified_number_of_results() {
  1055. ComponentDto project = dbTester.components().insertPublicProject();
  1056. userSessionRule.registerComponents(project);
  1057. indexPermissions();
  1058. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1059. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  1060. int total = 336;
  1061. int pageSize = 1 + new Random().nextInt(100);
  1062. verifyPaging(project, file, rule, total, pageSize);
  1063. }
  1064. private void verifyPaging(ComponentDto project, ComponentDto file, RuleDefinitionDto rule, int total, int pageSize) {
  1065. List<IssueDto> hotspots = IntStream.range(0, total)
  1066. .mapToObj(i -> dbTester.issues().insertHotspot(rule, project, file, t -> t.setLine(i).setKee("issue_" + i)))
  1067. .collect(toList());
  1068. indexIssues();
  1069. SearchWsResponse response = newRequest(project)
  1070. .setParam("p", "3")
  1071. .setParam("ps", String.valueOf(pageSize))
  1072. .executeProtobuf(SearchWsResponse.class);
  1073. assertThat(response.getHotspotsList())
  1074. .extracting(SearchWsResponse.Hotspot::getKey)
  1075. .containsExactly(hotspots.stream().skip(2 * pageSize).limit(pageSize).map(IssueDto::getKey).toArray(String[]::new));
  1076. assertThat(response.getPaging().getTotal()).isEqualTo(hotspots.size());
  1077. assertThat(response.getPaging().getPageIndex()).isEqualTo(3);
  1078. assertThat(response.getPaging().getPageSize()).isEqualTo(pageSize);
  1079. response = newRequest(project)
  1080. .setParam("p", "4")
  1081. .setParam("ps", String.valueOf(pageSize))
  1082. .executeProtobuf(SearchWsResponse.class);
  1083. assertThat(response.getHotspotsList())
  1084. .extracting(SearchWsResponse.Hotspot::getKey)
  1085. .containsExactly(hotspots.stream().skip(3 * pageSize).limit(pageSize).map(IssueDto::getKey).toArray(String[]::new));
  1086. assertThat(response.getPaging().getTotal()).isEqualTo(hotspots.size());
  1087. assertThat(response.getPaging().getPageIndex()).isEqualTo(4);
  1088. assertThat(response.getPaging().getPageSize()).isEqualTo(pageSize);
  1089. int emptyPage = (hotspots.size() / pageSize) + 10;
  1090. response = newRequest(project)
  1091. .setParam("p", String.valueOf(emptyPage))
  1092. .setParam("ps", String.valueOf(pageSize))
  1093. .executeProtobuf(SearchWsResponse.class);
  1094. assertThat(response.getHotspotsList())
  1095. .extracting(SearchWsResponse.Hotspot::getKey)
  1096. .isEmpty();
  1097. assertThat(response.getPaging().getTotal()).isEqualTo(hotspots.size());
  1098. assertThat(response.getPaging().getPageIndex()).isEqualTo(emptyPage);
  1099. assertThat(response.getPaging().getPageSize()).isEqualTo(pageSize);
  1100. }
  1101. @Test
  1102. public void returns_empty_if_none_of_hotspot_keys_exist() {
  1103. ComponentDto project = dbTester.components().insertPublicProject();
  1104. userSessionRule.registerComponents(project);
  1105. indexPermissions();
  1106. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1107. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  1108. List<IssueDto> hotspots = IntStream.range(0, 1 + RANDOM.nextInt(15))
  1109. .mapToObj(i -> dbTester.issues().insertHotspot(rule, project, file, t -> t.setLine(i)))
  1110. .collect(toList());
  1111. indexIssues();
  1112. SearchWsResponse response = newRequest(IntStream.range(0, 1 + RANDOM.nextInt(30)).mapToObj(i -> "key_" + i).collect(toList()))
  1113. .executeProtobuf(SearchWsResponse.class);
  1114. assertThat(response.getHotspotsList()).isEmpty();
  1115. }
  1116. @Test
  1117. public void returns_specified_hotspots() {
  1118. ComponentDto project = dbTester.components().insertPublicProject();
  1119. userSessionRule.registerComponents(project);
  1120. indexPermissions();
  1121. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1122. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  1123. int total = 1 + RANDOM.nextInt(20);
  1124. List<IssueDto> hotspots = IntStream.range(0, total)
  1125. .mapToObj(i -> dbTester.issues().insertHotspot(rule, project, file, t -> t.setLine(i)))
  1126. .collect(toList());
  1127. Collections.shuffle(hotspots);
  1128. List<IssueDto> selectedHotspots = hotspots.stream().limit(total == 1 ? 1 : 1 + RANDOM.nextInt(total - 1)).collect(toList());
  1129. indexIssues();
  1130. SearchWsResponse response = newRequest(selectedHotspots.stream().map(IssueDto::getKey).collect(toList()))
  1131. .executeProtobuf(SearchWsResponse.class);
  1132. assertThat(response.getHotspotsList())
  1133. .extracting(SearchWsResponse.Hotspot::getKey)
  1134. .containsExactlyInAnyOrder(selectedHotspots.stream().map(IssueDto::getKey).toArray(String[]::new));
  1135. }
  1136. @Test
  1137. public void returns_hotspots_on_the_leak_period_when_sinceLeakPeriod_is_true() {
  1138. ComponentDto project = dbTester.components().insertPublicProject();
  1139. userSessionRule.registerComponents(project);
  1140. indexPermissions();
  1141. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1142. long periodDate = 800_996_999_332L;
  1143. dbTester.components().insertSnapshot(project, t -> t.setPeriodDate(periodDate).setLast(false));
  1144. dbTester.components().insertSnapshot(project, t -> t.setPeriodDate(periodDate - 1_500).setLast(true));
  1145. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  1146. List<IssueDto> hotspotsInLeakPeriod = IntStream.range(0, 1 + RANDOM.nextInt(20))
  1147. .mapToObj(i -> {
  1148. long issueCreationDate = periodDate + ONE_MINUTE + (RANDOM.nextInt(300) * ONE_MINUTE);
  1149. return dbTester.issues().insertHotspot(rule, project, file,
  1150. t -> t.setLine(i).setIssueCreationTime(issueCreationDate));
  1151. })
  1152. .collect(toList());
  1153. // included because
  1154. List<IssueDto> atLeakPeriod = IntStream.range(0, 1 + RANDOM.nextInt(20))
  1155. .mapToObj(i -> dbTester.issues().insertHotspot(rule, project, file,
  1156. t -> t.setType(SECURITY_HOTSPOT).setLine(i).setIssueCreationTime(periodDate)))
  1157. .collect(toList());
  1158. List<IssueDto> hotspotsBefore = IntStream.range(0, 1 + RANDOM.nextInt(20))
  1159. .mapToObj(i -> {
  1160. long issueCreationDate = periodDate - ONE_MINUTE - (RANDOM.nextInt(300) * ONE_MINUTE);
  1161. return dbTester.issues().insertHotspot(rule, project, file,
  1162. t -> t.setLine(i).setIssueCreationTime(issueCreationDate));
  1163. })
  1164. .collect(toList());
  1165. indexIssues();
  1166. SearchWsResponse responseAll = newRequest(project)
  1167. .executeProtobuf(SearchWsResponse.class);
  1168. assertThat(responseAll.getHotspotsList())
  1169. .extracting(SearchWsResponse.Hotspot::getKey)
  1170. .containsExactlyInAnyOrder(Stream.of(
  1171. hotspotsInLeakPeriod.stream(),
  1172. atLeakPeriod.stream(),
  1173. hotspotsBefore.stream())
  1174. .flatMap(t -> t)
  1175. .map(IssueDto::getKey)
  1176. .toArray(String[]::new));
  1177. SearchWsResponse responseOnLeak = newRequest(project,
  1178. t -> t.setParam("sinceLeakPeriod", "true"))
  1179. .executeProtobuf(SearchWsResponse.class);
  1180. assertThat(responseOnLeak.getHotspotsList())
  1181. .extracting(SearchWsResponse.Hotspot::getKey)
  1182. .containsExactlyInAnyOrder(Stream.concat(
  1183. hotspotsInLeakPeriod.stream(),
  1184. atLeakPeriod.stream())
  1185. .map(IssueDto::getKey)
  1186. .toArray(String[]::new));
  1187. }
  1188. @Test
  1189. public void verify_response_example() {
  1190. ComponentDto project = dbTester.components().insertPublicProject(componentDto -> componentDto
  1191. .setName("test-project")
  1192. .setLongName("test-project")
  1193. .setDbKey("com.sonarsource:test-project")
  1194. );
  1195. userSessionRule.registerComponents(project);
  1196. indexPermissions();
  1197. ComponentDto fileWithHotspot = dbTester.components().insertComponent(newFileDto(project)
  1198. .setDbKey("com.sonarsource:test-project:src/main/java/com/sonarsource/FourthClass.java")
  1199. .setName("FourthClass.java")
  1200. .setLongName("src/main/java/com/sonarsource/FourthClass.java")
  1201. .setPath("src/main/java/com/sonarsource/FourthClass.java")
  1202. );
  1203. long time = 1577976190000L;
  1204. IssueDto[] hotspots = IntStream.range(0, 3)
  1205. .mapToObj(i -> {
  1206. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT)
  1207. .setSecurityStandards(Sets.newHashSet(SQCategory.WEAK_CRYPTOGRAPHY.getKey()));
  1208. return insertHotspot(rule, project, fileWithHotspot, issueDto -> issueDto.setKee("hotspot-" + i)
  1209. .setAssigneeUuid("assignee-uuid")
  1210. .setAuthorLogin("joe")
  1211. .setMessage("message-" +i)
  1212. .setLine(10 + i)
  1213. .setIssueCreationTime(time)
  1214. .setIssueUpdateTime(time)
  1215. );
  1216. })
  1217. .toArray(IssueDto[]::new);
  1218. indexIssues();
  1219. newRequest(project)
  1220. .execute()
  1221. .assertJson(actionTester.getDef().responseExampleAsString()
  1222. .replaceAll("default-organization", dbTester.getDefaultOrganization().getKey()));
  1223. }
  1224. private IssueDto insertHotspot(ComponentDto project, ComponentDto file, RuleDefinitionDto rule) {
  1225. return insertHotspot(rule, project, file, t -> {
  1226. });
  1227. }
  1228. private IssueDto insertHotspot(RuleDefinitionDto rule, ComponentDto project, ComponentDto file, @Nullable String assigneeUuid) {
  1229. return insertHotspot(rule, project, file, t -> t.setAssigneeUuid(assigneeUuid));
  1230. }
  1231. private IssueDto insertHotspot(RuleDefinitionDto rule, ComponentDto project, ComponentDto file, Consumer<IssueDto> consumer) {
  1232. return dbTester.issues().insertHotspot(rule, project, file, consumer);
  1233. }
  1234. private static IssueDto newHotspot(RuleDefinitionDto rule, ComponentDto project, ComponentDto component) {
  1235. return newHotspot(rule, project, component, t -> {
  1236. });
  1237. }
  1238. private static IssueDto newHotspot(RuleDefinitionDto rule, ComponentDto project, ComponentDto component, Consumer<IssueDto> consumer) {
  1239. IssueDto res = newIssue(rule, project, component)
  1240. .setStatus(STATUS_TO_REVIEW);
  1241. consumer.accept(res);
  1242. return res.setType(SECURITY_HOTSPOT);
  1243. }
  1244. private TestRequest newRequest(ComponentDto project) {
  1245. return newRequest(project, null, null);
  1246. }
  1247. private TestRequest newRequest(ComponentDto project, Consumer<TestRequest> consumer) {
  1248. return newRequest(project, null, null, consumer);
  1249. }
  1250. private TestRequest newRequest(ComponentDto project, @Nullable String status, @Nullable String resolution) {
  1251. return newRequest(project, status, resolution, t -> {
  1252. });
  1253. }
  1254. private TestRequest newRequest(ComponentDto project, @Nullable String status, @Nullable String resolution, Consumer<TestRequest> consumer) {
  1255. TestRequest res = actionTester.newRequest()
  1256. .setParam("projectKey", project.getKey());
  1257. String branch = project.getBranch();
  1258. if (branch != null) {
  1259. res.setParam("branch", branch);
  1260. }
  1261. String pullRequest = project.getPullRequest();
  1262. if (pullRequest != null) {
  1263. res.setParam("pullRequest", pullRequest);
  1264. }
  1265. if (status != null) {
  1266. res.setParam("status", status);
  1267. }
  1268. if (resolution != null) {
  1269. res.setParam("resolution", resolution);
  1270. }
  1271. consumer.accept(res);
  1272. return res;
  1273. }
  1274. private TestRequest newRequest(Collection<String> hotspotKeys) {
  1275. return actionTester.newRequest()
  1276. .setParam("hotspots", String.join(",", hotspotKeys));
  1277. }
  1278. private void indexPermissions() {
  1279. permissionIndexer.indexOnStartup(permissionIndexer.getIndexTypes());
  1280. }
  1281. private void indexIssues() {
  1282. issueIndexer.indexOnStartup(issueIndexer.getIndexTypes());
  1283. }
  1284. private void indexViews() {
  1285. viewIndexer.indexOnStartup(viewIndexer.getIndexTypes());
  1286. }
  1287. private RuleDefinitionDto newRule(RuleType ruleType) {
  1288. return newRule(ruleType, t -> {
  1289. });
  1290. }
  1291. private RuleDefinitionDto newRule(RuleType ruleType, Consumer<RuleDefinitionDto> populate) {
  1292. RuleDefinitionDto ruleDefinition = RuleTesting.newRule()
  1293. .setType(ruleType);
  1294. populate.accept(ruleDefinition);
  1295. dbTester.rules().insert(ruleDefinition);
  1296. return ruleDefinition;
  1297. }
  1298. }