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.

IssueDaoTest.java 34KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2023 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.db.issue;
  21. import java.util.Arrays;
  22. import java.util.Collection;
  23. import java.util.Collections;
  24. import java.util.List;
  25. import java.util.Set;
  26. import java.util.stream.IntStream;
  27. import java.util.stream.Stream;
  28. import org.junit.Rule;
  29. import org.junit.Test;
  30. import org.sonar.api.rule.RuleKey;
  31. import org.sonar.api.rules.RuleType;
  32. import org.sonar.api.utils.System2;
  33. import org.sonar.db.DbTester;
  34. import org.sonar.db.RowNotFoundException;
  35. import org.sonar.db.component.BranchType;
  36. import org.sonar.db.component.ComponentDto;
  37. import org.sonar.db.component.ComponentTesting;
  38. import org.sonar.db.component.ComponentUpdateDto;
  39. import org.sonar.db.protobuf.DbIssues;
  40. import org.sonar.db.rule.RuleDto;
  41. import org.sonar.db.rule.RuleTesting;
  42. import static java.util.Arrays.asList;
  43. import static java.util.Collections.emptyList;
  44. import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
  45. import static org.apache.commons.lang.math.RandomUtils.nextInt;
  46. import static org.assertj.core.api.Assertions.assertThat;
  47. import static org.assertj.core.api.Assertions.assertThatThrownBy;
  48. import static org.assertj.core.api.AssertionsForClassTypes.tuple;
  49. import static org.junit.Assert.assertFalse;
  50. import static org.sonar.api.issue.Issue.RESOLUTION_FALSE_POSITIVE;
  51. import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
  52. import static org.sonar.api.issue.Issue.RESOLUTION_WONT_FIX;
  53. import static org.sonar.api.issue.Issue.STATUSES;
  54. import static org.sonar.api.issue.Issue.STATUS_CLOSED;
  55. import static org.sonar.api.issue.Issue.STATUS_CONFIRMED;
  56. import static org.sonar.api.issue.Issue.STATUS_OPEN;
  57. import static org.sonar.api.issue.Issue.STATUS_REOPENED;
  58. import static org.sonar.api.issue.Issue.STATUS_RESOLVED;
  59. import static org.sonar.api.issue.Issue.STATUS_REVIEWED;
  60. import static org.sonar.db.component.ComponentTesting.newDirectory;
  61. import static org.sonar.db.component.ComponentTesting.newFileDto;
  62. import static org.sonar.db.component.ComponentTesting.newModuleDto;
  63. import static org.sonar.db.issue.IssueTesting.newCodeReferenceIssue;
  64. import static org.sonar.db.protobuf.DbIssues.MessageFormattingType.CODE;
  65. public class IssueDaoTest {
  66. private static final String PROJECT_UUID = "prj_uuid";
  67. private static final String PROJECT_KEY = "prj_key";
  68. private static final String FILE_UUID = "file_uuid";
  69. private static final String FILE_KEY = "file_key";
  70. private static final RuleDto RULE = RuleTesting.newXooX1();
  71. private static final String ISSUE_KEY1 = "I1";
  72. private static final String ISSUE_KEY2 = "I2";
  73. private static final String TEST_CONTEXT_KEY = "test_context_key";
  74. private static final RuleType[] RULE_TYPES_EXCEPT_HOTSPOT = Stream.of(RuleType.values())
  75. .filter(r -> r != RuleType.SECURITY_HOTSPOT)
  76. .toArray(RuleType[]::new);
  77. private static final DbIssues.MessageFormattings MESSAGE_FORMATTING = DbIssues.MessageFormattings.newBuilder()
  78. .addMessageFormatting(DbIssues.MessageFormatting.newBuilder()
  79. .setStart(0)
  80. .setEnd(4)
  81. .setType(CODE)
  82. .build())
  83. .build();
  84. @Rule
  85. public DbTester db = DbTester.create(System2.INSTANCE);
  86. private final IssueDao underTest = db.getDbClient().issueDao();
  87. @Test
  88. public void selectByKeyOrFail() {
  89. prepareTables();
  90. IssueDto issue = underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1);
  91. assertThat(issue.getKee()).isEqualTo(ISSUE_KEY1);
  92. assertThat(issue.getComponentUuid()).isEqualTo(FILE_UUID);
  93. assertThat(issue.getProjectUuid()).isEqualTo(PROJECT_UUID);
  94. assertThat(issue.getRuleUuid()).isEqualTo(RULE.getUuid());
  95. assertThat(issue.getLanguage()).isEqualTo(RULE.getLanguage());
  96. assertThat(issue.getSeverity()).isEqualTo("BLOCKER");
  97. assertThat(issue.getType()).isEqualTo(2);
  98. assertThat(issue.isManualSeverity()).isFalse();
  99. assertThat(issue.getMessage()).isEqualTo("the message");
  100. assertThat(issue.parseMessageFormattings()).isEqualTo(MESSAGE_FORMATTING);
  101. assertThat(issue.getOptionalRuleDescriptionContextKey()).contains(TEST_CONTEXT_KEY);
  102. assertThat(issue.getLine()).isEqualTo(500);
  103. assertThat(issue.getEffort()).isEqualTo(10L);
  104. assertThat(issue.getGap()).isEqualTo(3.14);
  105. assertThat(issue.getStatus()).isEqualTo("RESOLVED");
  106. assertThat(issue.getResolution()).isEqualTo("FIXED");
  107. assertThat(issue.getChecksum()).isEqualTo("123456789");
  108. assertThat(issue.getAuthorLogin()).isEqualTo("morgan");
  109. assertThat(issue.getAssigneeUuid()).isEqualTo("karadoc");
  110. assertThat(issue.getIssueCreationDate()).isNotNull();
  111. assertThat(issue.getIssueUpdateDate()).isNotNull();
  112. assertThat(issue.getIssueCloseDate()).isNotNull();
  113. assertThat(issue.getCreatedAt()).isEqualTo(1_440_000_000_000L);
  114. assertThat(issue.getUpdatedAt()).isEqualTo(1_440_000_000_000L);
  115. assertThat(issue.getRuleRepo()).isEqualTo(RULE.getRepositoryKey());
  116. assertThat(issue.getRule()).isEqualTo(RULE.getRuleKey());
  117. assertThat(issue.getComponentKey()).isEqualTo(FILE_KEY);
  118. assertThat(issue.getProjectKey()).isEqualTo(PROJECT_KEY);
  119. assertThat(issue.getLocations()).isNull();
  120. assertThat(issue.parseLocations()).isNull();
  121. assertThat(issue.isExternal()).isTrue();
  122. assertFalse(issue.isQuickFixAvailable());
  123. }
  124. @Test
  125. public void selectByKeyOrFail_fails_if_key_not_found() {
  126. assertThatThrownBy(() -> {
  127. prepareTables();
  128. underTest.selectOrFailByKey(db.getSession(), "DOES_NOT_EXIST");
  129. })
  130. .isInstanceOf(RowNotFoundException.class)
  131. .hasMessage("Issue with key 'DOES_NOT_EXIST' does not exist");
  132. }
  133. @Test
  134. public void selectByKeys() {
  135. // contains I1 and I2
  136. prepareTables();
  137. List<IssueDto> issues = underTest.selectByKeys(db.getSession(), asList("I1", "I2", "I3"));
  138. // results are not ordered, so do not use "containsExactly"
  139. assertThat(issues).extracting("key").containsOnly("I1", "I2");
  140. }
  141. @Test
  142. public void selectIssueKeysByComponentUuid() {
  143. // contains I1 and I2
  144. prepareTables();
  145. Set<String> issues = underTest.selectIssueKeysByComponentUuid(db.getSession(), PROJECT_UUID);
  146. // results are not ordered, so do not use "containsExactly"
  147. assertThat(issues).containsOnly("I1", "I2");
  148. }
  149. @Test
  150. public void selectIssueKeysByComponentUuidFiltersAccordingly() {
  151. // contains I1 and I2
  152. prepareTables();
  153. // adds I3
  154. underTest.insert(db.getSession(), newIssueDto("I3")
  155. .setMessage("the message")
  156. .setRuleUuid(RULE.getUuid())
  157. .setComponentUuid(FILE_UUID)
  158. .setStatus("OPEN")
  159. .setProjectUuid(PROJECT_UUID));
  160. // Filter by including repositories
  161. Set<String> issues = underTest.selectIssueKeysByComponentUuid(db.getSession(), PROJECT_UUID, List.of("xoo"),
  162. emptyList(), emptyList(), 1);
  163. // results are not ordered, so do not use "containsExactly"
  164. assertThat(issues).containsOnly("I1", "I3");
  165. // Filter by excluding repositories
  166. issues = underTest.selectIssueKeysByComponentUuid(db.getSession(), PROJECT_UUID, emptyList(), List.of("xoo"),
  167. emptyList(), 1);
  168. assertThat(issues).isEmpty();
  169. // Filter by language
  170. issues = underTest.selectIssueKeysByComponentUuid(db.getSession(), PROJECT_UUID, emptyList(), emptyList(), List.of("xoo"), 1);
  171. assertThat(issues).containsOnly("I1", "I3");
  172. }
  173. @Test
  174. public void selectIssueKeysByComponentUuidAndChangedSinceFiltersAccordingly() {
  175. long t1 = 1_340_000_000_000L;
  176. long t2 = 1_400_000_000_000L;
  177. // contains I1 and I2
  178. prepareTables();
  179. // Insert I3, I4, where t1 < t2
  180. IntStream.range(3, 5).forEach(i -> underTest.insert(db.getSession(), newIssueDto("I" + i).setUpdatedAt(t1)));
  181. // Filter by including repositories
  182. Set<String> issues = underTest.selectIssueKeysByComponentUuidAndChangedSinceDate(db.getSession(), PROJECT_UUID, t2, List.of("xoo"),
  183. emptyList(), emptyList(), 1);
  184. // results are not ordered, so do not use "containsExactly"
  185. assertThat(issues).contains("I1");
  186. // Filter by excluding repositories
  187. issues = underTest.selectIssueKeysByComponentUuidAndChangedSinceDate(db.getSession(), PROJECT_UUID, t2,
  188. emptyList(), List.of("xoo"), emptyList(), 1);
  189. assertThat(issues).isEmpty();
  190. // Filter by language
  191. issues = underTest.selectIssueKeysByComponentUuidAndChangedSinceDate(db.getSession(), PROJECT_UUID, t2, emptyList(),
  192. emptyList(), List.of("xoo"), 1);
  193. assertThat(issues).contains("I1");
  194. }
  195. @Test
  196. public void selectByBranch() {
  197. long updatedAt = 1_340_000_000_000L;
  198. long changedSince = 1_000_000_000_000L;
  199. ComponentDto project = db.components().insertPrivateProject();
  200. RuleDto rule = db.rules().insert(r -> r.setRepositoryKey("java").setLanguage("java"));
  201. ComponentDto branchA = db.components().insertProjectBranch(project, b -> b.setKey("branchA"));
  202. ComponentDto fileA = db.components().insertComponent(newFileDto(branchA));
  203. List<String> statusesA = List.of(STATUS_OPEN, STATUS_REVIEWED, STATUS_CLOSED, STATUS_RESOLVED);
  204. IntStream.range(0, statusesA.size()).forEach(i -> insertBranchIssue(branchA, fileA, rule, "A" + i, statusesA.get(i), updatedAt));
  205. ComponentDto branchB = db.components().insertProjectBranch(project, b -> b.setKey("branchB"));
  206. ComponentDto fileB = db.components().insertComponent(newFileDto(branchB));
  207. List<String> statusesB = List.of(STATUS_OPEN, STATUS_RESOLVED);
  208. IntStream.range(0, statusesB.size()).forEach(i -> insertBranchIssue(branchB, fileB, rule, "B" + i, statusesB.get(i), updatedAt));
  209. List<IssueDto> branchAIssuesA1 = underTest.selectByBranch(db.getSession(), Set.of("issueA0", "issueA1", "issueA3"),
  210. buildSelectByBranchQuery(branchA, "java", false, changedSince));
  211. assertThat(branchAIssuesA1)
  212. .extracting(IssueDto::getKey, IssueDto::getStatus)
  213. .containsExactlyInAnyOrder(
  214. tuple("issueA0", STATUS_OPEN),
  215. tuple("issueA1", STATUS_REVIEWED),
  216. tuple("issueA3", STATUS_RESOLVED));
  217. assertThat(branchAIssuesA1.get(0))
  218. .extracting(IssueDto::getMessage, IssueDto::parseMessageFormattings)
  219. .containsOnly("message", MESSAGE_FORMATTING);
  220. List<IssueDto> branchAIssuesA2 = underTest.selectByBranch(db.getSession(), Set.of("issueA0", "issueA1", "issueA3"),
  221. buildSelectByBranchQuery(branchA, "java", true, changedSince));
  222. assertThat(branchAIssuesA2)
  223. .extracting(IssueDto::getKey, IssueDto::getStatus)
  224. .containsExactlyInAnyOrder(tuple("issueA0", STATUS_OPEN),
  225. tuple("issueA1", STATUS_REVIEWED),
  226. tuple("issueA3", STATUS_RESOLVED));
  227. List<IssueDto> branchBIssuesB1 = underTest.selectByBranch(db.getSession(), Set.of("issueB0", "issueB1"), buildSelectByBranchQuery(branchB, "java", false, changedSince));
  228. assertThat(branchBIssuesB1)
  229. .extracting(IssueDto::getKey, IssueDto::getStatus)
  230. .containsExactlyInAnyOrder(
  231. tuple("issueB0", STATUS_OPEN),
  232. tuple("issueB1", STATUS_RESOLVED));
  233. List<IssueDto> branchBIssuesB2 = underTest.selectByBranch(db.getSession(), Set.of("issueB0", "issueB1"), buildSelectByBranchQuery(branchB, "java", true, changedSince));
  234. assertThat(branchBIssuesB2)
  235. .extracting(IssueDto::getKey, IssueDto::getStatus)
  236. .containsExactlyInAnyOrder(tuple("issueB0", STATUS_OPEN),
  237. tuple("issueB1", STATUS_RESOLVED));
  238. }
  239. @Test
  240. public void selectByComponentUuidPaginated() {
  241. // contains I1 and I2
  242. prepareTables();
  243. List<IssueDto> issues = underTest.selectByComponentUuidPaginated(db.getSession(), PROJECT_UUID, 1);
  244. // results are not ordered, so do not use "containsExactly"
  245. assertThat(issues).extracting("key").containsOnly("I1", "I2");
  246. }
  247. @Test
  248. public void scrollNonClosedByComponentUuid() {
  249. RuleDto rule = db.rules().insert();
  250. ComponentDto project = db.components().insertPrivateProject();
  251. ComponentDto file = db.components().insertComponent(newFileDto(project));
  252. IssueDto openIssue1OnFile = db.issues().insert(rule, project, file, i -> i.setStatus("OPEN").setResolution(null).setType(randomRuleTypeExceptHotspot()));
  253. IssueDto openIssue2OnFile = db.issues().insert(rule, project, file, i -> i.setStatus("OPEN").setResolution(null).setType(randomRuleTypeExceptHotspot()));
  254. IssueDto closedIssueOnFile = db.issues().insert(rule, project, file, i -> i.setStatus("CLOSED").setResolution("FIXED").setType(randomRuleTypeExceptHotspot()));
  255. IssueDto openIssueOnProject = db.issues().insert(rule, project, project, i -> i.setStatus("OPEN").setResolution(null).setType(randomRuleTypeExceptHotspot()));
  256. IssueDto securityHotspot = db.issues().insert(rule, project, file, i -> i.setType(RuleType.SECURITY_HOTSPOT));
  257. RuleDto external = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto.setIsExternal(true));
  258. IssueDto issueFromExteralruleOnFile = db.issues().insert(external, project, file, i -> i.setKee("ON_FILE_FROM_EXTERNAL").setType(randomRuleTypeExceptHotspot()));
  259. assertThat(underTest.selectNonClosedByComponentUuidExcludingExternalsAndSecurityHotspots(db.getSession(), file.uuid()))
  260. .extracting(IssueDto::getKey)
  261. .containsExactlyInAnyOrder(Arrays.stream(new IssueDto[] {openIssue1OnFile, openIssue2OnFile}).map(IssueDto::getKey).toArray(String[]::new));
  262. assertThat(underTest.selectNonClosedByComponentUuidExcludingExternalsAndSecurityHotspots(db.getSession(), project.uuid()))
  263. .extracting(IssueDto::getKey)
  264. .containsExactlyInAnyOrder(Arrays.stream(new IssueDto[] {openIssueOnProject}).map(IssueDto::getKey).toArray(String[]::new));
  265. assertThat(underTest.selectNonClosedByComponentUuidExcludingExternalsAndSecurityHotspots(db.getSession(), "does_not_exist")).isEmpty();
  266. }
  267. @Test
  268. public void scrollNonClosedByModuleOrProject() {
  269. RuleDto rule = db.rules().insert();
  270. ComponentDto project = db.components().insertPrivateProject();
  271. ComponentDto anotherProject = db.components().insertPrivateProject();
  272. ComponentDto module = db.components().insertComponent(newModuleDto(project));
  273. ComponentDto file = db.components().insertComponent(newFileDto(module));
  274. IssueDto openIssue1OnFile = db.issues().insert(rule, project, file, i -> i.setStatus("OPEN").setResolution(null).setType(randomRuleTypeExceptHotspot()));
  275. IssueDto openIssue2OnFile = db.issues().insert(rule, project, file, i -> i.setStatus("OPEN").setResolution(null).setType(randomRuleTypeExceptHotspot()));
  276. IssueDto closedIssueOnFile = db.issues().insert(rule, project, file, i -> i.setStatus("CLOSED").setResolution("FIXED").setType(randomRuleTypeExceptHotspot()));
  277. IssueDto openIssueOnModule = db.issues().insert(rule, project, module, i -> i.setStatus("OPEN").setResolution(null).setType(randomRuleTypeExceptHotspot()));
  278. IssueDto openIssueOnProject = db.issues().insert(rule, project, project, i -> i.setStatus("OPEN").setResolution(null).setType(randomRuleTypeExceptHotspot()));
  279. IssueDto openIssueOnAnotherProject = db.issues().insert(rule, anotherProject, anotherProject,
  280. i -> i.setStatus("OPEN").setResolution(null).setType(randomRuleTypeExceptHotspot()));
  281. IssueDto securityHotspot = db.issues().insert(rule, project, file, i -> i.setType(RuleType.SECURITY_HOTSPOT));
  282. RuleDto external = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto.setIsExternal(true));
  283. IssueDto issueFromExteralruleOnFile = db.issues().insert(external, project, file, i -> i.setKee("ON_FILE_FROM_EXTERNAL").setType(randomRuleTypeExceptHotspot()));
  284. assertThat(underTest.selectNonClosedByModuleOrProjectExcludingExternalsAndSecurityHotspots(db.getSession(), project))
  285. .extracting(IssueDto::getKey)
  286. .containsExactlyInAnyOrder(
  287. Arrays.stream(new IssueDto[] {openIssue1OnFile, openIssue2OnFile, openIssueOnModule, openIssueOnProject}).map(IssueDto::getKey).toArray(String[]::new));
  288. assertThat(underTest.selectNonClosedByModuleOrProjectExcludingExternalsAndSecurityHotspots(db.getSession(), module))
  289. .extracting(IssueDto::getKey)
  290. .containsExactlyInAnyOrder(Arrays.stream(new IssueDto[] {openIssue1OnFile, openIssue2OnFile, openIssueOnModule}).map(IssueDto::getKey).toArray(String[]::new));
  291. ComponentDto notPersisted = ComponentTesting.newPrivateProjectDto();
  292. assertThat(underTest.selectNonClosedByModuleOrProjectExcludingExternalsAndSecurityHotspots(db.getSession(), notPersisted)).isEmpty();
  293. }
  294. @Test
  295. public void selectOpenByComponentUuid() {
  296. RuleDto rule = db.rules().insert();
  297. ComponentDto project = db.components().insertPublicProject();
  298. ComponentDto projectBranch = db.components().insertProjectBranch(project,
  299. b -> b.setKey("feature/foo")
  300. .setBranchType(BranchType.BRANCH));
  301. ComponentDto file = db.components().insertComponent(newFileDto(projectBranch));
  302. IssueDto openIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(STATUS_OPEN).setResolution(null));
  303. IssueDto closedIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(STATUS_CLOSED).setResolution(RESOLUTION_FIXED));
  304. IssueDto reopenedIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(STATUS_REOPENED).setResolution(null));
  305. IssueDto confirmedIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(STATUS_CONFIRMED).setResolution(null));
  306. IssueDto wontfixIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_WONT_FIX));
  307. IssueDto fpIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_FALSE_POSITIVE));
  308. assertThat(underTest.selectOpenByComponentUuids(db.getSession(), Collections.singletonList(file.uuid())))
  309. .extracting("kee")
  310. .containsOnly(openIssue.getKey(), reopenedIssue.getKey(), confirmedIssue.getKey(), wontfixIssue.getKey(), fpIssue.getKey());
  311. }
  312. @Test
  313. public void selectOpenByComponentUuid_should_correctly_map_required_fields() {
  314. RuleDto rule = db.rules().insert();
  315. ComponentDto project = db.components().insertPublicProject();
  316. ComponentDto projectBranch = db.components().insertProjectBranch(project,
  317. b -> b.setKey("feature/foo")
  318. .setBranchType(BranchType.BRANCH));
  319. ComponentDto file = db.components().insertComponent(newFileDto(projectBranch));
  320. IssueDto fpIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus("RESOLVED").setResolution("FALSE-POSITIVE"));
  321. PrIssueDto fp = underTest.selectOpenByComponentUuids(db.getSession(), Collections.singletonList(file.uuid())).get(0);
  322. assertThat(fp.getLine()).isEqualTo(fpIssue.getLine());
  323. assertThat(fp.getMessage()).isEqualTo(fpIssue.getMessage());
  324. assertThat(fp.getChecksum()).isEqualTo(fpIssue.getChecksum());
  325. assertThat(fp.getRuleKey()).isEqualTo(fpIssue.getRuleKey());
  326. assertThat(fp.getStatus()).isEqualTo(fpIssue.getStatus());
  327. assertThat(fp.getLine()).isNotNull();
  328. assertThat(fp.getLine()).isNotZero();
  329. assertThat(fp.getMessage()).isNotNull();
  330. assertThat(fp.getChecksum()).isNotNull();
  331. assertThat(fp.getChecksum()).isNotEmpty();
  332. assertThat(fp.getRuleKey()).isNotNull();
  333. assertThat(fp.getStatus()).isNotNull();
  334. assertThat(fp.getBranchKey()).isEqualTo("feature/foo");
  335. assertThat(fp.getIssueUpdateDate()).isNotNull();
  336. }
  337. @Test
  338. public void test_selectIssueGroupsByComponent_on_component_without_issues() {
  339. ComponentDto project = db.components().insertPublicProject();
  340. ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
  341. Collection<IssueGroupDto> groups = underTest.selectIssueGroupsByComponent(db.getSession(), file, 1_000L);
  342. assertThat(groups).isEmpty();
  343. }
  344. @Test
  345. public void selectByKey_givenOneIssueWithQuickFix_selectOneIssueWithQuickFix() {
  346. prepareIssuesComponent();
  347. underTest.insert(db.getSession(), newIssueDto(ISSUE_KEY1)
  348. .setMessage("the message")
  349. .setRuleUuid(RULE.getUuid())
  350. .setComponentUuid(FILE_UUID)
  351. .setProjectUuid(PROJECT_UUID)
  352. .setQuickFixAvailable(true));
  353. IssueDto issue = underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1);
  354. assertThat(issue.getKee()).isEqualTo(ISSUE_KEY1);
  355. assertThat(issue.isQuickFixAvailable()).isTrue();
  356. }
  357. @Test
  358. public void selectByKey_givenOneIssueWithoutQuickFix_selectOneIssueWithoutQuickFix() {
  359. prepareIssuesComponent();
  360. String issueKey = ISSUE_KEY1;
  361. underTest.insert(db.getSession(), createIssueWithKey(issueKey));
  362. IssueDto issue = underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1);
  363. assertThat(issue.getKee()).isEqualTo(ISSUE_KEY1);
  364. assertThat(issue.isQuickFixAvailable()).isFalse();
  365. }
  366. @Test
  367. public void selectIssueGroupsByComponent_on_file() {
  368. ComponentDto project = db.components().insertPublicProject();
  369. ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
  370. RuleDto rule = db.rules().insert();
  371. IssueDto fpBug = db.issues().insert(rule, project, file,
  372. i -> i.setStatus("RESOLVED").setResolution("FALSE-POSITIVE").setSeverity("MAJOR").setType(RuleType.BUG).setIssueCreationTime(1_500L));
  373. IssueDto criticalBug1 = db.issues().insert(rule, project, file,
  374. i -> i.setStatus("OPEN").setResolution(null).setSeverity("CRITICAL").setType(RuleType.BUG).setIssueCreationTime(1_600L));
  375. IssueDto criticalBug2 = db.issues().insert(rule, project, file,
  376. i -> i.setStatus("OPEN").setResolution(null).setSeverity("CRITICAL").setType(RuleType.BUG).setIssueCreationTime(1_700L));
  377. // closed issues are ignored
  378. IssueDto closed = db.issues().insert(rule, project, file,
  379. i -> i.setStatus("CLOSED").setResolution("REMOVED").setSeverity("CRITICAL").setType(RuleType.BUG).setIssueCreationTime(1_700L));
  380. Collection<IssueGroupDto> result = underTest.selectIssueGroupsByComponent(db.getSession(), file, 1_000L);
  381. assertThat(result.stream().mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(3);
  382. assertThat(result.stream().filter(g -> g.getRuleType() == RuleType.BUG.getDbConstant()).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(3);
  383. assertThat(result.stream().filter(g -> g.getRuleType() == RuleType.CODE_SMELL.getDbConstant()).mapToLong(IssueGroupDto::getCount).sum()).isZero();
  384. assertThat(result.stream().filter(g -> g.getRuleType() == RuleType.VULNERABILITY.getDbConstant()).mapToLong(IssueGroupDto::getCount).sum()).isZero();
  385. assertThat(result.stream().filter(g -> g.getSeverity().equals("CRITICAL")).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(2);
  386. assertThat(result.stream().filter(g -> g.getSeverity().equals("MAJOR")).mapToLong(IssueGroupDto::getCount).sum()).isOne();
  387. assertThat(result.stream().filter(g -> g.getSeverity().equals("MINOR")).mapToLong(IssueGroupDto::getCount).sum()).isZero();
  388. assertThat(result.stream().filter(g -> g.getStatus().equals("OPEN")).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(2);
  389. assertThat(result.stream().filter(g -> g.getStatus().equals("RESOLVED")).mapToLong(IssueGroupDto::getCount).sum()).isOne();
  390. assertThat(result.stream().filter(g -> g.getStatus().equals("CLOSED")).mapToLong(IssueGroupDto::getCount).sum()).isZero();
  391. assertThat(result.stream().filter(g -> "FALSE-POSITIVE".equals(g.getResolution())).mapToLong(IssueGroupDto::getCount).sum()).isOne();
  392. assertThat(result.stream().filter(g -> g.getResolution() == null).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(2);
  393. assertThat(result.stream().filter(IssueGroupDto::isInLeak).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(3);
  394. assertThat(result.stream().filter(g -> !g.isInLeak()).mapToLong(IssueGroupDto::getCount).sum()).isZero();
  395. // test leak
  396. result = underTest.selectIssueGroupsByComponent(db.getSession(), file, 999_999_999L);
  397. assertThat(result.stream().filter(IssueGroupDto::isInLeak).mapToLong(IssueGroupDto::getCount).sum()).isZero();
  398. assertThat(result.stream().filter(g -> !g.isInLeak()).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(3);
  399. // test leak using exact creation time of criticalBug2 issue
  400. result = underTest.selectIssueGroupsByComponent(db.getSession(), file, criticalBug2.getIssueCreationTime());
  401. assertThat(result.stream().filter(IssueGroupDto::isInLeak).mapToLong(IssueGroupDto::getCount).sum()).isZero();
  402. assertThat(result.stream().filter(g -> !g.isInLeak()).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(3);
  403. }
  404. @Test
  405. public void selectGroupsOfComponentTreeOnLeak_on_file_new_code_reference_branch() {
  406. ComponentDto project = db.components().insertPublicProject();
  407. ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
  408. RuleDto rule = db.rules().insert();
  409. IssueDto fpBug = db.issues().insert(rule, project, file,
  410. i -> i.setStatus("RESOLVED").setResolution("FALSE-POSITIVE").setSeverity("MAJOR").setType(RuleType.BUG));
  411. IssueDto criticalBug1 = db.issues().insert(rule, project, file,
  412. i -> i.setStatus("OPEN").setResolution(null).setSeverity("CRITICAL").setType(RuleType.BUG));
  413. IssueDto criticalBug2 = db.issues().insert(rule, project, file,
  414. i -> i.setStatus("OPEN").setResolution(null).setSeverity("CRITICAL").setType(RuleType.BUG));
  415. db.issues().insert(rule, project, file,
  416. i -> i.setStatus("OPEN").setResolution(null).setSeverity("CRITICAL").setType(RuleType.BUG));
  417. // two issues part of new code period on reference branch
  418. db.issues().insertNewCodeReferenceIssue(fpBug);
  419. db.issues().insertNewCodeReferenceIssue(criticalBug1);
  420. db.issues().insertNewCodeReferenceIssue(criticalBug2);
  421. Collection<IssueGroupDto> result = underTest.selectIssueGroupsByComponent(db.getSession(), file, -1);
  422. assertThat(result.stream().mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(4);
  423. assertThat(result.stream().filter(g -> g.getRuleType() == RuleType.BUG.getDbConstant()).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(4);
  424. assertThat(result.stream().filter(g -> g.getRuleType() == RuleType.CODE_SMELL.getDbConstant()).mapToLong(IssueGroupDto::getCount).sum()).isZero();
  425. assertThat(result.stream().filter(g -> g.getRuleType() == RuleType.VULNERABILITY.getDbConstant()).mapToLong(IssueGroupDto::getCount).sum()).isZero();
  426. assertThat(result.stream().filter(g -> g.getSeverity().equals("CRITICAL")).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(3);
  427. assertThat(result.stream().filter(g -> g.getSeverity().equals("MAJOR")).mapToLong(IssueGroupDto::getCount).sum()).isOne();
  428. assertThat(result.stream().filter(g -> g.getSeverity().equals("MINOR")).mapToLong(IssueGroupDto::getCount).sum()).isZero();
  429. assertThat(result.stream().filter(g -> g.getStatus().equals("OPEN")).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(3);
  430. assertThat(result.stream().filter(g -> g.getStatus().equals("RESOLVED")).mapToLong(IssueGroupDto::getCount).sum()).isOne();
  431. assertThat(result.stream().filter(g -> g.getStatus().equals("CLOSED")).mapToLong(IssueGroupDto::getCount).sum()).isZero();
  432. assertThat(result.stream().filter(g -> "FALSE-POSITIVE".equals(g.getResolution())).mapToLong(IssueGroupDto::getCount).sum()).isOne();
  433. assertThat(result.stream().filter(g -> g.getResolution() == null).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(3);
  434. assertThat(result.stream().filter(IssueGroupDto::isInLeak).mapToLong(IssueGroupDto::getCount).sum()).isEqualTo(3);
  435. assertThat(result.stream().filter(g -> !g.isInLeak()).mapToLong(IssueGroupDto::getCount).sum()).isOne();
  436. }
  437. @Test
  438. public void selectByKey_givenOneIssueNewOnReferenceBranch_selectOneIssueWithNewOnReferenceBranch() {
  439. prepareIssuesComponent();
  440. underTest.insert(db.getSession(), newIssueDto(ISSUE_KEY1)
  441. .setMessage("the message")
  442. .setRuleUuid(RULE.getUuid())
  443. .setComponentUuid(FILE_UUID)
  444. .setProjectUuid(PROJECT_UUID)
  445. .setQuickFixAvailable(true));
  446. underTest.insert(db.getSession(), newIssueDto(ISSUE_KEY2)
  447. .setMessage("the message")
  448. .setRuleUuid(RULE.getUuid())
  449. .setComponentUuid(FILE_UUID)
  450. .setProjectUuid(PROJECT_UUID)
  451. .setQuickFixAvailable(true));
  452. IssueDto issue1 = underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1);
  453. IssueDto issue2 = underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY2);
  454. assertThat(issue1.isNewCodeReferenceIssue()).isFalse();
  455. assertThat(issue2.isNewCodeReferenceIssue()).isFalse();
  456. underTest.insertAsNewCodeOnReferenceBranch(db.getSession(), newCodeReferenceIssue(issue1));
  457. assertThat(underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1).isNewCodeReferenceIssue()).isTrue();
  458. assertThat(underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY2).isNewCodeReferenceIssue()).isFalse();
  459. underTest.deleteAsNewCodeOnReferenceBranch(db.getSession(), ISSUE_KEY1);
  460. assertThat(underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1).isNewCodeReferenceIssue()).isFalse();
  461. }
  462. @Test
  463. public void selectByKey_givenOneIssueWithoutRuleDescriptionContextKey_returnsEmptyOptional() {
  464. prepareIssuesComponent();
  465. underTest.insert(db.getSession(), createIssueWithKey(ISSUE_KEY1)
  466. .setRuleDescriptionContextKey(null));
  467. IssueDto issue1 = underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1);
  468. assertThat(issue1.getOptionalRuleDescriptionContextKey()).isEmpty();
  469. }
  470. @Test
  471. public void selectByKey_givenOneIssueWithRuleDescriptionContextKey_returnsContextKey() {
  472. prepareIssuesComponent();
  473. underTest.insert(db.getSession(), createIssueWithKey(ISSUE_KEY1)
  474. .setRuleDescriptionContextKey(TEST_CONTEXT_KEY));
  475. IssueDto issue1 = underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1);
  476. assertThat(issue1.getOptionalRuleDescriptionContextKey()).contains(TEST_CONTEXT_KEY);
  477. }
  478. @Test
  479. public void update_whenUpdatingRuleDescriptionContextKeyToNull_returnsEmptyContextKey() {
  480. prepareIssuesComponent();
  481. IssueDto issue = createIssueWithKey(ISSUE_KEY1).setRuleDescriptionContextKey(TEST_CONTEXT_KEY);
  482. underTest.insert(db.getSession(), issue);
  483. issue.setRuleDescriptionContextKey(null);
  484. underTest.update(db.getSession(), issue);
  485. issue = underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1);
  486. assertThat(issue.getOptionalRuleDescriptionContextKey()).isEmpty();
  487. }
  488. @Test
  489. public void update_whenUpdatingRuleDescriptionContextKeyToNotNull_returnsContextKey() {
  490. prepareIssuesComponent();
  491. IssueDto issue = createIssueWithKey(ISSUE_KEY1).setRuleDescriptionContextKey(null);
  492. underTest.insert(db.getSession(), issue);
  493. issue.setRuleDescriptionContextKey(TEST_CONTEXT_KEY);
  494. underTest.update(db.getSession(), issue);
  495. issue = underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1);
  496. assertThat(issue.getOptionalRuleDescriptionContextKey()).contains(TEST_CONTEXT_KEY);
  497. }
  498. @Test
  499. public void update_givenOneIssueWithoutRuleDescriptionContextKey_returnsContextKey() {
  500. prepareIssuesComponent();
  501. IssueDto issue = createIssueWithKey(ISSUE_KEY1).setRuleDescriptionContextKey(TEST_CONTEXT_KEY);
  502. underTest.insert(db.getSession(), issue);
  503. issue.setRuleDescriptionContextKey(null);
  504. underTest.update(db.getSession(), issue);
  505. issue = underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1);
  506. assertThat(issue.getOptionalRuleDescriptionContextKey()).isEmpty();
  507. issue.setRuleDescriptionContextKey(TEST_CONTEXT_KEY);
  508. underTest.update(db.getSession(), issue);
  509. issue = underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1);
  510. assertThat(issue.getOptionalRuleDescriptionContextKey()).contains(TEST_CONTEXT_KEY);
  511. }
  512. private static IssueDto createIssueWithKey(String issueKey) {
  513. return newIssueDto(issueKey)
  514. .setMessage("the message")
  515. .setRuleUuid(RULE.getUuid())
  516. .setComponentUuid(FILE_UUID)
  517. .setProjectUuid(PROJECT_UUID)
  518. .setQuickFixAvailable(false);
  519. }
  520. private static IssueDto newIssueDto(String key) {
  521. IssueDto dto = new IssueDto();
  522. dto.setComponent(new ComponentDto().setKey("struts:Action").setUuid("component-uuid"));
  523. dto.setProject(new ComponentDto().setKey("struts").setUuid("project-uuid"));
  524. dto.setRule(RuleTesting.newRule(RuleKey.of("java", "S001")).setUuid("uuid-200"));
  525. dto.setKee(key);
  526. dto.setType(2);
  527. dto.setLine(500);
  528. dto.setGap(3.14);
  529. dto.setEffort(10L);
  530. dto.setResolution("FIXED");
  531. dto.setStatus("RESOLVED");
  532. dto.setSeverity("BLOCKER");
  533. dto.setAuthorLogin("morgan");
  534. dto.setAssigneeUuid("karadoc");
  535. dto.setChecksum("123456789");
  536. dto.setMessage("the message");
  537. dto.setMessageFormattings(MESSAGE_FORMATTING);
  538. dto.setRuleDescriptionContextKey(TEST_CONTEXT_KEY);
  539. dto.setCreatedAt(1_440_000_000_000L);
  540. dto.setUpdatedAt(1_440_000_000_000L);
  541. dto.setIssueCreationTime(1_450_000_000_000L);
  542. dto.setIssueUpdateTime(1_450_000_000_000L);
  543. dto.setIssueCloseTime(1_450_000_000_000L);
  544. return dto;
  545. }
  546. private void prepareIssuesComponent() {
  547. db.rules().insert(RULE.setIsExternal(true));
  548. ComponentDto projectDto = db.components().insertPrivateProject(t -> t.setUuid(PROJECT_UUID).setKey(PROJECT_KEY));
  549. db.components().insertComponent(newFileDto(projectDto).setUuid(FILE_UUID).setKey(FILE_KEY));
  550. }
  551. private void prepareTables() {
  552. prepareIssuesComponent();
  553. underTest.insert(db.getSession(), newIssueDto(ISSUE_KEY1)
  554. .setMessage("the message")
  555. .setRuleUuid(RULE.getUuid())
  556. .setComponentUuid(FILE_UUID)
  557. .setProjectUuid(PROJECT_UUID));
  558. underTest.insert(db.getSession(), newIssueDto(ISSUE_KEY2)
  559. .setRuleUuid(RULE.getUuid())
  560. .setComponentUuid(FILE_UUID)
  561. .setStatus("CLOSED")
  562. .setProjectUuid(PROJECT_UUID));
  563. db.getSession().commit();
  564. }
  565. private static RuleType randomRuleTypeExceptHotspot() {
  566. return RULE_TYPES_EXCEPT_HOTSPOT[nextInt(RULE_TYPES_EXCEPT_HOTSPOT.length)];
  567. }
  568. private void insertBranchIssue(ComponentDto branch, ComponentDto file, RuleDto rule, String id, String status, Long updateAt) {
  569. db.issues().insert(rule, branch, file, i -> i.setKee("issue" + id).setStatus(status).setUpdatedAt(updateAt).setType(randomRuleTypeExceptHotspot())
  570. .setMessage("message")
  571. .setMessageFormattings(MESSAGE_FORMATTING));
  572. }
  573. private static IssueQueryParams buildSelectByBranchQuery(ComponentDto branch, String language, boolean resolvedOnly, Long changedSince) {
  574. return new IssueQueryParams(branch.uuid(), List.of(language), List.of(), List.of(), resolvedOnly, changedSince);
  575. }
  576. }