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.

ShowActionTest.java 35KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  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.tngtech.java.junit.dataprovider.DataProvider;
  23. import com.tngtech.java.junit.dataprovider.DataProviderRunner;
  24. import com.tngtech.java.junit.dataprovider.UseDataProvider;
  25. import java.util.Arrays;
  26. import java.util.Collections;
  27. import java.util.List;
  28. import java.util.Random;
  29. import java.util.Set;
  30. import java.util.function.Consumer;
  31. import java.util.stream.Collectors;
  32. import java.util.stream.IntStream;
  33. import java.util.stream.Stream;
  34. import javax.annotation.Nullable;
  35. import org.assertj.core.groups.Tuple;
  36. import org.junit.Rule;
  37. import org.junit.Test;
  38. import org.junit.runner.RunWith;
  39. import org.mockito.ArgumentMatcher;
  40. import org.mockito.Mockito;
  41. import org.sonar.api.issue.Issue;
  42. import org.sonar.api.rules.RuleType;
  43. import org.sonar.api.utils.System2;
  44. import org.sonar.api.web.UserRole;
  45. import org.sonar.db.DbClient;
  46. import org.sonar.db.DbSession;
  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.issue.IssueDto;
  51. import org.sonar.db.protobuf.DbCommons;
  52. import org.sonar.db.protobuf.DbIssues;
  53. import org.sonar.db.rule.RuleDefinitionDto;
  54. import org.sonar.db.rule.RuleTesting;
  55. import org.sonar.db.user.UserDto;
  56. import org.sonar.db.user.UserTesting;
  57. import org.sonar.server.es.EsTester;
  58. import org.sonar.server.exceptions.ForbiddenException;
  59. import org.sonar.server.exceptions.NotFoundException;
  60. import org.sonar.server.issue.AvatarResolver;
  61. import org.sonar.server.issue.AvatarResolverImpl;
  62. import org.sonar.server.issue.IssueChangeWSSupport;
  63. import org.sonar.server.issue.IssueChangeWSSupport.FormattingContext;
  64. import org.sonar.server.issue.IssueChangeWSSupport.Load;
  65. import org.sonar.server.issue.TextRangeResponseFormatter;
  66. import org.sonar.server.issue.ws.UserResponseFormatter;
  67. import org.sonar.server.organization.TestDefaultOrganizationProvider;
  68. import org.sonar.server.security.SecurityStandards;
  69. import org.sonar.server.security.SecurityStandards.SQCategory;
  70. import org.sonar.server.tester.UserSessionRule;
  71. import org.sonar.server.ws.TestRequest;
  72. import org.sonar.server.ws.WsActionTester;
  73. import org.sonarqube.ws.Common;
  74. import org.sonarqube.ws.Common.User;
  75. import org.sonarqube.ws.Hotspots;
  76. import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
  77. import static org.assertj.core.api.Assertions.assertThat;
  78. import static org.assertj.core.api.Assertions.assertThatThrownBy;
  79. import static org.assertj.core.api.Assertions.tuple;
  80. import static org.mockito.ArgumentMatchers.any;
  81. import static org.mockito.ArgumentMatchers.anySet;
  82. import static org.mockito.ArgumentMatchers.argThat;
  83. import static org.mockito.ArgumentMatchers.eq;
  84. import static org.mockito.Mockito.verify;
  85. import static org.mockito.Mockito.when;
  86. import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
  87. import static org.sonar.db.component.ComponentTesting.newFileDto;
  88. @RunWith(DataProviderRunner.class)
  89. public class ShowActionTest {
  90. private static final Random RANDOM = new Random();
  91. @Rule
  92. public DbTester dbTester = DbTester.create(System2.INSTANCE);
  93. @Rule
  94. public EsTester es = EsTester.create();
  95. @Rule
  96. public UserSessionRule userSessionRule = UserSessionRule.standalone();
  97. private DbClient dbClient = dbTester.getDbClient();
  98. private TestDefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(dbTester);
  99. private AvatarResolver avatarResolver = new AvatarResolverImpl();
  100. private HotspotWsResponseFormatter responseFormatter = new HotspotWsResponseFormatter(defaultOrganizationProvider);
  101. private IssueChangeWSSupport issueChangeSupport = Mockito.mock(IssueChangeWSSupport.class);
  102. private HotspotWsSupport hotspotWsSupport = new HotspotWsSupport(dbClient, userSessionRule, System2.INSTANCE);
  103. private UserResponseFormatter userFormatter = new UserResponseFormatter(new AvatarResolverImpl());
  104. private TextRangeResponseFormatter textRangeFormatter = new TextRangeResponseFormatter();
  105. private ShowAction underTest = new ShowAction(dbClient, hotspotWsSupport, responseFormatter, textRangeFormatter, userFormatter, issueChangeSupport);
  106. private WsActionTester actionTester = new WsActionTester(underTest);
  107. @Test
  108. public void ws_is_internal() {
  109. assertThat(actionTester.getDef().isInternal()).isTrue();
  110. }
  111. @Test
  112. public void fails_with_IAE_if_parameter_hotspot_is_missing() {
  113. TestRequest request = actionTester.newRequest();
  114. assertThatThrownBy(request::execute)
  115. .isInstanceOf(IllegalArgumentException.class)
  116. .hasMessage("The 'hotspot' parameter is missing");
  117. }
  118. @Test
  119. public void fails_with_NotFoundException_if_hotspot_does_not_exist() {
  120. String key = randomAlphabetic(12);
  121. TestRequest request = actionTester.newRequest()
  122. .setParam("hotspot", key);
  123. assertThatThrownBy(request::execute)
  124. .isInstanceOf(NotFoundException.class)
  125. .hasMessage("Hotspot '%s' does not exist", key);
  126. }
  127. @Test
  128. @UseDataProvider("ruleTypesButHotspot")
  129. public void fails_with_NotFoundException_if_issue_is_not_a_hotspot(RuleType ruleType) {
  130. ComponentDto project = dbTester.components().insertPublicProject();
  131. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  132. RuleDefinitionDto rule = newRule(ruleType);
  133. IssueDto notAHotspot = dbTester.issues().insertIssue(rule, project, file, i -> i.setType(ruleType));
  134. TestRequest request = newRequest(notAHotspot);
  135. assertThatThrownBy(request::execute)
  136. .isInstanceOf(NotFoundException.class)
  137. .hasMessage("Hotspot '%s' does not exist", notAHotspot.getKey());
  138. }
  139. @DataProvider
  140. public static Object[][] ruleTypesButHotspot() {
  141. return Arrays.stream(RuleType.values())
  142. .filter(t -> t != SECURITY_HOTSPOT)
  143. .map(t -> new Object[] {t})
  144. .toArray(Object[][]::new);
  145. }
  146. @Test
  147. public void fails_with_NotFoundException_if_issue_is_hotspot_is_closed() {
  148. ComponentDto project = dbTester.components().insertPublicProject();
  149. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  150. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  151. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file, t -> t.setStatus(Issue.STATUS_CLOSED));
  152. TestRequest request = newRequest(hotspot);
  153. assertThatThrownBy(request::execute)
  154. .isInstanceOf(NotFoundException.class)
  155. .hasMessage("Hotspot '%s' does not exist", hotspot.getKey());
  156. }
  157. @Test
  158. public void fails_with_ForbiddenException_if_project_is_private_and_not_allowed() {
  159. ComponentDto project = dbTester.components().insertPrivateProject();
  160. userSessionRule.registerComponents(project);
  161. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  162. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  163. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file);
  164. TestRequest request = newRequest(hotspot);
  165. assertThatThrownBy(request::execute)
  166. .isInstanceOf(ForbiddenException.class)
  167. .hasMessage("Insufficient privileges");
  168. }
  169. @Test
  170. public void succeeds_on_public_project() {
  171. ComponentDto project = dbTester.components().insertPublicProject();
  172. userSessionRule.registerComponents(project);
  173. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  174. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  175. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file);
  176. mockChangelogAndCommentsFormattingContext();
  177. Hotspots.ShowWsResponse response = newRequest(hotspot)
  178. .executeProtobuf(Hotspots.ShowWsResponse.class);
  179. assertThat(response.getKey()).isEqualTo(hotspot.getKey());
  180. }
  181. @Test
  182. public void succeeds_on_private_project_with_permission() {
  183. ComponentDto project = dbTester.components().insertPrivateProject();
  184. userSessionRule.registerComponents(project);
  185. userSessionRule.logIn().addProjectPermission(UserRole.USER, project);
  186. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  187. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  188. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file);
  189. mockChangelogAndCommentsFormattingContext();
  190. Hotspots.ShowWsResponse response = newRequest(hotspot)
  191. .executeProtobuf(Hotspots.ShowWsResponse.class);
  192. assertThat(response.getKey()).isEqualTo(hotspot.getKey());
  193. }
  194. @Test
  195. @UseDataProvider("statusAndResolutionCombinations")
  196. public void returns_status_and_resolution(String status, @Nullable String resolution) {
  197. ComponentDto project = dbTester.components().insertPrivateProject();
  198. userSessionRule.registerComponents(project);
  199. userSessionRule.logIn().addProjectPermission(UserRole.USER, project);
  200. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  201. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  202. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file, t -> t.setStatus(status).setResolution(resolution));
  203. mockChangelogAndCommentsFormattingContext();
  204. Hotspots.ShowWsResponse response = newRequest(hotspot)
  205. .executeProtobuf(Hotspots.ShowWsResponse.class);
  206. assertThat(response.getStatus()).isEqualTo(status);
  207. if (resolution == null) {
  208. assertThat(response.hasResolution()).isFalse();
  209. } else {
  210. assertThat(response.getResolution()).isEqualTo(resolution);
  211. }
  212. }
  213. @DataProvider
  214. public static Object[][] statusAndResolutionCombinations() {
  215. return new Object[][] {
  216. {Issue.STATUS_TO_REVIEW, null},
  217. {Issue.STATUS_REVIEWED, Issue.RESOLUTION_FIXED},
  218. {Issue.STATUS_REVIEWED, Issue.RESOLUTION_SAFE}
  219. };
  220. }
  221. @Test
  222. public void returns_hotspot_component_and_rule() {
  223. ComponentDto project = dbTester.components().insertPublicProject();
  224. userSessionRule.registerComponents(project);
  225. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  226. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  227. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file);
  228. mockChangelogAndCommentsFormattingContext();
  229. Hotspots.ShowWsResponse response = newRequest(hotspot)
  230. .executeProtobuf(Hotspots.ShowWsResponse.class);
  231. assertThat(response.getKey()).isEqualTo(hotspot.getKey());
  232. verifyComponent(response.getComponent(), file, null, null);
  233. verifyComponent(response.getProject(), project, null, null);
  234. verifyRule(response.getRule(), rule);
  235. assertThat(response.hasTextRange()).isFalse();
  236. }
  237. @Test
  238. public void returns_no_textRange_when_locations_have_none() {
  239. ComponentDto project = dbTester.components().insertPublicProject();
  240. userSessionRule.registerComponents(project);
  241. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  242. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  243. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file,
  244. t -> t.setLocations(DbIssues.Locations.newBuilder().build()));
  245. mockChangelogAndCommentsFormattingContext();
  246. Hotspots.ShowWsResponse response = newRequest(hotspot)
  247. .executeProtobuf(Hotspots.ShowWsResponse.class);
  248. assertThat(response.hasTextRange()).isFalse();
  249. }
  250. @Test
  251. @UseDataProvider("randomTextRangeValues")
  252. public void returns_textRange(int startLine, int endLine, int startOffset, int endOffset) {
  253. ComponentDto project = dbTester.components().insertPublicProject();
  254. userSessionRule.registerComponents(project);
  255. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  256. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  257. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file,
  258. t -> t.setLocations(DbIssues.Locations.newBuilder()
  259. .setTextRange(DbCommons.TextRange.newBuilder()
  260. .setStartLine(startLine)
  261. .setEndLine(endLine)
  262. .setStartOffset(startOffset)
  263. .setEndOffset(endOffset)
  264. .build())
  265. .build()));
  266. mockChangelogAndCommentsFormattingContext();
  267. Hotspots.ShowWsResponse response = newRequest(hotspot)
  268. .executeProtobuf(Hotspots.ShowWsResponse.class);
  269. assertThat(response.hasTextRange()).isTrue();
  270. Common.TextRange textRange = response.getTextRange();
  271. assertThat(textRange.getStartLine()).isEqualTo(startLine);
  272. assertThat(textRange.getEndLine()).isEqualTo(endLine);
  273. assertThat(textRange.getStartOffset()).isEqualTo(startOffset);
  274. assertThat(textRange.getEndOffset()).isEqualTo(endOffset);
  275. }
  276. @Test
  277. public void returns_no_assignee_when_user_does_not_exist() {
  278. ComponentDto project = dbTester.components().insertPublicProject();
  279. userSessionRule.registerComponents(project);
  280. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  281. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  282. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file, t -> t.setAssigneeUuid(randomAlphabetic(10)));
  283. mockChangelogAndCommentsFormattingContext();
  284. Hotspots.ShowWsResponse response = newRequest(hotspot)
  285. .executeProtobuf(Hotspots.ShowWsResponse.class);
  286. assertThat(response.hasAssignee()).isFalse();
  287. }
  288. @Test
  289. public void returns_assignee_details_when_user_exists() {
  290. ComponentDto project = dbTester.components().insertPublicProject();
  291. userSessionRule.registerComponents(project);
  292. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  293. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  294. UserDto assignee = dbTester.users().insertUser();
  295. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file, t -> t.setAssigneeUuid(assignee.getUuid()));
  296. mockChangelogAndCommentsFormattingContext();
  297. Hotspots.ShowWsResponse response = newRequest(hotspot)
  298. .executeProtobuf(Hotspots.ShowWsResponse.class);
  299. assertThat(response.getAssignee()).isEqualTo(assignee.getLogin());
  300. assertThat(response.getUsersList()).hasSize(1);
  301. User wsAssignee = response.getUsersList().iterator().next();
  302. assertThat(wsAssignee.getLogin()).isEqualTo(assignee.getLogin());
  303. assertThat(wsAssignee.getName()).isEqualTo(assignee.getName());
  304. assertThat(wsAssignee.getActive()).isEqualTo(assignee.isActive());
  305. assertThat(wsAssignee.getAvatar()).isEqualTo(avatarResolver.create(assignee));
  306. }
  307. @Test
  308. public void returns_no_avatar_if_assignee_has_no_email() {
  309. ComponentDto project = dbTester.components().insertPublicProject();
  310. userSessionRule.registerComponents(project);
  311. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  312. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  313. UserDto assignee = dbTester.users().insertUser(t -> t.setEmail(null));
  314. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file, t -> t.setAssigneeUuid(assignee.getUuid()));
  315. mockChangelogAndCommentsFormattingContext();
  316. Hotspots.ShowWsResponse response = newRequest(hotspot)
  317. .executeProtobuf(Hotspots.ShowWsResponse.class);
  318. assertThat(response.getUsersList()).hasSize(1);
  319. assertThat(response.getUsersList().iterator().next().hasAvatar()).isFalse();
  320. }
  321. @Test
  322. public void returns_inactive_when_assignee_is_inactive() {
  323. ComponentDto project = dbTester.components().insertPublicProject();
  324. userSessionRule.registerComponents(project);
  325. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  326. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  327. UserDto assignee = dbTester.users().insertUser(t -> t.setActive(false));
  328. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file, t -> t.setAssigneeUuid(assignee.getUuid()));
  329. mockChangelogAndCommentsFormattingContext();
  330. Hotspots.ShowWsResponse response = newRequest(hotspot)
  331. .executeProtobuf(Hotspots.ShowWsResponse.class);
  332. assertThat(response.getUsersList()).hasSize(1);
  333. assertThat(response.getUsersList().iterator().next().getActive()).isFalse();
  334. }
  335. @Test
  336. public void returns_author_login_when_user_does_not_exist() {
  337. ComponentDto project = dbTester.components().insertPublicProject();
  338. userSessionRule.registerComponents(project);
  339. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  340. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  341. String authorLogin = randomAlphabetic(10);
  342. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file, t -> t.setAuthorLogin(authorLogin));
  343. mockChangelogAndCommentsFormattingContext();
  344. Hotspots.ShowWsResponse response = newRequest(hotspot)
  345. .executeProtobuf(Hotspots.ShowWsResponse.class);
  346. assertThat(response.getUsersList()).isEmpty();
  347. assertThat(response.getAuthor()).isEqualTo(authorLogin);
  348. }
  349. @Test
  350. public void returns_author_details_when_user_exists() {
  351. ComponentDto project = dbTester.components().insertPublicProject();
  352. userSessionRule.registerComponents(project);
  353. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  354. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  355. UserDto author = dbTester.users().insertUser();
  356. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file, t -> t.setAuthorLogin(author.getLogin()));
  357. mockChangelogAndCommentsFormattingContext();
  358. Hotspots.ShowWsResponse response = newRequest(hotspot)
  359. .executeProtobuf(Hotspots.ShowWsResponse.class);
  360. assertThat(response.getAuthor()).isEqualTo(author.getLogin());
  361. User wsAuthorFromList = response.getUsersList().iterator().next();
  362. assertThat(wsAuthorFromList.getLogin()).isEqualTo(author.getLogin());
  363. assertThat(wsAuthorFromList.getName()).isEqualTo(author.getName());
  364. assertThat(wsAuthorFromList.getActive()).isEqualTo(author.isActive());
  365. assertThat(wsAuthorFromList.getAvatar()).isEqualTo(avatarResolver.create(author));
  366. }
  367. @Test
  368. public void returns_no_avatar_if_author_has_no_email() {
  369. ComponentDto project = dbTester.components().insertPublicProject();
  370. userSessionRule.registerComponents(project);
  371. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  372. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  373. UserDto author = dbTester.users().insertUser(t -> t.setEmail(null));
  374. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file, t -> t.setAuthorLogin(author.getLogin()));
  375. mockChangelogAndCommentsFormattingContext();
  376. Hotspots.ShowWsResponse response = newRequest(hotspot)
  377. .executeProtobuf(Hotspots.ShowWsResponse.class);
  378. assertThat(response.getUsersList()).hasSize(1);
  379. assertThat(response.getUsersList().iterator().next().hasAvatar()).isFalse();
  380. }
  381. @Test
  382. public void returns_inactive_if_author_is_inactive() {
  383. ComponentDto project = dbTester.components().insertPublicProject();
  384. userSessionRule.registerComponents(project);
  385. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  386. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  387. UserDto author = dbTester.users().insertUser(t -> t.setActive(false));
  388. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file, t -> t.setAuthorLogin(author.getLogin()));
  389. mockChangelogAndCommentsFormattingContext();
  390. Hotspots.ShowWsResponse response = newRequest(hotspot)
  391. .executeProtobuf(Hotspots.ShowWsResponse.class);
  392. assertThat(response.getUsersList()).hasSize(1);
  393. assertThat(response.getUsersList().iterator().next().getActive()).isFalse();
  394. }
  395. @DataProvider
  396. public static Object[][] randomTextRangeValues() {
  397. int startLine = RANDOM.nextInt(200);
  398. int endLine = RANDOM.nextInt(200);
  399. int startOffset = RANDOM.nextInt(200);
  400. int endOffset = RANDOM.nextInt(200);
  401. return new Object[][] {
  402. {startLine, endLine, startOffset, endOffset}
  403. };
  404. }
  405. @Test
  406. public void returns_textRange_missing_fields() {
  407. ComponentDto project = dbTester.components().insertPublicProject();
  408. userSessionRule.registerComponents(project);
  409. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  410. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  411. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file,
  412. t -> t.setLocations(DbIssues.Locations.newBuilder()
  413. .setTextRange(DbCommons.TextRange.newBuilder().build())
  414. .build()));
  415. mockChangelogAndCommentsFormattingContext();
  416. Hotspots.ShowWsResponse response = newRequest(hotspot)
  417. .executeProtobuf(Hotspots.ShowWsResponse.class);
  418. assertThat(response.hasTextRange()).isTrue();
  419. Common.TextRange textRange = response.getTextRange();
  420. assertThat(textRange.hasStartLine()).isFalse();
  421. assertThat(textRange.hasEndLine()).isFalse();
  422. assertThat(textRange.hasStartOffset()).isFalse();
  423. assertThat(textRange.hasEndOffset()).isFalse();
  424. }
  425. @Test
  426. @UseDataProvider("allSQCategoryAndVulnerabilityProbability")
  427. public void returns_securityCategory_and_vulnerabilityProbability_of_rule(Set<String> standards,
  428. SQCategory expected) {
  429. ComponentDto project = dbTester.components().insertPublicProject();
  430. userSessionRule.registerComponents(project);
  431. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  432. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT, t -> t.setSecurityStandards(standards));
  433. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file,
  434. t -> t.setLocations(DbIssues.Locations.newBuilder()
  435. .setTextRange(DbCommons.TextRange.newBuilder().build())
  436. .build()));
  437. mockChangelogAndCommentsFormattingContext();
  438. Hotspots.ShowWsResponse response = newRequest(hotspot)
  439. .executeProtobuf(Hotspots.ShowWsResponse.class);
  440. Hotspots.Rule wsRule = response.getRule();
  441. assertThat(wsRule.getSecurityCategory()).isEqualTo(expected.getKey());
  442. assertThat(wsRule.getVulnerabilityProbability()).isEqualTo(expected.getVulnerability().name());
  443. }
  444. @DataProvider
  445. public static Object[][] allSQCategoryAndVulnerabilityProbability() {
  446. Stream<Object[]> allButOthers = SecurityStandards.CWES_BY_SQ_CATEGORY
  447. .entrySet()
  448. .stream()
  449. .map(t -> new Object[] {
  450. t.getValue().stream().map(s -> "cwe:" + s).collect(Collectors.toSet()),
  451. t.getKey()
  452. });
  453. Stream<Object[]> others = Stream.of(
  454. new Object[] {Collections.emptySet(), SQCategory.OTHERS},
  455. new Object[] {ImmutableSet.of("foo", "bar", "acme"), SQCategory.OTHERS});
  456. return Stream.concat(allButOthers, others)
  457. .toArray(Object[][]::new);
  458. }
  459. @Test
  460. public void returns_project_twice_when_hotspot_on_project() {
  461. ComponentDto project = dbTester.components().insertPublicProject();
  462. userSessionRule.registerComponents(project);
  463. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  464. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, project,
  465. t -> t.setLocations(DbIssues.Locations.newBuilder()
  466. .setTextRange(DbCommons.TextRange.newBuilder().build())
  467. .build()));
  468. mockChangelogAndCommentsFormattingContext();
  469. Hotspots.ShowWsResponse response = newRequest(hotspot)
  470. .executeProtobuf(Hotspots.ShowWsResponse.class);
  471. verifyComponent(response.getProject(), project, null, null);
  472. verifyComponent(response.getComponent(), project, null, null);
  473. }
  474. @Test
  475. public void returns_branch_but_no_pullRequest_on_component_and_project_on_non_main_branch() {
  476. ComponentDto project = dbTester.components().insertPublicProject();
  477. ComponentDto branch = dbTester.components().insertProjectBranch(project);
  478. ComponentDto file = dbTester.components().insertComponent(newFileDto(branch));
  479. userSessionRule.registerComponents(project);
  480. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  481. IssueDto hotspot = dbTester.issues().insertHotspot(rule, branch, file,
  482. t -> t.setLocations(DbIssues.Locations.newBuilder()
  483. .setTextRange(DbCommons.TextRange.newBuilder().build())
  484. .build()));
  485. mockChangelogAndCommentsFormattingContext();
  486. Hotspots.ShowWsResponse response = newRequest(hotspot)
  487. .executeProtobuf(Hotspots.ShowWsResponse.class);
  488. verifyComponent(response.getProject(), branch, branch.getBranch(), null);
  489. verifyComponent(response.getComponent(), file, branch.getBranch(), null);
  490. }
  491. @Test
  492. public void returns_pullRequest_but_no_branch_on_component_and_project_on_pullRequest() {
  493. ComponentDto project = dbTester.components().insertPublicProject();
  494. ComponentDto pullRequest = dbTester.components().insertProjectBranch(project,
  495. t -> t.setBranchType(BranchType.PULL_REQUEST));
  496. ComponentDto file = dbTester.components().insertComponent(newFileDto(pullRequest));
  497. userSessionRule.registerComponents(project);
  498. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  499. IssueDto hotspot = dbTester.issues().insertHotspot(rule, pullRequest, file,
  500. t -> t.setLocations(DbIssues.Locations.newBuilder()
  501. .setTextRange(DbCommons.TextRange.newBuilder().build())
  502. .build()));
  503. mockChangelogAndCommentsFormattingContext();
  504. Hotspots.ShowWsResponse response = newRequest(hotspot)
  505. .executeProtobuf(Hotspots.ShowWsResponse.class);
  506. verifyComponent(response.getProject(), pullRequest, null, pullRequest.getPullRequest());
  507. verifyComponent(response.getComponent(), file, null, pullRequest.getPullRequest());
  508. }
  509. @Test
  510. public void returns_hotspot_changelog_and_comments() {
  511. ComponentDto project = dbTester.components().insertPublicProject();
  512. userSessionRule.registerComponents(project);
  513. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  514. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  515. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file,
  516. t -> t.setLocations(DbIssues.Locations.newBuilder()
  517. .setTextRange(DbCommons.TextRange.newBuilder().build())
  518. .build()));
  519. List<Common.Changelog> changelog = IntStream.range(0, 1 + new Random().nextInt(12))
  520. .mapToObj(i -> Common.Changelog.newBuilder().setUser("u" + i).build())
  521. .collect(Collectors.toList());
  522. List<Common.Comment> comments = IntStream.range(0, 1 + new Random().nextInt(12))
  523. .mapToObj(i -> Common.Comment.newBuilder().setKey("u" + i).build())
  524. .collect(Collectors.toList());
  525. FormattingContext formattingContext = mockChangelogAndCommentsFormattingContext();
  526. when(issueChangeSupport.formatChangelog(any(), any())).thenReturn(changelog.stream());
  527. when(issueChangeSupport.formatComments(any(), any(), any())).thenReturn(comments.stream());
  528. Hotspots.ShowWsResponse response = newRequest(hotspot)
  529. .executeProtobuf(Hotspots.ShowWsResponse.class);
  530. assertThat(response.getChangelogList())
  531. .extracting(Common.Changelog::getUser)
  532. .containsExactly(changelog.stream().map(Common.Changelog::getUser).toArray(String[]::new));
  533. assertThat(response.getCommentList())
  534. .extracting(Common.Comment::getKey)
  535. .containsExactly(comments.stream().map(Common.Comment::getKey).toArray(String[]::new));
  536. verify(issueChangeSupport).newFormattingContext(any(DbSession.class),
  537. argThat(new IssueDtoSetArgumentMatcher(hotspot)),
  538. eq(Load.ALL),
  539. eq(Collections.emptySet()), eq(ImmutableSet.of(project, file)));
  540. verify(issueChangeSupport).formatChangelog(argThat(new IssueDtoArgumentMatcher(hotspot)), eq(formattingContext));
  541. verify(issueChangeSupport).formatComments(argThat(new IssueDtoArgumentMatcher(hotspot)), any(Common.Comment.Builder.class), eq(formattingContext));
  542. }
  543. @Test
  544. public void returns_user_details_of_users_from_ChangelogAndComments() {
  545. ComponentDto project = dbTester.components().insertPublicProject();
  546. userSessionRule.registerComponents(project);
  547. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  548. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  549. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file);
  550. FormattingContext formattingContext = mockChangelogAndCommentsFormattingContext();
  551. Set<UserDto> changeLogAndCommentsUsers = IntStream.range(0, 1 + RANDOM.nextInt(14))
  552. .mapToObj(i -> UserTesting.newUserDto())
  553. .collect(Collectors.toSet());
  554. when(formattingContext.getUsers()).thenReturn(changeLogAndCommentsUsers);
  555. Hotspots.ShowWsResponse response = newRequest(hotspot)
  556. .executeProtobuf(Hotspots.ShowWsResponse.class);
  557. assertThat(response.getUsersList())
  558. .extracting(User::getLogin, User::getName, User::getActive)
  559. .containsExactlyInAnyOrder(
  560. changeLogAndCommentsUsers.stream()
  561. .map(t -> tuple(t.getLogin(), t.getName(), t.isActive()))
  562. .toArray(Tuple[]::new));
  563. }
  564. @Test
  565. public void returns_user_of_users_from_ChangelogAndComments_and_assignee_and_author() {
  566. ComponentDto project = dbTester.components().insertPublicProject();
  567. userSessionRule.registerComponents(project);
  568. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  569. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  570. UserDto author = dbTester.users().insertUser();
  571. UserDto assignee = dbTester.users().insertUser();
  572. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file,
  573. t -> t.setAuthorLogin(author.getLogin())
  574. .setAssigneeUuid(assignee.getUuid()));
  575. FormattingContext formattingContext = mockChangelogAndCommentsFormattingContext();
  576. Set<UserDto> changeLogAndCommentsUsers = IntStream.range(0, 1 + RANDOM.nextInt(14))
  577. .mapToObj(i -> UserTesting.newUserDto())
  578. .collect(Collectors.toSet());
  579. when(formattingContext.getUsers()).thenReturn(changeLogAndCommentsUsers);
  580. Hotspots.ShowWsResponse response = newRequest(hotspot)
  581. .executeProtobuf(Hotspots.ShowWsResponse.class);
  582. assertThat(response.getUsersList())
  583. .extracting(User::getLogin, User::getName, User::getActive)
  584. .containsExactlyInAnyOrder(
  585. Stream.concat(
  586. Stream.of(author, assignee),
  587. changeLogAndCommentsUsers.stream())
  588. .map(t -> tuple(t.getLogin(), t.getName(), t.isActive()))
  589. .toArray(Tuple[]::new));
  590. }
  591. @Test
  592. public void do_not_duplicate_user_if_author_assignee_ChangeLogComment_user() {
  593. ComponentDto project = dbTester.components().insertPublicProject();
  594. userSessionRule.registerComponents(project);
  595. RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT);
  596. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  597. UserDto author = dbTester.users().insertUser();
  598. IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file,
  599. t -> t.setAuthorLogin(author.getLogin())
  600. .setAssigneeUuid(author.getUuid()));
  601. FormattingContext formattingContext = mockChangelogAndCommentsFormattingContext();
  602. when(formattingContext.getUsers()).thenReturn(ImmutableSet.of(author));
  603. Hotspots.ShowWsResponse response = newRequest(hotspot)
  604. .executeProtobuf(Hotspots.ShowWsResponse.class);
  605. assertThat(response.getUsersList())
  606. .extracting(User::getLogin, User::getName, User::getActive)
  607. .containsOnly(tuple(author.getLogin(), author.getName(), author.isActive()));
  608. }
  609. private FormattingContext mockChangelogAndCommentsFormattingContext() {
  610. FormattingContext formattingContext = Mockito.mock(FormattingContext.class);
  611. when(issueChangeSupport.newFormattingContext(any(), any(), any(), anySet(), anySet())).thenReturn(formattingContext);
  612. return formattingContext;
  613. }
  614. private void verifyRule(Hotspots.Rule wsRule, RuleDefinitionDto dto) {
  615. assertThat(wsRule.getKey()).isEqualTo(dto.getKey().toString());
  616. assertThat(wsRule.getName()).isEqualTo(dto.getName());
  617. assertThat(wsRule.getSecurityCategory()).isEqualTo(SQCategory.OTHERS.getKey());
  618. assertThat(wsRule.getVulnerabilityProbability()).isEqualTo(SQCategory.OTHERS.getVulnerability().name());
  619. }
  620. private static void verifyComponent(Hotspots.Component wsComponent, ComponentDto dto, @Nullable String branch, @Nullable String pullRequest) {
  621. assertThat(wsComponent.getKey()).isEqualTo(dto.getKey());
  622. if (dto.path() == null) {
  623. assertThat(wsComponent.hasPath()).isFalse();
  624. } else {
  625. assertThat(wsComponent.getPath()).isEqualTo(dto.path());
  626. }
  627. assertThat(wsComponent.getQualifier()).isEqualTo(dto.qualifier());
  628. assertThat(wsComponent.getName()).isEqualTo(dto.name());
  629. assertThat(wsComponent.getLongName()).isEqualTo(dto.longName());
  630. if (branch == null) {
  631. assertThat(wsComponent.hasBranch()).isFalse();
  632. } else {
  633. assertThat(wsComponent.getBranch()).isEqualTo(branch);
  634. }
  635. if (pullRequest == null) {
  636. assertThat(wsComponent.hasPullRequest()).isFalse();
  637. } else {
  638. assertThat(wsComponent.getPullRequest()).isEqualTo(pullRequest);
  639. }
  640. }
  641. private TestRequest newRequest(IssueDto hotspot) {
  642. return actionTester.newRequest()
  643. .setParam("hotspot", hotspot.getKey());
  644. }
  645. private RuleDefinitionDto newRule(RuleType ruleType) {
  646. return newRule(ruleType, t -> {
  647. });
  648. }
  649. private RuleDefinitionDto newRule(RuleType ruleType, Consumer<RuleDefinitionDto> populate) {
  650. RuleDefinitionDto ruleDefinition = RuleTesting.newRule()
  651. .setType(ruleType);
  652. populate.accept(ruleDefinition);
  653. dbTester.rules().insert(ruleDefinition);
  654. return ruleDefinition;
  655. }
  656. private static class IssueDtoSetArgumentMatcher implements ArgumentMatcher<Set<IssueDto>> {
  657. private final IssueDto expected;
  658. private IssueDtoSetArgumentMatcher(IssueDto expected) {
  659. this.expected = expected;
  660. }
  661. @Override
  662. public boolean matches(Set<IssueDto> argument) {
  663. return argument != null && argument.size() == 1 && argument.iterator().next().getKey().equals(expected.getKey());
  664. }
  665. @Override
  666. public String toString() {
  667. return "Set<IssueDto>[" + expected.getKey() + "]";
  668. }
  669. }
  670. private static class IssueDtoArgumentMatcher implements ArgumentMatcher<IssueDto> {
  671. private final IssueDto expected;
  672. private IssueDtoArgumentMatcher(IssueDto expected) {
  673. this.expected = expected;
  674. }
  675. @Override
  676. public boolean matches(IssueDto argument) {
  677. return argument != null && argument.getKey().equals(expected.getKey());
  678. }
  679. @Override
  680. public String toString() {
  681. return "IssueDto[key=" + expected.getKey() + "]";
  682. }
  683. }
  684. }