3 * Copyright (C) 2009-2021 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.MapSettings;
33 import org.sonar.api.utils.System2;
34 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
35 import org.sonar.ce.task.projectanalysis.analysis.Branch;
36 import org.sonar.db.DbSession;
37 import org.sonar.db.DbTester;
38 import org.sonar.db.component.BranchDto;
39 import org.sonar.db.component.BranchType;
40 import org.sonar.db.component.ComponentDto;
41 import org.sonar.db.component.ComponentTesting;
42 import org.sonar.db.property.PropertyDto;
43 import org.sonar.db.protobuf.DbProjectBranches;
44 import org.sonar.server.project.Project;
45 import org.sonar.server.setting.ProjectConfigurationLoader;
46 import org.sonar.server.setting.ProjectConfigurationLoaderImpl;
48 import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
49 import static org.assertj.core.api.Assertions.assertThat;
50 import static org.assertj.core.api.Assertions.assertThatThrownBy;
51 import static org.mockito.Mockito.mock;
52 import static org.mockito.Mockito.when;
53 import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
54 import static org.sonar.core.config.PurgeConstants.BRANCHES_TO_KEEP_WHEN_INACTIVE;
55 import static org.sonar.db.component.BranchType.BRANCH;
56 import static org.sonar.db.component.BranchType.PULL_REQUEST;
58 @RunWith(DataProviderRunner.class)
59 public class BranchPersisterImplTest {
60 private final static Component MAIN = builder(Component.Type.PROJECT, 1, "PROJECT_KEY").setUuid("PROJECT_UUID").setName("p1").build();
61 private final static Component BRANCH1 = builder(Component.Type.PROJECT, 2, "BRANCH_KEY").setUuid("BRANCH_UUID").build();
62 private final static Component PR1 = builder(Component.Type.PROJECT, 3, "develop").setUuid("PR_UUID").build();
63 private static final Project PROJECT = new Project(MAIN.getUuid(), MAIN.getKey(), MAIN.getName(), null, Collections.emptyList());
66 public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
68 public DbTester dbTester = DbTester.create(System2.INSTANCE);
70 public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
72 private final MapSettings globalSettings = new MapSettings();
73 private final ProjectConfigurationLoader projectConfiguration = new ProjectConfigurationLoaderImpl(globalSettings, dbTester.getDbClient());
74 private final BranchPersister underTest = new BranchPersisterImpl(dbTester.getDbClient(), treeRootHolder, analysisMetadataHolder,
75 projectConfiguration);
78 public void persist_fails_with_ISE_if_no_component_for_main_branches() {
79 analysisMetadataHolder.setBranch(createBranch(BRANCH, true, "master"));
80 treeRootHolder.setRoot(MAIN);
81 DbSession dbSession = dbTester.getSession();
83 expectMissingComponentISE(() -> underTest.persist(dbSession));
87 public void persist_fails_with_ISE_if_no_component_for_branches() {
88 analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "foo"));
89 treeRootHolder.setRoot(BRANCH1);
90 DbSession dbSession = dbTester.getSession();
92 expectMissingComponentISE(() -> underTest.persist(dbSession));
96 public void persist_fails_with_ISE_if_no_component_for_pull_request() {
97 analysisMetadataHolder.setBranch(createBranch(BranchType.PULL_REQUEST, false, "12"));
98 treeRootHolder.setRoot(BRANCH1);
99 DbSession dbSession = dbTester.getSession();
101 expectMissingComponentISE(() -> underTest.persist(dbSession));
105 @UseDataProvider("nullOrNotNullString")
106 public void persist_creates_row_in_PROJECTS_BRANCHES_for_branch(@Nullable String mergeBranchUuid) {
107 String branchName = "branch";
109 // add project and branch in table PROJECTS
110 ComponentDto mainComponent = ComponentTesting.newPrivateProjectDto(MAIN.getUuid()).setDbKey(MAIN.getKey());
111 ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
112 new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
113 dbTester.components().insertComponents(mainComponent, component);
114 // set project in metadata
115 treeRootHolder.setRoot(BRANCH1);
116 analysisMetadataHolder.setBranch(createBranch(BRANCH, false, branchName, mergeBranchUuid));
117 analysisMetadataHolder.setProject(Project.from(mainComponent));
119 underTest.persist(dbTester.getSession());
121 dbTester.getSession().commit();
123 assertThat(dbTester.countRowsOfTable("components")).isEqualTo(2);
124 Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
125 assertThat(branchDto).isPresent();
126 assertThat(branchDto.get().getBranchType()).isEqualTo(BRANCH);
127 assertThat(branchDto.get().getKey()).isEqualTo(branchName);
128 assertThat(branchDto.get().getMergeBranchUuid()).isEqualTo(mergeBranchUuid);
129 assertThat(branchDto.get().getProjectUuid()).isEqualTo(MAIN.getUuid());
130 assertThat(branchDto.get().getPullRequestData()).isNull();
134 public void main_branch_is_excluded_from_branch_purge_by_default() {
135 analysisMetadataHolder.setBranch(createBranch(BRANCH, true, "master"));
136 treeRootHolder.setRoot(MAIN);
137 dbTester.components().insertPublicProject(p -> p.setDbKey(MAIN.getDbKey()).setUuid(MAIN.getUuid()));
140 underTest.persist(dbTester.getSession());
142 Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), MAIN.getUuid());
143 assertThat(branchDto).isPresent();
144 assertThat(branchDto.get().isExcludeFromPurge()).isTrue();
148 public void non_main_branch_is_excluded_from_branch_purge_if_matches_sonar_dbcleaner_keepFromPurge_property() {
149 globalSettings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "BRANCH.*");
150 analysisMetadataHolder.setProject(PROJECT);
151 analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "BRANCH_KEY"));
152 treeRootHolder.setRoot(BRANCH1);
153 ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setDbKey(MAIN.getDbKey()).setUuid(MAIN.getUuid()));
154 ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
155 new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
156 dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component);
159 underTest.persist(dbTester.getSession());
161 Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
162 assertThat(branchDto).isPresent();
163 assertThat(branchDto.get().isExcludeFromPurge()).isTrue();
167 public void branch_is_excluded_from_purge_when_it_matches_project_level_but_not_global_level_keepFromPurge_setting() {
168 analysisMetadataHolder.setProject(PROJECT);
169 analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "BRANCH_KEY"));
170 treeRootHolder.setRoot(BRANCH1);
171 ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setDbKey(MAIN.getDbKey()).setUuid(MAIN.getUuid()));
172 ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
173 new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
174 dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component);
175 globalSettings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "abc.*");
176 insertProjectProperty(mainComponent, BRANCHES_TO_KEEP_WHEN_INACTIVE, "BRANCH.*");
179 underTest.persist(dbTester.getSession());
181 Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
182 assertThat(branchDto).isPresent();
183 assertThat(branchDto.get().isExcludeFromPurge()).isTrue();
187 public void branch_is_not_excluded_from_purge_when_it_does_not_match_overriden_global_keepFromPurge_setting_on_project_level() {
188 analysisMetadataHolder.setProject(PROJECT);
189 analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "BRANCH_KEY"));
190 treeRootHolder.setRoot(BRANCH1);
191 ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setDbKey(MAIN.getDbKey()).setUuid(MAIN.getUuid()));
192 ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
193 new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
194 dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component);
195 globalSettings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "BRANCH.*");
196 insertProjectProperty(mainComponent, BRANCHES_TO_KEEP_WHEN_INACTIVE, "abc.*");
199 underTest.persist(dbTester.getSession());
201 Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
202 assertThat(branchDto).isPresent();
203 assertThat(branchDto.get().isExcludeFromPurge()).isFalse();
207 public void pull_request_is_never_excluded_from_branch_purge_even_if_its_source_branch_name_matches_sonar_dbcleaner_keepFromPurge_property() {
208 globalSettings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "develop");
209 analysisMetadataHolder.setBranch(createPullRequest(PR1.getKey(), MAIN.getUuid()));
210 analysisMetadataHolder.setPullRequestKey(PR1.getKey());
211 treeRootHolder.setRoot(PR1);
212 ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setDbKey(MAIN.getDbKey()).setUuid(MAIN.getUuid()));
213 ComponentDto component = ComponentTesting.newBranchComponent(mainComponent, new BranchDto()
214 .setUuid(PR1.getUuid())
215 .setKey(PR1.getKey())
216 .setProjectUuid(MAIN.getUuid())
217 .setBranchType(PULL_REQUEST)
218 .setMergeBranchUuid(MAIN.getUuid()));
219 dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component);
222 underTest.persist(dbTester.getSession());
224 Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), PR1.getUuid());
225 assertThat(branchDto).isPresent();
226 assertThat(branchDto.get().isExcludeFromPurge()).isFalse();
230 public void non_main_branch_is_included_in_branch_purge_if_branch_name_does_not_match_sonar_dbcleaner_keepFromPurge_property() {
231 globalSettings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "foobar-.*");
232 analysisMetadataHolder.setProject(PROJECT);
233 analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "BRANCH_KEY"));
234 treeRootHolder.setRoot(BRANCH1);
235 ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setDbKey(MAIN.getDbKey()).setUuid(MAIN.getUuid()));
236 ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
237 new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
238 dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component);
241 underTest.persist(dbTester.getSession());
243 Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
244 assertThat(branchDto).isPresent();
245 assertThat(branchDto.get().isExcludeFromPurge()).isFalse();
249 public static Object[][] nullOrNotNullString() {
250 return new Object[][] {
252 {randomAlphabetic(12)}
257 public void persist_creates_row_in_PROJECTS_BRANCHES_for_pull_request() {
258 String pullRequestId = "pr-123";
260 // add project and branch in table PROJECTS
261 ComponentDto mainComponent = ComponentTesting.newPrivateProjectDto(MAIN.getUuid()).setDbKey(MAIN.getKey());
262 ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
263 new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(PULL_REQUEST));
264 dbTester.components().insertComponents(mainComponent, component);
265 // set project in metadata
266 treeRootHolder.setRoot(BRANCH1);
267 analysisMetadataHolder.setBranch(createBranch(PULL_REQUEST, false, pullRequestId, "mergeBanchUuid"));
268 analysisMetadataHolder.setProject(Project.from(mainComponent));
269 analysisMetadataHolder.setPullRequestKey(pullRequestId);
271 underTest.persist(dbTester.getSession());
273 dbTester.getSession().commit();
275 assertThat(dbTester.countRowsOfTable("components")).isEqualTo(2);
276 Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
277 assertThat(branchDto).isPresent();
278 assertThat(branchDto.get().getBranchType()).isEqualTo(PULL_REQUEST);
279 assertThat(branchDto.get().getKey()).isEqualTo(pullRequestId);
280 assertThat(branchDto.get().getMergeBranchUuid()).isEqualTo("mergeBanchUuid");
281 assertThat(branchDto.get().getProjectUuid()).isEqualTo(MAIN.getUuid());
282 assertThat(branchDto.get().getPullRequestData()).isEqualTo(DbProjectBranches.PullRequestData.newBuilder()
283 .setBranch(pullRequestId)
284 .setTarget("mergeBanchUuid")
285 .setTitle(pullRequestId)
289 private static Branch createBranch(BranchType type, boolean isMain, String name) {
290 return createBranch(type, isMain, name, null);
293 private static Branch createPullRequest(String key, String mergeBranchUuid) {
294 Branch branch = createBranch(PULL_REQUEST, false, key, mergeBranchUuid);
295 when(branch.getPullRequestKey()).thenReturn(key);
299 private static Branch createBranch(BranchType type, boolean isMain, String name, @Nullable String mergeBranchUuid) {
300 Branch branch = mock(Branch.class);
301 when(branch.getType()).thenReturn(type);
302 when(branch.getName()).thenReturn(name);
303 when(branch.isMain()).thenReturn(isMain);
304 when(branch.getReferenceBranchUuid()).thenReturn(mergeBranchUuid);
305 when(branch.getTargetBranchName()).thenReturn(mergeBranchUuid);
309 private void expectMissingComponentISE(ThrowableAssert.ThrowingCallable callable) {
310 assertThatThrownBy(callable)
311 .isInstanceOf(IllegalStateException.class)
312 .hasMessage("Component has been deleted by end-user during analysis");
315 private void insertProjectProperty(ComponentDto project, String propertyKey, String propertyValue) {
316 dbTester.properties().insertProperties(new PropertyDto().setKey(propertyKey).setValue(propertyValue).setComponentUuid(project.uuid()));