]> source.dussan.org Git - sonarqube.git/blob
09ad2bef97cf50f9769af8395f438141a03c0a1c
[sonarqube.git] /
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.server.issue.index;
21
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Optional;
29 import org.junit.Before;
30 import org.junit.Rule;
31 import org.junit.Test;
32 import org.mockito.ArgumentCaptor;
33 import org.sonar.api.utils.System2;
34 import org.sonar.api.utils.log.LogTester;
35 import org.sonar.api.utils.log.LoggerLevel;
36 import org.sonar.ce.queue.CeQueue;
37 import org.sonar.ce.queue.CeTaskSubmit;
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.ce.CeTaskCharacteristicDto;
46 import org.sonar.db.component.BranchDto;
47 import org.sonar.db.component.SnapshotDto;
48 import org.sonar.db.project.ProjectDto;
49
50 import static org.assertj.core.api.Assertions.assertThat;
51 import static org.assertj.core.api.Assertions.assertThatCode;
52 import static org.assertj.core.api.Assertions.tuple;
53 import static org.mockito.ArgumentMatchers.anyCollection;
54 import static org.mockito.Mockito.mock;
55 import static org.mockito.Mockito.times;
56 import static org.mockito.Mockito.verify;
57 import static org.mockito.Mockito.when;
58 import static org.sonar.db.ce.CeTaskCharacteristicDto.BRANCH_TYPE_KEY;
59 import static org.sonar.db.ce.CeTaskTypes.BRANCH_ISSUE_SYNC;
60 import static org.sonar.db.ce.CeTaskTypes.REPORT;
61 import static org.sonar.db.component.BranchType.BRANCH;
62 import static org.sonar.db.component.BranchType.PULL_REQUEST;
63 import static org.sonar.db.component.SnapshotDto.STATUS_PROCESSED;
64
65 public class AsyncIssueIndexingImplTest {
66
67   @Rule
68   public DbTester dbTester = DbTester.create(System2.INSTANCE);
69   @Rule
70   public LogTester logTester = new LogTester();
71
72   private final DbClient dbClient = dbTester.getDbClient();
73   private final CeQueue ceQueue = mock(CeQueue.class);
74   private final UuidFactory uuidFactory = new SequenceUuidFactory();
75
76   private final AsyncIssueIndexingImpl underTest = new AsyncIssueIndexingImpl(ceQueue, dbClient);
77
78   @Before
79   public void before() {
80     when(ceQueue.prepareSubmit()).thenReturn(new CeTaskSubmit.Builder(uuidFactory.create()));
81   }
82
83   @Test
84   public void triggerOnIndexCreation() {
85     BranchDto dto = new BranchDto()
86       .setBranchType(BRANCH)
87       .setKey("branchName")
88       .setUuid("branch_uuid")
89       .setProjectUuid("project_uuid");
90     dbClient.branchDao().insert(dbTester.getSession(), dto);
91     dbTester.commit();
92
93     underTest.triggerOnIndexCreation();
94
95     Optional<BranchDto> branch = dbClient.branchDao().selectByUuid(dbTester.getSession(), "branch_uuid");
96     assertThat(branch).isPresent();
97     assertThat(branch.get().isNeedIssueSync()).isTrue();
98     verify(ceQueue, times(1)).prepareSubmit();
99     verify(ceQueue, times(1)).massSubmit(anyCollection());
100     assertThat(logTester.logs(LoggerLevel.INFO))
101       .contains("1 branch found in need of issue sync.");
102   }
103
104   @Test
105   public void triggerForProject() {
106     ProjectDto projectDto = dbTester.components().insertPrivateProjectDto();
107     BranchDto dto = new BranchDto()
108       .setBranchType(BRANCH)
109       .setKey("branchName")
110       .setUuid("branch_uuid")
111       .setProjectUuid(projectDto.getUuid());
112     dbTester.components().insertProjectBranch(projectDto, dto);
113
114     underTest.triggerForProject(projectDto.getUuid());
115
116     Optional<BranchDto> branch = dbClient.branchDao().selectByUuid(dbTester.getSession(), "branch_uuid");
117     assertThat(branch).isPresent();
118     assertThat(branch.get().isNeedIssueSync()).isTrue();
119     verify(ceQueue, times(2)).prepareSubmit();
120     verify(ceQueue, times(1)).massSubmit(anyCollection());
121     assertThat(logTester.logs(LoggerLevel.INFO))
122       .contains("2 branch(es) found in need of issue sync for project.");
123   }
124
125   @Test
126   public void triggerOnIndexCreation_no_branch() {
127     underTest.triggerOnIndexCreation();
128
129     assertThat(logTester.logs(LoggerLevel.INFO)).contains("0 branch found in need of issue sync.");
130   }
131
132   @Test
133   public void triggerForProject_no_branch() {
134     underTest.triggerForProject("some-random-uuid");
135     assertThat(logTester.logs(LoggerLevel.INFO)).contains("0 branch(es) found in need of issue sync for project.");
136   }
137
138   @Test
139   public void remove_existing_indexation_task() {
140     String reportTaskUuid = persistReportTasks();
141
142     CeQueueDto task = new CeQueueDto();
143     task.setUuid("uuid_2");
144     task.setTaskType(BRANCH_ISSUE_SYNC);
145     dbClient.ceQueueDao().insert(dbTester.getSession(), task);
146     CeActivityDto activityDto = new CeActivityDto(task);
147     activityDto.setStatus(Status.SUCCESS);
148     dbClient.ceActivityDao().insert(dbTester.getSession(), activityDto);
149     dbTester.commit();
150
151     underTest.triggerOnIndexCreation();
152
153     assertCeTasks(reportTaskUuid);
154     assertThat(logTester.logs(LoggerLevel.INFO))
155       .contains(
156         "1 pending indexation task found to be deleted...",
157         "1 completed indexation task found to be deleted...",
158         "Indexation task deletion complete.",
159         "Deleting tasks characteristics...",
160         "Tasks characteristics deletion complete.");
161   }
162
163   @Test
164   public void remove_existing_indexation_for_project_task() {
165     String reportTaskUuid = persistReportTasks();
166
167     ProjectDto projectDto = dbTester.components().insertPrivateProjectDto();
168     String branchUuid = "branch_uuid";
169     dbTester.components().insertProjectBranch(projectDto, b -> b.setBranchType(BRANCH).setUuid(branchUuid));
170     CeQueueDto mainBranchTask = new CeQueueDto().setUuid("uuid_2").setTaskType(BRANCH_ISSUE_SYNC)
171       .setMainComponentUuid(projectDto.getUuid()).setComponentUuid(projectDto.getUuid());
172     CeQueueDto branchTask = new CeQueueDto().setUuid("uuid_3").setTaskType(BRANCH_ISSUE_SYNC)
173       .setMainComponentUuid(projectDto.getUuid()).setComponentUuid(branchUuid);
174     dbClient.ceQueueDao().insert(dbTester.getSession(), mainBranchTask);
175     dbClient.ceQueueDao().insert(dbTester.getSession(), branchTask);
176     dbTester.commit();
177
178     underTest.triggerForProject(projectDto.getUuid());
179
180     assertCeTasks(reportTaskUuid);
181     assertThat(logTester.logs(LoggerLevel.INFO))
182       .contains(
183         "2 pending indexation task found to be deleted...",
184         "2 completed indexation task found to be deleted...",
185         "Indexation task deletion complete.",
186         "Deleting tasks characteristics...",
187         "Tasks characteristics deletion complete.",
188         "Tasks characteristics deletion complete.",
189         "2 branch(es) found in need of issue sync for project.");
190   }
191
192   @Test
193   public void order_by_last_analysis_date() {
194     BranchDto dto = new BranchDto()
195       .setBranchType(BRANCH)
196       .setKey("branch_1")
197       .setUuid("branch_uuid1")
198       .setProjectUuid("project_uuid1");
199     dbClient.branchDao().insert(dbTester.getSession(), dto);
200     dbTester.commit();
201     insertSnapshot("analysis_1", "project_uuid1", 1);
202
203     BranchDto dto2 = new BranchDto()
204       .setBranchType(BRANCH)
205       .setKey("branch_2")
206       .setUuid("branch_uuid2")
207       .setProjectUuid("project_uuid2");
208     dbClient.branchDao().insert(dbTester.getSession(), dto2);
209     dbTester.commit();
210     insertSnapshot("analysis_2", "project_uuid2", 2);
211
212     underTest.triggerOnIndexCreation();
213
214     verify(ceQueue, times(2)).prepareSubmit();
215
216     ArgumentCaptor<Collection<CeTaskSubmit>> captor = ArgumentCaptor.forClass(Collection.class);
217
218     verify(ceQueue, times(1)).massSubmit(captor.capture());
219     List<Collection<CeTaskSubmit>> captures = captor.getAllValues();
220     assertThat(captures).hasSize(1);
221     Collection<CeTaskSubmit> tasks = captures.get(0);
222     assertThat(tasks).hasSize(2);
223     assertThat(tasks)
224       .extracting(p -> p.getComponent().get().getUuid())
225       .containsExactly("branch_uuid2", "branch_uuid1");
226
227     assertThat(logTester.logs(LoggerLevel.INFO))
228       .contains("2 projects found in need of issue sync.");
229   }
230
231   @Test
232   public void characteristics_are_defined() {
233     BranchDto dto = new BranchDto()
234       .setBranchType(BRANCH)
235       .setKey("branch_1")
236       .setUuid("branch_uuid1")
237       .setProjectUuid("project_uuid1");
238     dbClient.branchDao().insert(dbTester.getSession(), dto);
239     dbTester.commit();
240     insertSnapshot("analysis_1", "project_uuid1", 1);
241
242     BranchDto dto2 = new BranchDto()
243       .setBranchType(PULL_REQUEST)
244       .setKey("pr_1")
245       .setUuid("pr_uuid_1")
246       .setProjectUuid("project_uuid2");
247     dbClient.branchDao().insert(dbTester.getSession(), dto2);
248     dbTester.commit();
249     insertSnapshot("analysis_2", "project_uuid2", 2);
250
251     underTest.triggerOnIndexCreation();
252
253     ArgumentCaptor<Collection<CeTaskSubmit>> captor = ArgumentCaptor.forClass(Collection.class);
254     verify(ceQueue, times(1)).massSubmit(captor.capture());
255     List<Collection<CeTaskSubmit>> captures = captor.getAllValues();
256     assertThat(captures).hasSize(1);
257     Collection<CeTaskSubmit> tasks = captures.get(0);
258     assertThat(tasks).hasSize(2);
259
260     assertThat(tasks)
261       .extracting(p -> p.getCharacteristics().get(BRANCH_TYPE_KEY),
262         p -> p.getCharacteristics().get(CeTaskCharacteristicDto.BRANCH_KEY),
263         p -> p.getCharacteristics().get(CeTaskCharacteristicDto.PULL_REQUEST))
264       .containsExactlyInAnyOrder(
265         tuple("BRANCH", "branch_1", null),
266         tuple("PULL_REQUEST", null, "pr_1"));
267   }
268
269   @Test
270   public void verify_comparator_transitivity() {
271     Map<String, SnapshotDto> map = new HashMap<>();
272     map.put("A", new SnapshotDto().setCreatedAt(1L));
273     map.put("B", new SnapshotDto().setCreatedAt(2L));
274     map.put("C", new SnapshotDto().setCreatedAt(-1L));
275     List<String> uuids = new ArrayList<>(map.keySet());
276     uuids.add("D");
277     Comparators.verifyTransitivity(AsyncIssueIndexingImpl.compareBySnapshot(map), uuids);
278   }
279
280   @Test
281   public void trigger_with_lot_of_not_analyzed_project_should_not_raise_exception() {
282     for (int i = 0; i < 100; i++) {
283       BranchDto dto = new BranchDto()
284         .setBranchType(BRANCH)
285         .setKey("branch_" + i)
286         .setUuid("branch_uuid" + i)
287         .setProjectUuid("project_uuid" + i);
288       dbClient.branchDao().insert(dbTester.getSession(), dto);
289       dbTester.commit();
290       insertSnapshot("analysis_" + i, "project_uuid" + i, 1);
291     }
292
293     for (int i = 100; i < 200; i++) {
294       BranchDto dto = new BranchDto()
295         .setBranchType(BRANCH)
296         .setKey("branch_" + i)
297         .setUuid("branch_uuid" + i)
298         .setProjectUuid("project_uuid" + i);
299       dbClient.branchDao().insert(dbTester.getSession(), dto);
300       dbTester.commit();
301     }
302
303     assertThatCode(underTest::triggerOnIndexCreation).doesNotThrowAnyException();
304   }
305
306   private SnapshotDto insertSnapshot(String analysisUuid, String projectUuid, long createdAt) {
307     SnapshotDto snapshot = new SnapshotDto()
308       .setUuid(analysisUuid)
309       .setComponentUuid(projectUuid)
310       .setStatus(STATUS_PROCESSED)
311       .setCreatedAt(createdAt)
312       .setLast(true);
313     dbTester.getDbClient().snapshotDao().insert(dbTester.getSession(), snapshot);
314     dbTester.commit();
315     return snapshot;
316   }
317
318   private String persistReportTasks() {
319     CeQueueDto reportTask = new CeQueueDto();
320     reportTask.setUuid("uuid_1");
321     reportTask.setTaskType(REPORT);
322     dbClient.ceQueueDao().insert(dbTester.getSession(), reportTask);
323
324     CeActivityDto reportActivity = new CeActivityDto(reportTask);
325     reportActivity.setStatus(Status.SUCCESS);
326     dbClient.ceActivityDao().insert(dbTester.getSession(), reportActivity);
327     return reportTask.getUuid();
328   }
329
330   private void assertCeTasks(String reportTaskUuid) {
331     assertThat(dbClient.ceQueueDao().selectAllInAscOrder(dbTester.getSession())).extracting("uuid")
332       .containsExactly(reportTaskUuid);
333     assertThat(dbClient.ceActivityDao().selectByTaskType(dbTester.getSession(), BRANCH_ISSUE_SYNC)).isEmpty();
334     assertThat(dbClient.ceActivityDao().selectByTaskType(dbTester.getSession(), REPORT)).hasSize(1);
335     assertThat(dbClient.ceTaskCharacteristicsDao().selectByTaskUuids(dbTester.getSession(), new HashSet<>(List.of("uuid_2")))).isEmpty();
336   }
337
338 }