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.

AsyncIssueIndexingImplTest.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.server.issue.index;
  21. import java.util.ArrayList;
  22. import java.util.Collection;
  23. import java.util.HashMap;
  24. import java.util.HashSet;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.Optional;
  28. import org.junit.Before;
  29. import org.junit.Rule;
  30. import org.junit.Test;
  31. import org.mockito.ArgumentCaptor;
  32. import org.slf4j.event.Level;
  33. import org.sonar.api.testfixtures.log.LogTester;
  34. import org.sonar.api.utils.System2;
  35. import org.sonar.ce.queue.CeQueue;
  36. import org.sonar.ce.queue.CeTaskSubmit;
  37. import org.sonar.core.ce.CeTaskCharacteristics;
  38. import org.sonar.core.util.SequenceUuidFactory;
  39. import org.sonar.core.util.UuidFactory;
  40. import org.sonar.db.DbClient;
  41. import org.sonar.db.DbTester;
  42. import org.sonar.db.ce.CeActivityDto;
  43. import org.sonar.db.ce.CeActivityDto.Status;
  44. import org.sonar.db.ce.CeQueueDto;
  45. import org.sonar.db.component.BranchDto;
  46. import org.sonar.db.component.SnapshotDto;
  47. import org.sonar.db.project.ProjectDto;
  48. import static org.assertj.core.api.Assertions.assertThat;
  49. import static org.assertj.core.api.Assertions.assertThatCode;
  50. import static org.assertj.core.api.Assertions.tuple;
  51. import static org.mockito.ArgumentMatchers.anyCollection;
  52. import static org.mockito.Mockito.mock;
  53. import static org.mockito.Mockito.times;
  54. import static org.mockito.Mockito.verify;
  55. import static org.mockito.Mockito.when;
  56. import static org.sonar.core.ce.CeTaskCharacteristics.BRANCH_TYPE;
  57. import static org.sonar.db.ce.CeTaskTypes.BRANCH_ISSUE_SYNC;
  58. import static org.sonar.db.ce.CeTaskTypes.REPORT;
  59. import static org.sonar.db.component.BranchType.BRANCH;
  60. import static org.sonar.db.component.BranchType.PULL_REQUEST;
  61. import static org.sonar.db.component.SnapshotDto.STATUS_PROCESSED;
  62. public class AsyncIssueIndexingImplTest {
  63. @Rule
  64. public DbTester dbTester = DbTester.create(System2.INSTANCE);
  65. @Rule
  66. public LogTester logTester = new LogTester();
  67. private final DbClient dbClient = dbTester.getDbClient();
  68. private final CeQueue ceQueue = mock(CeQueue.class);
  69. private final UuidFactory uuidFactory = new SequenceUuidFactory();
  70. private final AsyncIssueIndexingImpl underTest = new AsyncIssueIndexingImpl(ceQueue, dbClient);
  71. @Before
  72. public void before() {
  73. when(ceQueue.prepareSubmit()).thenReturn(new CeTaskSubmit.Builder(uuidFactory.create()));
  74. }
  75. @Test
  76. public void triggerOnIndexCreation() {
  77. BranchDto dto = new BranchDto()
  78. .setBranchType(BRANCH)
  79. .setKey("branchName")
  80. .setUuid("branch_uuid")
  81. .setProjectUuid("project_uuid")
  82. .setIsMain(false);
  83. dbClient.branchDao().insert(dbTester.getSession(), dto);
  84. dbTester.commit();
  85. underTest.triggerOnIndexCreation();
  86. Optional<BranchDto> branch = dbClient.branchDao().selectByUuid(dbTester.getSession(), "branch_uuid");
  87. assertThat(branch).isPresent();
  88. assertThat(branch.get().isNeedIssueSync()).isTrue();
  89. verify(ceQueue, times(1)).prepareSubmit();
  90. verify(ceQueue, times(1)).massSubmit(anyCollection());
  91. assertThat(logTester.logs(Level.INFO))
  92. .contains("1 branch found in need of issue sync.");
  93. }
  94. @Test
  95. public void triggerForProject() {
  96. ProjectDto projectDto = dbTester.components().insertPrivateProject().getProjectDto();
  97. BranchDto dto = new BranchDto()
  98. .setBranchType(BRANCH)
  99. .setKey("branchName")
  100. .setUuid("branch_uuid")
  101. .setProjectUuid(projectDto.getUuid())
  102. .setIsMain(true);
  103. dbTester.components().insertProjectBranch(projectDto, dto);
  104. underTest.triggerForProject(projectDto.getUuid());
  105. Optional<BranchDto> branch = dbClient.branchDao().selectByUuid(dbTester.getSession(), "branch_uuid");
  106. assertThat(branch).isPresent();
  107. assertThat(branch.get().isNeedIssueSync()).isTrue();
  108. verify(ceQueue, times(2)).prepareSubmit();
  109. verify(ceQueue, times(1)).massSubmit(anyCollection());
  110. assertThat(logTester.logs(Level.INFO))
  111. .contains("2 branch(es) found in need of issue sync for project.");
  112. }
  113. @Test
  114. public void triggerOnIndexCreation_no_branch() {
  115. underTest.triggerOnIndexCreation();
  116. assertThat(logTester.logs(Level.INFO)).contains("0 branch found in need of issue sync.");
  117. }
  118. @Test
  119. public void triggerForProject_no_branch() {
  120. underTest.triggerForProject("some-random-uuid");
  121. assertThat(logTester.logs(Level.INFO)).contains("0 branch(es) found in need of issue sync for project.");
  122. }
  123. @Test
  124. public void remove_existing_indexation_task() {
  125. String reportTaskUuid = persistReportTasks();
  126. CeQueueDto task = new CeQueueDto();
  127. task.setUuid("uuid_2");
  128. task.setTaskType(BRANCH_ISSUE_SYNC);
  129. dbClient.ceQueueDao().insert(dbTester.getSession(), task);
  130. CeActivityDto activityDto = new CeActivityDto(task);
  131. activityDto.setStatus(Status.SUCCESS);
  132. dbClient.ceActivityDao().insert(dbTester.getSession(), activityDto);
  133. dbTester.commit();
  134. underTest.triggerOnIndexCreation();
  135. assertThat(dbClient.ceQueueDao().selectAllInAscOrder(dbTester.getSession())).extracting("uuid").containsExactly(reportTaskUuid);
  136. assertThat(dbClient.ceActivityDao().selectByTaskType(dbTester.getSession(), BRANCH_ISSUE_SYNC)).isEmpty();
  137. assertThat(dbClient.ceActivityDao().selectByTaskType(dbTester.getSession(), REPORT)).hasSize(1);
  138. assertThat(dbClient.ceTaskCharacteristicsDao().selectByTaskUuids(dbTester.getSession(), new HashSet<>(List.of("uuid_2")))).isEmpty();
  139. assertThat(logTester.logs(Level.INFO))
  140. .contains(
  141. "1 pending indexing task found to be deleted...",
  142. "1 completed indexing task found to be deleted...",
  143. "Indexing task deletion complete.",
  144. "Deleting tasks characteristics...",
  145. "Tasks characteristics deletion complete.");
  146. }
  147. @Test
  148. public void remove_existing_indexation_for_project_task() {
  149. String reportTaskUuid = persistReportTasks();
  150. ProjectDto projectDto = dbTester.components().insertPrivateProject().getProjectDto();
  151. String branchUuid = "branch_uuid";
  152. dbTester.components().insertProjectBranch(projectDto, b -> b.setBranchType(BRANCH).setUuid(branchUuid));
  153. CeQueueDto mainBranchTask = new CeQueueDto().setUuid("uuid_2").setTaskType(BRANCH_ISSUE_SYNC)
  154. .setEntityUuid(projectDto.getUuid()).setComponentUuid(projectDto.getUuid());
  155. dbClient.ceQueueDao().insert(dbTester.getSession(), mainBranchTask);
  156. CeQueueDto branchTask = new CeQueueDto().setUuid("uuid_3").setTaskType(BRANCH_ISSUE_SYNC)
  157. .setEntityUuid(projectDto.getUuid()).setComponentUuid(branchUuid);
  158. dbClient.ceQueueDao().insert(dbTester.getSession(), branchTask);
  159. ProjectDto anotherProjectDto = dbTester.components().insertPrivateProject().getProjectDto();
  160. CeQueueDto taskOnAnotherProject = new CeQueueDto().setUuid("uuid_4").setTaskType(BRANCH_ISSUE_SYNC)
  161. .setEntityUuid(anotherProjectDto.getUuid()).setComponentUuid("another-branchUuid");
  162. CeActivityDto canceledTaskOnAnotherProject = new CeActivityDto(taskOnAnotherProject).setStatus(Status.CANCELED);
  163. dbClient.ceActivityDao().insert(dbTester.getSession(), canceledTaskOnAnotherProject);
  164. dbTester.commit();
  165. underTest.triggerForProject(projectDto.getUuid());
  166. assertThat(dbClient.ceQueueDao().selectAllInAscOrder(dbTester.getSession())).extracting("uuid")
  167. .containsExactly(reportTaskUuid);
  168. assertThat(dbClient.ceActivityDao().selectByTaskType(dbTester.getSession(), REPORT)).hasSize(1);
  169. assertThat(dbClient.ceTaskCharacteristicsDao().selectByTaskUuids(dbTester.getSession(), new HashSet<>(List.of("uuid_2")))).isEmpty();
  170. // verify that the canceled tasks on anotherProject is still here, and was not removed by the project reindexing
  171. assertThat(dbClient.ceActivityDao().selectByTaskType(dbTester.getSession(), BRANCH_ISSUE_SYNC))
  172. .hasSize(1)
  173. .extracting(CeActivityDto::getEntityUuid)
  174. .containsExactly(anotherProjectDto.getUuid());
  175. assertThat(logTester.logs(Level.INFO))
  176. .contains(
  177. "2 pending indexing task found to be deleted...",
  178. "2 completed indexing task found to be deleted...",
  179. "Indexing task deletion complete.",
  180. "Deleting tasks characteristics...",
  181. "Tasks characteristics deletion complete.",
  182. "Tasks characteristics deletion complete.",
  183. "2 branch(es) found in need of issue sync for project.");
  184. }
  185. @Test
  186. public void order_by_last_analysis_date() {
  187. BranchDto dto = new BranchDto()
  188. .setBranchType(BRANCH)
  189. .setKey("branch_1")
  190. .setUuid("branch_uuid1")
  191. .setProjectUuid("project_uuid1")
  192. .setIsMain(false);
  193. dbClient.branchDao().insert(dbTester.getSession(), dto);
  194. dbTester.commit();
  195. insertSnapshot("analysis_1", "project_uuid1", 1);
  196. BranchDto dto2 = new BranchDto()
  197. .setBranchType(BRANCH)
  198. .setKey("branch_2")
  199. .setUuid("branch_uuid2")
  200. .setProjectUuid("project_uuid2")
  201. .setIsMain(false);
  202. dbClient.branchDao().insert(dbTester.getSession(), dto2);
  203. dbTester.commit();
  204. insertSnapshot("analysis_2", "project_uuid2", 2);
  205. underTest.triggerOnIndexCreation();
  206. verify(ceQueue, times(2)).prepareSubmit();
  207. ArgumentCaptor<Collection<CeTaskSubmit>> captor = ArgumentCaptor.forClass(Collection.class);
  208. verify(ceQueue, times(1)).massSubmit(captor.capture());
  209. List<Collection<CeTaskSubmit>> captures = captor.getAllValues();
  210. assertThat(captures).hasSize(1);
  211. Collection<CeTaskSubmit> tasks = captures.get(0);
  212. assertThat(tasks).hasSize(2);
  213. assertThat(tasks)
  214. .extracting(p -> p.getComponent().get().getUuid())
  215. .containsExactly("branch_uuid2", "branch_uuid1");
  216. assertThat(logTester.logs(Level.INFO))
  217. .contains("2 projects found in need of issue sync.");
  218. }
  219. @Test
  220. public void characteristics_are_defined() {
  221. BranchDto dto = new BranchDto()
  222. .setBranchType(BRANCH)
  223. .setKey("branch_1")
  224. .setUuid("branch_uuid1")
  225. .setProjectUuid("project_uuid1")
  226. .setIsMain(false);
  227. dbClient.branchDao().insert(dbTester.getSession(), dto);
  228. dbTester.commit();
  229. insertSnapshot("analysis_1", "project_uuid1", 1);
  230. BranchDto dto2 = new BranchDto()
  231. .setBranchType(PULL_REQUEST)
  232. .setKey("pr_1")
  233. .setUuid("pr_uuid_1")
  234. .setProjectUuid("project_uuid2")
  235. .setIsMain(false);
  236. dbClient.branchDao().insert(dbTester.getSession(), dto2);
  237. dbTester.commit();
  238. insertSnapshot("analysis_2", "project_uuid2", 2);
  239. underTest.triggerOnIndexCreation();
  240. ArgumentCaptor<Collection<CeTaskSubmit>> captor = ArgumentCaptor.forClass(Collection.class);
  241. verify(ceQueue, times(1)).massSubmit(captor.capture());
  242. List<Collection<CeTaskSubmit>> captures = captor.getAllValues();
  243. assertThat(captures).hasSize(1);
  244. Collection<CeTaskSubmit> tasks = captures.get(0);
  245. assertThat(tasks).hasSize(2);
  246. assertThat(tasks)
  247. .extracting(p -> p.getCharacteristics().get(BRANCH_TYPE),
  248. p -> p.getCharacteristics().get(CeTaskCharacteristics.BRANCH),
  249. p -> p.getCharacteristics().get(CeTaskCharacteristics.PULL_REQUEST))
  250. .containsExactlyInAnyOrder(
  251. tuple("BRANCH", "branch_1", null),
  252. tuple("PULL_REQUEST", null, "pr_1"));
  253. }
  254. @Test
  255. public void verify_comparator_transitivity() {
  256. Map<String, SnapshotDto> map = new HashMap<>();
  257. map.put("A", new SnapshotDto().setCreatedAt(1L));
  258. map.put("B", new SnapshotDto().setCreatedAt(2L));
  259. map.put("C", new SnapshotDto().setCreatedAt(-1L));
  260. List<String> uuids = new ArrayList<>(map.keySet());
  261. uuids.add("D");
  262. Comparators.verifyTransitivity(AsyncIssueIndexingImpl.compareBySnapshot(map), uuids);
  263. }
  264. @Test
  265. public void trigger_with_lot_of_not_analyzed_project_should_not_raise_exception() {
  266. for (int i = 0; i < 100; i++) {
  267. BranchDto dto = new BranchDto()
  268. .setBranchType(BRANCH)
  269. .setKey("branch_" + i)
  270. .setUuid("branch_uuid" + i)
  271. .setProjectUuid("project_uuid" + i)
  272. .setIsMain(false);
  273. dbClient.branchDao().insert(dbTester.getSession(), dto);
  274. dbTester.commit();
  275. insertSnapshot("analysis_" + i, "project_uuid" + i, 1);
  276. }
  277. for (int i = 100; i < 200; i++) {
  278. BranchDto dto = new BranchDto()
  279. .setBranchType(BRANCH)
  280. .setKey("branch_" + i)
  281. .setUuid("branch_uuid" + i)
  282. .setProjectUuid("project_uuid" + i)
  283. .setIsMain(false);
  284. dbClient.branchDao().insert(dbTester.getSession(), dto);
  285. dbTester.commit();
  286. }
  287. assertThatCode(underTest::triggerOnIndexCreation).doesNotThrowAnyException();
  288. }
  289. private SnapshotDto insertSnapshot(String analysisUuid, String projectUuid, long createdAt) {
  290. SnapshotDto snapshot = new SnapshotDto()
  291. .setUuid(analysisUuid)
  292. .setRootComponentUuid(projectUuid)
  293. .setStatus(STATUS_PROCESSED)
  294. .setCreatedAt(createdAt)
  295. .setLast(true);
  296. dbTester.getDbClient().snapshotDao().insert(dbTester.getSession(), snapshot);
  297. dbTester.commit();
  298. return snapshot;
  299. }
  300. private String persistReportTasks() {
  301. CeQueueDto reportTask = new CeQueueDto();
  302. reportTask.setUuid("uuid_1");
  303. reportTask.setTaskType(REPORT);
  304. dbClient.ceQueueDao().insert(dbTester.getSession(), reportTask);
  305. CeActivityDto reportActivity = new CeActivityDto(reportTask);
  306. reportActivity.setStatus(Status.SUCCESS);
  307. dbClient.ceActivityDao().insert(dbTester.getSession(), reportActivity);
  308. return reportTask.getUuid();
  309. }
  310. }