3 * Copyright (C) 2009-2024 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.ce.task.projectanalysis.component;
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.Collections;
26 import java.util.Optional;
27 import javax.annotation.Nullable;
28 import org.assertj.core.api.ThrowableAssert;
29 import org.junit.Rule;
30 import org.junit.Test;
31 import org.junit.runner.RunWith;
32 import org.sonar.api.config.internal.ConfigurationBridge;
33 import org.sonar.api.config.internal.MapSettings;
34 import org.sonar.api.utils.System2;
35 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
36 import org.sonar.ce.task.projectanalysis.analysis.Branch;
37 import org.sonar.db.DbSession;
38 import org.sonar.db.DbTester;
39 import org.sonar.db.component.BranchDto;
40 import org.sonar.db.component.BranchType;
41 import org.sonar.db.component.ComponentDto;
42 import org.sonar.db.component.ComponentTesting;
43 import org.sonar.db.component.ProjectData;
44 import org.sonar.db.protobuf.DbProjectBranches;
45 import org.sonar.server.project.Project;
47 import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
48 import static org.assertj.core.api.Assertions.assertThat;
49 import static org.assertj.core.api.Assertions.assertThatThrownBy;
50 import static org.mockito.Mockito.mock;
51 import static org.mockito.Mockito.when;
52 import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
53 import static org.sonar.core.config.PurgeConstants.BRANCHES_TO_KEEP_WHEN_INACTIVE;
54 import static org.sonar.db.component.BranchType.BRANCH;
55 import static org.sonar.db.component.BranchType.PULL_REQUEST;
57 @RunWith(DataProviderRunner.class)
58 public class BranchPersisterImplIT {
59 private final static Component MAIN = builder(Component.Type.PROJECT, 1, "PROJECT_KEY").setUuid("PROJECT_UUID").setName("p1").build();
60 private final static Component BRANCH1 = builder(Component.Type.PROJECT, 2, "BRANCH_KEY").setUuid("BRANCH_UUID").build();
61 private final static Component PR1 = builder(Component.Type.PROJECT, 3, "develop").setUuid("PR_UUID").build();
62 private static final Project PROJECT = new Project("PROJECT_UUID", MAIN.getKey(), MAIN.getName(), null, Collections.emptyList());
65 public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
67 public DbTester dbTester = DbTester.create(System2.INSTANCE);
69 public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
71 private final MapSettings settings = new MapSettings();
72 private final ConfigurationRepository configurationRepository = new TestSettingsRepository(new ConfigurationBridge(settings));
73 private final BranchPersister underTest = new BranchPersisterImpl(dbTester.getDbClient(), treeRootHolder, analysisMetadataHolder, configurationRepository);
76 public void persist_fails_with_ISE_if_no_component_for_main_branches() {
77 analysisMetadataHolder.setBranch(createBranch(BRANCH, true, "master"));
78 analysisMetadataHolder.setProject(PROJECT);
79 treeRootHolder.setRoot(MAIN);
80 DbSession dbSession = dbTester.getSession();
82 expectMissingComponentISE(() -> underTest.persist(dbSession));
86 public void persist_fails_with_ISE_if_no_component_for_branches() {
87 analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "foo"));
88 analysisMetadataHolder.setProject(PROJECT);
90 treeRootHolder.setRoot(BRANCH1);
91 DbSession dbSession = dbTester.getSession();
93 expectMissingComponentISE(() -> underTest.persist(dbSession));
97 public void persist_fails_with_ISE_if_no_component_for_pull_request() {
98 analysisMetadataHolder.setBranch(createBranch(BranchType.PULL_REQUEST, false, "12"));
99 analysisMetadataHolder.setProject(PROJECT);
101 treeRootHolder.setRoot(BRANCH1);
102 DbSession dbSession = dbTester.getSession();
104 expectMissingComponentISE(() -> underTest.persist(dbSession));
108 @UseDataProvider("nullOrNotNullString")
109 public void persist_creates_row_in_PROJECTS_BRANCHES_for_branch(@Nullable String mergeBranchUuid) {
110 String branchName = "branch";
112 // add project and branch in table PROJECTS
113 ComponentDto mainComponent = ComponentTesting.newPrivateProjectDto(MAIN.getUuid()).setKey(MAIN.getKey());
114 ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
115 new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
116 dbTester.components().insertComponents(mainComponent, component);
117 // set project in metadata
118 treeRootHolder.setRoot(BRANCH1);
119 analysisMetadataHolder.setBranch(createBranch(BRANCH, false, branchName, mergeBranchUuid));
120 analysisMetadataHolder.setProject(Project.from(mainComponent));
122 underTest.persist(dbTester.getSession());
124 dbTester.getSession().commit();
126 assertThat(dbTester.countRowsOfTable("components")).isEqualTo(2);
127 Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
128 assertThat(branchDto).isPresent();
129 assertThat(branchDto.get().getBranchType()).isEqualTo(BRANCH);
130 assertThat(branchDto.get().getKey()).isEqualTo(branchName);
131 assertThat(branchDto.get().getMergeBranchUuid()).isEqualTo(mergeBranchUuid);
132 assertThat(branchDto.get().getProjectUuid()).isEqualTo(MAIN.getUuid());
133 assertThat(branchDto.get().getPullRequestData()).isNull();
137 public void main_branch_is_excluded_from_branch_purge_by_default() {
138 analysisMetadataHolder.setBranch(createBranch(BRANCH, true, "master"));
139 analysisMetadataHolder.setProject(PROJECT);
140 treeRootHolder.setRoot(MAIN);
141 dbTester.components().insertPublicProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid())).getMainBranchComponent();
144 underTest.persist(dbTester.getSession());
146 Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), MAIN.getUuid());
147 assertThat(branchDto).isPresent();
148 assertThat(branchDto.get().isExcludeFromPurge()).isTrue();
152 public void non_main_branch_is_excluded_from_branch_purge_if_matches_sonar_dbcleaner_keepFromPurge_property() {
153 settings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "BRANCH.*");
154 analysisMetadataHolder.setProject(PROJECT);
155 analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "BRANCH_KEY"));
156 treeRootHolder.setRoot(BRANCH1);
157 ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid())).getMainBranchComponent();
158 ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
159 new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
160 dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component, false);
163 underTest.persist(dbTester.getSession());
165 Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
166 assertThat(branchDto).isPresent();
167 assertThat(branchDto.get().isExcludeFromPurge()).isTrue();
171 public void branch_is_excluded_from_purge_when_it_matches_setting() {
172 analysisMetadataHolder.setProject(PROJECT);
173 analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "BRANCH_KEY"));
174 treeRootHolder.setRoot(BRANCH1);
175 ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid())).getMainBranchComponent();
176 ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
177 new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
178 dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component, false);
179 settings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "BRANCH.*");
182 underTest.persist(dbTester.getSession());
184 Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
185 assertThat(branchDto).isPresent();
186 assertThat(branchDto.get().isExcludeFromPurge()).isTrue();
190 public void branch_is_not_excluded_from_purge_when_it_does_not_match_setting() {
191 analysisMetadataHolder.setProject(PROJECT);
192 analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "BRANCH_KEY"));
193 treeRootHolder.setRoot(BRANCH1);
194 ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid())).getMainBranchComponent();
195 ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
196 new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
197 dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component, false);
198 settings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "abc.*");
202 underTest.persist(dbTester.getSession());
204 Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
205 assertThat(branchDto).isPresent();
206 assertThat(branchDto.get().isExcludeFromPurge()).isFalse();
210 public void pull_request_is_never_excluded_from_branch_purge_even_if_its_source_branch_name_matches_sonar_dbcleaner_keepFromPurge_property() {
211 settings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "develop");
212 analysisMetadataHolder.setBranch(createPullRequest(PR1.getKey(), MAIN.getUuid()));
213 analysisMetadataHolder.setProject(PROJECT);
215 analysisMetadataHolder.setPullRequestKey(PR1.getKey());
216 treeRootHolder.setRoot(PR1);
217 ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid())).getMainBranchComponent();
218 ComponentDto component = ComponentTesting.newBranchComponent(mainComponent, new BranchDto()
219 .setUuid(PR1.getUuid())
220 .setKey(PR1.getKey())
221 .setProjectUuid(MAIN.getUuid())
222 .setBranchType(PULL_REQUEST)
223 .setMergeBranchUuid(MAIN.getUuid()));
224 dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component, false);
227 underTest.persist(dbTester.getSession());
229 Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), PR1.getUuid());
230 assertThat(branchDto).isPresent();
231 assertThat(branchDto.get().isExcludeFromPurge()).isFalse();
235 public void non_main_branch_is_included_in_branch_purge_if_branch_name_does_not_match_sonar_dbcleaner_keepFromPurge_property() {
236 settings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "foobar-.*");
237 analysisMetadataHolder.setProject(PROJECT);
238 analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "BRANCH_KEY"));
239 treeRootHolder.setRoot(BRANCH1);
240 ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid())).getMainBranchComponent();
241 ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
242 new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
243 dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component, false);
246 underTest.persist(dbTester.getSession());
248 Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
249 assertThat(branchDto).isPresent();
250 assertThat(branchDto.get().isExcludeFromPurge()).isFalse();
254 public static Object[][] nullOrNotNullString() {
255 return new Object[][] {
257 {randomAlphabetic(12)}
262 public void persist_creates_row_in_PROJECTS_BRANCHES_for_pull_request() {
263 String pullRequestId = "pr-123";
265 // add project and branch in table PROJECTS
266 ProjectData projectData = dbTester.components().insertPrivateProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid()));
267 ComponentDto mainComponent = projectData.getMainBranchComponent();
268 ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
269 new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(PULL_REQUEST));
270 dbTester.components().insertComponents(component);
271 // set project in metadata
272 treeRootHolder.setRoot(BRANCH1);
273 analysisMetadataHolder.setBranch(createBranch(PULL_REQUEST, false, pullRequestId, "mergeBanchUuid"));
274 analysisMetadataHolder.setProject(Project.from(projectData.getProjectDto()));
275 analysisMetadataHolder.setPullRequestKey(pullRequestId);
277 underTest.persist(dbTester.getSession());
279 dbTester.getSession().commit();
281 assertThat(dbTester.countRowsOfTable("components")).isEqualTo(2);
282 Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
283 assertThat(branchDto).isPresent();
284 assertThat(branchDto.get().getBranchType()).isEqualTo(PULL_REQUEST);
285 assertThat(branchDto.get().getKey()).isEqualTo(pullRequestId);
286 assertThat(branchDto.get().getMergeBranchUuid()).isEqualTo("mergeBanchUuid");
287 assertThat(branchDto.get().getProjectUuid()).isEqualTo(projectData.projectUuid());
288 assertThat(branchDto.get().getPullRequestData()).isEqualTo(DbProjectBranches.PullRequestData.newBuilder()
289 .setBranch(pullRequestId)
290 .setTarget("mergeBanchUuid")
291 .setTitle(pullRequestId)
295 private static Branch createBranch(BranchType type, boolean isMain, String name) {
296 return createBranch(type, isMain, name, null);
299 private static Branch createPullRequest(String key, String mergeBranchUuid) {
300 Branch branch = createBranch(PULL_REQUEST, false, key, mergeBranchUuid);
301 when(branch.getPullRequestKey()).thenReturn(key);
305 private static Branch createBranch(BranchType type, boolean isMain, String name, @Nullable String mergeBranchUuid) {
306 Branch branch = mock(Branch.class);
307 when(branch.getType()).thenReturn(type);
308 when(branch.getName()).thenReturn(name);
309 when(branch.isMain()).thenReturn(isMain);
310 when(branch.getReferenceBranchUuid()).thenReturn(mergeBranchUuid);
311 when(branch.getTargetBranchName()).thenReturn(mergeBranchUuid);
315 private void expectMissingComponentISE(ThrowableAssert.ThrowingCallable callable) {
316 assertThatThrownBy(callable)
317 .isInstanceOf(IllegalStateException.class)
318 .hasMessage("Component has been deleted by end-user during analysis");