3 * Copyright (C) 2009-2023 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 package org.sonar.server.issue.index;
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.stream.IntStream;
29 import org.junit.Rule;
30 import org.junit.Test;
31 import org.junit.runner.RunWith;
32 import org.sonar.api.utils.System2;
33 import org.sonar.db.DbSession;
34 import org.sonar.db.DbTester;
35 import org.sonar.db.ce.CeActivityDto;
36 import org.sonar.db.ce.CeQueueDto;
37 import org.sonar.db.ce.CeQueueDto.Status;
38 import org.sonar.db.ce.CeTaskTypes;
39 import org.sonar.db.component.ComponentDto;
40 import org.sonar.db.project.ProjectDto;
41 import org.sonar.server.es.EsIndexSyncInProgressException;
43 import static org.assertj.core.api.Assertions.assertThat;
44 import static org.assertj.core.api.Assertions.assertThatThrownBy;
45 import static org.sonar.db.ce.CeActivityDto.Status.FAILED;
46 import static org.sonar.db.ce.CeActivityDto.Status.SUCCESS;
48 @RunWith(DataProviderRunner.class)
49 public class IssueIndexSyncProgressCheckerTest {
51 private final System2 system2 = new System2();
54 public DbTester db = DbTester.create(System2.INSTANCE);
56 private final IssueIndexSyncProgressChecker underTest = new IssueIndexSyncProgressChecker(db.getDbClient());
59 public void return_100_if_there_is_no_tasks_left() {
60 IssueSyncProgress issueSyncProgress = underTest.getIssueSyncProgress(db.getSession());
61 assertThat(issueSyncProgress.getCompleted()).isZero();
62 assertThat(issueSyncProgress.getTotal()).isZero();
63 assertThat(issueSyncProgress.toPercentCompleted()).isEqualTo(100);
64 assertThat(issueSyncProgress.isCompleted()).isTrue();
65 assertThat(issueSyncProgress.hasFailures()).isFalse();
69 public void return_100_if_all_branches_have_need_issue_sync_set_FALSE() {
70 IntStream.range(0, 13).forEach(value -> insertProjectWithBranches(false, 2));
71 IntStream.range(0, 14).forEach(value -> insertProjectWithBranches(false, 4));
72 IntStream.range(0, 4).forEach(value -> insertProjectWithBranches(false, 10));
74 IssueSyncProgress result = underTest.getIssueSyncProgress(db.getSession());
75 assertThat(result.getCompleted()).isEqualTo(153);
76 assertThat(result.getTotal()).isEqualTo(153);
77 assertThat(result.toPercentCompleted()).isEqualTo(100);
78 assertThat(result.isCompleted()).isTrue();
82 public void return_has_failure_true_if_exists_task() {
83 assertThat(underTest.getIssueSyncProgress(db.getSession()).hasFailures()).isFalse();
85 ProjectDto projectDto1 = insertProjectWithBranches(false, 0);
86 insertCeActivity("TASK_1", projectDto1, SUCCESS);
88 ProjectDto projectDto2 = insertProjectWithBranches(false, 0);
89 insertCeActivity("TASK_2", projectDto2, SUCCESS);
91 assertThat(underTest.getIssueSyncProgress(db.getSession()).hasFailures()).isFalse();
93 ProjectDto projectDto3 = insertProjectWithBranches(true, 0);
94 insertCeActivity("TASK_3", projectDto3, FAILED);
96 assertThat(underTest.getIssueSyncProgress(db.getSession()).hasFailures()).isTrue();
100 @UseDataProvider("various_task_numbers")
101 public void return_correct_percent_value_for_branches_to_sync(int toSync, int synced, int expectedPercent) {
102 IntStream.range(0, toSync).forEach(value -> insertProjectWithBranches(true, 0));
103 IntStream.range(0, synced).forEach(value -> insertProjectWithBranches(false, 0));
105 IssueSyncProgress result = underTest.getIssueSyncProgress(db.getSession());
106 assertThat(result.getCompleted()).isEqualTo(synced);
107 assertThat(result.getTotal()).isEqualTo(toSync + synced);
108 assertThat(result.toPercentCompleted()).isEqualTo(expectedPercent);
112 public static Object[][] various_task_numbers() {
113 return new Object[][] {
114 // toSync, synced, expected result
129 public void return_0_if_all_branches_have_need_issue_sync_set_true() {
131 IntStream.range(0, 10).forEach(value -> insertProjectWithBranches(true, 0));
133 // project + additional branch
134 IntStream.range(0, 10).forEach(value -> insertProjectWithBranches(true, 1));
136 IssueSyncProgress result = underTest.getIssueSyncProgress(db.getSession());
137 assertThat(result.getCompleted()).isZero();
138 assertThat(result.getTotal()).isEqualTo(30);
139 assertThat(result.toPercentCompleted()).isZero();
143 public void return_is_completed_true_if_no_pending_or_in_progress_tasks() {
145 IntStream.range(0, 10).forEach(value -> insertProjectWithBranches(false, 0));
147 // project + additional branch
148 IntStream.range(0, 10).forEach(value -> insertProjectWithBranches(false, 1));
150 IssueSyncProgress result = underTest.getIssueSyncProgress(db.getSession());
151 assertThat(result.isCompleted()).isTrue();
155 public void return_is_completed_true_if_pending_task_exist_but_all_branches_have_been_synced() {
156 insertCeQueue("TASK_1", Status.PENDING);
158 IntStream.range(0, 10).forEach(value -> insertProjectWithBranches(false, 0));
160 // project + additional branch
161 IntStream.range(0, 10).forEach(value -> insertProjectWithBranches(false, 1));
163 IssueSyncProgress result = underTest.getIssueSyncProgress(db.getSession());
164 assertThat(result.isCompleted()).isTrue();
168 public void return_is_completed_true_if_in_progress_task_exist_but_all_branches_have_been_synced() {
169 insertCeQueue("TASK_1", Status.IN_PROGRESS);
171 IntStream.range(0, 10).forEach(value -> insertProjectWithBranches(false, 0));
173 // project + additional branch
174 IntStream.range(0, 10).forEach(value -> insertProjectWithBranches(false, 1));
176 IssueSyncProgress result = underTest.getIssueSyncProgress(db.getSession());
177 assertThat(result.isCompleted()).isTrue();
181 public void return_is_completed_false_if_pending_task_exist_and_branches_need_issue_sync() {
182 insertCeQueue("TASK_1", Status.PENDING);
184 IntStream.range(0, 10).forEach(value -> insertProjectWithBranches(true, 0));
186 // project + additional branch
187 IntStream.range(0, 10).forEach(value -> insertProjectWithBranches(false, 1));
189 IssueSyncProgress result = underTest.getIssueSyncProgress(db.getSession());
190 assertThat(result.isCompleted()).isFalse();
194 public void return_is_completed_false_if_in_progress_task_exist_and_branches_need_issue_sync() {
195 insertCeQueue("TASK_1", Status.IN_PROGRESS);
197 IntStream.range(0, 10).forEach(value -> insertProjectWithBranches(true, 0));
199 // project + additional branch
200 IntStream.range(0, 10).forEach(value -> insertProjectWithBranches(false, 1));
202 IssueSyncProgress result = underTest.getIssueSyncProgress(db.getSession());
203 assertThat(result.isCompleted()).isFalse();
207 public void checkIfAnyComponentsNeedIssueSync_throws_exception_if_all_components_have_need_issue_sync_TRUE() {
208 ProjectDto projectDto1 = insertProjectWithBranches(true, 0);
209 ProjectDto projectDto2 = insertProjectWithBranches(true, 0);
210 DbSession session = db.getSession();
211 List<String> projectKeys = Arrays.asList(projectDto1.getKey(), projectDto2.getKey());
212 assertThatThrownBy(() -> underTest.checkIfAnyComponentsNeedIssueSync(session, projectKeys))
213 .isInstanceOf(EsIndexSyncInProgressException.class)
214 .hasFieldOrPropertyWithValue("httpCode", 503)
215 .hasMessage("Results are temporarily unavailable. Indexing of issues is in progress.");
219 public void checkIfAnyComponentsNeedIssueSync_does_not_throw_exception_if_all_components_have_need_issue_sync_FALSE() {
220 underTest.checkIfAnyComponentsNeedIssueSync(db.getSession(), Collections.emptyList());
221 ProjectDto projectDto1 = insertProjectWithBranches(false, 0);
222 ProjectDto projectDto2 = insertProjectWithBranches(false, 0);
223 underTest.checkIfAnyComponentsNeedIssueSync(db.getSession(), Arrays.asList(projectDto1.getKey(), projectDto2.getKey()));
227 public void checkIfAnyComponentsNeedIssueSync_throws_exception_if_at_least_one_component_has_need_issue_sync_TRUE() {
228 ProjectDto projectDto1 = insertProjectWithBranches(false, 0);
229 ProjectDto projectDto2 = insertProjectWithBranches(true, 0);
231 DbSession session = db.getSession();
232 List<String> projectKeys = Arrays.asList(projectDto1.getKey(), projectDto2.getKey());
233 assertThatThrownBy(() -> underTest.checkIfAnyComponentsNeedIssueSync(session, projectKeys))
234 .isInstanceOf(EsIndexSyncInProgressException.class)
235 .hasFieldOrPropertyWithValue("httpCode", 503)
236 .hasMessage("Results are temporarily unavailable. Indexing of issues is in progress.");
240 public void checkIfComponentNeedIssueSync_single_component() {
241 ProjectDto projectDto1 = insertProjectWithBranches(true, 0);
242 ProjectDto projectDto2 = insertProjectWithBranches(false, 0);
244 DbSession session = db.getSession();
245 // do nothing when need issue sync false
246 underTest.checkIfComponentNeedIssueSync(session, projectDto2.getKey());
248 // throws if flag set to TRUE
249 String key = projectDto1.getKey();
250 assertThatThrownBy(() -> underTest.checkIfComponentNeedIssueSync(session, key))
251 .isInstanceOf(EsIndexSyncInProgressException.class)
252 .hasFieldOrPropertyWithValue("httpCode", 503)
253 .hasMessage("Results are temporarily unavailable. Indexing of issues is in progress.");
257 public void checkIfAnyComponentsNeedIssueSync_single_view_subview_or_app() {
258 ProjectDto projectDto1 = insertProjectWithBranches(true, 0);
260 ComponentDto app = db.components().insertPublicApplication().getMainBranchComponent();
261 ComponentDto view = db.components().insertPrivatePortfolio();
262 ComponentDto subview = db.components().insertSubView(view);
264 DbSession session = db.getSession();
265 List<String> appViewOrSubviewKeys = Arrays.asList(projectDto1.getKey(), app.getKey(), view.getKey(), subview.getKey());
267 // throws if flag set to TRUE
268 assertThatThrownBy(() -> underTest.checkIfAnyComponentsNeedIssueSync(session,
269 appViewOrSubviewKeys))
270 .isInstanceOf(EsIndexSyncInProgressException.class)
271 .hasFieldOrPropertyWithValue("httpCode", 503)
272 .hasMessage("Results are temporarily unavailable. Indexing of issues is in progress.");
276 public void checkIfIssueSyncInProgress_throws_exception_if_at_least_one_component_has_need_issue_sync_TRUE() {
277 insertProjectWithBranches(false, 0);
278 underTest.checkIfIssueSyncInProgress(db.getSession());
279 insertProjectWithBranches(true, 0);
281 DbSession session = db.getSession();
282 assertThatThrownBy(() -> underTest.checkIfIssueSyncInProgress(session))
283 .isInstanceOf(EsIndexSyncInProgressException.class)
284 .hasFieldOrPropertyWithValue("httpCode", 503)
285 .hasMessage("Results are temporarily unavailable. Indexing of issues is in progress.");
289 public void doProjectNeedIssueSync() {
290 ProjectDto projectDto1 = insertProjectWithBranches(false, 0);
291 assertThat(underTest.doProjectNeedIssueSync(db.getSession(), projectDto1.getUuid())).isFalse();
292 ProjectDto projectDto2 = insertProjectWithBranches(true, 0);
293 assertThat(underTest.doProjectNeedIssueSync(db.getSession(), projectDto2.getUuid())).isTrue();
297 public void findProjectUuidsWithIssuesSyncNeed() {
298 ProjectDto projectDto1 = insertProjectWithBranches(false, 0);
299 ProjectDto projectDto2 = insertProjectWithBranches(false, 0);
300 ProjectDto projectDto3 = insertProjectWithBranches(true, 0);
301 ProjectDto projectDto4 = insertProjectWithBranches(true, 0);
303 assertThat(underTest.findProjectUuidsWithIssuesSyncNeed(db.getSession(),
304 Arrays.asList(projectDto1.getUuid(), projectDto2.getUuid(), projectDto3.getUuid(), projectDto4.getUuid())))
305 .containsOnly(projectDto3.getUuid(), projectDto4.getUuid());
308 private ProjectDto insertProjectWithBranches(boolean needIssueSync, int numberOfBranches) {
309 ProjectDto projectDto = db.components()
310 .insertPrivateProject(branchDto -> branchDto.setNeedIssueSync(needIssueSync), c -> {
313 IntStream.range(0, numberOfBranches).forEach(
314 i -> db.components().insertProjectBranch(projectDto, branchDto -> branchDto.setNeedIssueSync(needIssueSync)));
318 private CeQueueDto insertCeQueue(String uuid, CeQueueDto.Status status) {
319 CeQueueDto queueDto = new CeQueueDto();
320 queueDto.setUuid(uuid);
321 queueDto.setStatus(status);
322 queueDto.setTaskType(CeTaskTypes.BRANCH_ISSUE_SYNC);
323 db.getDbClient().ceQueueDao().insert(db.getSession(), queueDto);
327 private CeActivityDto insertCeActivity(String uuid, ProjectDto projectDto, CeActivityDto.Status status) {
328 CeQueueDto queueDto = new CeQueueDto();
329 queueDto.setUuid(uuid);
330 queueDto.setTaskType(CeTaskTypes.BRANCH_ISSUE_SYNC);
332 CeActivityDto dto = new CeActivityDto(queueDto);
333 dto.setComponentUuid(projectDto.getUuid());
334 dto.setMainComponentUuid(projectDto.getUuid());
335 dto.setStatus(status);
336 dto.setTaskType(CeTaskTypes.BRANCH_ISSUE_SYNC);
337 dto.setAnalysisUuid(uuid + "_AA");
338 dto.setCreatedAt(system2.now());
339 db.getDbClient().ceActivityDao().insert(db.getSession(), dto);