--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.component;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.util.Collections;
+import java.util.Optional;
+import javax.annotation.Nullable;
+import org.assertj.core.api.ThrowableAssert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sonar.api.config.internal.ConfigurationBridge;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.utils.System2;
+import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
+import org.sonar.ce.task.projectanalysis.analysis.Branch;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.protobuf.DbProjectBranches;
+import org.sonar.server.project.Project;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
+import static org.sonar.core.config.PurgeConstants.BRANCHES_TO_KEEP_WHEN_INACTIVE;
+import static org.sonar.db.component.BranchType.BRANCH;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
+
+@RunWith(DataProviderRunner.class)
+public class BranchPersisterImplIT {
+ private final static Component MAIN = builder(Component.Type.PROJECT, 1, "PROJECT_KEY").setUuid("PROJECT_UUID").setName("p1").build();
+ private final static Component BRANCH1 = builder(Component.Type.PROJECT, 2, "BRANCH_KEY").setUuid("BRANCH_UUID").build();
+ private final static Component PR1 = builder(Component.Type.PROJECT, 3, "develop").setUuid("PR_UUID").build();
+ private static final Project PROJECT = new Project(MAIN.getUuid(), MAIN.getKey(), MAIN.getName(), null, Collections.emptyList());
+
+ @Rule
+ public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+
+ private final MapSettings settings = new MapSettings();
+ private final ConfigurationRepository configurationRepository = new TestSettingsRepository(new ConfigurationBridge(settings));
+ private final BranchPersister underTest = new BranchPersisterImpl(dbTester.getDbClient(), treeRootHolder, analysisMetadataHolder, configurationRepository);
+
+ @Test
+ public void persist_fails_with_ISE_if_no_component_for_main_branches() {
+ analysisMetadataHolder.setBranch(createBranch(BRANCH, true, "master"));
+ treeRootHolder.setRoot(MAIN);
+ DbSession dbSession = dbTester.getSession();
+
+ expectMissingComponentISE(() -> underTest.persist(dbSession));
+ }
+
+ @Test
+ public void persist_fails_with_ISE_if_no_component_for_branches() {
+ analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "foo"));
+ treeRootHolder.setRoot(BRANCH1);
+ DbSession dbSession = dbTester.getSession();
+
+ expectMissingComponentISE(() -> underTest.persist(dbSession));
+ }
+
+ @Test
+ public void persist_fails_with_ISE_if_no_component_for_pull_request() {
+ analysisMetadataHolder.setBranch(createBranch(BranchType.PULL_REQUEST, false, "12"));
+ treeRootHolder.setRoot(BRANCH1);
+ DbSession dbSession = dbTester.getSession();
+
+ expectMissingComponentISE(() -> underTest.persist(dbSession));
+ }
+
+ @Test
+ @UseDataProvider("nullOrNotNullString")
+ public void persist_creates_row_in_PROJECTS_BRANCHES_for_branch(@Nullable String mergeBranchUuid) {
+ String branchName = "branch";
+
+ // add project and branch in table PROJECTS
+ ComponentDto mainComponent = ComponentTesting.newPrivateProjectDto(MAIN.getUuid()).setKey(MAIN.getKey());
+ ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
+ new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
+ dbTester.components().insertComponents(mainComponent, component);
+ // set project in metadata
+ treeRootHolder.setRoot(BRANCH1);
+ analysisMetadataHolder.setBranch(createBranch(BRANCH, false, branchName, mergeBranchUuid));
+ analysisMetadataHolder.setProject(Project.from(mainComponent));
+
+ underTest.persist(dbTester.getSession());
+
+ dbTester.getSession().commit();
+
+ assertThat(dbTester.countRowsOfTable("components")).isEqualTo(2);
+ Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
+ assertThat(branchDto).isPresent();
+ assertThat(branchDto.get().getBranchType()).isEqualTo(BRANCH);
+ assertThat(branchDto.get().getKey()).isEqualTo(branchName);
+ assertThat(branchDto.get().getMergeBranchUuid()).isEqualTo(mergeBranchUuid);
+ assertThat(branchDto.get().getProjectUuid()).isEqualTo(MAIN.getUuid());
+ assertThat(branchDto.get().getPullRequestData()).isNull();
+ }
+
+ @Test
+ public void main_branch_is_excluded_from_branch_purge_by_default() {
+ analysisMetadataHolder.setBranch(createBranch(BRANCH, true, "master"));
+ treeRootHolder.setRoot(MAIN);
+ dbTester.components().insertPublicProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid()));
+ dbTester.commit();
+
+ underTest.persist(dbTester.getSession());
+
+ Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), MAIN.getUuid());
+ assertThat(branchDto).isPresent();
+ assertThat(branchDto.get().isExcludeFromPurge()).isTrue();
+ }
+
+ @Test
+ public void non_main_branch_is_excluded_from_branch_purge_if_matches_sonar_dbcleaner_keepFromPurge_property() {
+ settings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "BRANCH.*");
+ analysisMetadataHolder.setProject(PROJECT);
+ analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "BRANCH_KEY"));
+ treeRootHolder.setRoot(BRANCH1);
+ ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid()));
+ ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
+ new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
+ dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component);
+ dbTester.commit();
+
+ underTest.persist(dbTester.getSession());
+
+ Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
+ assertThat(branchDto).isPresent();
+ assertThat(branchDto.get().isExcludeFromPurge()).isTrue();
+ }
+
+ @Test
+ public void branch_is_excluded_from_purge_when_it_matches_setting() {
+ analysisMetadataHolder.setProject(PROJECT);
+ analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "BRANCH_KEY"));
+ treeRootHolder.setRoot(BRANCH1);
+ ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid()));
+ ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
+ new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
+ dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component);
+ settings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "BRANCH.*");
+ dbTester.commit();
+
+ underTest.persist(dbTester.getSession());
+
+ Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
+ assertThat(branchDto).isPresent();
+ assertThat(branchDto.get().isExcludeFromPurge()).isTrue();
+ }
+
+ @Test
+ public void branch_is_not_excluded_from_purge_when_it_does_not_match_setting() {
+ analysisMetadataHolder.setProject(PROJECT);
+ analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "BRANCH_KEY"));
+ treeRootHolder.setRoot(BRANCH1);
+ ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid()));
+ ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
+ new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
+ dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component);
+ settings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "abc.*");
+
+ dbTester.commit();
+
+ underTest.persist(dbTester.getSession());
+
+ Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
+ assertThat(branchDto).isPresent();
+ assertThat(branchDto.get().isExcludeFromPurge()).isFalse();
+ }
+
+ @Test
+ public void pull_request_is_never_excluded_from_branch_purge_even_if_its_source_branch_name_matches_sonar_dbcleaner_keepFromPurge_property() {
+ settings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "develop");
+ analysisMetadataHolder.setBranch(createPullRequest(PR1.getKey(), MAIN.getUuid()));
+ analysisMetadataHolder.setPullRequestKey(PR1.getKey());
+ treeRootHolder.setRoot(PR1);
+ ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid()));
+ ComponentDto component = ComponentTesting.newBranchComponent(mainComponent, new BranchDto()
+ .setUuid(PR1.getUuid())
+ .setKey(PR1.getKey())
+ .setProjectUuid(MAIN.getUuid())
+ .setBranchType(PULL_REQUEST)
+ .setMergeBranchUuid(MAIN.getUuid()));
+ dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component);
+ dbTester.commit();
+
+ underTest.persist(dbTester.getSession());
+
+ Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), PR1.getUuid());
+ assertThat(branchDto).isPresent();
+ assertThat(branchDto.get().isExcludeFromPurge()).isFalse();
+ }
+
+ @Test
+ public void non_main_branch_is_included_in_branch_purge_if_branch_name_does_not_match_sonar_dbcleaner_keepFromPurge_property() {
+ settings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "foobar-.*");
+ analysisMetadataHolder.setProject(PROJECT);
+ analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "BRANCH_KEY"));
+ treeRootHolder.setRoot(BRANCH1);
+ ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid()));
+ ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
+ new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
+ dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component);
+ dbTester.commit();
+
+ underTest.persist(dbTester.getSession());
+
+ Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
+ assertThat(branchDto).isPresent();
+ assertThat(branchDto.get().isExcludeFromPurge()).isFalse();
+ }
+
+ @DataProvider
+ public static Object[][] nullOrNotNullString() {
+ return new Object[][] {
+ {null},
+ {randomAlphabetic(12)}
+ };
+ }
+
+ @Test
+ public void persist_creates_row_in_PROJECTS_BRANCHES_for_pull_request() {
+ String pullRequestId = "pr-123";
+
+ // add project and branch in table PROJECTS
+ ComponentDto mainComponent = ComponentTesting.newPrivateProjectDto(MAIN.getUuid()).setKey(MAIN.getKey());
+ ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
+ new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(PULL_REQUEST));
+ dbTester.components().insertComponents(mainComponent, component);
+ // set project in metadata
+ treeRootHolder.setRoot(BRANCH1);
+ analysisMetadataHolder.setBranch(createBranch(PULL_REQUEST, false, pullRequestId, "mergeBanchUuid"));
+ analysisMetadataHolder.setProject(Project.from(mainComponent));
+ analysisMetadataHolder.setPullRequestKey(pullRequestId);
+
+ underTest.persist(dbTester.getSession());
+
+ dbTester.getSession().commit();
+
+ assertThat(dbTester.countRowsOfTable("components")).isEqualTo(2);
+ Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
+ assertThat(branchDto).isPresent();
+ assertThat(branchDto.get().getBranchType()).isEqualTo(PULL_REQUEST);
+ assertThat(branchDto.get().getKey()).isEqualTo(pullRequestId);
+ assertThat(branchDto.get().getMergeBranchUuid()).isEqualTo("mergeBanchUuid");
+ assertThat(branchDto.get().getProjectUuid()).isEqualTo(MAIN.getUuid());
+ assertThat(branchDto.get().getPullRequestData()).isEqualTo(DbProjectBranches.PullRequestData.newBuilder()
+ .setBranch(pullRequestId)
+ .setTarget("mergeBanchUuid")
+ .setTitle(pullRequestId)
+ .build());
+ }
+
+ private static Branch createBranch(BranchType type, boolean isMain, String name) {
+ return createBranch(type, isMain, name, null);
+ }
+
+ private static Branch createPullRequest(String key, String mergeBranchUuid) {
+ Branch branch = createBranch(PULL_REQUEST, false, key, mergeBranchUuid);
+ when(branch.getPullRequestKey()).thenReturn(key);
+ return branch;
+ }
+
+ private static Branch createBranch(BranchType type, boolean isMain, String name, @Nullable String mergeBranchUuid) {
+ Branch branch = mock(Branch.class);
+ when(branch.getType()).thenReturn(type);
+ when(branch.getName()).thenReturn(name);
+ when(branch.isMain()).thenReturn(isMain);
+ when(branch.getReferenceBranchUuid()).thenReturn(mergeBranchUuid);
+ when(branch.getTargetBranchName()).thenReturn(mergeBranchUuid);
+ return branch;
+ }
+
+ private void expectMissingComponentISE(ThrowableAssert.ThrowingCallable callable) {
+ assertThatThrownBy(callable)
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Component has been deleted by end-user during analysis");
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.filemove;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.IntStream;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.ce.task.projectanalysis.analysis.Analysis;
+import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.FileAttributes;
+import org.sonar.ce.task.projectanalysis.component.ReportComponent;
+import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
+import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepository;
+import org.sonar.ce.task.step.TestComputationStepContext;
+import org.sonar.core.hash.SourceLineHashesComputer;
+import org.sonar.core.util.Uuids;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.source.FileSourceDto;
+
+import static java.util.Arrays.stream;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
+import static org.sonar.ce.task.projectanalysis.filemove.FileMoveDetectionStep.MIN_REQUIRED_SCORE;
+import static org.sonar.db.component.BranchType.*;
+
+public class FileMoveDetectionStepIT {
+
+ private static final String SNAPSHOT_UUID = "uuid_1";
+ private static final Analysis ANALYSIS = new Analysis.Builder()
+ .setUuid(SNAPSHOT_UUID)
+ .setCreatedAt(86521)
+ .build();
+ private static final int ROOT_REF = 1;
+ private static final int FILE_1_REF = 2;
+ private static final int FILE_2_REF = 3;
+ private static final int FILE_3_REF = 4;
+ private static final String[] CONTENT1 = {
+ "package org.sonar.ce.task.projectanalysis.filemove;",
+ "",
+ "public class Foo {",
+ " public String bar() {",
+ " return \"Doh!\";",
+ " }",
+ "}"
+ };
+
+ private static final String[] LESS_CONTENT1 = {
+ "package org.sonar.ce.task.projectanalysis.filemove;",
+ "",
+ "public class Foo {",
+ " public String foo() {",
+ " return \"Donut!\";",
+ " }",
+ "}"
+ };
+ private static final String[] CONTENT_EMPTY = {
+ ""
+ };
+ private static final String[] CONTENT2 = {
+ "package org.sonar.ce.queue;",
+ "",
+ "import com.google.common.base.MoreObjects;",
+ "import javax.annotation.CheckForNull;",
+ "import javax.annotation.Nullable;",
+ "import javax.annotation.concurrent.Immutable;",
+ "",
+ "import static com.google.common.base.Strings.emptyToNull;",
+ "import static java.util.Objects.requireNonNull;",
+ "",
+ "@Immutable",
+ "public class CeTask {",
+ "",
+ ", private final String type;",
+ ", private final String uuid;",
+ ", private final String componentUuid;",
+ ", private final String componentKey;",
+ ", private final String componentName;",
+ ", private final String submitterLogin;",
+ "",
+ ", private CeTask(Builder builder) {",
+ ", this.uuid = requireNonNull(emptyToNull(builder.uuid));",
+ ", this.type = requireNonNull(emptyToNull(builder.type));",
+ ", this.componentUuid = emptyToNull(builder.componentUuid);",
+ ", this.componentKey = emptyToNull(builder.componentKey);",
+ ", this.componentName = emptyToNull(builder.componentName);",
+ ", this.submitterLogin = emptyToNull(builder.submitterLogin);",
+ ", }",
+ "",
+ ", public String getUuid() {",
+ ", return uuid;",
+ ", }",
+ "",
+ ", public String getType() {",
+ ", return type;",
+ ", }",
+ "",
+ ", @CheckForNull",
+ ", public String getComponentUuid() {",
+ ", return componentUuid;",
+ ", }",
+ "",
+ ", @CheckForNull",
+ ", public String getComponentKey() {",
+ ", return componentKey;",
+ ", }",
+ "",
+ ", @CheckForNull",
+ ", public String getComponentName() {",
+ ", return componentName;",
+ ", }",
+ "",
+ ", @CheckForNull",
+ ", public String getSubmitterLogin() {",
+ ", return submitterLogin;",
+ ", }",
+ ",}",
+ };
+ // removed immutable annotation
+ private static final String[] LESS_CONTENT2 = {
+ "package org.sonar.ce.queue;",
+ "",
+ "import com.google.common.base.MoreObjects;",
+ "import javax.annotation.CheckForNull;",
+ "import javax.annotation.Nullable;",
+ "",
+ "import static com.google.common.base.Strings.emptyToNull;",
+ "import static java.util.Objects.requireNonNull;",
+ "",
+ "public class CeTask {",
+ "",
+ ", private final String type;",
+ ", private final String uuid;",
+ ", private final String componentUuid;",
+ ", private final String componentKey;",
+ ", private final String componentName;",
+ ", private final String submitterLogin;",
+ "",
+ ", private CeTask(Builder builder) {",
+ ", this.uuid = requireNonNull(emptyToNull(builder.uuid));",
+ ", this.type = requireNonNull(emptyToNull(builder.type));",
+ ", this.componentUuid = emptyToNull(builder.componentUuid);",
+ ", this.componentKey = emptyToNull(builder.componentKey);",
+ ", this.componentName = emptyToNull(builder.componentName);",
+ ", this.submitterLogin = emptyToNull(builder.submitterLogin);",
+ ", }",
+ "",
+ ", public String getUuid() {",
+ ", return uuid;",
+ ", }",
+ "",
+ ", public String getType() {",
+ ", return type;",
+ ", }",
+ "",
+ ", @CheckForNull",
+ ", public String getComponentUuid() {",
+ ", return componentUuid;",
+ ", }",
+ "",
+ ", @CheckForNull",
+ ", public String getComponentKey() {",
+ ", return componentKey;",
+ ", }",
+ "",
+ ", @CheckForNull",
+ ", public String getComponentName() {",
+ ", return componentName;",
+ ", }",
+ "",
+ ", @CheckForNull",
+ ", public String getSubmitterLogin() {",
+ ", return submitterLogin;",
+ ", }",
+ ",}",
+ };
+
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+ @Rule
+ public MutableMovedFilesRepositoryRule movedFilesRepository = new MutableMovedFilesRepositoryRule();
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ private final DbClient dbClient = dbTester.getDbClient();
+ private ComponentDto project;
+
+ private final AnalysisMetadataHolderRule analysisMetadataHolder = mock(AnalysisMetadataHolderRule.class);
+ private final SourceLinesHashRepository sourceLinesHash = mock(SourceLinesHashRepository.class);
+ private final FileSimilarity fileSimilarity = new FileSimilarityImpl(new SourceSimilarityImpl());
+ private final CapturingScoreMatrixDumper scoreMatrixDumper = new CapturingScoreMatrixDumper();
+ private final RecordingMutableAddedFileRepository addedFileRepository = new RecordingMutableAddedFileRepository();
+
+ private final FileMoveDetectionStep underTest = new FileMoveDetectionStep(analysisMetadataHolder, treeRootHolder, dbClient,
+ fileSimilarity, movedFilesRepository, sourceLinesHash, scoreMatrixDumper, addedFileRepository);
+
+ @Before
+ public void setUp() throws Exception {
+ project = dbTester.components().insertPrivateProject();
+ treeRootHolder.setRoot(builder(Component.Type.PROJECT, ROOT_REF).setUuid(project.uuid()).build());
+ }
+
+ @Test
+ public void getDescription_returns_description() {
+ assertThat(underTest.getDescription()).isEqualTo("Detect file moves");
+ }
+
+ @Test
+ public void execute_detects_no_move_if_in_pull_request_scope() {
+ prepareAnalysis(PULL_REQUEST, ANALYSIS);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ verifyStatistics(context, null, null, null, null);
+ }
+
+ @Test
+ public void execute_detects_no_move_on_first_analysis() {
+ prepareAnalysis(BRANCH, null);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ verifyStatistics(context, 0, null, null, null);
+ }
+
+ @Test
+ public void execute_detects_no_move_if_baseSnapshot_has_no_file_and_report_has_no_file() {
+ prepareBranchAnalysis(ANALYSIS);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ assertThat(addedFileRepository.getComponents()).isEmpty();
+ verifyStatistics(context, 0, null, null, null);
+ }
+
+ @Test
+ public void execute_detects_no_move_if_baseSnapshot_has_no_file() {
+ prepareBranchAnalysis(ANALYSIS);
+ Component file1 = fileComponent(FILE_1_REF, null);
+ Component file2 = fileComponent(FILE_2_REF, null);
+ setFilesInReport(file1, file2);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ assertThat(addedFileRepository.getComponents()).containsOnly(file1, file2);
+ verifyStatistics(context, 2, 0, 2, null);
+ }
+
+ @Test
+ public void execute_detects_no_move_if_there_is_no_file_in_report() {
+ prepareBranchAnalysis(ANALYSIS);
+ insertFiles( /* no components */);
+ setFilesInReport();
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ assertThat(addedFileRepository.getComponents()).isEmpty();
+ verifyStatistics(context, 0, null, null, null);
+ }
+
+ @Test
+ public void execute_detects_no_move_if_file_key_exists_in_both_DB_and_report() {
+ prepareBranchAnalysis(ANALYSIS);
+ Component file1 = fileComponent(FILE_1_REF, null);
+ Component file2 = fileComponent(FILE_2_REF, null);
+ insertFiles(file1.getUuid(), file2.getUuid());
+ insertContentOfFileInDb(file1.getUuid(), CONTENT1);
+ insertContentOfFileInDb(file2.getUuid(), CONTENT2);
+ setFilesInReport(file2, file1);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ assertThat(addedFileRepository.getComponents()).isEmpty();
+ verifyStatistics(context, 2, 2, 0, null);
+ }
+
+ @Test
+ public void execute_detects_move_if_content_of_file_is_same_in_DB_and_report() {
+ prepareBranchAnalysis(ANALYSIS);
+ Component file1 = fileComponent(FILE_1_REF, null);
+ Component file2 = fileComponent(FILE_2_REF, CONTENT1);
+ ComponentDto[] dtos = insertFiles(file1.getUuid());
+ insertContentOfFileInDb(file1.getUuid(), CONTENT1);
+ setFilesInReport(file2);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).containsExactly(file2);
+ MovedFilesRepository.OriginalFile originalFile = movedFilesRepository.getOriginalFile(file2).get();
+ assertThat(originalFile.key()).isEqualTo(dtos[0].getKey());
+ assertThat(originalFile.uuid()).isEqualTo(dtos[0].uuid());
+ assertThat(addedFileRepository.getComponents()).isEmpty();
+ verifyStatistics(context, 1, 1, 1, 1);
+ }
+
+ @Test
+ public void execute_detects_no_move_if_content_of_file_is_not_similar_enough() {
+ prepareBranchAnalysis(ANALYSIS);
+ Component file1 = fileComponent(FILE_1_REF, null);
+ Component file2 = fileComponent(FILE_2_REF, LESS_CONTENT1);
+ insertFiles(file1.getKey());
+ insertContentOfFileInDb(file1.getKey(), CONTENT1);
+ setFilesInReport(file2);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore())
+ .isPositive()
+ .isLessThan(MIN_REQUIRED_SCORE);
+ assertThat(addedFileRepository.getComponents()).contains(file2);
+ verifyStatistics(context, 1, 1, 1, 0);
+ }
+
+ @Test
+ public void execute_detects_no_move_if_content_of_file_is_empty_in_DB() {
+ prepareBranchAnalysis(ANALYSIS);
+ Component file1 = fileComponent(FILE_1_REF, null);
+ Component file2 = fileComponent(FILE_2_REF, CONTENT1);
+ insertFiles(file1.getKey());
+ insertContentOfFileInDb(file1.getKey(), CONTENT_EMPTY);
+ setFilesInReport(file2);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore()).isZero();
+ assertThat(addedFileRepository.getComponents()).contains(file2);
+ verifyStatistics(context, 1, 1, 1, 0);
+ }
+
+ @Test
+ public void execute_detects_no_move_if_content_of_file_has_no_path_in_DB() {
+ prepareBranchAnalysis(ANALYSIS);
+ Component file1 = fileComponent(FILE_1_REF, null);
+ Component file2 = fileComponent(FILE_2_REF, CONTENT1);
+ insertFiles(key -> newComponentDto(key).setPath(null), file1.getKey());
+ insertContentOfFileInDb(file1.getKey(), CONTENT1);
+ setFilesInReport(file2);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ assertThat(scoreMatrixDumper.scoreMatrix).isNull();
+ assertThat(addedFileRepository.getComponents()).containsOnly(file2);
+ verifyStatistics(context, 1, 0, 1, null);
+ }
+
+ @Test
+ public void execute_detects_no_move_if_content_of_file_is_empty_in_report() {
+ prepareBranchAnalysis(ANALYSIS);
+ Component file1 = fileComponent(FILE_1_REF, null);
+ Component file2 = fileComponent(FILE_2_REF, CONTENT_EMPTY);
+ insertFiles(file1.getKey());
+ insertContentOfFileInDb(file1.getKey(), CONTENT1);
+ setFilesInReport(file2);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore()).isZero();
+ assertThat(addedFileRepository.getComponents()).contains(file2);
+ verifyStatistics(context, 1, 1, 1, 0);
+ assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("max score in matrix is less than min required score (85). Do nothing.");
+ }
+
+ @Test
+ public void execute_detects_no_move_if_two_added_files_have_same_content_as_the_one_in_db() {
+ prepareBranchAnalysis(ANALYSIS);
+ Component file1 = fileComponent(FILE_1_REF, null);
+ Component file2 = fileComponent(FILE_2_REF, CONTENT1);
+ Component file3 = fileComponent(FILE_3_REF, CONTENT1);
+ insertFiles(file1.getKey());
+ insertContentOfFileInDb(file1.getKey(), CONTENT1);
+ setFilesInReport(file2, file3);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore()).isEqualTo(100);
+ assertThat(addedFileRepository.getComponents()).containsOnly(file2, file3);
+ verifyStatistics(context, 2, 1, 2, 0);
+ }
+
+ @Test
+ public void execute_detects_no_move_if_two_deleted_files_have_same_content_as_the_one_added() {
+ prepareBranchAnalysis(ANALYSIS);
+ Component file1 = fileComponent(FILE_1_REF, null);
+ Component file2 = fileComponent(FILE_2_REF, null);
+ Component file3 = fileComponent(FILE_3_REF, CONTENT1);
+ insertFiles(file1.getUuid(), file2.getUuid());
+ insertContentOfFileInDb(file1.getUuid(), CONTENT1);
+ insertContentOfFileInDb(file2.getUuid(), CONTENT1);
+ setFilesInReport(file3);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore()).isEqualTo(100);
+ assertThat(addedFileRepository.getComponents()).containsOnly(file3);
+ verifyStatistics(context, 1, 2, 1, 0);
+ }
+
+ @Test
+ public void execute_detects_no_move_if_two_files_are_empty_in_DB() {
+ prepareBranchAnalysis(ANALYSIS);
+ Component file1 = fileComponent(FILE_1_REF, null);
+ Component file2 = fileComponent(FILE_2_REF, null);
+ insertFiles(file1.getUuid(), file2.getUuid());
+ insertContentOfFileInDb(file1.getUuid(), null);
+ insertContentOfFileInDb(file2.getUuid(), null);
+ setFilesInReport(file1, file2);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ assertThat(scoreMatrixDumper.scoreMatrix).isNull();
+ assertThat(addedFileRepository.getComponents()).isEmpty();
+ verifyStatistics(context, 2, 2, 0, null);
+ }
+
+ @Test
+ public void execute_detects_several_moves() {
+ // testing:
+ // - file1 renamed to file3
+ // - file2 deleted
+ // - file4 untouched
+ // - file5 renamed to file6 with a small change
+ prepareBranchAnalysis(ANALYSIS);
+ Component file1 = fileComponent(FILE_1_REF, null);
+ Component file2 = fileComponent(FILE_2_REF, null);
+ Component file3 = fileComponent(FILE_3_REF, CONTENT1);
+ Component file4 = fileComponent(5, new String[] {"a", "b"});
+ Component file5 = fileComponent(6, null);
+ Component file6 = fileComponent(7, LESS_CONTENT2);
+ ComponentDto[] dtos = insertFiles(file1.getUuid(), file2.getUuid(), file4.getUuid(), file5.getUuid());
+ insertContentOfFileInDb(file1.getUuid(), CONTENT1);
+ insertContentOfFileInDb(file2.getUuid(), LESS_CONTENT1);
+ insertContentOfFileInDb(file4.getUuid(), new String[] {"e", "f", "g", "h", "i"});
+ insertContentOfFileInDb(file5.getUuid(), CONTENT2);
+ setFilesInReport(file3, file4, file6);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).containsOnly(file3, file6);
+ MovedFilesRepository.OriginalFile originalFile2 = movedFilesRepository.getOriginalFile(file3).get();
+ assertThat(originalFile2.key()).isEqualTo(dtos[0].getKey());
+ assertThat(originalFile2.uuid()).isEqualTo(dtos[0].uuid());
+ MovedFilesRepository.OriginalFile originalFile5 = movedFilesRepository.getOriginalFile(file6).get();
+ assertThat(originalFile5.key()).isEqualTo(dtos[3].getKey());
+ assertThat(originalFile5.uuid()).isEqualTo(dtos[3].uuid());
+ assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore()).isGreaterThan(MIN_REQUIRED_SCORE);
+ assertThat(addedFileRepository.getComponents()).isEmpty();
+ verifyStatistics(context, 3, 4, 2, 2);
+ }
+
+ @Test
+ public void execute_does_not_compute_any_distance_if_all_files_sizes_are_all_too_different() {
+ prepareBranchAnalysis(ANALYSIS);
+ Component file1 = fileComponent(FILE_1_REF, null);
+ Component file2 = fileComponent(FILE_2_REF, null);
+ Component file3 = fileComponent(FILE_3_REF, arrayOf(118));
+ Component file4 = fileComponent(5, arrayOf(25));
+ insertFiles(file1.getKey(), file2.getKey());
+ insertContentOfFileInDb(file1.getKey(), arrayOf(100));
+ insertContentOfFileInDb(file2.getKey(), arrayOf(30));
+ setFilesInReport(file3, file4);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore()).isZero();
+ verifyStatistics(context, 2, 2, 2, 0);
+ }
+
+ /**
+ * Creates an array of {@code numberOfElements} int values as String, starting with zero.
+ */
+ private static String[] arrayOf(int numberOfElements) {
+ return IntStream.range(0, numberOfElements).mapToObj(String::valueOf).toArray(String[]::new);
+ }
+
+ /**
+ * JH: A bug was encountered in the algorithm and I didn't manage to forge a simpler test case.
+ */
+ @Test
+ public void real_life_use_case() throws Exception {
+ prepareBranchAnalysis(ANALYSIS);
+ for (File f : FileUtils.listFiles(new File("src/it/resources/org/sonar/ce/task/projectanalysis/filemove/FileMoveDetectionStepIT/v1"), null, false)) {
+ insertFiles("uuid_" + f.getName().hashCode());
+ insertContentOfFileInDb("uuid_" + f.getName().hashCode(), readLines(f));
+ }
+
+ Map<String, Component> comps = new HashMap<>();
+ int i = 1;
+ for (File f : FileUtils.listFiles(new File("src/it/resources/org/sonar/ce/task/projectanalysis/filemove/FileMoveDetectionStepIT/v2"), null, false)) {
+ String[] lines = readLines(f);
+ Component c = builder(Component.Type.FILE, i++)
+ .setUuid("uuid_" + f.getName().hashCode())
+ .setKey(f.getName())
+ .setName(f.getName())
+ .setFileAttributes(new FileAttributes(false, null, lines.length))
+ .build();
+
+ comps.put(f.getName(), c);
+ setFileLineHashesInReport(c, lines);
+ }
+
+ setFilesInReport(comps.values().toArray(new Component[0]));
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ Component makeComponentUuidAndAnalysisUuidNotNullOnDuplicationsIndex = comps.get("MakeComponentUuidAndAnalysisUuidNotNullOnDuplicationsIndex.java");
+ Component migrationRb1238 = comps.get("1238_make_component_uuid_and_analysis_uuid_not_null_on_duplications_index.rb");
+ Component addComponentUuidAndAnalysisUuidColumnToDuplicationsIndex = comps.get("AddComponentUuidAndAnalysisUuidColumnToDuplicationsIndex.java");
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).containsOnly(
+ makeComponentUuidAndAnalysisUuidNotNullOnDuplicationsIndex,
+ migrationRb1238,
+ addComponentUuidAndAnalysisUuidColumnToDuplicationsIndex);
+
+ assertThat(movedFilesRepository.getOriginalFile(makeComponentUuidAndAnalysisUuidNotNullOnDuplicationsIndex).get().uuid())
+ .isEqualTo("uuid_" + "MakeComponentUuidNotNullOnDuplicationsIndex.java".hashCode());
+ assertThat(movedFilesRepository.getOriginalFile(migrationRb1238).get().uuid())
+ .isEqualTo("uuid_" + "1242_make_analysis_uuid_not_null_on_duplications_index.rb".hashCode());
+ assertThat(movedFilesRepository.getOriginalFile(addComponentUuidAndAnalysisUuidColumnToDuplicationsIndex).get().uuid())
+ .isEqualTo("uuid_" + "AddComponentUuidColumnToDuplicationsIndex.java".hashCode());
+ verifyStatistics(context, comps.values().size(), 12, 6, 3);
+ }
+
+ private String[] readLines(File filename) throws IOException {
+ return FileUtils
+ .readLines(filename, StandardCharsets.UTF_8)
+ .toArray(new String[0]);
+ }
+
+ @CheckForNull
+ private FileSourceDto insertContentOfFileInDb(String uuid, @Nullable String[] content) {
+ return dbTester.getDbClient().componentDao().selectByUuid(dbTester.getSession(), uuid)
+ .map(file -> {
+ SourceLineHashesComputer linesHashesComputer = new SourceLineHashesComputer();
+ if (content != null) {
+ stream(content).forEach(linesHashesComputer::addLine);
+ }
+ FileSourceDto fileSourceDto = new FileSourceDto()
+ .setUuid(Uuids.createFast())
+ .setFileUuid(file.uuid())
+ .setProjectUuid(file.branchUuid())
+ .setLineHashes(linesHashesComputer.getLineHashes());
+ dbTester.getDbClient().fileSourceDao().insert(dbTester.getSession(), fileSourceDto);
+ dbTester.commit();
+ return fileSourceDto;
+ }).orElse(null);
+ }
+
+ private void setFilesInReport(Component... files) {
+ treeRootHolder.setRoot(builder(Component.Type.PROJECT, ROOT_REF)
+ .setUuid(project.uuid())
+ .addChildren(files)
+ .build());
+ }
+
+ private ComponentDto[] insertFiles(String... uuids) {
+ return insertFiles(this::newComponentDto, uuids);
+ }
+
+ private ComponentDto[] insertFiles(Function<String, ComponentDto> newComponentDto, String... uuids) {
+ return stream(uuids)
+ .map(newComponentDto)
+ .map(dto -> dbTester.components().insertComponent(dto))
+ .toArray(ComponentDto[]::new);
+ }
+
+ private ComponentDto newComponentDto(String uuid) {
+ return ComponentTesting.newFileDto(project)
+ .setKey("key_" + uuid)
+ .setUuid(uuid)
+ .setPath("path_" + uuid);
+ }
+
+ private Component fileComponent(int ref, @Nullable String[] content) {
+ ReportComponent component = builder(Component.Type.FILE, ref)
+ .setName("report_path" + ref)
+ .setFileAttributes(new FileAttributes(false, null, content == null ? 1 : content.length))
+ .build();
+ if (content != null) {
+ setFileLineHashesInReport(component, content);
+ }
+ return component;
+ }
+
+ private void setFileLineHashesInReport(Component file, String[] content) {
+ SourceLineHashesComputer computer = new SourceLineHashesComputer();
+ for (String line : content) {
+ computer.addLine(line);
+ }
+ when(sourceLinesHash.getLineHashesMatchingDBVersion(file)).thenReturn(computer.getLineHashes());
+ }
+
+ private static class CapturingScoreMatrixDumper implements ScoreMatrixDumper {
+ private ScoreMatrix scoreMatrix;
+
+ @Override
+ public void dumpAsCsv(ScoreMatrix scoreMatrix) {
+ this.scoreMatrix = scoreMatrix;
+ }
+ }
+
+ private void prepareBranchAnalysis(Analysis analysis) {
+ prepareAnalysis(BRANCH, analysis);
+ }
+
+ private void prepareAnalysis(BranchType branch, Analysis analysis) {
+ mockBranchType(branch);
+ analysisMetadataHolder.setBaseAnalysis(analysis);
+ }
+
+ private void mockBranchType(BranchType branchType) {
+ when(analysisMetadataHolder.isPullRequest()).thenReturn(branchType == PULL_REQUEST);
+ }
+
+ public static void verifyStatistics(TestComputationStepContext context,
+ @Nullable Integer expectedReportFiles, @Nullable Integer expectedDbFiles,
+ @Nullable Integer expectedAddedFiles, @Nullable Integer expectedMovedFiles) {
+ context.getStatistics().assertValue("reportFiles", expectedReportFiles);
+ context.getStatistics().assertValue("dbFiles", expectedDbFiles);
+ context.getStatistics().assertValue("addedFiles", expectedAddedFiles);
+ context.getStatistics().assertValue("movedFiles", expectedMovedFiles);
+ }
+
+ public static class RecordingMutableAddedFileRepository implements MutableAddedFileRepository {
+ private final List<Component> components = new ArrayList<>();
+
+ @Override
+ public void register(Component file) {
+ components.add(file);
+ }
+
+ @Override
+ public boolean isAdded(Component component) {
+ throw new UnsupportedOperationException("isAdded should not be called");
+ }
+
+ public List<Component> getComponents() {
+ return components;
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.filemove;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.ce.task.projectanalysis.analysis.Analysis;
+import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
+import org.sonar.ce.task.projectanalysis.analysis.Branch;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.FileAttributes;
+import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
+import org.sonar.ce.task.projectanalysis.filemove.FileMoveDetectionStepIT.RecordingMutableAddedFileRepository;
+import org.sonar.ce.task.projectanalysis.filemove.MovedFilesRepository.OriginalFile;
+import org.sonar.ce.task.step.TestComputationStepContext;
+import org.sonar.core.util.Uuids;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.source.FileSourceDto;
+import org.sonar.server.project.Project;
+
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
+import static java.util.stream.Collectors.toSet;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
+import static org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT;
+import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
+import static org.sonar.ce.task.projectanalysis.filemove.FileMoveDetectionStepIT.verifyStatistics;
+import static org.sonar.db.component.BranchType.BRANCH;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
+
+public class PullRequestFileMoveDetectionStepIT {
+ private static final String ROOT_REF = "0";
+ private static final String FILE_1_REF = "1";
+ private static final String FILE_2_REF = "2";
+ private static final String FILE_3_REF = "3";
+ private static final String FILE_4_REF = "4";
+ private static final String FILE_5_REF = "5";
+ private static final String FILE_6_REF = "6";
+ private static final String FILE_7_REF = "7";
+ private static final String TARGET_BRANCH = "target_branch";
+ private static final String BRANCH_UUID = "branch_uuid";
+ private static final String SNAPSHOT_UUID = "uuid_1";
+
+ private static final Analysis ANALYSIS = new Analysis.Builder()
+ .setUuid(SNAPSHOT_UUID)
+ .setCreatedAt(86521)
+ .build();
+
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+ @Rule
+ public MutableMovedFilesRepositoryRule movedFilesRepository = new MutableMovedFilesRepositoryRule();
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ private ComponentDto branch;
+ private ComponentDto project;
+
+ private final DbClient dbClient = dbTester.getDbClient();
+ private final AnalysisMetadataHolderRule analysisMetadataHolder = mock(AnalysisMetadataHolderRule.class);
+ private final RecordingMutableAddedFileRepository addedFileRepository = new RecordingMutableAddedFileRepository();
+ private final PullRequestFileMoveDetectionStep underTest = new PullRequestFileMoveDetectionStep(analysisMetadataHolder, treeRootHolder, dbClient, movedFilesRepository, addedFileRepository);
+
+ @Before
+ public void setUp() throws Exception {
+ project = dbTester.components().insertPrivateProject();
+ branch = dbTester.components().insertProjectBranch(project, branchDto -> branchDto.setUuid(BRANCH_UUID).setKey(TARGET_BRANCH));
+ treeRootHolder.setRoot(builder(Component.Type.PROJECT, Integer.parseInt(ROOT_REF)).setUuid(project.uuid()).build());
+ }
+
+ @Test
+ public void getDescription_returns_description() {
+ assertThat(underTest.getDescription()).isEqualTo("Detect file moves in Pull Request scope");
+ }
+
+ @Test
+ public void execute_does_not_detect_any_files_if_not_in_pull_request_scope() {
+ prepareAnalysis(BRANCH, ANALYSIS);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ verifyStatistics(context, null, null, null, null);
+ }
+
+ @Test
+ public void execute_detects_no_move_if_report_has_no_file() {
+ preparePullRequestAnalysis(ANALYSIS);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ assertThat(addedFileRepository.getComponents()).isEmpty();
+ verifyStatistics(context, 0, null, null, null);
+ }
+
+ @Test
+ public void execute_detects_no_move_if_target_branch_has_no_files() {
+ preparePullRequestAnalysis(ANALYSIS);
+ Set<FileReference> fileReferences = Set.of(FileReference.of(FILE_1_REF), FileReference.of(FILE_2_REF));
+ Map<String, Component> reportFilesByUuid = initializeAnalysisReportComponents(fileReferences);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ assertThat(addedFileRepository.getComponents()).containsOnlyOnceElementsOf(reportFilesByUuid.values());
+ verifyStatistics(context, 2, 0, 2, null);
+ }
+
+ @Test
+ public void execute_detects_no_move_if_there_are_no_files_in_report() {
+ preparePullRequestAnalysis(ANALYSIS);
+ initializeAnalysisReportComponents(Set.of());
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ assertThat(addedFileRepository.getComponents()).isEmpty();
+ verifyStatistics(context, 0, null, null, null);
+ }
+
+ @Test
+ public void execute_detects_no_move_if_file_key_exists_in_both_database_and_report() {
+ preparePullRequestAnalysis(ANALYSIS);
+
+ Set<FileReference> fileReferences = Set.of(FileReference.of(FILE_1_REF), FileReference.of(FILE_2_REF));
+ initializeAnalysisReportComponents(fileReferences);
+ initializeTargetBranchDatabaseComponents(fileReferences);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
+ assertThat(addedFileRepository.getComponents()).isEmpty();
+ verifyStatistics(context, 2, 2, 0, 0);
+ }
+
+ @Test
+ public void execute_detects_renamed_file() {
+ // - FILE_1_REF on target branch is renamed to FILE_2_REF on Pull Request
+ preparePullRequestAnalysis(ANALYSIS);
+
+ Set<FileReference> reportFileReferences = Set.of(FileReference.of(FILE_2_REF, FILE_1_REF));
+ Set<FileReference> databaseFileReferences = Set.of(FileReference.of(FILE_1_REF));
+
+ Map<String, Component> reportFilesByUuid = initializeAnalysisReportComponents(reportFileReferences);
+ Map<String, Component> databaseFilesByUuid = initializeTargetBranchDatabaseComponents(databaseFileReferences);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(addedFileRepository.getComponents()).isEmpty();
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).hasSize(1);
+ assertThatFileRenameHasBeenDetected(reportFilesByUuid, databaseFilesByUuid, FILE_2_REF, FILE_1_REF);
+ verifyStatistics(context, 1, 1, 0, 1);
+ }
+
+ @Test
+ public void execute_detects_several_renamed_file() {
+ // - FILE_1_REF has been renamed to FILE_3_REF on Pull Request
+ // - FILE_2_REF has been deleted on Pull Request
+ // - FILE_4_REF has been left untouched
+ // - FILE_5_REF has been renamed to FILE_6_REF on Pull Request
+ // - FILE_7_REF has been added on Pull Request
+ preparePullRequestAnalysis(ANALYSIS);
+
+ Set<FileReference> reportFileReferences = Set.of(
+ FileReference.of(FILE_3_REF, FILE_1_REF),
+ FileReference.of(FILE_4_REF),
+ FileReference.of(FILE_6_REF, FILE_5_REF),
+ FileReference.of(FILE_7_REF));
+
+ Set<FileReference> databaseFileReferences = Set.of(
+ FileReference.of(FILE_1_REF),
+ FileReference.of(FILE_2_REF),
+ FileReference.of(FILE_4_REF),
+ FileReference.of(FILE_5_REF));
+
+ Map<String, Component> reportFilesByUuid = initializeAnalysisReportComponents(reportFileReferences);
+ Map<String, Component> databaseFilesByUuid = initializeTargetBranchDatabaseComponents(databaseFileReferences);
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(addedFileRepository.getComponents()).hasSize(1);
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).hasSize(2);
+ assertThatFileAdditionHasBeenDetected(reportFilesByUuid, FILE_7_REF);
+ assertThatFileRenameHasBeenDetected(reportFilesByUuid, databaseFilesByUuid, FILE_3_REF, FILE_1_REF);
+ assertThatFileRenameHasBeenDetected(reportFilesByUuid, databaseFilesByUuid, FILE_6_REF, FILE_5_REF);
+ verifyStatistics(context, 4, 4, 1, 2);
+ }
+
+ private void assertThatFileAdditionHasBeenDetected(Map<String, Component> reportFilesByUuid, String fileInReportReference) {
+ Component fileInReport = reportFilesByUuid.get(fileInReportReference);
+
+ assertThat(addedFileRepository.getComponents()).contains(fileInReport);
+ assertThat(movedFilesRepository.getOriginalPullRequestFile(fileInReport)).isEmpty();
+ }
+
+
+ private void assertThatFileRenameHasBeenDetected(Map<String, Component> reportFilesByUuid, Map<String, Component> databaseFilesByUuid, String fileInReportReference, String originalFileInDatabaseReference) {
+ Component fileInReport = reportFilesByUuid.get(fileInReportReference);
+ Component originalFileInDatabase = databaseFilesByUuid.get(originalFileInDatabaseReference);
+
+ assertThat(movedFilesRepository.getComponentsWithOriginal()).contains(fileInReport);
+ assertThat(movedFilesRepository.getOriginalPullRequestFile(fileInReport)).isPresent();
+
+ OriginalFile detectedOriginalFile = movedFilesRepository.getOriginalPullRequestFile(fileInReport).get();
+ assertThat(detectedOriginalFile.key()).isEqualTo(originalFileInDatabase.getKey());
+ assertThat(detectedOriginalFile.uuid()).isEqualTo(originalFileInDatabase.getUuid());
+ }
+
+ private Map<String, Component> initializeTargetBranchDatabaseComponents(Set<FileReference> references) {
+ Set<Component> fileComponents = createFileComponents(references);
+ insertFileComponentsInDatabase(fileComponents);
+ return toFileComponentsByUuidMap(fileComponents);
+ }
+
+ private Map<String, Component> initializeAnalysisReportComponents(Set<FileReference> refs) {
+ Set<Component> fileComponents = createFileComponents(refs);
+ insertFileComponentsInReport(fileComponents);
+ return toFileComponentsByUuidMap(fileComponents);
+ }
+
+ private Map<String, Component> toFileComponentsByUuidMap(Set<Component> fileComponents) {
+ return fileComponents
+ .stream()
+ .collect(toMap(Component::getUuid, identity()));
+ }
+
+ private static Set<Component> createFileComponents(Set<FileReference> references) {
+ return references
+ .stream()
+ .map(PullRequestFileMoveDetectionStepIT::createReportFileComponent)
+ .collect(toSet());
+ }
+
+ private static Component createReportFileComponent(FileReference fileReference) {
+ return builder(FILE, Integer.parseInt(fileReference.getReference()))
+ .setUuid(fileReference.getReference())
+ .setName("report_path" + fileReference.getReference())
+ .setFileAttributes(new FileAttributes(false, null, 1, false, composeComponentPath(fileReference.getPastReference())))
+ .build();
+ }
+
+ private void insertFileComponentsInReport(Set<Component> files) {
+ treeRootHolder
+ .setRoot(builder(PROJECT, Integer.parseInt(ROOT_REF))
+ .setUuid(project.uuid())
+ .addChildren(files.toArray(Component[]::new))
+ .build());
+ }
+
+ private Set<ComponentDto> insertFileComponentsInDatabase(Set<Component> files) {
+ return files
+ .stream()
+ .map(Component::getUuid)
+ .map(this::composeComponentDto)
+ .peek(this::insertComponentDto)
+ .peek(this::insertContentOfFileInDatabase)
+ .collect(toSet());
+ }
+
+ private void insertComponentDto(ComponentDto component) {
+ dbTester.components().insertComponent(component);
+ }
+
+ private ComponentDto composeComponentDto(String uuid) {
+ return ComponentTesting
+ .newFileDto(project)
+ .setBranchUuid(branch.uuid())
+ .setKey("key_" + uuid)
+ .setUuid(uuid)
+ .setPath(composeComponentPath(uuid));
+ }
+
+ @CheckForNull
+ private static String composeComponentPath(@Nullable String reference) {
+ return Optional.ofNullable(reference)
+ .map(r -> String.join("_", "path", r))
+ .orElse(null);
+ }
+
+ private FileSourceDto insertContentOfFileInDatabase(ComponentDto file) {
+ FileSourceDto fileSourceDto = composeFileSourceDto(file);
+ persistFileSourceDto(fileSourceDto);
+ return fileSourceDto;
+ }
+
+ private static FileSourceDto composeFileSourceDto(ComponentDto file) {
+ return new FileSourceDto()
+ .setUuid(Uuids.createFast())
+ .setFileUuid(file.uuid())
+ .setProjectUuid(file.branchUuid());
+ }
+
+ private void persistFileSourceDto(FileSourceDto fileSourceDto) {
+ dbTester.getDbClient().fileSourceDao().insert(dbTester.getSession(), fileSourceDto);
+ dbTester.commit();
+ }
+
+ private void preparePullRequestAnalysis(Analysis analysis) {
+ prepareAnalysis(PULL_REQUEST, analysis);
+ }
+
+ private void prepareAnalysis(BranchType branch, Analysis analysis) {
+ mockBranchType(branch);
+ analysisMetadataHolder.setBaseAnalysis(analysis);
+ }
+
+ private void mockBranchType(BranchType branchType) {
+ Branch branch = mock(Branch.class);
+ when(analysisMetadataHolder.getBranch()).thenReturn(branch);
+ when(analysisMetadataHolder.getBranch().getTargetBranchName()).thenReturn(TARGET_BRANCH);
+ when(analysisMetadataHolder.isPullRequest()).thenReturn(branchType == PULL_REQUEST);
+ when(analysisMetadataHolder.getProject()).thenReturn(Project.from(project));
+ }
+
+ @Immutable
+ private static class FileReference {
+ private final String reference;
+ private final String pastReference;
+
+ private FileReference(String reference, @Nullable String pastReference) {
+ this.reference = reference;
+ this.pastReference = pastReference;
+ }
+
+ public String getReference() {
+ return reference;
+ }
+
+ @CheckForNull
+ public String getPastReference() {
+ return pastReference;
+ }
+
+ public static FileReference of(String reference, String pastReference) {
+ return new FileReference(reference, pastReference);
+ }
+
+ public static FileReference of(String reference) {
+ return new FileReference(reference, null);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.metric;
+
+import java.util.List;
+import java.util.Random;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.metric.MetricDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class MetricRepositoryImplIT {
+ private static final String SOME_KEY = "some_key";
+ private static final String SOME_UUID = "uuid";
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private DbClient dbClient = dbTester.getDbClient();
+ private MetricRepositoryImpl underTest = new MetricRepositoryImpl(dbClient);
+
+ @Test
+ public void getByKey_throws_NPE_if_arg_is_null() {
+ assertThatThrownBy(() -> underTest.getByKey(null))
+ .isInstanceOf(NullPointerException.class);
+ }
+
+ @Test
+ public void getByKey_throws_ISE_if_start_has_not_been_called() {
+ assertThatThrownBy(() -> underTest.getByKey(SOME_KEY))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Metric cache has not been initialized");
+ }
+
+ @Test
+ public void getByKey_throws_ISE_of_Metric_does_not_exist() {
+ assertThatThrownBy(() -> {
+ underTest.start();
+ underTest.getByKey(SOME_KEY);
+ })
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage(String.format("Metric with key '%s' does not exist", SOME_KEY));
+ }
+
+ @Test
+ public void getByKey_throws_ISE_of_Metric_is_disabled() {
+ dbTester.measures().insertMetric(t -> t.setKey("complexity").setEnabled(false));
+
+ underTest.start();
+
+ assertThatThrownBy(() -> underTest.getByKey("complexity"))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage(String.format("Metric with key '%s' does not exist", "complexity"));
+ }
+
+ @Test
+ public void getByKey_find_enabled_Metrics() {
+ MetricDto ncloc = dbTester.measures().insertMetric(t -> t.setKey("ncloc").setEnabled(true));
+ MetricDto coverage = dbTester.measures().insertMetric(t -> t.setKey("coverage").setEnabled(true));
+
+ underTest.start();
+
+ assertThat(underTest.getByKey("ncloc").getUuid()).isEqualTo(ncloc.getUuid());
+ assertThat(underTest.getByKey("coverage").getUuid()).isEqualTo(coverage.getUuid());
+ }
+
+ @Test
+ public void getById_throws_ISE_if_start_has_not_been_called() {
+ assertThatThrownBy(() -> underTest.getByUuid(SOME_UUID))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Metric cache has not been initialized");
+ }
+
+ @Test
+ public void getById_throws_ISE_of_Metric_does_not_exist() {
+ underTest.start();
+
+ assertThatThrownBy(() -> underTest.getByUuid(SOME_UUID))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage(String.format("Metric with uuid '%s' does not exist", SOME_UUID));
+ }
+
+ @Test
+ public void getById_throws_ISE_of_Metric_is_disabled() {
+ dbTester.measures().insertMetric(t -> t.setKey("complexity").setEnabled(false));
+
+ underTest.start();
+
+ assertThatThrownBy(() -> underTest.getByUuid(SOME_UUID))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage(String.format("Metric with uuid '%s' does not exist", SOME_UUID));
+ }
+
+ @Test
+ public void getById_find_enabled_Metrics() {
+ MetricDto ncloc = dbTester.measures().insertMetric(t -> t.setKey("ncloc").setEnabled(true));
+ MetricDto coverage = dbTester.measures().insertMetric(t -> t.setKey("coverage").setEnabled(true));
+
+ underTest.start();
+
+ assertThat(underTest.getByUuid(ncloc.getUuid()).getKey()).isEqualTo("ncloc");
+ assertThat(underTest.getByUuid(coverage.getUuid()).getKey()).isEqualTo("coverage");
+ }
+
+ @Test
+ public void getOptionalById_throws_ISE_if_start_has_not_been_called() {
+ assertThatThrownBy(() -> underTest.getOptionalByUuid(SOME_UUID))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Metric cache has not been initialized");
+ }
+
+ @Test
+ public void getOptionalById_returns_empty_of_Metric_does_not_exist() {
+ underTest.start();
+
+ assertThat(underTest.getOptionalByUuid(SOME_UUID)).isEmpty();
+ }
+
+ @Test
+ public void getOptionalById_returns_empty_of_Metric_is_disabled() {
+ dbTester.measures().insertMetric(t -> t.setKey("complexity").setEnabled(false));
+
+ underTest.start();
+
+ assertThat(underTest.getOptionalByUuid(SOME_UUID)).isEmpty();
+ }
+
+ @Test
+ public void getOptionalById_find_enabled_Metrics() {
+ MetricDto ncloc = dbTester.measures().insertMetric(t -> t.setKey("ncloc").setEnabled(true));
+ MetricDto coverage = dbTester.measures().insertMetric(t -> t.setKey("coverage").setEnabled(true));
+
+ underTest.start();
+
+ assertThat(underTest.getOptionalByUuid(ncloc.getUuid()).get().getKey()).isEqualTo("ncloc");
+ assertThat(underTest.getOptionalByUuid(coverage.getUuid()).get().getKey()).isEqualTo("coverage");
+ }
+
+ @Test
+ public void get_all_metrics() {
+ List<MetricDto> enabledMetrics = IntStream.range(0, 1 + new Random().nextInt(12))
+ .mapToObj(i -> dbTester.measures().insertMetric(t -> t.setKey("key_enabled_" + i).setEnabled(true)))
+ .toList();
+ IntStream.range(0, 1 + new Random().nextInt(12))
+ .forEach(i -> dbTester.measures().insertMetric(t -> t.setKey("key_disabled_" + i).setEnabled(false)));
+
+ underTest.start();
+ assertThat(underTest.getAll())
+ .extracting(Metric::getKey)
+ .containsOnly(enabledMetrics.stream().map(MetricDto::getKey).toArray(String[]::new));
+ }
+
+ @Test
+ public void getMetricsByType_givenRatingType_returnRatingMetrics() {
+ List<MetricDto> enabledMetrics = IntStream.range(0, 1 + new Random().nextInt(12))
+ .mapToObj(i -> dbTester.measures().insertMetric(t -> t.setKey("key_enabled_" + i).setEnabled(true).setValueType("RATING")))
+ .toList();
+
+ underTest.start();
+ assertThat(underTest.getMetricsByType(Metric.MetricType.RATING))
+ .extracting(Metric::getKey)
+ .containsOnly(enabledMetrics.stream().map(MetricDto::getKey).toArray(String[]::new));
+ }
+
+ @Test
+ public void getMetricsByType_givenRatingTypeAndWantedMilisecType_returnEmptyList() {
+ IntStream.range(0, 1 + new Random().nextInt(12))
+ .mapToObj(i -> dbTester.measures().insertMetric(t -> t.setKey("key_enabled_" + i).setEnabled(true).setValueType("RATING")))
+ .collect(Collectors.toList());
+
+ underTest.start();
+ assertThat(underTest.getMetricsByType(Metric.MetricType.MILLISEC)).isEmpty();
+ }
+
+ @Test
+ public void getMetricsByType_givenOnlyMilisecTypeAndWantedRatingMetrics_returnEmptyList() {
+ IntStream.range(0, 1 + new Random().nextInt(12))
+ .mapToObj(i -> dbTester.measures().insertMetric(t -> t.setKey("key_enabled_" + i).setEnabled(true).setValueType("MILISEC")));
+
+ underTest.start();
+ assertThat(underTest.getMetricsByType(Metric.MetricType.RATING)).isEmpty();
+ }
+
+ @Test
+ public void getMetricsByType_givenMetricsAreNull_throwException() {
+ assertThatThrownBy(() -> underTest.getMetricsByType(Metric.MetricType.RATING))
+ .isInstanceOf(IllegalStateException.class);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.scm;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.ce.task.projectanalysis.analysis.Analysis;
+import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
+import org.sonar.ce.task.projectanalysis.analysis.Branch;
+import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.ReferenceBranchComponentUuids;
+import org.sonar.ce.task.projectanalysis.filemove.MutableMovedFilesRepositoryRule;
+import org.sonar.ce.task.projectanalysis.period.NewCodeReferenceBranchComponentUuids;
+import org.sonar.ce.task.projectanalysis.period.Period;
+import org.sonar.ce.task.projectanalysis.period.PeriodHolderRule;
+import org.sonar.core.hash.SourceHashComputer;
+import org.sonar.core.util.Uuids;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.newcodeperiod.NewCodePeriodType;
+import org.sonar.db.protobuf.DbFileSources;
+import org.sonar.db.source.FileSourceDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.utils.log.LoggerLevel.TRACE;
+import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
+
+public class ScmInfoDbLoaderIT {
+ static final int FILE_REF = 1;
+ static final Component FILE = builder(Component.Type.FILE, FILE_REF).setKey("FILE_KEY").setUuid("FILE_UUID").build();
+ static final long DATE_1 = 123456789L;
+
+ static Analysis baseProjectAnalysis = new Analysis.Builder()
+ .setUuid("uuid_1")
+ .setCreatedAt(123456789L)
+ .build();
+
+ @Rule
+ public LogTester logTester = new LogTester();
+ @Rule
+ public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+ @Rule
+ public BatchReportReaderRule reportReader = new BatchReportReaderRule();
+ @Rule
+ public MutableMovedFilesRepositoryRule movedFiles = new MutableMovedFilesRepositoryRule();
+ @Rule
+ public PeriodHolderRule periodHolder = new PeriodHolderRule();
+
+ private final Branch branch = mock(Branch.class);
+ private final ReferenceBranchComponentUuids referenceBranchComponentUuids = mock(ReferenceBranchComponentUuids.class);
+ private final NewCodeReferenceBranchComponentUuids newCodeReferenceBranchComponentUuids = mock(NewCodeReferenceBranchComponentUuids.class);
+
+ private final ScmInfoDbLoader underTest = new ScmInfoDbLoader(analysisMetadataHolder, movedFiles, dbTester.getDbClient(), referenceBranchComponentUuids,
+ newCodeReferenceBranchComponentUuids, periodHolder);
+
+ @Before
+ public void before() {
+ periodHolder.setPeriod(new Period(NewCodePeriodType.PREVIOUS_VERSION.name(), null, null));
+ }
+
+ @Test
+ public void returns_ScmInfo_from_DB() {
+ analysisMetadataHolder.setBaseAnalysis(baseProjectAnalysis);
+ analysisMetadataHolder.setBranch(null);
+
+ String hash = computeSourceHash(1);
+ addFileSourceInDb("henry", DATE_1, "rev-1", hash);
+
+ DbScmInfo scmInfo = underTest.getScmInfo(FILE).get();
+ assertThat(scmInfo.getAllChangesets()).hasSize(1);
+ assertThat(scmInfo.fileHash()).isEqualTo(hash);
+
+ assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from DB for file 'FILE_UUID'");
+ }
+
+ @Test
+ public void read_from_reference_branch_if_no_base() {
+ analysisMetadataHolder.setBaseAnalysis(null);
+ analysisMetadataHolder.setBranch(branch);
+
+ String referenceFileUuid = "referenceFileUuid";
+ String hash = computeSourceHash(1);
+
+ when(referenceBranchComponentUuids.getComponentUuid(FILE.getKey())).thenReturn(referenceFileUuid);
+ addFileSourceInDb("henry", DATE_1, "rev-1", hash, referenceFileUuid);
+
+ DbScmInfo scmInfo = underTest.getScmInfo(FILE).get();
+ assertThat(scmInfo.getAllChangesets()).hasSize(1);
+ assertThat(scmInfo.fileHash()).isEqualTo(hash);
+ assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from DB for file 'referenceFileUuid'");
+ }
+
+ @Test
+ public void read_from_target_if_pullrequest() {
+ Branch branch = mock(Branch.class);
+ when(branch.getType()).thenReturn(BranchType.PULL_REQUEST);
+ analysisMetadataHolder.setBaseAnalysis(null);
+ analysisMetadataHolder.setBranch(branch);
+
+ String targetBranchFileUuid = "targetBranchFileUuid";
+ String hash = computeSourceHash(1);
+
+ when(referenceBranchComponentUuids.getComponentUuid(FILE.getKey())).thenReturn(targetBranchFileUuid);
+ addFileSourceInDb("henry", DATE_1, "rev-1", hash, targetBranchFileUuid);
+
+ DbScmInfo scmInfo = underTest.getScmInfo(FILE).get();
+ assertThat(scmInfo.getAllChangesets()).hasSize(1);
+ assertThat(scmInfo.fileHash()).isEqualTo(hash);
+ assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from DB for file 'targetBranchFileUuid'");
+ }
+
+ @Test
+ public void read_from_target_if_reference_branch() {
+ periodHolder.setPeriod(new Period(NewCodePeriodType.REFERENCE_BRANCH.name(), null, null));
+
+ Branch branch = mock(Branch.class);
+ when(branch.getType()).thenReturn(BranchType.BRANCH);
+ analysisMetadataHolder.setBaseAnalysis(null);
+ analysisMetadataHolder.setBranch(branch);
+
+ String targetBranchFileUuid = "targetBranchFileUuid";
+ String hash = computeSourceHash(1);
+
+ when(newCodeReferenceBranchComponentUuids.getComponentUuid(FILE.getKey())).thenReturn(targetBranchFileUuid);
+ addFileSourceInDb("henry", DATE_1, "rev-1", hash, targetBranchFileUuid);
+
+ DbScmInfo scmInfo = underTest.getScmInfo(FILE).get();
+ assertThat(scmInfo.getAllChangesets()).hasSize(1);
+ assertThat(scmInfo.fileHash()).isEqualTo(hash);
+ assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from DB for file 'targetBranchFileUuid'");
+ }
+
+ @Test
+ public void read_from_db_if_not_exist_in_reference_branch() {
+ periodHolder.setPeriod(new Period(NewCodePeriodType.REFERENCE_BRANCH.name(), null, null));
+
+ Branch branch = mock(Branch.class);
+ when(branch.getType()).thenReturn(BranchType.BRANCH);
+ analysisMetadataHolder.setBaseAnalysis(null);
+ analysisMetadataHolder.setBranch(branch);
+
+ String hash = computeSourceHash(1);
+
+ addFileSourceInDb("henry", DATE_1, "rev-1", hash, FILE.getUuid());
+
+ DbScmInfo scmInfo = underTest.getScmInfo(FILE).get();
+ assertThat(scmInfo.getAllChangesets()).hasSize(1);
+ assertThat(scmInfo.fileHash()).isEqualTo(hash);
+ assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from DB for file 'FILE_UUID'");
+ }
+
+ @Test
+ public void return_empty_if_no_dto_available() {
+ analysisMetadataHolder.setBaseAnalysis(baseProjectAnalysis);
+ analysisMetadataHolder.setBranch(null);
+
+ Optional<DbScmInfo> scmInfo = underTest.getScmInfo(FILE);
+
+ assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from DB for file 'FILE_UUID'");
+ assertThat(scmInfo).isEmpty();
+ }
+
+ @Test
+ public void do_not_read_from_db_on_first_analysis_if_there_is_no_reference_branch() {
+ Branch branch = mock(Branch.class);
+ when(branch.getType()).thenReturn(BranchType.PULL_REQUEST);
+ analysisMetadataHolder.setBaseAnalysis(null);
+ analysisMetadataHolder.setBranch(branch);
+
+ assertThat(underTest.getScmInfo(FILE)).isEmpty();
+ assertThat(logTester.logs(TRACE)).isEmpty();
+ }
+
+ private static List<String> generateLines(int lineCount) {
+ ImmutableList.Builder<String> builder = ImmutableList.builder();
+ for (int i = 0; i < lineCount; i++) {
+ builder.add("line " + i);
+ }
+ return builder.build();
+ }
+
+ private static String computeSourceHash(int lineCount) {
+ SourceHashComputer sourceHashComputer = new SourceHashComputer();
+ Iterator<String> lines = generateLines(lineCount).iterator();
+ while (lines.hasNext()) {
+ sourceHashComputer.addLine(lines.next(), lines.hasNext());
+ }
+ return sourceHashComputer.getHash();
+ }
+
+ private void addFileSourceInDb(@Nullable String author, @Nullable Long date, @Nullable String revision, String srcHash) {
+ addFileSourceInDb(author, date, revision, srcHash, FILE.getUuid());
+ }
+
+ private void addFileSourceInDb(@Nullable String author, @Nullable Long date, @Nullable String revision, String srcHash, String fileUuid) {
+ DbFileSources.Data.Builder fileDataBuilder = DbFileSources.Data.newBuilder();
+ DbFileSources.Line.Builder builder = fileDataBuilder.addLinesBuilder()
+ .setLine(1);
+ if (author != null) {
+ builder.setScmAuthor(author);
+ }
+ if (date != null) {
+ builder.setScmDate(date);
+ }
+ if (revision != null) {
+ builder.setScmRevision(revision);
+ }
+ dbTester.getDbClient().fileSourceDao().insert(dbTester.getSession(), new FileSourceDto()
+ .setUuid(Uuids.createFast())
+ .setLineHashes(Collections.singletonList("lineHash"))
+ .setFileUuid(fileUuid)
+ .setProjectUuid("PROJECT_UUID")
+ .setSourceData(fileDataBuilder.build())
+ .setSrcHash(srcHash));
+ dbTester.commit();
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.step;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sonar.api.utils.System2;
+import org.sonar.ce.task.projectanalysis.analysis.Branch;
+import org.sonar.ce.task.projectanalysis.analysis.MutableAnalysisMetadataHolderRule;
+import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.DefaultBranchImpl;
+import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolderRule;
+import org.sonar.ce.task.step.TestComputationStepContext;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType;
+import org.sonar.scanner.protocol.output.ScannerReport.Component.FileStatus;
+import org.sonar.server.project.Project;
+
+import static java.util.Optional.ofNullable;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.db.component.BranchDto.DEFAULT_MAIN_BRANCH_NAME;
+import static org.sonar.db.component.ComponentTesting.newDirectory;
+import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
+import static org.sonar.db.component.SnapshotTesting.newAnalysis;
+import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.FILE;
+import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.PROJECT;
+
+@RunWith(DataProviderRunner.class)
+public class BuildComponentTreeStepIT {
+ private static final String NO_SCANNER_PROJECT_VERSION = null;
+ private static final String NO_SCANNER_BUILD_STRING = null;
+
+ private static final int ROOT_REF = 1;
+ private static final int FILE_1_REF = 4;
+ private static final int FILE_2_REF = 5;
+ private static final int FILE_3_REF = 7;
+ private static final int UNCHANGED_FILE_REF = 10;
+
+ private static final String REPORT_PROJECT_KEY = "REPORT_PROJECT_KEY";
+ private static final String REPORT_DIR_PATH_1 = "src/main/java/dir1";
+ private static final String REPORT_FILE_PATH_1 = "src/main/java/dir1/File1.java";
+ private static final String REPORT_FILE_NAME_1 = "File1.java";
+ private static final String REPORT_DIR_PATH_2 = "src/main/java/dir2";
+ private static final String REPORT_FILE_PATH_2 = "src/main/java/dir2/File2.java";
+ private static final String REPORT_FILE_PATH_3 = "src/main/java/dir2/File3.java";
+ private static final String REPORT_UNCHANGED_FILE_PATH = "src/main/File3.java";
+
+ private static final long ANALYSIS_DATE = 123456789L;
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+ @Rule
+ public BatchReportReaderRule reportReader = new BatchReportReaderRule().setMetadata(createReportMetadata(NO_SCANNER_PROJECT_VERSION, NO_SCANNER_BUILD_STRING));
+ @Rule
+ public MutableTreeRootHolderRule treeRootHolder = new MutableTreeRootHolderRule();
+ @Rule
+ public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule();
+
+ private DbClient dbClient = dbTester.getDbClient();
+ private BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
+
+ @Test
+ public void fails_if_root_component_does_not_exist_in_reportReader() {
+ setAnalysisMetadataHolder();
+
+ assertThatThrownBy(() -> underTest.execute(new TestComputationStepContext()))
+ .isInstanceOf(NullPointerException.class);
+ }
+
+ @Test
+ public void verify_tree_is_correctly_built() {
+ setAnalysisMetadataHolder();
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF, FILE_2_REF, FILE_3_REF));
+ reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));
+ reportReader.putComponent(componentWithPath(FILE_2_REF, FILE, REPORT_FILE_PATH_2));
+ reportReader.putComponent(componentWithPath(FILE_3_REF, FILE, REPORT_FILE_PATH_3));
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ Component root = treeRootHolder.getRoot();
+ assertThat(root).isNotNull();
+ verifyComponent(root, Component.Type.PROJECT, ROOT_REF, 1);
+
+ Component dir = root.getChildren().iterator().next();
+ verifyComponent(dir, Component.Type.DIRECTORY, null, 2);
+
+ Component dir1 = dir.getChildren().get(0);
+ verifyComponent(dir1, Component.Type.DIRECTORY, null, 1);
+ verifyComponent(dir1.getChildren().get(0), Component.Type.FILE, FILE_1_REF, 0);
+
+ Component dir2 = dir.getChildren().get(1);
+ verifyComponent(dir2, Component.Type.DIRECTORY, null, 2);
+ verifyComponent(dir2.getChildren().get(0), Component.Type.FILE, FILE_2_REF, 0);
+ verifyComponent(dir2.getChildren().get(1), Component.Type.FILE, FILE_3_REF, 0);
+
+ context.getStatistics().assertValue("components", 7);
+ }
+
+ @Test
+ public void verify_tree_is_correctly_built_in_prs() {
+ setAnalysisMetadataHolder(true);
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF, FILE_2_REF, FILE_3_REF, UNCHANGED_FILE_REF));
+ reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));
+ reportReader.putComponent(componentWithPath(FILE_2_REF, FILE, REPORT_FILE_PATH_2));
+ reportReader.putComponent(componentWithPath(FILE_3_REF, FILE, REPORT_FILE_PATH_3));
+ reportReader.putComponent(unchangedComponentWithPath(UNCHANGED_FILE_REF, FILE, REPORT_UNCHANGED_FILE_PATH));
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ // modified root
+ Component mRoot = treeRootHolder.getRoot();
+ verifyComponent(mRoot, Component.Type.PROJECT, ROOT_REF, 1);
+
+ Component mDir = mRoot.getChildren().get(0);
+ assertThat(mDir.getName()).isEqualTo("src/main/java");
+ verifyComponent(mDir, Component.Type.DIRECTORY, null, 2);
+
+ Component mDir1 = mDir.getChildren().get(0);
+ assertThat(mDir1.getName()).isEqualTo("src/main/java/dir1");
+ verifyComponent(mDir1, Component.Type.DIRECTORY, null, 1);
+ verifyComponent(mDir1.getChildren().get(0), Component.Type.FILE, FILE_1_REF, 0);
+
+ Component mDir2 = mDir.getChildren().get(1);
+ assertThat(mDir2.getName()).isEqualTo("src/main/java/dir2");
+ verifyComponent(mDir2, Component.Type.DIRECTORY, null, 2);
+ verifyComponent(mDir2.getChildren().get(0), Component.Type.FILE, FILE_2_REF, 0);
+ verifyComponent(mDir2.getChildren().get(1), Component.Type.FILE, FILE_3_REF, 0);
+
+ // root
+ Component root = treeRootHolder.getReportTreeRoot();
+ verifyComponent(root, Component.Type.PROJECT, ROOT_REF, 1);
+
+ Component dir = root.getChildren().get(0);
+ assertThat(dir.getName()).isEqualTo("src/main");
+ verifyComponent(dir, Component.Type.DIRECTORY, null, 2);
+
+ Component dir1 = dir.getChildren().get(0);
+ assertThat(dir1.getName()).isEqualTo("src/main/java");
+ verifyComponent(dir1, Component.Type.DIRECTORY, null, 2);
+ verifyComponent(dir1.getChildren().get(0), Component.Type.DIRECTORY, null, 1);
+ verifyComponent(dir1.getChildren().get(1), Component.Type.DIRECTORY, null, 2);
+
+ Component dir2 = dir1.getChildren().get(0);
+ assertThat(dir2.getName()).isEqualTo("src/main/java/dir1");
+ verifyComponent(dir2, Component.Type.DIRECTORY, null, 1);
+ verifyComponent(dir2.getChildren().get(0), Component.Type.FILE, FILE_1_REF, 0);
+
+ Component dir3 = dir1.getChildren().get(1);
+ assertThat(dir3.getName()).isEqualTo("src/main/java/dir2");
+ verifyComponent(dir3, Component.Type.DIRECTORY, null, 2);
+ verifyComponent(dir3.getChildren().get(0), Component.Type.FILE, FILE_2_REF, 0);
+ verifyComponent(dir3.getChildren().get(1), Component.Type.FILE, FILE_3_REF, 0);
+
+ context.getStatistics().assertValue("components", 7);
+ }
+
+ /**
+ * SONAR-13262
+ */
+ @Test
+ public void verify_tree_is_correctly_built_in_prs_with_repeated_names() {
+ setAnalysisMetadataHolder(true);
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF));
+ reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_PROJECT_KEY + "/file.js"));
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ // modified root
+ Component mRoot = treeRootHolder.getRoot();
+ verifyComponent(mRoot, Component.Type.PROJECT, ROOT_REF, 1);
+
+ Component dir = mRoot.getChildren().get(0);
+ assertThat(dir.getName()).isEqualTo(REPORT_PROJECT_KEY);
+ assertThat(dir.getShortName()).isEqualTo(REPORT_PROJECT_KEY);
+
+ verifyComponent(dir, Component.Type.DIRECTORY, null, 1);
+ }
+
+ @Test
+ public void compute_keys_and_uuids() {
+ setAnalysisMetadataHolder();
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF));
+ reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));
+
+ underTest.execute(new TestComputationStepContext());
+
+ verifyComponentByRef(ROOT_REF, REPORT_PROJECT_KEY, analysisMetadataHolder.getProject().getName());
+ verifyComponentByKey(REPORT_PROJECT_KEY + ":" + REPORT_DIR_PATH_1, REPORT_DIR_PATH_1);
+ verifyComponentByRef(FILE_1_REF, REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1, REPORT_FILE_NAME_1);
+ }
+
+ @Test
+ public void return_existing_uuids() {
+ setAnalysisMetadataHolder();
+ ComponentDto project = insertComponent(newPrivateProjectDto("ABCD").setKey(REPORT_PROJECT_KEY));
+ ComponentDto directory = newDirectory(project, "CDEF", REPORT_DIR_PATH_1);
+ insertComponent(directory.setKey(REPORT_PROJECT_KEY + ":" + REPORT_DIR_PATH_1));
+ insertComponent(newFileDto(project, directory, "DEFG")
+ .setKey(REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1)
+ .setPath(REPORT_FILE_PATH_1));
+
+ // new structure, without modules
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF));
+ reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));
+
+ underTest.execute(new TestComputationStepContext());
+
+ verifyComponentByRef(ROOT_REF, REPORT_PROJECT_KEY, analysisMetadataHolder.getProject().getName(), "ABCD");
+ verifyComponentByKey(REPORT_PROJECT_KEY + ":" + REPORT_DIR_PATH_1, REPORT_DIR_PATH_1, "CDEF");
+ verifyComponentByRef(FILE_1_REF, REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1, REPORT_FILE_NAME_1, "DEFG");
+ }
+
+ @Test
+ public void generate_keys_when_using_new_branch() {
+ Branch branch = mock(Branch.class);
+ when(branch.getName()).thenReturn("origin/feature");
+ when(branch.isMain()).thenReturn(false);
+ when(branch.generateKey(any(), any())).thenReturn("generated");
+ analysisMetadataHolder.setRootComponentRef(ROOT_REF)
+ .setAnalysisDate(ANALYSIS_DATE)
+ .setProject(Project.from(newPrivateProjectDto().setKey(REPORT_PROJECT_KEY)))
+ .setBranch(branch);
+ BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF));
+ reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));
+
+ underTest.execute(new TestComputationStepContext());
+
+ verifyComponentByRef(ROOT_REF, "generated", analysisMetadataHolder.getProject().getName(), null);
+ verifyComponentByRef(FILE_1_REF, "generated", REPORT_FILE_NAME_1, null);
+ }
+
+ @Test
+ public void generate_keys_when_using_existing_branch() {
+ ComponentDto projectDto = dbTester.components().insertPublicProject();
+ String branchName = randomAlphanumeric(248);
+ ComponentDto componentDto = dbTester.components().insertProjectBranch(projectDto, b -> b.setKey(branchName));
+ Branch branch = mock(Branch.class);
+ when(branch.getName()).thenReturn(branchName);
+ when(branch.isMain()).thenReturn(false);
+ when(branch.generateKey(any(), any())).thenReturn(componentDto.getKey());
+ analysisMetadataHolder.setRootComponentRef(ROOT_REF)
+ .setAnalysisDate(ANALYSIS_DATE)
+ .setProject(Project.from(projectDto))
+ .setBranch(branch);
+ BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
+ reportReader.putComponent(component(ROOT_REF, PROJECT, componentDto.getKey()));
+
+ underTest.execute(new TestComputationStepContext());
+
+ verifyComponentByRef(ROOT_REF, componentDto.getKey(), analysisMetadataHolder.getProject().getName(), componentDto.uuid());
+ }
+
+ @Test
+ public void generate_keys_when_using_main_branch() {
+ setAnalysisMetadataHolder();
+ BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF));
+ reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));
+
+ underTest.execute(new TestComputationStepContext());
+
+ verifyComponentByRef(ROOT_REF, REPORT_PROJECT_KEY, analysisMetadataHolder.getProject().getName(), null);
+ verifyComponentByKey(REPORT_PROJECT_KEY + ":" + REPORT_DIR_PATH_1, REPORT_DIR_PATH_1);
+ verifyComponentByRef(FILE_1_REF, REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1, REPORT_FILE_NAME_1, null);
+ }
+
+ @Test
+ public void compute_keys_and_uuids_on_project_having_module_and_directory() {
+ setAnalysisMetadataHolder();
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF, FILE_2_REF));
+ reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));
+ reportReader.putComponent(componentWithPath(FILE_2_REF, FILE, REPORT_FILE_PATH_2));
+
+ underTest.execute(new TestComputationStepContext());
+
+ verifyComponentByRef(ROOT_REF, REPORT_PROJECT_KEY, analysisMetadataHolder.getProject().getName());
+ verifyComponentByKey(REPORT_PROJECT_KEY + ":" + REPORT_DIR_PATH_1, "dir1");
+ verifyComponentByRef(FILE_1_REF, REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1, REPORT_FILE_NAME_1);
+ verifyComponentByKey(REPORT_PROJECT_KEY + ":" + REPORT_DIR_PATH_2, "dir2");
+ verifyComponentByRef(FILE_2_REF, REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_2, "File2.java");
+ }
+
+ @Test
+ public void compute_keys_and_uuids_on_multi_modules() {
+ setAnalysisMetadataHolder();
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF));
+ reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));
+
+ underTest.execute(new TestComputationStepContext());
+
+ verifyComponentByRef(ROOT_REF, REPORT_PROJECT_KEY, analysisMetadataHolder.getProject().getName());
+ verifyComponentByKey(REPORT_PROJECT_KEY + ":" + REPORT_DIR_PATH_1, REPORT_DIR_PATH_1);
+ verifyComponentByRef(FILE_1_REF, REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1, REPORT_FILE_NAME_1);
+ }
+
+ @Test
+ public void set_no_base_project_snapshot_when_no_snapshot() {
+ setAnalysisMetadataHolder();
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY));
+ underTest.execute(new TestComputationStepContext());
+
+ assertThat(analysisMetadataHolder.isFirstAnalysis()).isTrue();
+ }
+
+ @Test
+ public void set_no_base_project_snapshot_when_no_last_snapshot() {
+ setAnalysisMetadataHolder();
+ ComponentDto project = insertComponent(newPrivateProjectDto("ABCD").setKey(REPORT_PROJECT_KEY));
+ insertSnapshot(newAnalysis(project).setLast(false));
+
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY));
+ underTest.execute(new TestComputationStepContext());
+
+ assertThat(analysisMetadataHolder.isFirstAnalysis()).isTrue();
+ }
+
+ @Test
+ public void set_base_project_snapshot_when_last_snapshot_exist() {
+ setAnalysisMetadataHolder();
+ ComponentDto project = insertComponent(newPrivateProjectDto("ABCD").setKey(REPORT_PROJECT_KEY));
+ insertSnapshot(newAnalysis(project).setLast(true));
+
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY));
+ underTest.execute(new TestComputationStepContext());
+
+ assertThat(analysisMetadataHolder.isFirstAnalysis()).isFalse();
+ }
+
+ @Test
+ public void set_projectVersion_to_not_provided_when_not_set_on_first_analysis() {
+ setAnalysisMetadataHolder();
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY));
+
+ underTest.execute(new TestComputationStepContext());
+
+ assertThat(treeRootHolder.getReportTreeRoot().getProjectAttributes().getProjectVersion()).isEqualTo("not provided");
+ }
+
+ @Test
+ @UseDataProvider("oneParameterNullNonNullCombinations")
+ public void set_projectVersion_to_previous_analysis_when_not_set(@Nullable String previousAnalysisProjectVersion) {
+ setAnalysisMetadataHolder();
+ ComponentDto project = insertComponent(newPrivateProjectDto("ABCD").setKey(REPORT_PROJECT_KEY));
+ insertSnapshot(newAnalysis(project).setProjectVersion(previousAnalysisProjectVersion).setLast(true));
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY));
+
+ underTest.execute(new TestComputationStepContext());
+
+ String projectVersion = treeRootHolder.getReportTreeRoot().getProjectAttributes().getProjectVersion();
+ if (previousAnalysisProjectVersion == null) {
+ assertThat(projectVersion).isEqualTo("not provided");
+ } else {
+ assertThat(projectVersion).isEqualTo(previousAnalysisProjectVersion);
+ }
+ }
+
+ @Test
+ public void set_projectVersion_when_it_is_set_on_first_analysis() {
+ String scannerProjectVersion = randomAlphabetic(12);
+ setAnalysisMetadataHolder();
+ reportReader.setMetadata(createReportMetadata(scannerProjectVersion, NO_SCANNER_BUILD_STRING));
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY));
+
+ underTest.execute(new TestComputationStepContext());
+
+ assertThat(treeRootHolder.getReportTreeRoot().getProjectAttributes().getProjectVersion())
+ .isEqualTo(scannerProjectVersion);
+ }
+
+ @Test
+ @UseDataProvider("oneParameterNullNonNullCombinations")
+ public void set_projectVersion_when_it_is_set_on_later_analysis(@Nullable String previousAnalysisProjectVersion) {
+ String scannerProjectVersion = randomAlphabetic(12);
+ setAnalysisMetadataHolder();
+ reportReader.setMetadata(createReportMetadata(scannerProjectVersion, NO_SCANNER_BUILD_STRING));
+ ComponentDto project = insertComponent(newPrivateProjectDto("ABCD").setKey(REPORT_PROJECT_KEY));
+ insertSnapshot(newAnalysis(project).setProjectVersion(previousAnalysisProjectVersion).setLast(true));
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY));
+
+ underTest.execute(new TestComputationStepContext());
+
+ assertThat(treeRootHolder.getReportTreeRoot().getProjectAttributes().getProjectVersion())
+ .isEqualTo(scannerProjectVersion);
+ }
+
+ @Test
+ @UseDataProvider("oneParameterNullNonNullCombinations")
+ public void set_buildString(@Nullable String buildString) {
+ String projectVersion = randomAlphabetic(7);
+ setAnalysisMetadataHolder();
+ reportReader.setMetadata(createReportMetadata(projectVersion, buildString));
+ reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY));
+
+ underTest.execute(new TestComputationStepContext());
+
+ assertThat(treeRootHolder.getReportTreeRoot().getProjectAttributes().getBuildString()).isEqualTo(Optional.ofNullable(buildString));
+ }
+
+ @DataProvider
+ public static Object[][] oneParameterNullNonNullCombinations() {
+ return new Object[][] {
+ {null},
+ {randomAlphabetic(7)}
+ };
+ }
+
+ private void verifyComponent(Component component, Component.Type type, @Nullable Integer componentRef, int size) {
+ assertThat(component.getType()).isEqualTo(type);
+ assertThat(component.getReportAttributes().getRef()).isEqualTo(componentRef);
+ assertThat(component.getChildren()).hasSize(size);
+ }
+
+ private void verifyComponentByRef(int ref, String key, String shortName) {
+ verifyComponentByRef(ref, key, shortName, null);
+ }
+
+ private void verifyComponentByKey(String key, String shortName) {
+ verifyComponentByKey(key, shortName, null);
+ }
+
+ private void verifyComponentByKey(String key, String shortName, @Nullable String uuid) {
+ Map<String, Component> componentsByKey = indexAllComponentsInTreeByKey(treeRootHolder.getRoot());
+ Component component = componentsByKey.get(key);
+ assertThat(component.getKey()).isEqualTo(key);
+ assertThat(component.getReportAttributes().getRef()).isNull();
+ assertThat(component.getShortName()).isEqualTo(shortName);
+ if (uuid != null) {
+ assertThat(component.getUuid()).isEqualTo(uuid);
+ } else {
+ assertThat(component.getUuid()).isNotNull();
+ }
+ }
+
+ private void verifyComponentByRef(int ref, String key, String shortName, @Nullable String uuid) {
+ Map<Integer, Component> componentsByRef = indexAllComponentsInTreeByRef(treeRootHolder.getRoot());
+ Component component = componentsByRef.get(ref);
+ assertThat(component.getKey()).isEqualTo(key);
+ assertThat(component.getShortName()).isEqualTo(shortName);
+ if (uuid != null) {
+ assertThat(component.getUuid()).isEqualTo(uuid);
+ } else {
+ assertThat(component.getUuid()).isNotNull();
+ }
+ }
+
+ private static ScannerReport.Component component(int componentRef, ComponentType componentType, String key, int... children) {
+ return component(componentRef, componentType, key, FileStatus.CHANGED, null, children);
+ }
+
+ private static ScannerReport.Component unchangedComponentWithPath(int componentRef, ComponentType componentType, String path, int... children) {
+ return component(componentRef, componentType, REPORT_PROJECT_KEY + ":" + path, FileStatus.SAME, path, children);
+ }
+
+ private static ScannerReport.Component componentWithPath(int componentRef, ComponentType componentType, String path, int... children) {
+ return component(componentRef, componentType, REPORT_PROJECT_KEY + ":" + path, FileStatus.CHANGED, path, children);
+ }
+
+ private static ScannerReport.Component component(int componentRef, ComponentType componentType, String key, FileStatus status, @Nullable String path, int... children) {
+ ScannerReport.Component.Builder builder = ScannerReport.Component.newBuilder()
+ .setType(componentType)
+ .setRef(componentRef)
+ .setName(key)
+ .setStatus(status)
+ .setLines(1)
+ .setKey(key);
+ if (path != null) {
+ builder.setProjectRelativePath(path);
+ }
+ for (int child : children) {
+ builder.addChildRef(child);
+ }
+ return builder.build();
+ }
+
+ private static Map<Integer, Component> indexAllComponentsInTreeByRef(Component root) {
+ Map<Integer, Component> componentsByRef = new HashMap<>();
+ feedComponentByRef(root, componentsByRef);
+ return componentsByRef;
+ }
+
+ private static Map<String, Component> indexAllComponentsInTreeByKey(Component root) {
+ Map<String, Component> componentsByKey = new HashMap<>();
+ feedComponentByKey(root, componentsByKey);
+ return componentsByKey;
+ }
+
+ private static void feedComponentByKey(Component component, Map<String, Component> map) {
+ map.put(component.getKey(), component);
+ for (Component child : component.getChildren()) {
+ feedComponentByKey(child, map);
+ }
+ }
+
+ private static void feedComponentByRef(Component component, Map<Integer, Component> map) {
+ if (component.getReportAttributes().getRef() != null) {
+ map.put(component.getReportAttributes().getRef(), component);
+ }
+ for (Component child : component.getChildren()) {
+ feedComponentByRef(child, map);
+ }
+ }
+
+ private ComponentDto insertComponent(ComponentDto component) {
+ return dbTester.components().insertComponent(component);
+ }
+
+ private SnapshotDto insertSnapshot(SnapshotDto snapshot) {
+ dbClient.snapshotDao().insert(dbTester.getSession(), snapshot);
+ dbTester.getSession().commit();
+ return snapshot;
+ }
+
+ private void setAnalysisMetadataHolder() {
+ setAnalysisMetadataHolder(false);
+ }
+
+ private void setAnalysisMetadataHolder(boolean isPr) {
+ Branch branch = isPr ? new PrBranch(DEFAULT_MAIN_BRANCH_NAME) : new DefaultBranchImpl(DEFAULT_MAIN_BRANCH_NAME);
+ analysisMetadataHolder.setRootComponentRef(ROOT_REF)
+ .setAnalysisDate(ANALYSIS_DATE)
+ .setBranch(branch)
+ .setProject(Project.from(newPrivateProjectDto().setKey(REPORT_PROJECT_KEY).setName(REPORT_PROJECT_KEY)));
+ }
+
+ public static ScannerReport.Metadata createReportMetadata(@Nullable String projectVersion, @Nullable String buildString) {
+ ScannerReport.Metadata.Builder builder = ScannerReport.Metadata.newBuilder()
+ .setProjectKey(REPORT_PROJECT_KEY)
+ .setRootComponentRef(ROOT_REF);
+ ofNullable(projectVersion).ifPresent(builder::setProjectVersion);
+ ofNullable(buildString).ifPresent(builder::setBuildString);
+ return builder.build();
+ }
+
+ private static class PrBranch extends DefaultBranchImpl {
+ public PrBranch(String branch) {
+ super(branch);
+ }
+
+ @Override
+ public BranchType getType() {
+ return BranchType.PULL_REQUEST;
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.step;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.utils.System2;
+import org.sonar.ce.task.projectanalysis.analysis.Analysis;
+import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
+import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.FileAttributes;
+import org.sonar.ce.task.projectanalysis.component.ReportComponent;
+import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
+import org.sonar.ce.task.projectanalysis.duplication.CrossProjectDuplicationStatusHolder;
+import org.sonar.ce.task.projectanalysis.duplication.IntegrateCrossProjectDuplications;
+import org.sonar.ce.task.step.ComputationStep;
+import org.sonar.ce.task.step.TestComputationStepContext;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.component.SnapshotTesting;
+import org.sonar.db.duplication.DuplicationUnitDto;
+import org.sonar.duplications.block.Block;
+import org.sonar.duplications.block.ByteArray;
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
+import static org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT;
+
+public class LoadCrossProjectDuplicationsRepositoryStepIT {
+
+
+ private static final String XOO_LANGUAGE = "xoo";
+ private static final int PROJECT_REF = 1;
+ private static final int FILE_REF = 2;
+ private static final String CURRENT_FILE_KEY = "FILE_KEY";
+
+ private static final Component CURRENT_FILE = ReportComponent.builder(FILE, FILE_REF)
+ .setKey(CURRENT_FILE_KEY)
+ .setFileAttributes(new FileAttributes(false, XOO_LANGUAGE, 1))
+ .build();
+
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(
+ ReportComponent.builder(PROJECT, PROJECT_REF)
+ .addChildren(CURRENT_FILE).build());
+
+ @Rule
+ public BatchReportReaderRule batchReportReader = new BatchReportReaderRule();
+
+ @Rule
+ public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
+
+ private CrossProjectDuplicationStatusHolder crossProjectDuplicationStatusHolder = mock(CrossProjectDuplicationStatusHolder.class);
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private DbClient dbClient = dbTester.getDbClient();
+ private DbSession dbSession = dbTester.getSession();
+ private IntegrateCrossProjectDuplications integrateCrossProjectDuplications = mock(IntegrateCrossProjectDuplications.class);
+ private Analysis baseProjectAnalysis;
+
+ private ComputationStep underTest = new LoadCrossProjectDuplicationsRepositoryStep(treeRootHolder, batchReportReader, analysisMetadataHolder, crossProjectDuplicationStatusHolder,
+ integrateCrossProjectDuplications, dbClient);
+
+ @Before
+ public void setUp() {
+ ComponentDto project = ComponentTesting.newPrivateProjectDto();
+ dbTester.components().insertComponent(project);
+ SnapshotDto projectSnapshot = SnapshotTesting.newAnalysis(project);
+ dbClient.snapshotDao().insert(dbSession, projectSnapshot);
+ dbSession.commit();
+
+ baseProjectAnalysis = new Analysis.Builder()
+ .setUuid(projectSnapshot.getUuid())
+ .setCreatedAt(projectSnapshot.getCreatedAt())
+ .build();
+ }
+
+ @Test
+ public void call_compute_cpd_on_one_duplication() {
+ when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(true);
+ analysisMetadataHolder.setBaseAnalysis(baseProjectAnalysis);
+
+ ComponentDto otherProject = createProject("OTHER_PROJECT_KEY");
+ SnapshotDto otherProjectSnapshot = createProjectSnapshot(otherProject);
+
+ ComponentDto otherFile = createFile("OTHER_FILE_KEY", otherProject);
+
+ String hash = "a8998353e96320ec";
+ DuplicationUnitDto duplicate = new DuplicationUnitDto()
+ .setHash(hash)
+ .setStartLine(40)
+ .setEndLine(55)
+ .setIndexInFile(0)
+ .setAnalysisUuid(otherProjectSnapshot.getUuid())
+ .setComponentUuid(otherFile.uuid());
+ dbClient.duplicationDao().insert(dbSession, duplicate);
+ dbSession.commit();
+
+ ScannerReport.CpdTextBlock originBlock = ScannerReport.CpdTextBlock.newBuilder()
+ .setHash(hash)
+ .setStartLine(30)
+ .setEndLine(45)
+ .setStartTokenIndex(0)
+ .setEndTokenIndex(10)
+ .build();
+ batchReportReader.putDuplicationBlocks(FILE_REF, singletonList(originBlock));
+
+ underTest.execute(new TestComputationStepContext());
+
+ verify(integrateCrossProjectDuplications).computeCpd(CURRENT_FILE,
+ singletonList(
+ new Block.Builder()
+ .setResourceId(CURRENT_FILE_KEY)
+ .setBlockHash(new ByteArray(hash))
+ .setIndexInFile(0)
+ .setLines(originBlock.getStartLine(), originBlock.getEndLine())
+ .setUnit(originBlock.getStartTokenIndex(), originBlock.getEndTokenIndex())
+ .build()),
+ singletonList(
+ new Block.Builder()
+ .setResourceId(otherFile.getKey())
+ .setBlockHash(new ByteArray(hash))
+ .setIndexInFile(duplicate.getIndexInFile())
+ .setLines(duplicate.getStartLine(), duplicate.getEndLine())
+ .build()));
+ }
+
+ @Test
+ public void call_compute_cpd_on_many_duplication() {
+ when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(true);
+ analysisMetadataHolder.setBaseAnalysis(baseProjectAnalysis);
+
+ ComponentDto otherProject = createProject("OTHER_PROJECT_KEY");
+ SnapshotDto otherProjectSnapshot = createProjectSnapshot(otherProject);
+
+ ComponentDto otherFile = createFile("OTHER_FILE_KEY", otherProject);
+
+ ScannerReport.CpdTextBlock originBlock1 = ScannerReport.CpdTextBlock.newBuilder()
+ .setHash("a8998353e96320ec")
+ .setStartLine(30)
+ .setEndLine(45)
+ .setStartTokenIndex(0)
+ .setEndTokenIndex(10)
+ .build();
+ ScannerReport.CpdTextBlock originBlock2 = ScannerReport.CpdTextBlock.newBuilder()
+ .setHash("b1234353e96320ff")
+ .setStartLine(10)
+ .setEndLine(25)
+ .setStartTokenIndex(5)
+ .setEndTokenIndex(15)
+ .build();
+ batchReportReader.putDuplicationBlocks(FILE_REF, asList(originBlock1, originBlock2));
+
+ DuplicationUnitDto duplicate1 = new DuplicationUnitDto()
+ .setHash(originBlock1.getHash())
+ .setStartLine(40)
+ .setEndLine(55)
+ .setIndexInFile(0)
+ .setAnalysisUuid(otherProjectSnapshot.getUuid())
+ .setComponentUuid(otherFile.uuid());
+
+ DuplicationUnitDto duplicate2 = new DuplicationUnitDto()
+ .setHash(originBlock2.getHash())
+ .setStartLine(20)
+ .setEndLine(35)
+ .setIndexInFile(1)
+ .setAnalysisUuid(otherProjectSnapshot.getUuid())
+ .setComponentUuid(otherFile.uuid());
+ dbClient.duplicationDao().insert(dbSession, duplicate1);
+ dbClient.duplicationDao().insert(dbSession, duplicate2);
+ dbSession.commit();
+
+ underTest.execute(new TestComputationStepContext());
+
+ ArgumentCaptor<List<Block>> originBlocks = ArgumentCaptor.forClass(List.class);
+ ArgumentCaptor<List<Block>> duplicationBlocks = ArgumentCaptor.forClass(List.class);
+
+ verify(integrateCrossProjectDuplications).computeCpd(eq(CURRENT_FILE), originBlocks.capture(), duplicationBlocks.capture());
+
+ Map<Integer, Block> originBlocksByIndex = blocksByIndexInFile(originBlocks.getValue());
+ assertThat(originBlocksByIndex).containsExactly(
+ Map.entry(0, new Block.Builder()
+ .setResourceId(CURRENT_FILE_KEY)
+ .setBlockHash(new ByteArray(originBlock1.getHash()))
+ .setIndexInFile(0)
+ .setLines(originBlock1.getStartLine(), originBlock1.getEndLine())
+ .setUnit(originBlock1.getStartTokenIndex(), originBlock1.getEndTokenIndex())
+ .build()),
+ Map.entry(1, new Block.Builder()
+ .setResourceId(CURRENT_FILE_KEY)
+ .setBlockHash(new ByteArray(originBlock2.getHash()))
+ .setIndexInFile(1)
+ .setLines(originBlock2.getStartLine(), originBlock2.getEndLine())
+ .setUnit(originBlock2.getStartTokenIndex(), originBlock2.getEndTokenIndex())
+ .build()));
+
+ Map<Integer, Block> duplicationBlocksByIndex = blocksByIndexInFile(duplicationBlocks.getValue());
+ assertThat(duplicationBlocksByIndex).containsExactly(
+ Map.entry(0, new Block.Builder()
+ .setResourceId(otherFile.getKey())
+ .setBlockHash(new ByteArray(originBlock1.getHash()))
+ .setIndexInFile(duplicate1.getIndexInFile())
+ .setLines(duplicate1.getStartLine(), duplicate1.getEndLine())
+ .build()),
+ Map.entry(1, new Block.Builder()
+ .setResourceId(otherFile.getKey())
+ .setBlockHash(new ByteArray(originBlock2.getHash()))
+ .setIndexInFile(duplicate2.getIndexInFile())
+ .setLines(duplicate2.getStartLine(), duplicate2.getEndLine())
+ .build()));
+ }
+
+ @Test
+ public void nothing_to_do_when_cross_project_duplication_is_disabled() {
+ when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(false);
+ analysisMetadataHolder.setBaseAnalysis(baseProjectAnalysis);
+
+ ComponentDto otherProject = createProject("OTHER_PROJECT_KEY");
+ SnapshotDto otherProjectSnapshot = createProjectSnapshot(otherProject);
+
+ ComponentDto otherFIle = createFile("OTHER_FILE_KEY", otherProject);
+
+ String hash = "a8998353e96320ec";
+ DuplicationUnitDto duplicate = new DuplicationUnitDto()
+ .setHash(hash)
+ .setStartLine(40)
+ .setEndLine(55)
+ .setIndexInFile(0)
+ .setAnalysisUuid(otherProjectSnapshot.getUuid())
+ .setComponentUuid(otherFIle.uuid());
+ dbClient.duplicationDao().insert(dbSession, duplicate);
+ dbSession.commit();
+
+ ScannerReport.CpdTextBlock originBlock = ScannerReport.CpdTextBlock.newBuilder()
+ .setHash(hash)
+ .setStartLine(30)
+ .setEndLine(45)
+ .setStartTokenIndex(0)
+ .setEndTokenIndex(10)
+ .build();
+ batchReportReader.putDuplicationBlocks(FILE_REF, singletonList(originBlock));
+
+ underTest.execute(new TestComputationStepContext());
+
+ verifyNoInteractions(integrateCrossProjectDuplications);
+ }
+
+ @Test
+ public void nothing_to_do_when_no_cpd_text_blocks_found() {
+ when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(true);
+ analysisMetadataHolder.setBaseAnalysis(baseProjectAnalysis);
+
+ batchReportReader.putDuplicationBlocks(FILE_REF, Collections.emptyList());
+
+ underTest.execute(new TestComputationStepContext());
+
+ verifyNoInteractions(integrateCrossProjectDuplications);
+ }
+
+ @Test
+ public void nothing_to_do_when_cpd_text_blocks_exists_but_no_duplicated_found() {
+ when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(true);
+ analysisMetadataHolder.setBaseAnalysis(baseProjectAnalysis);
+
+ ScannerReport.CpdTextBlock originBlock = ScannerReport.CpdTextBlock.newBuilder()
+ .setHash("a8998353e96320ec")
+ .setStartLine(30)
+ .setEndLine(45)
+ .setStartTokenIndex(0)
+ .setEndTokenIndex(10)
+ .build();
+ batchReportReader.putDuplicationBlocks(FILE_REF, singletonList(originBlock));
+
+ underTest.execute(new TestComputationStepContext());
+
+ verifyNoInteractions(integrateCrossProjectDuplications);
+ }
+
+ private ComponentDto createProject(String projectKey) {
+ ComponentDto project = ComponentTesting.newPrivateProjectDto().setKey(projectKey);
+ return dbTester.components().insertComponent(project);
+ }
+
+ private SnapshotDto createProjectSnapshot(ComponentDto project) {
+ SnapshotDto projectSnapshot = SnapshotTesting.newAnalysis(project);
+ dbClient.snapshotDao().insert(dbSession, projectSnapshot);
+ dbSession.commit();
+ return projectSnapshot;
+ }
+
+ private ComponentDto createFile(String fileKey, ComponentDto project) {
+ ComponentDto file = ComponentTesting.newFileDto(project, null)
+ .setKey(fileKey)
+ .setLanguage(XOO_LANGUAGE);
+ dbClient.componentDao().insert(dbSession, file);
+ dbSession.commit();
+ return file;
+ }
+
+ private static Map<Integer, Block> blocksByIndexInFile(List<Block> blocks) {
+ Map<Integer, Block> blocksByIndexInFile = new HashMap<>();
+ for (Block block : blocks) {
+ blocksByIndexInFile.put(block.getIndexInFile(), block);
+ }
+ return blocksByIndexInFile;
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.step;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
+import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
+import org.sonar.ce.task.step.TestComputationStepContext;
+import org.sonar.core.util.CloseableIterator;
+import org.sonar.core.util.UuidFactoryFast;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.AnalysisPropertyDto;
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class PersistAnalysisPropertiesStepIT {
+ private static final String SNAPSHOT_UUID = randomAlphanumeric(40);
+ private static final String SMALL_VALUE1 = randomAlphanumeric(50);
+ private static final String SMALL_VALUE2 = randomAlphanumeric(50);
+ private static final String SMALL_VALUE3 = randomAlphanumeric(50);
+ private static final String BIG_VALUE = randomAlphanumeric(5000);
+ private static final String VALUE_PREFIX_FOR_PR_PROPERTIES = "pr_";
+ private static final List<ScannerReport.ContextProperty> PROPERTIES = Arrays.asList(
+ newContextProperty("key1", "value1"),
+ newContextProperty("key2", "value1"),
+ newContextProperty("sonar.analysis", SMALL_VALUE1),
+ newContextProperty("sonar.analysis.branch", SMALL_VALUE2),
+ newContextProperty("sonar.analysis.empty_string", ""),
+ newContextProperty("sonar.analysis.big_value", BIG_VALUE),
+ newContextProperty("sonar.analysis.", SMALL_VALUE3),
+ newContextProperty("sonar.pullrequest", VALUE_PREFIX_FOR_PR_PROPERTIES + SMALL_VALUE1),
+ newContextProperty("sonar.pullrequest.branch", VALUE_PREFIX_FOR_PR_PROPERTIES + SMALL_VALUE2),
+ newContextProperty("sonar.pullrequest.empty_string", ""),
+ newContextProperty("sonar.pullrequest.big_value", VALUE_PREFIX_FOR_PR_PROPERTIES + BIG_VALUE),
+ newContextProperty("sonar.pullrequest.", VALUE_PREFIX_FOR_PR_PROPERTIES + SMALL_VALUE3));
+ private static final String SCM_REV_ID = "sha1";
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private BatchReportReader batchReportReader = mock(BatchReportReader.class);
+ private AnalysisMetadataHolder analysisMetadataHolder = mock(AnalysisMetadataHolder.class);
+ private PersistAnalysisPropertiesStep underTest = new PersistAnalysisPropertiesStep(dbTester.getDbClient(), analysisMetadataHolder, batchReportReader,
+ UuidFactoryFast.getInstance());
+
+ @Test
+ public void persist_should_stores_sonarDotAnalysisDot_and_sonarDotPullRequestDot_properties() {
+ when(batchReportReader.readContextProperties()).thenReturn(CloseableIterator.from(PROPERTIES.iterator()));
+ when(analysisMetadataHolder.getUuid()).thenReturn(SNAPSHOT_UUID);
+ when(analysisMetadataHolder.getScmRevision()).thenReturn(Optional.of(SCM_REV_ID));
+
+ underTest.execute(new TestComputationStepContext());
+
+ assertThat(dbTester.countRowsOfTable("analysis_properties")).isEqualTo(8);
+ List<AnalysisPropertyDto> propertyDtos = dbTester.getDbClient()
+ .analysisPropertiesDao().selectByAnalysisUuid(dbTester.getSession(), SNAPSHOT_UUID);
+
+ assertThat(propertyDtos)
+ .extracting(AnalysisPropertyDto::getAnalysisUuid, AnalysisPropertyDto::getKey, AnalysisPropertyDto::getValue)
+ .containsExactlyInAnyOrder(
+ tuple(SNAPSHOT_UUID, "sonar.analysis.branch", SMALL_VALUE2),
+ tuple(SNAPSHOT_UUID, "sonar.analysis.empty_string", ""),
+ tuple(SNAPSHOT_UUID, "sonar.analysis.big_value", BIG_VALUE),
+ tuple(SNAPSHOT_UUID, "sonar.analysis.", SMALL_VALUE3),
+ tuple(SNAPSHOT_UUID, "sonar.pullrequest.branch", VALUE_PREFIX_FOR_PR_PROPERTIES + SMALL_VALUE2),
+ tuple(SNAPSHOT_UUID, "sonar.pullrequest.empty_string", ""),
+ tuple(SNAPSHOT_UUID, "sonar.pullrequest.big_value", VALUE_PREFIX_FOR_PR_PROPERTIES + BIG_VALUE),
+ tuple(SNAPSHOT_UUID, "sonar.pullrequest.", VALUE_PREFIX_FOR_PR_PROPERTIES + SMALL_VALUE3));
+ }
+
+ @Test
+ public void persist_filtering_of_properties_is_case_sensitive() {
+ when(analysisMetadataHolder.getScmRevision()).thenReturn(Optional.of(SCM_REV_ID));
+ when(batchReportReader.readContextProperties()).thenReturn(CloseableIterator.from(ImmutableList.of(
+ newContextProperty("sonar.ANALYSIS.foo", "foo"),
+ newContextProperty("sonar.anaLysis.bar", "bar"),
+ newContextProperty("sonar.anaLYSIS.doo", "doh"),
+ newContextProperty("sonar.PULLREQUEST.foo", "foo"),
+ newContextProperty("sonar.pullRequest.bar", "bar"),
+ newContextProperty("sonar.pullREQUEST.doo", "doh")).iterator()));
+ when(analysisMetadataHolder.getUuid()).thenReturn(SNAPSHOT_UUID);
+
+ underTest.execute(new TestComputationStepContext());
+
+ assertThat(dbTester.countRowsOfTable("analysis_properties")).isZero();
+ }
+
+ @Test
+ public void persist_should_store_nothing_if_there_are_no_context_properties() {
+ when(analysisMetadataHolder.getScmRevision()).thenReturn(Optional.of(SCM_REV_ID));
+ when(batchReportReader.readContextProperties()).thenReturn(CloseableIterator.emptyCloseableIterator());
+ when(analysisMetadataHolder.getUuid()).thenReturn(SNAPSHOT_UUID);
+
+ underTest.execute(new TestComputationStepContext());
+
+ assertThat(dbTester.countRowsOfTable("analysis_properties")).isZero();
+ }
+
+ @Test
+ public void verify_description_value() {
+ assertThat(underTest.getDescription()).isEqualTo("Persist analysis properties");
+ }
+
+ private static ScannerReport.ContextProperty newContextProperty(String key, String value) {
+ return ScannerReport.ContextProperty.newBuilder()
+ .setKey(key)
+ .setValue(value)
+ .build();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.step;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.ce.task.projectanalysis.analysis.Analysis;
+import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
+import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.ReportComponent;
+import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
+import org.sonar.ce.task.projectanalysis.duplication.CrossProjectDuplicationStatusHolder;
+import org.sonar.ce.task.step.ComputationStep;
+import org.sonar.ce.task.step.TestComputationStepContext;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class PersistCrossProjectDuplicationIndexStepIT {
+
+ private static final int FILE_1_REF = 2;
+ private static final int FILE_2_REF = 3;
+ private static final String FILE_2_UUID = "file2";
+
+ private static final Component FILE_1 = ReportComponent.builder(Component.Type.FILE, FILE_1_REF).build();
+ private static final Component FILE_2 = ReportComponent.builder(Component.Type.FILE, FILE_2_REF)
+ .setStatus(Component.Status.SAME).setUuid(FILE_2_UUID).build();
+
+ private static final Component PROJECT = ReportComponent.builder(Component.Type.PROJECT, 1)
+ .addChildren(FILE_1)
+ .addChildren(FILE_2)
+ .build();
+
+ private static final ScannerReport.CpdTextBlock CPD_TEXT_BLOCK = ScannerReport.CpdTextBlock.newBuilder()
+ .setHash("a8998353e96320ec")
+ .setStartLine(30)
+ .setEndLine(45)
+ .build();
+ private static final String ANALYSIS_UUID = "analysis uuid";
+ private static final String BASE_ANALYSIS_UUID = "base analysis uuid";
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+ @Rule
+ public BatchReportReaderRule reportReader = new BatchReportReaderRule();
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(PROJECT);
+ @Rule
+ public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
+
+ private CrossProjectDuplicationStatusHolder crossProjectDuplicationStatusHolder = mock(CrossProjectDuplicationStatusHolder.class);
+ private Analysis baseAnalysis = mock(Analysis.class);
+ private DbClient dbClient = dbTester.getDbClient();
+
+ private ComputationStep underTest;
+
+ @Before
+ public void setUp() {
+ when(baseAnalysis.getUuid()).thenReturn(BASE_ANALYSIS_UUID);
+ analysisMetadataHolder.setUuid(ANALYSIS_UUID);
+ analysisMetadataHolder.setBaseAnalysis(baseAnalysis);
+ underTest = new PersistCrossProjectDuplicationIndexStep(crossProjectDuplicationStatusHolder, dbClient, treeRootHolder, analysisMetadataHolder, reportReader);
+ }
+
+ @Test
+ public void persist_cpd_text_block() {
+ when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(true);
+ reportReader.putDuplicationBlocks(FILE_1_REF, singletonList(CPD_TEXT_BLOCK));
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ Map<String, Object> dto = dbTester.selectFirst("select HASH, START_LINE, END_LINE, INDEX_IN_FILE, COMPONENT_UUID, ANALYSIS_UUID from duplications_index");
+ assertThat(dto)
+ .containsEntry("HASH", CPD_TEXT_BLOCK.getHash())
+ .containsEntry("START_LINE", 30L)
+ .containsEntry("END_LINE", 45L)
+ .containsEntry("INDEX_IN_FILE", 0L)
+ .containsEntry("COMPONENT_UUID", FILE_1.getUuid())
+ .containsEntry("ANALYSIS_UUID", ANALYSIS_UUID);
+ context.getStatistics().assertValue("inserts", 1);
+ }
+
+ @Test
+ public void persist_many_cpd_text_blocks() {
+ when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(true);
+ reportReader.putDuplicationBlocks(FILE_1_REF, Arrays.asList(
+ CPD_TEXT_BLOCK,
+ ScannerReport.CpdTextBlock.newBuilder()
+ .setHash("b1234353e96320ff")
+ .setStartLine(20)
+ .setEndLine(15)
+ .build()));
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ List<Map<String, Object>> dtos = dbTester.select("select HASH, START_LINE, END_LINE, INDEX_IN_FILE, COMPONENT_UUID, ANALYSIS_UUID from duplications_index");
+ assertThat(dtos).extracting("HASH").containsOnly(CPD_TEXT_BLOCK.getHash(), "b1234353e96320ff");
+ assertThat(dtos).extracting("START_LINE").containsOnly(30L, 20L);
+ assertThat(dtos).extracting("END_LINE").containsOnly(45L, 15L);
+ assertThat(dtos).extracting("INDEX_IN_FILE").containsOnly(0L, 1L);
+ assertThat(dtos).extracting("COMPONENT_UUID").containsOnly(FILE_1.getUuid());
+ assertThat(dtos).extracting("ANALYSIS_UUID").containsOnly(ANALYSIS_UUID);
+ context.getStatistics().assertValue("inserts", 2);
+ }
+
+ @Test
+ public void nothing_to_persist_when_no_cpd_text_blocks_in_report() {
+ when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(true);
+ reportReader.putDuplicationBlocks(FILE_1_REF, Collections.emptyList());
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(dbTester.countRowsOfTable("duplications_index")).isZero();
+ context.getStatistics().assertValue("inserts", 0);
+ }
+
+ @Test
+ public void nothing_to_do_when_cross_project_duplication_is_disabled() {
+ when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(false);
+ reportReader.putDuplicationBlocks(FILE_1_REF, singletonList(CPD_TEXT_BLOCK));
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(dbTester.countRowsOfTable("duplications_index")).isZero();
+ context.getStatistics().assertValue("inserts", null);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.step;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.ReportComponent;
+import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
+import org.sonar.ce.task.projectanalysis.event.Event;
+import org.sonar.ce.task.projectanalysis.event.EventRepository;
+import org.sonar.ce.task.step.ComputationStep;
+import org.sonar.ce.task.step.TestComputationStepContext;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.core.util.UuidFactoryImpl;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.event.EventDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.ce.task.projectanalysis.component.Component.Type.DIRECTORY;
+import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
+import static org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT;
+import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
+import static org.sonar.db.event.EventDto.CATEGORY_ALERT;
+import static org.sonar.db.event.EventDto.CATEGORY_PROFILE;
+import static org.sonar.db.event.EventDto.CATEGORY_VERSION;
+
+public class PersistEventsStepIT extends BaseStepTest {
+
+ private static final long NOW = 1225630680000L;
+ private static final ReportComponent ROOT = builder(PROJECT, 1)
+ .setUuid("ABCD")
+ .setProjectVersion("version_1")
+ .addChildren(
+ builder(DIRECTORY, 2)
+ .setUuid("BCDE")
+ .addChildren(
+ builder(DIRECTORY, 3)
+ .setUuid("Q")
+ .addChildren(
+ builder(FILE, 4)
+ .setUuid("Z")
+ .build())
+ .build())
+ .build())
+ .build();
+ private static final String ANALYSIS_UUID = "uuid_1";
+
+ System2 system2 = mock(System2.class);
+
+ @Rule
+ public DbTester dbTester = DbTester.create(system2);
+ @Rule
+ public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+
+ private Date someDate = new Date(150000000L);
+
+ private EventRepository eventRepository = mock(EventRepository.class);
+ private UuidFactory uuidFactory = UuidFactoryImpl.INSTANCE;
+
+ private PersistEventsStep underTest;
+
+ @Before
+ public void setup() {
+ analysisMetadataHolder.setAnalysisDate(someDate.getTime()).setUuid(ANALYSIS_UUID);
+ underTest = new PersistEventsStep(dbTester.getDbClient(), system2, treeRootHolder, analysisMetadataHolder, eventRepository, uuidFactory);
+ when(eventRepository.getEvents(any(Component.class))).thenReturn(Collections.emptyList());
+ }
+
+ @Override
+ protected ComputationStep step() {
+ return underTest;
+ }
+
+ @Test
+ public void create_version_event() {
+ when(system2.now()).thenReturn(NOW);
+ Component project = builder(PROJECT, 1)
+ .setUuid("ABCD")
+ .setProjectVersion("1.0")
+ .addChildren(
+ builder(DIRECTORY, 2)
+ .setUuid("BCDE")
+ .addChildren(
+ builder(DIRECTORY, 3)
+ .setUuid("Q")
+ .addChildren(
+ builder(FILE, 4)
+ .setUuid("Z")
+ .build())
+ .build())
+ .build())
+ .build();
+ treeRootHolder.setRoot(project);
+
+ underTest.execute(new TestComputationStepContext());
+
+ assertThat(dbTester.countRowsOfTable(dbTester.getSession(), "events")).isOne();
+ List<EventDto> eventDtos = dbTester.getDbClient().eventDao().selectByComponentUuid(dbTester.getSession(), ROOT.getUuid());
+ assertThat(eventDtos).hasSize(1);
+ EventDto eventDto = eventDtos.iterator().next();
+ assertThat(eventDto.getComponentUuid()).isEqualTo(ROOT.getUuid());
+ assertThat(eventDto.getName()).isEqualTo("1.0");
+ assertThat(eventDto.getDescription()).isNull();
+ assertThat(eventDto.getCategory()).isEqualTo(CATEGORY_VERSION);
+ assertThat(eventDto.getData()).isNull();
+ assertThat(eventDto.getDate()).isEqualTo(analysisMetadataHolder.getAnalysisDate());
+ assertThat(eventDto.getCreatedAt()).isEqualTo(NOW);
+ }
+
+ @Test
+ public void persist_alert_events_on_root() {
+ when(system2.now()).thenReturn(NOW);
+ treeRootHolder.setRoot(ROOT);
+ Event alert = Event.createAlert("Failed", null, "Open issues > 0");
+ when(eventRepository.getEvents(ROOT)).thenReturn(ImmutableList.of(alert));
+
+ underTest.execute(new TestComputationStepContext());
+
+ assertThat(dbTester.countRowsOfTable(dbTester.getSession(), "events")).isEqualTo(2);
+ List<EventDto> eventDtos = dbTester.getDbClient().eventDao().selectByComponentUuid(dbTester.getSession(), ROOT.getUuid());
+ assertThat(eventDtos)
+ .extracting(EventDto::getCategory)
+ .containsOnly(CATEGORY_ALERT, CATEGORY_VERSION);
+ EventDto eventDto = eventDtos.stream().filter(t -> CATEGORY_ALERT.equals(t.getCategory())).findAny().get();
+ assertThat(eventDto.getComponentUuid()).isEqualTo(ROOT.getUuid());
+ assertThat(eventDto.getName()).isEqualTo(alert.getName());
+ assertThat(eventDto.getDescription()).isEqualTo(alert.getDescription());
+ assertThat(eventDto.getCategory()).isEqualTo(CATEGORY_ALERT);
+ assertThat(eventDto.getData()).isNull();
+ assertThat(eventDto.getDate()).isEqualTo(analysisMetadataHolder.getAnalysisDate());
+ assertThat(eventDto.getCreatedAt()).isEqualTo(NOW);
+ }
+
+ @Test
+ public void persist_profile_events_on_root() {
+ when(system2.now()).thenReturn(NOW);
+ treeRootHolder.setRoot(ROOT);
+ Event profile = Event.createProfile("foo", null, "bar");
+ when(eventRepository.getEvents(ROOT)).thenReturn(ImmutableList.of(profile));
+
+ underTest.execute(new TestComputationStepContext());
+
+ assertThat(dbTester.countRowsOfTable(dbTester.getSession(), "events")).isEqualTo(2);
+ List<EventDto> eventDtos = dbTester.getDbClient().eventDao().selectByComponentUuid(dbTester.getSession(), ROOT.getUuid());
+ assertThat(eventDtos)
+ .extracting(EventDto::getCategory)
+ .containsOnly(CATEGORY_PROFILE, CATEGORY_VERSION);
+ EventDto eventDto = eventDtos.stream().filter(t -> CATEGORY_PROFILE.equals(t.getCategory())).findAny().get();
+ assertThat(eventDto.getComponentUuid()).isEqualTo(ROOT.getUuid());
+ assertThat(eventDto.getName()).isEqualTo(profile.getName());
+ assertThat(eventDto.getDescription()).isEqualTo(profile.getDescription());
+ assertThat(eventDto.getCategory()).isEqualTo(EventDto.CATEGORY_PROFILE);
+ assertThat(eventDto.getData()).isNull();
+ assertThat(eventDto.getDate()).isEqualTo(analysisMetadataHolder.getAnalysisDate());
+ assertThat(eventDto.getCreatedAt()).isEqualTo(NOW);
+ }
+
+ @Test
+ public void keep_one_event_by_version() {
+ ComponentDto projectDto = dbTester.components().insertPublicProject();
+ EventDto[] existingEvents = new EventDto[] {
+ dbTester.events().insertEvent(newVersionEventDto(projectDto, 120_000_000L, "1.3-SNAPSHOT")),
+ dbTester.events().insertEvent(newVersionEventDto(projectDto, 130_000_000L, "1.4")),
+ dbTester.events().insertEvent(newVersionEventDto(projectDto, 140_000_000L, "1.5-SNAPSHOT"))
+ };
+
+ Component project = builder(PROJECT, 1)
+ .setUuid(projectDto.uuid())
+ .setProjectVersion("1.5-SNAPSHOT")
+ .addChildren(
+ builder(DIRECTORY, 2)
+ .setUuid("BCDE")
+ .addChildren(
+ builder(DIRECTORY, 3)
+ .setUuid("Q")
+ .addChildren(
+ builder(FILE, 4)
+ .setUuid("Z")
+ .build())
+ .build())
+ .build())
+ .build();
+ treeRootHolder.setRoot(project);
+
+ underTest.execute(new TestComputationStepContext());
+
+ assertThat(dbTester.countRowsOfTable(dbTester.getSession(), "events")).isEqualTo(3);
+ List<EventDto> eventDtos = dbTester.getDbClient().eventDao().selectByComponentUuid(dbTester.getSession(), projectDto.uuid());
+ assertThat(eventDtos).hasSize(3);
+ assertThat(eventDtos)
+ .extracting(EventDto::getName)
+ .containsOnly("1.3-SNAPSHOT", "1.4", "1.5-SNAPSHOT");
+ assertThat(eventDtos)
+ .extracting(EventDto::getUuid)
+ .contains(existingEvents[0].getUuid(), existingEvents[1].getUuid())
+ .doesNotContain(existingEvents[2].getUuid());
+ }
+
+ private EventDto newVersionEventDto(ComponentDto project, long date, String name) {
+ return new EventDto().setUuid(uuidFactory.create()).setComponentUuid(project.uuid())
+ .setAnalysisUuid("analysis_uuid")
+ .setCategory(CATEGORY_VERSION)
+ .setName(name).setDate(date).setCreatedAt(date);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.taskprocessor;
+
+import java.util.Optional;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.ce.task.CeTask;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.sonar.db.component.BranchType.BRANCH;
+
+public class IgnoreOrphanBranchStepIT {
+
+ private String BRANCH_UUID = "branch_uuid";
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private CeTask.Component component = new CeTask.Component(BRANCH_UUID, "component key", "component name");
+ private CeTask ceTask = new CeTask.Builder()
+ .setType("type")
+ .setUuid("uuid")
+ .setComponent(component)
+ .setMainComponent(component)
+ .build();
+
+ private DbClient dbClient = dbTester.getDbClient();
+ private IgnoreOrphanBranchStep underTest = new IgnoreOrphanBranchStep(ceTask, dbClient);
+
+ @Test
+ public void execute() {
+ BranchDto branchDto = new BranchDto()
+ .setBranchType(BRANCH)
+ .setKey("branchName")
+ .setUuid(BRANCH_UUID)
+ .setProjectUuid("project_uuid")
+ .setNeedIssueSync(true);
+ dbClient.branchDao().insert(dbTester.getSession(), branchDto);
+ dbTester.commit();
+
+ underTest.execute(() -> null);
+
+ Optional<BranchDto> branch = dbClient.branchDao().selectByUuid(dbTester.getSession(), BRANCH_UUID);
+ assertThat(branch.get().isNeedIssueSync()).isFalse();
+ assertThat(branch.get().isExcludeFromPurge()).isFalse();
+ }
+
+ @Test
+ public void execute_on_already_indexed_branch() {
+ BranchDto branchDto = new BranchDto()
+ .setBranchType(BRANCH)
+ .setKey("branchName")
+ .setUuid(BRANCH_UUID)
+ .setProjectUuid("project_uuid")
+ .setNeedIssueSync(false);
+ dbClient.branchDao().insert(dbTester.getSession(), branchDto);
+ dbTester.commit();
+
+ underTest.execute(() -> null);
+
+ Optional<BranchDto> branch = dbClient.branchDao().selectByUuid(dbTester.getSession(), BRANCH_UUID);
+ assertThat(branch.get().isNeedIssueSync()).isFalse();
+ assertThat(branch.get().isExcludeFromPurge()).isFalse();
+ }
+
+ @Test
+ public void fail_if_missing_main_component_in_task() {
+ CeTask ceTask = new CeTask.Builder()
+ .setType("type")
+ .setUuid("uuid")
+ .setComponent(null)
+ .setMainComponent(null)
+ .build();
+ IgnoreOrphanBranchStep underTest = new IgnoreOrphanBranchStep(ceTask, dbClient);
+
+ assertThatThrownBy(() -> underTest.execute(() -> null))
+ .isInstanceOf(UnsupportedOperationException.class)
+ .hasMessage("main component not found in task");
+ }
+
+ @Test
+ public void verify_step_description() {
+ assertThat(underTest.getDescription()).isEqualTo("Ignore orphan component");
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectexport.component;
+
+import com.google.common.collect.ImmutableSet;
+import com.sonarsource.governance.projectdump.protobuf.ProjectDump;
+import java.util.Date;
+import java.util.List;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.Scopes;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.ce.task.projectexport.steps.DumpElement;
+import org.sonar.ce.task.projectexport.steps.FakeDumpWriter;
+import org.sonar.ce.task.projectexport.steps.ProjectHolder;
+import org.sonar.ce.task.step.TestComputationStepContext;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.db.component.ComponentDto.UUID_PATH_OF_ROOT;
+import static org.sonar.db.component.ComponentDto.UUID_PATH_SEPARATOR;
+
+public class ExportComponentsStepIT {
+
+ private static final String PROJECT_UUID = "PROJECT_UUID";
+ private static final ComponentDto PROJECT = new ComponentDto()
+ // no id yet
+ .setScope(Scopes.PROJECT)
+ .setQualifier(Qualifiers.PROJECT)
+ .setKey("the_project")
+ .setName("The Project")
+ .setDescription("The project description")
+ .setEnabled(true)
+ .setUuid(PROJECT_UUID)
+ .setUuidPath(UUID_PATH_OF_ROOT)
+ .setCreatedAt(new Date(1596749115856L))
+ .setBranchUuid(PROJECT_UUID);
+
+ private static final String FILE_UUID = "FILE_UUID";
+ private static final String FILE_UUID_PATH = PROJECT_UUID + FILE_UUID + UUID_PATH_SEPARATOR;
+ private static final ComponentDto FILE = new ComponentDto()
+ // no id yet
+ .setScope(Scopes.FILE)
+ .setQualifier(Qualifiers.FILE)
+ .setKey("the_file")
+ .setName("The File")
+ .setUuid(FILE_UUID)
+ .setUuidPath(FILE_UUID_PATH)
+ .setEnabled(true)
+ .setCreatedAt(new Date(1596749148406L))
+ .setBranchUuid(PROJECT_UUID);
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ private final FakeDumpWriter dumpWriter = new FakeDumpWriter();
+ private final ProjectHolder projectHolder = mock(ProjectHolder.class);
+ private final MutableComponentRepository componentRepository = new ComponentRepositoryImpl();
+ private final ExportComponentsStep underTest = new ExportComponentsStep(dbTester.getDbClient(), projectHolder, componentRepository, dumpWriter);
+
+ @After
+ public void tearDown() {
+ dbTester.getSession().close();
+ }
+
+ @Test
+ public void export_components_including_project() {
+ dbTester.components().insertPublicProject(PROJECT);
+ dbTester.getDbClient().componentDao().insert(dbTester.getSession(), FILE);
+ dbTester.commit();
+ when(projectHolder.projectDto()).thenReturn(dbTester.components().getProjectDto(PROJECT));
+
+ underTest.execute(new TestComputationStepContext());
+
+ assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("2 components exported");
+ List<ProjectDump.Component> components = dumpWriter.getWrittenMessagesOf(DumpElement.COMPONENTS);
+ assertThat(components).extracting(ProjectDump.Component::getQualifier, ProjectDump.Component::getUuid, ProjectDump.Component::getUuidPath)
+ .containsExactlyInAnyOrder(
+ tuple(Qualifiers.FILE, FILE_UUID, FILE_UUID_PATH),
+ tuple(Qualifiers.PROJECT, PROJECT_UUID, UUID_PATH_OF_ROOT));
+ }
+
+ @Test
+ public void execute_register_all_components_uuids_as_their_id_in_ComponentRepository() {
+ dbTester.components().insertPublicProject(PROJECT);
+ dbTester.getDbClient().componentDao().insert(dbTester.getSession(), FILE);
+ dbTester.commit();
+ when(projectHolder.projectDto()).thenReturn(dbTester.components().getProjectDto(PROJECT));
+
+ underTest.execute(new TestComputationStepContext());
+
+ assertThat(ImmutableSet.of(
+ componentRepository.getRef(PROJECT.uuid()),
+ componentRepository.getRef(FILE.uuid()))).containsExactlyInAnyOrder(1L, 2L);
+ }
+
+ @Test
+ public void throws_ISE_if_error() {
+ dbTester.components().insertPublicProject(PROJECT);
+ dbTester.getDbClient().componentDao().insert(dbTester.getSession(), FILE);
+ dbTester.commit();
+ when(projectHolder.projectDto()).thenReturn(dbTester.components().getProjectDto(PROJECT));
+ dumpWriter.failIfMoreThan(1, DumpElement.COMPONENTS);
+
+ assertThatThrownBy(() -> underTest.execute(new TestComputationStepContext()))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Component Export failed after processing 1 components successfully");
+ }
+
+ @Test
+ public void getDescription_is_defined() {
+ assertThat(underTest.getDescription()).isEqualTo("Export components");
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectexport.steps;
+
+import com.sonarsource.governance.projectdump.protobuf.ProjectDump;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.ce.task.projectexport.component.ComponentRepositoryImpl;
+import org.sonar.ce.task.step.TestComputationStepContext;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.measure.MeasureDto;
+import org.sonar.db.metric.MetricDto;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.db.component.ComponentDto.UUID_PATH_OF_ROOT;
+import static org.sonar.db.component.ComponentDto.UUID_PATH_SEPARATOR;
+import static org.sonar.db.component.SnapshotDto.STATUS_PROCESSED;
+import static org.sonar.db.component.SnapshotDto.STATUS_UNPROCESSED;
+
+public class ExportMeasuresStepIT {
+
+ private static final ComponentDto PROJECT = new ComponentDto()
+ .setKey("project_key")
+ .setUuid("project_uuid")
+ .setBranchUuid("project_uuid")
+ .setUuidPath(UUID_PATH_OF_ROOT)
+ .setEnabled(true);
+ private static final ComponentDto FILE = new ComponentDto()
+ .setKey("file_key")
+ .setUuid("file_uuid")
+ .setBranchUuid("project_uuid")
+ .setUuidPath(UUID_PATH_OF_ROOT + PROJECT.uuid() + UUID_PATH_SEPARATOR)
+ .setEnabled(true);
+ private static final ComponentDto ANOTHER_PROJECT = new ComponentDto()
+ .setKey("another_project_key")
+ .setUuid("another_project_uuid")
+ .setBranchUuid("another_project_uuid")
+ .setUuidPath(UUID_PATH_OF_ROOT)
+ .setEnabled(true);
+
+ private static final MetricDto NCLOC = new MetricDto()
+ .setUuid("3")
+ .setKey("ncloc")
+ .setShortName("Lines of code")
+ .setEnabled(true);
+
+ private static final MetricDto DISABLED_METRIC = new MetricDto()
+ .setUuid("4")
+ .setKey("coverage")
+ .setShortName("Coverage")
+ .setEnabled(false);
+
+ private static final MetricDto NEW_NCLOC = new MetricDto()
+ .setUuid("5")
+ .setKey("new_ncloc")
+ .setShortName("New Lines of code")
+ .setEnabled(true);
+
+ private static final List<BranchDto> BRANCHES = newArrayList(
+ new BranchDto()
+ .setBranchType(BranchType.BRANCH)
+ .setKey("master")
+ .setUuid(PROJECT.uuid())
+ .setProjectUuid(PROJECT.uuid()));
+
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private ComponentRepositoryImpl componentRepository = new ComponentRepositoryImpl();
+ private MutableMetricRepository metricRepository = new MutableMetricRepositoryImpl();
+ private ProjectHolder projectHolder = mock(ProjectHolder.class);
+ private FakeDumpWriter dumpWriter = new FakeDumpWriter();
+ private ExportMeasuresStep underTest = new ExportMeasuresStep(dbTester.getDbClient(), projectHolder, componentRepository, metricRepository, dumpWriter);
+
+ @Before
+ public void setUp() {
+ String projectUuid = dbTester.components().insertPublicProject(PROJECT).uuid();
+ componentRepository.register(1, projectUuid, false);
+ dbTester.getDbClient().componentDao().insert(dbTester.getSession(), FILE, ANOTHER_PROJECT);
+ dbTester.getDbClient().metricDao().insert(dbTester.getSession(), NCLOC, DISABLED_METRIC, NEW_NCLOC);
+ dbTester.commit();
+ when(projectHolder.projectDto()).thenReturn(dbTester.components().getProjectDto(PROJECT));
+ when(projectHolder.branches()).thenReturn(BRANCHES);
+ }
+
+ @Test
+ public void export_zero_measures() {
+ underTest.execute(new TestComputationStepContext());
+
+ assertThat(dumpWriter.getWrittenMessagesOf(DumpElement.MEASURES)).isEmpty();
+ assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("0 measures exported");
+ assertThat(metricRepository.getRefByUuid()).isEmpty();
+ }
+
+ @Test
+ public void export_measures() {
+ SnapshotDto firstAnalysis = insertSnapshot("U_1", PROJECT, STATUS_PROCESSED);
+ insertMeasure(firstAnalysis, PROJECT, new MeasureDto().setValue(100.0).setMetricUuid(NCLOC.getUuid()));
+ SnapshotDto secondAnalysis = insertSnapshot("U_2", PROJECT, STATUS_PROCESSED);
+ insertMeasure(secondAnalysis, PROJECT, new MeasureDto().setValue(110.0).setMetricUuid(NCLOC.getUuid()));
+ SnapshotDto anotherProjectAnalysis = insertSnapshot("U_3", ANOTHER_PROJECT, STATUS_PROCESSED);
+ insertMeasure(anotherProjectAnalysis, ANOTHER_PROJECT, new MeasureDto().setValue(500.0).setMetricUuid(NCLOC.getUuid()));
+ dbTester.commit();
+
+ underTest.execute(new TestComputationStepContext());
+
+ List<ProjectDump.Measure> exportedMeasures = dumpWriter.getWrittenMessagesOf(DumpElement.MEASURES);
+ assertThat(exportedMeasures).hasSize(2);
+ assertThat(exportedMeasures).extracting(ProjectDump.Measure::getAnalysisUuid).containsOnly(firstAnalysis.getUuid(), secondAnalysis.getUuid());
+ assertThat(exportedMeasures).extracting(ProjectDump.Measure::getMetricRef).containsOnly(0);
+ assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("2 measures exported");
+ assertThat(metricRepository.getRefByUuid()).containsOnlyKeys(NCLOC.getUuid());
+ }
+
+ @Test
+ public void do_not_export_measures_on_unprocessed_snapshots() {
+ SnapshotDto firstAnalysis = insertSnapshot("U_1", PROJECT, STATUS_UNPROCESSED);
+ insertMeasure(firstAnalysis, PROJECT, new MeasureDto().setValue(100.0).setMetricUuid(NCLOC.getUuid()));
+ dbTester.commit();
+
+ underTest.execute(new TestComputationStepContext());
+
+ List<ProjectDump.Measure> exportedMeasures = dumpWriter.getWrittenMessagesOf(DumpElement.MEASURES);
+ assertThat(exportedMeasures).isEmpty();
+ }
+
+ @Test
+ public void do_not_export_measures_on_disabled_metrics() {
+ SnapshotDto firstAnalysis = insertSnapshot("U_1", PROJECT, STATUS_PROCESSED);
+ insertMeasure(firstAnalysis, PROJECT, new MeasureDto().setValue(100.0).setMetricUuid(DISABLED_METRIC.getUuid()));
+ dbTester.commit();
+
+ underTest.execute(new TestComputationStepContext());
+
+ List<ProjectDump.Measure> exportedMeasures = dumpWriter.getWrittenMessagesOf(DumpElement.MEASURES);
+ assertThat(exportedMeasures).isEmpty();
+ }
+
+ @Test
+ public void test_exported_fields() {
+ SnapshotDto analysis = insertSnapshot("U_1", PROJECT, STATUS_PROCESSED);
+ MeasureDto dto = new MeasureDto()
+ .setMetricUuid(NCLOC.getUuid())
+ .setValue(100.0)
+ .setData("data")
+ .setAlertStatus("OK")
+ .setAlertText("alert text");
+ insertMeasure(analysis, PROJECT, dto);
+ dbTester.commit();
+
+ underTest.execute(new TestComputationStepContext());
+
+ List<ProjectDump.Measure> exportedMeasures = dumpWriter.getWrittenMessagesOf(DumpElement.MEASURES);
+ ProjectDump.Measure measure = exportedMeasures.get(0);
+ assertThat(measure.getAlertStatus()).isEqualTo(dto.getAlertStatus());
+ assertThat(measure.getAlertText()).isEqualTo(dto.getAlertText());
+ assertThat(measure.getDoubleValue().getValue()).isEqualTo(dto.getValue());
+ assertThat(measure.getTextValue()).isEqualTo(dto.getData());
+ assertThat(measure.getMetricRef()).isZero();
+ assertThat(measure.getAnalysisUuid()).isEqualTo(analysis.getUuid());
+ assertThat(measure.getVariation1().getValue()).isZero();
+ }
+
+ @Test
+ public void test_exported_fields_new_metric() {
+ SnapshotDto analysis = insertSnapshot("U_1", PROJECT, STATUS_PROCESSED);
+ MeasureDto dto = new MeasureDto()
+ .setMetricUuid(NEW_NCLOC.getUuid())
+ .setValue(100.0)
+ .setData("data")
+ .setAlertStatus("OK")
+ .setAlertText("alert text");
+ insertMeasure(analysis, PROJECT, dto);
+ dbTester.commit();
+
+ underTest.execute(new TestComputationStepContext());
+
+ List<ProjectDump.Measure> exportedMeasures = dumpWriter.getWrittenMessagesOf(DumpElement.MEASURES);
+ ProjectDump.Measure measure = exportedMeasures.get(0);
+ assertThat(measure.getAlertStatus()).isEqualTo(dto.getAlertStatus());
+ assertThat(measure.getAlertText()).isEqualTo(dto.getAlertText());
+ assertThat(measure.getDoubleValue().getValue()).isZero();
+ assertThat(measure.getTextValue()).isEqualTo(dto.getData());
+ assertThat(measure.getMetricRef()).isZero();
+ assertThat(measure.getAnalysisUuid()).isEqualTo(analysis.getUuid());
+ assertThat(measure.getVariation1().getValue()).isEqualTo(dto.getValue());
+ }
+
+ @Test
+ public void test_null_exported_fields() {
+ SnapshotDto analysis = insertSnapshot("U_1", PROJECT, STATUS_PROCESSED);
+ insertMeasure(analysis, PROJECT, new MeasureDto().setMetricUuid(NCLOC.getUuid()));
+ dbTester.commit();
+
+ underTest.execute(new TestComputationStepContext());
+
+ ProjectDump.Measure measure = dumpWriter.getWrittenMessagesOf(DumpElement.MEASURES).get(0);
+ assertThat(measure.getAlertStatus()).isEmpty();
+ assertThat(measure.getAlertText()).isEmpty();
+ assertThat(measure.hasDoubleValue()).isFalse();
+ assertThat(measure.getTextValue()).isEmpty();
+ assertThat(measure.hasVariation1()).isFalse();
+ }
+
+ @Test
+ public void test_getDescription() {
+ assertThat(underTest.getDescription()).isEqualTo("Export measures");
+ }
+
+ private SnapshotDto insertSnapshot(String snapshotUuid, ComponentDto project, String status) {
+ SnapshotDto snapshot = new SnapshotDto()
+ .setUuid(snapshotUuid)
+ .setComponentUuid(project.uuid())
+ .setStatus(status)
+ .setLast(true);
+ dbTester.getDbClient().snapshotDao().insert(dbTester.getSession(), snapshot);
+ return snapshot;
+ }
+
+ private void insertMeasure(SnapshotDto analysisDto, ComponentDto componentDto, MeasureDto measureDto) {
+ measureDto
+ .setAnalysisUuid(analysisDto.getUuid())
+ .setComponentUuid(componentDto.uuid());
+ dbTester.getDbClient().measureDao().insert(dbTester.getSession(), measureDto);
+ }
+}
--- /dev/null
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2016 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+#
+# SonarQube 6.0
+#
+class AddComponentUuidToDuplicationsIndex < ActiveRecord::Migration
+
+ def self.up
+ execute_java_migration('org.sonar.db.version.v60.AddComponentUuidColumnToDuplicationsIndex')
+ end
+end
--- /dev/null
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2016 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+#
+# SonarQube 6.0
+#
+class PopulateComponentUuidOfDuplicationsIndex < ActiveRecord::Migration
+
+ def self.up
+ execute_java_migration('org.sonar.db.version.v60.PopulateComponentUuidOfDuplicationsIndex')
+ end
+end
--- /dev/null
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2016 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+#
+# SonarQube 6.0
+#
+class DeleteOrphanDuplicationsIndexRowsWithoutComponent < ActiveRecord::Migration
+
+ def self.up
+ execute_java_migration('org.sonar.db.version.v60.DeleteOrphanDuplicationsIndexRowsWithoutComponent')
+ end
+end
--- /dev/null
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2016 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+#
+# SonarQube 6.0
+#
+class MakeComponentUuidNotNullOnDuplicationsIndex < ActiveRecord::Migration
+
+ def self.up
+ execute_java_migration('org.sonar.db.version.v60.MakeComponentUuidNotNullOnDuplicationsIndex')
+ end
+end
--- /dev/null
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2016 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+#
+# SonarQube 6.0
+#
+class AddAnalysisUuidToDuplicationsIndex < ActiveRecord::Migration
+
+ def self.up
+ execute_java_migration('org.sonar.db.version.v60.AddAnalysisUuidColumnToDuplicationsIndex')
+ end
+end
--- /dev/null
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2016 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+#
+# SonarQube 6.0
+#
+class PopulateAnalysisUuidOfDuplicationsIndex < ActiveRecord::Migration
+
+ def self.up
+ execute_java_migration('org.sonar.db.version.v60.PopulateAnalysisUuidOfDuplicationsIndex')
+ end
+end
--- /dev/null
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2016 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+#
+# SonarQube 6.0
+#
+class DeleteOrphanDuplicationsIndexRowsWithoutAnalysis < ActiveRecord::Migration
+
+ def self.up
+ execute_java_migration('org.sonar.db.version.v60.DeleteOrphanDuplicationsIndexRowsWithoutAnalysis')
+ end
+end
--- /dev/null
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2016 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+#
+# SonarQube 6.0
+#
+class MakeAnalysisUuidNotNullOnDuplicationsIndex < ActiveRecord::Migration
+
+ def self.up
+ execute_java_migration('org.sonar.db.version.v60.MakeAnalysisUuidNotNullOnDuplicationsIndex')
+
+ add_index :duplications_index, [:analysis_uuid, :component_uuid], :name => 'duplication_analysis_component'
+ end
+end
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.filemove.FileMoveDetectionStepTest.v1;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.AddColumnsBuilder;
+
+import static org.sonar.db.version.VarcharColumnDef.UUID_VARCHAR_SIZE;
+import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class AddAnalysisUuidColumnToDuplicationsIndex extends DdlChange {
+
+ private static final String TABLE_DUPLICATIONS_INDEX = "duplications_index";
+
+ public AddAnalysisUuidColumnToDuplicationsIndex(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AddColumnsBuilder(getDatabase().getDialect(), TABLE_DUPLICATIONS_INDEX)
+ .addColumn(newVarcharColumnDefBuilder().setColumnName("analysis_uuid").setLimit(UUID_VARCHAR_SIZE).setIsNullable(true).build())
+ .build());
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.filemove.FileMoveDetectionStepTest.v1;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.AddColumnsBuilder;
+
+import static org.sonar.db.version.VarcharColumnDef.UUID_VARCHAR_SIZE;
+import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class AddComponentUuidAndAnalysisUuidColumnToDuplicationsIndex extends DdlChange {
+
+ private static final String TABLE_PUBLICATIONS_INDEX = "duplications_index";
+
+ public AddComponentUuidAndAnalysisUuidColumnToDuplicationsIndex(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AddColumnsBuilder(getDatabase().getDialect(), TABLE_PUBLICATIONS_INDEX)
+ .addColumn(newVarcharColumnDefBuilder().setColumnName("component_uuid").setLimit(UUID_VARCHAR_SIZE).setIsNullable(true).build())
+ .addColumn(newVarcharColumnDefBuilder().setColumnName("analysis_uuid").setLimit(UUID_VARCHAR_SIZE).setIsNullable(true).build())
+ .build());
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.filemove.FileMoveDetectionStepTest.v1;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+
+public class DeleteOrphanDuplicationsIndexRowsWithoutComponent extends BaseDataChange {
+
+ public DeleteOrphanDuplicationsIndexRowsWithoutComponent(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("SELECT id from duplications_index where component_uuid is null");
+ massUpdate.update("DELETE from duplications_index WHERE id=?");
+ massUpdate.rowPluralName("resources_index entries");
+ massUpdate.execute((row, update) -> {
+ update.setLong(1, row.getLong(1));
+ return true;
+ });
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.filemove.FileMoveDetectionStepTest.v1;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.AlterColumnsBuilder;
+
+import static org.sonar.db.version.VarcharColumnDef.UUID_VARCHAR_SIZE;
+import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class MakeComponentUuidNotNullOnDuplicationsIndex extends DdlChange {
+
+ private static final String TABLE_DUPLICATIONS_INDEX = "duplications_index";
+
+ public MakeComponentUuidNotNullOnDuplicationsIndex(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AlterColumnsBuilder(getDatabase().getDialect(), TABLE_DUPLICATIONS_INDEX)
+ .updateColumn(newVarcharColumnDefBuilder().setColumnName("component_uuid").setLimit(UUID_VARCHAR_SIZE).setIsNullable(false).build())
+ .build());
+ }
+
+}
--- /dev/null
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2016 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+#
+# SonarQube 6.0
+#
+class AddComponentUuidAndAnalysisUuidToDuplicationsIndex < ActiveRecord::Migration
+
+ def self.up
+ execute_java_migration('org.sonar.db.version.v60.AddComponentUuidAndAnalysisUuidColumnToDuplicationsIndex')
+ end
+end
--- /dev/null
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2016 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+#
+# SonarQube 6.0
+#
+class PopulateComponentUuidAndAnalysisUuidOfDuplicationsIndex < ActiveRecord::Migration
+
+ def self.up
+ execute_java_migration('org.sonar.db.version.v60.PopulateComponentUuidAndAnalysisUuidOfDuplicationsIndex')
+ end
+end
--- /dev/null
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2016 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+#
+# SonarQube 6.0
+#
+class DeleteOrphanDuplicationsIndexRowsWithoutComponentOrAnalysis < ActiveRecord::Migration
+
+ def self.up
+ execute_java_migration('org.sonar.db.version.v60.DeleteOrphanDuplicationsIndexRowsWithoutComponentOrAnalysis')
+ end
+end
--- /dev/null
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2016 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+#
+# SonarQube 6.0
+#
+class MakeComponentUuidAndAnalysisUuidNotNullOnDuplicationsIndex < ActiveRecord::Migration
+
+ def self.up
+ execute_java_migration('org.sonar.db.version.v60.MakeComponentUuidAndAnalysisUuidNotNullOnDuplicationsIndex')
+
+ add_index :duplications_index, [:analysis_uuid, :component_uuid], :name => 'duplication_analysis_component'
+ end
+end
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.filemove.FileMoveDetectionStepTest.v2;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.AddColumnsBuilder;
+
+import static org.sonar.db.version.VarcharColumnDef.UUID_VARCHAR_SIZE;
+import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class AddComponentUuidColumnToDuplicationsIndex extends DdlChange {
+
+ private static final String TABLE_PUBLICATIONS_INDEX = "duplications_index";
+
+ public AddComponentUuidColumnToDuplicationsIndex(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AddColumnsBuilder(getDatabase().getDialect(), TABLE_PUBLICATIONS_INDEX)
+ .addColumn(newVarcharColumnDefBuilder().setColumnName("component_uuid").setLimit(UUID_VARCHAR_SIZE).setIsNullable(true).build())
+ .build());
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.filemove.FileMoveDetectionStepTest.v2;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.AlterColumnsBuilder;
+
+import static org.sonar.db.version.VarcharColumnDef.UUID_VARCHAR_SIZE;
+import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class MakeComponentUuidAndAnalysisUuidNotNullOnDuplicationsIndex extends DdlChange {
+
+ private static final String TABLE_DUPLICATIONS_INDEX = "duplications_index";
+
+ public MakeComponentUuidAndAnalysisUuidNotNullOnDuplicationsIndex(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AlterColumnsBuilder(getDatabase().getDialect(), TABLE_DUPLICATIONS_INDEX)
+ .updateColumn(newVarcharColumnDefBuilder().setColumnName("component_uuid").setLimit(UUID_VARCHAR_SIZE).setIsNullable(false).build())
+ .updateColumn(newVarcharColumnDefBuilder().setColumnName("analysis_uuid").setLimit(UUID_VARCHAR_SIZE).setIsNullable(false).build())
+ .build());
+ }
+
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.component;
-
-import com.tngtech.java.junit.dataprovider.DataProvider;
-import com.tngtech.java.junit.dataprovider.DataProviderRunner;
-import com.tngtech.java.junit.dataprovider.UseDataProvider;
-import java.util.Collections;
-import java.util.Optional;
-import javax.annotation.Nullable;
-import org.assertj.core.api.ThrowableAssert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.sonar.api.config.internal.ConfigurationBridge;
-import org.sonar.api.config.internal.MapSettings;
-import org.sonar.api.utils.System2;
-import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
-import org.sonar.ce.task.projectanalysis.analysis.Branch;
-import org.sonar.db.DbSession;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.BranchDto;
-import org.sonar.db.component.BranchType;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentTesting;
-import org.sonar.db.protobuf.DbProjectBranches;
-import org.sonar.server.project.Project;
-
-import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
-import static org.sonar.core.config.PurgeConstants.BRANCHES_TO_KEEP_WHEN_INACTIVE;
-import static org.sonar.db.component.BranchType.BRANCH;
-import static org.sonar.db.component.BranchType.PULL_REQUEST;
-
-@RunWith(DataProviderRunner.class)
-public class BranchPersisterImplTest {
- private final static Component MAIN = builder(Component.Type.PROJECT, 1, "PROJECT_KEY").setUuid("PROJECT_UUID").setName("p1").build();
- private final static Component BRANCH1 = builder(Component.Type.PROJECT, 2, "BRANCH_KEY").setUuid("BRANCH_UUID").build();
- private final static Component PR1 = builder(Component.Type.PROJECT, 3, "develop").setUuid("PR_UUID").build();
- private static final Project PROJECT = new Project(MAIN.getUuid(), MAIN.getKey(), MAIN.getName(), null, Collections.emptyList());
-
- @Rule
- public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
- @Rule
- public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
-
- private final MapSettings settings = new MapSettings();
- private final ConfigurationRepository configurationRepository = new TestSettingsRepository(new ConfigurationBridge(settings));
- private final BranchPersister underTest = new BranchPersisterImpl(dbTester.getDbClient(), treeRootHolder, analysisMetadataHolder, configurationRepository);
-
- @Test
- public void persist_fails_with_ISE_if_no_component_for_main_branches() {
- analysisMetadataHolder.setBranch(createBranch(BRANCH, true, "master"));
- treeRootHolder.setRoot(MAIN);
- DbSession dbSession = dbTester.getSession();
-
- expectMissingComponentISE(() -> underTest.persist(dbSession));
- }
-
- @Test
- public void persist_fails_with_ISE_if_no_component_for_branches() {
- analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "foo"));
- treeRootHolder.setRoot(BRANCH1);
- DbSession dbSession = dbTester.getSession();
-
- expectMissingComponentISE(() -> underTest.persist(dbSession));
- }
-
- @Test
- public void persist_fails_with_ISE_if_no_component_for_pull_request() {
- analysisMetadataHolder.setBranch(createBranch(BranchType.PULL_REQUEST, false, "12"));
- treeRootHolder.setRoot(BRANCH1);
- DbSession dbSession = dbTester.getSession();
-
- expectMissingComponentISE(() -> underTest.persist(dbSession));
- }
-
- @Test
- @UseDataProvider("nullOrNotNullString")
- public void persist_creates_row_in_PROJECTS_BRANCHES_for_branch(@Nullable String mergeBranchUuid) {
- String branchName = "branch";
-
- // add project and branch in table PROJECTS
- ComponentDto mainComponent = ComponentTesting.newPrivateProjectDto(MAIN.getUuid()).setKey(MAIN.getKey());
- ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
- new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
- dbTester.components().insertComponents(mainComponent, component);
- // set project in metadata
- treeRootHolder.setRoot(BRANCH1);
- analysisMetadataHolder.setBranch(createBranch(BRANCH, false, branchName, mergeBranchUuid));
- analysisMetadataHolder.setProject(Project.from(mainComponent));
-
- underTest.persist(dbTester.getSession());
-
- dbTester.getSession().commit();
-
- assertThat(dbTester.countRowsOfTable("components")).isEqualTo(2);
- Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
- assertThat(branchDto).isPresent();
- assertThat(branchDto.get().getBranchType()).isEqualTo(BRANCH);
- assertThat(branchDto.get().getKey()).isEqualTo(branchName);
- assertThat(branchDto.get().getMergeBranchUuid()).isEqualTo(mergeBranchUuid);
- assertThat(branchDto.get().getProjectUuid()).isEqualTo(MAIN.getUuid());
- assertThat(branchDto.get().getPullRequestData()).isNull();
- }
-
- @Test
- public void main_branch_is_excluded_from_branch_purge_by_default() {
- analysisMetadataHolder.setBranch(createBranch(BRANCH, true, "master"));
- treeRootHolder.setRoot(MAIN);
- dbTester.components().insertPublicProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid()));
- dbTester.commit();
-
- underTest.persist(dbTester.getSession());
-
- Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), MAIN.getUuid());
- assertThat(branchDto).isPresent();
- assertThat(branchDto.get().isExcludeFromPurge()).isTrue();
- }
-
- @Test
- public void non_main_branch_is_excluded_from_branch_purge_if_matches_sonar_dbcleaner_keepFromPurge_property() {
- settings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "BRANCH.*");
- analysisMetadataHolder.setProject(PROJECT);
- analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "BRANCH_KEY"));
- treeRootHolder.setRoot(BRANCH1);
- ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid()));
- ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
- new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
- dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component);
- dbTester.commit();
-
- underTest.persist(dbTester.getSession());
-
- Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
- assertThat(branchDto).isPresent();
- assertThat(branchDto.get().isExcludeFromPurge()).isTrue();
- }
-
- @Test
- public void branch_is_excluded_from_purge_when_it_matches_setting() {
- analysisMetadataHolder.setProject(PROJECT);
- analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "BRANCH_KEY"));
- treeRootHolder.setRoot(BRANCH1);
- ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid()));
- ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
- new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
- dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component);
- settings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "BRANCH.*");
- dbTester.commit();
-
- underTest.persist(dbTester.getSession());
-
- Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
- assertThat(branchDto).isPresent();
- assertThat(branchDto.get().isExcludeFromPurge()).isTrue();
- }
-
- @Test
- public void branch_is_not_excluded_from_purge_when_it_does_not_match_setting() {
- analysisMetadataHolder.setProject(PROJECT);
- analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "BRANCH_KEY"));
- treeRootHolder.setRoot(BRANCH1);
- ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid()));
- ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
- new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
- dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component);
- settings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "abc.*");
-
- dbTester.commit();
-
- underTest.persist(dbTester.getSession());
-
- Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
- assertThat(branchDto).isPresent();
- assertThat(branchDto.get().isExcludeFromPurge()).isFalse();
- }
-
- @Test
- public void pull_request_is_never_excluded_from_branch_purge_even_if_its_source_branch_name_matches_sonar_dbcleaner_keepFromPurge_property() {
- settings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "develop");
- analysisMetadataHolder.setBranch(createPullRequest(PR1.getKey(), MAIN.getUuid()));
- analysisMetadataHolder.setPullRequestKey(PR1.getKey());
- treeRootHolder.setRoot(PR1);
- ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid()));
- ComponentDto component = ComponentTesting.newBranchComponent(mainComponent, new BranchDto()
- .setUuid(PR1.getUuid())
- .setKey(PR1.getKey())
- .setProjectUuid(MAIN.getUuid())
- .setBranchType(PULL_REQUEST)
- .setMergeBranchUuid(MAIN.getUuid()));
- dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component);
- dbTester.commit();
-
- underTest.persist(dbTester.getSession());
-
- Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), PR1.getUuid());
- assertThat(branchDto).isPresent();
- assertThat(branchDto.get().isExcludeFromPurge()).isFalse();
- }
-
- @Test
- public void non_main_branch_is_included_in_branch_purge_if_branch_name_does_not_match_sonar_dbcleaner_keepFromPurge_property() {
- settings.setProperty(BRANCHES_TO_KEEP_WHEN_INACTIVE, "foobar-.*");
- analysisMetadataHolder.setProject(PROJECT);
- analysisMetadataHolder.setBranch(createBranch(BRANCH, false, "BRANCH_KEY"));
- treeRootHolder.setRoot(BRANCH1);
- ComponentDto mainComponent = dbTester.components().insertPublicProject(p -> p.setKey(MAIN.getKey()).setUuid(MAIN.getUuid()));
- ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
- new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(BRANCH));
- dbTester.getDbClient().componentDao().insert(dbTester.getSession(), component);
- dbTester.commit();
-
- underTest.persist(dbTester.getSession());
-
- Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
- assertThat(branchDto).isPresent();
- assertThat(branchDto.get().isExcludeFromPurge()).isFalse();
- }
-
- @DataProvider
- public static Object[][] nullOrNotNullString() {
- return new Object[][] {
- {null},
- {randomAlphabetic(12)}
- };
- }
-
- @Test
- public void persist_creates_row_in_PROJECTS_BRANCHES_for_pull_request() {
- String pullRequestId = "pr-123";
-
- // add project and branch in table PROJECTS
- ComponentDto mainComponent = ComponentTesting.newPrivateProjectDto(MAIN.getUuid()).setKey(MAIN.getKey());
- ComponentDto component = ComponentTesting.newBranchComponent(mainComponent,
- new BranchDto().setUuid(BRANCH1.getUuid()).setKey(BRANCH1.getKey()).setBranchType(PULL_REQUEST));
- dbTester.components().insertComponents(mainComponent, component);
- // set project in metadata
- treeRootHolder.setRoot(BRANCH1);
- analysisMetadataHolder.setBranch(createBranch(PULL_REQUEST, false, pullRequestId, "mergeBanchUuid"));
- analysisMetadataHolder.setProject(Project.from(mainComponent));
- analysisMetadataHolder.setPullRequestKey(pullRequestId);
-
- underTest.persist(dbTester.getSession());
-
- dbTester.getSession().commit();
-
- assertThat(dbTester.countRowsOfTable("components")).isEqualTo(2);
- Optional<BranchDto> branchDto = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), BRANCH1.getUuid());
- assertThat(branchDto).isPresent();
- assertThat(branchDto.get().getBranchType()).isEqualTo(PULL_REQUEST);
- assertThat(branchDto.get().getKey()).isEqualTo(pullRequestId);
- assertThat(branchDto.get().getMergeBranchUuid()).isEqualTo("mergeBanchUuid");
- assertThat(branchDto.get().getProjectUuid()).isEqualTo(MAIN.getUuid());
- assertThat(branchDto.get().getPullRequestData()).isEqualTo(DbProjectBranches.PullRequestData.newBuilder()
- .setBranch(pullRequestId)
- .setTarget("mergeBanchUuid")
- .setTitle(pullRequestId)
- .build());
- }
-
- private static Branch createBranch(BranchType type, boolean isMain, String name) {
- return createBranch(type, isMain, name, null);
- }
-
- private static Branch createPullRequest(String key, String mergeBranchUuid) {
- Branch branch = createBranch(PULL_REQUEST, false, key, mergeBranchUuid);
- when(branch.getPullRequestKey()).thenReturn(key);
- return branch;
- }
-
- private static Branch createBranch(BranchType type, boolean isMain, String name, @Nullable String mergeBranchUuid) {
- Branch branch = mock(Branch.class);
- when(branch.getType()).thenReturn(type);
- when(branch.getName()).thenReturn(name);
- when(branch.isMain()).thenReturn(isMain);
- when(branch.getReferenceBranchUuid()).thenReturn(mergeBranchUuid);
- when(branch.getTargetBranchName()).thenReturn(mergeBranchUuid);
- return branch;
- }
-
- private void expectMissingComponentISE(ThrowableAssert.ThrowingCallable callable) {
- assertThatThrownBy(callable)
- .isInstanceOf(IllegalStateException.class)
- .hasMessage("Component has been deleted by end-user during analysis");
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.filemove;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-import java.util.stream.IntStream;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import org.apache.commons.io.FileUtils;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.System2;
-import org.sonar.api.utils.log.LogTester;
-import org.sonar.api.utils.log.LoggerLevel;
-import org.sonar.ce.task.projectanalysis.analysis.Analysis;
-import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
-import org.sonar.ce.task.projectanalysis.component.Component;
-import org.sonar.ce.task.projectanalysis.component.FileAttributes;
-import org.sonar.ce.task.projectanalysis.component.ReportComponent;
-import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
-import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepository;
-import org.sonar.ce.task.step.TestComputationStepContext;
-import org.sonar.core.hash.SourceLineHashesComputer;
-import org.sonar.core.util.Uuids;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.BranchType;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentTesting;
-import org.sonar.db.source.FileSourceDto;
-
-import static java.util.Arrays.stream;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
-import static org.sonar.ce.task.projectanalysis.filemove.FileMoveDetectionStep.MIN_REQUIRED_SCORE;
-import static org.sonar.db.component.BranchType.*;
-
-public class FileMoveDetectionStepTest {
-
- private static final String SNAPSHOT_UUID = "uuid_1";
- private static final Analysis ANALYSIS = new Analysis.Builder()
- .setUuid(SNAPSHOT_UUID)
- .setCreatedAt(86521)
- .build();
- private static final int ROOT_REF = 1;
- private static final int FILE_1_REF = 2;
- private static final int FILE_2_REF = 3;
- private static final int FILE_3_REF = 4;
- private static final String[] CONTENT1 = {
- "package org.sonar.ce.task.projectanalysis.filemove;",
- "",
- "public class Foo {",
- " public String bar() {",
- " return \"Doh!\";",
- " }",
- "}"
- };
-
- private static final String[] LESS_CONTENT1 = {
- "package org.sonar.ce.task.projectanalysis.filemove;",
- "",
- "public class Foo {",
- " public String foo() {",
- " return \"Donut!\";",
- " }",
- "}"
- };
- private static final String[] CONTENT_EMPTY = {
- ""
- };
- private static final String[] CONTENT2 = {
- "package org.sonar.ce.queue;",
- "",
- "import com.google.common.base.MoreObjects;",
- "import javax.annotation.CheckForNull;",
- "import javax.annotation.Nullable;",
- "import javax.annotation.concurrent.Immutable;",
- "",
- "import static com.google.common.base.Strings.emptyToNull;",
- "import static java.util.Objects.requireNonNull;",
- "",
- "@Immutable",
- "public class CeTask {",
- "",
- ", private final String type;",
- ", private final String uuid;",
- ", private final String componentUuid;",
- ", private final String componentKey;",
- ", private final String componentName;",
- ", private final String submitterLogin;",
- "",
- ", private CeTask(Builder builder) {",
- ", this.uuid = requireNonNull(emptyToNull(builder.uuid));",
- ", this.type = requireNonNull(emptyToNull(builder.type));",
- ", this.componentUuid = emptyToNull(builder.componentUuid);",
- ", this.componentKey = emptyToNull(builder.componentKey);",
- ", this.componentName = emptyToNull(builder.componentName);",
- ", this.submitterLogin = emptyToNull(builder.submitterLogin);",
- ", }",
- "",
- ", public String getUuid() {",
- ", return uuid;",
- ", }",
- "",
- ", public String getType() {",
- ", return type;",
- ", }",
- "",
- ", @CheckForNull",
- ", public String getComponentUuid() {",
- ", return componentUuid;",
- ", }",
- "",
- ", @CheckForNull",
- ", public String getComponentKey() {",
- ", return componentKey;",
- ", }",
- "",
- ", @CheckForNull",
- ", public String getComponentName() {",
- ", return componentName;",
- ", }",
- "",
- ", @CheckForNull",
- ", public String getSubmitterLogin() {",
- ", return submitterLogin;",
- ", }",
- ",}",
- };
- // removed immutable annotation
- private static final String[] LESS_CONTENT2 = {
- "package org.sonar.ce.queue;",
- "",
- "import com.google.common.base.MoreObjects;",
- "import javax.annotation.CheckForNull;",
- "import javax.annotation.Nullable;",
- "",
- "import static com.google.common.base.Strings.emptyToNull;",
- "import static java.util.Objects.requireNonNull;",
- "",
- "public class CeTask {",
- "",
- ", private final String type;",
- ", private final String uuid;",
- ", private final String componentUuid;",
- ", private final String componentKey;",
- ", private final String componentName;",
- ", private final String submitterLogin;",
- "",
- ", private CeTask(Builder builder) {",
- ", this.uuid = requireNonNull(emptyToNull(builder.uuid));",
- ", this.type = requireNonNull(emptyToNull(builder.type));",
- ", this.componentUuid = emptyToNull(builder.componentUuid);",
- ", this.componentKey = emptyToNull(builder.componentKey);",
- ", this.componentName = emptyToNull(builder.componentName);",
- ", this.submitterLogin = emptyToNull(builder.submitterLogin);",
- ", }",
- "",
- ", public String getUuid() {",
- ", return uuid;",
- ", }",
- "",
- ", public String getType() {",
- ", return type;",
- ", }",
- "",
- ", @CheckForNull",
- ", public String getComponentUuid() {",
- ", return componentUuid;",
- ", }",
- "",
- ", @CheckForNull",
- ", public String getComponentKey() {",
- ", return componentKey;",
- ", }",
- "",
- ", @CheckForNull",
- ", public String getComponentName() {",
- ", return componentName;",
- ", }",
- "",
- ", @CheckForNull",
- ", public String getSubmitterLogin() {",
- ", return submitterLogin;",
- ", }",
- ",}",
- };
-
- @Rule
- public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
- @Rule
- public MutableMovedFilesRepositoryRule movedFilesRepository = new MutableMovedFilesRepositoryRule();
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
- @Rule
- public LogTester logTester = new LogTester();
-
- private final DbClient dbClient = dbTester.getDbClient();
- private ComponentDto project;
-
- private final AnalysisMetadataHolderRule analysisMetadataHolder = mock(AnalysisMetadataHolderRule.class);
- private final SourceLinesHashRepository sourceLinesHash = mock(SourceLinesHashRepository.class);
- private final FileSimilarity fileSimilarity = new FileSimilarityImpl(new SourceSimilarityImpl());
- private final CapturingScoreMatrixDumper scoreMatrixDumper = new CapturingScoreMatrixDumper();
- private final RecordingMutableAddedFileRepository addedFileRepository = new RecordingMutableAddedFileRepository();
-
- private final FileMoveDetectionStep underTest = new FileMoveDetectionStep(analysisMetadataHolder, treeRootHolder, dbClient,
- fileSimilarity, movedFilesRepository, sourceLinesHash, scoreMatrixDumper, addedFileRepository);
-
- @Before
- public void setUp() throws Exception {
- project = dbTester.components().insertPrivateProject();
- treeRootHolder.setRoot(builder(Component.Type.PROJECT, ROOT_REF).setUuid(project.uuid()).build());
- }
-
- @Test
- public void getDescription_returns_description() {
- assertThat(underTest.getDescription()).isEqualTo("Detect file moves");
- }
-
- @Test
- public void execute_detects_no_move_if_in_pull_request_scope() {
- prepareAnalysis(PULL_REQUEST, ANALYSIS);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- verifyStatistics(context, null, null, null, null);
- }
-
- @Test
- public void execute_detects_no_move_on_first_analysis() {
- prepareAnalysis(BRANCH, null);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- verifyStatistics(context, 0, null, null, null);
- }
-
- @Test
- public void execute_detects_no_move_if_baseSnapshot_has_no_file_and_report_has_no_file() {
- prepareBranchAnalysis(ANALYSIS);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- assertThat(addedFileRepository.getComponents()).isEmpty();
- verifyStatistics(context, 0, null, null, null);
- }
-
- @Test
- public void execute_detects_no_move_if_baseSnapshot_has_no_file() {
- prepareBranchAnalysis(ANALYSIS);
- Component file1 = fileComponent(FILE_1_REF, null);
- Component file2 = fileComponent(FILE_2_REF, null);
- setFilesInReport(file1, file2);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- assertThat(addedFileRepository.getComponents()).containsOnly(file1, file2);
- verifyStatistics(context, 2, 0, 2, null);
- }
-
- @Test
- public void execute_detects_no_move_if_there_is_no_file_in_report() {
- prepareBranchAnalysis(ANALYSIS);
- insertFiles( /* no components */);
- setFilesInReport();
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- assertThat(addedFileRepository.getComponents()).isEmpty();
- verifyStatistics(context, 0, null, null, null);
- }
-
- @Test
- public void execute_detects_no_move_if_file_key_exists_in_both_DB_and_report() {
- prepareBranchAnalysis(ANALYSIS);
- Component file1 = fileComponent(FILE_1_REF, null);
- Component file2 = fileComponent(FILE_2_REF, null);
- insertFiles(file1.getUuid(), file2.getUuid());
- insertContentOfFileInDb(file1.getUuid(), CONTENT1);
- insertContentOfFileInDb(file2.getUuid(), CONTENT2);
- setFilesInReport(file2, file1);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- assertThat(addedFileRepository.getComponents()).isEmpty();
- verifyStatistics(context, 2, 2, 0, null);
- }
-
- @Test
- public void execute_detects_move_if_content_of_file_is_same_in_DB_and_report() {
- prepareBranchAnalysis(ANALYSIS);
- Component file1 = fileComponent(FILE_1_REF, null);
- Component file2 = fileComponent(FILE_2_REF, CONTENT1);
- ComponentDto[] dtos = insertFiles(file1.getUuid());
- insertContentOfFileInDb(file1.getUuid(), CONTENT1);
- setFilesInReport(file2);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).containsExactly(file2);
- MovedFilesRepository.OriginalFile originalFile = movedFilesRepository.getOriginalFile(file2).get();
- assertThat(originalFile.key()).isEqualTo(dtos[0].getKey());
- assertThat(originalFile.uuid()).isEqualTo(dtos[0].uuid());
- assertThat(addedFileRepository.getComponents()).isEmpty();
- verifyStatistics(context, 1, 1, 1, 1);
- }
-
- @Test
- public void execute_detects_no_move_if_content_of_file_is_not_similar_enough() {
- prepareBranchAnalysis(ANALYSIS);
- Component file1 = fileComponent(FILE_1_REF, null);
- Component file2 = fileComponent(FILE_2_REF, LESS_CONTENT1);
- insertFiles(file1.getKey());
- insertContentOfFileInDb(file1.getKey(), CONTENT1);
- setFilesInReport(file2);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore())
- .isPositive()
- .isLessThan(MIN_REQUIRED_SCORE);
- assertThat(addedFileRepository.getComponents()).contains(file2);
- verifyStatistics(context, 1, 1, 1, 0);
- }
-
- @Test
- public void execute_detects_no_move_if_content_of_file_is_empty_in_DB() {
- prepareBranchAnalysis(ANALYSIS);
- Component file1 = fileComponent(FILE_1_REF, null);
- Component file2 = fileComponent(FILE_2_REF, CONTENT1);
- insertFiles(file1.getKey());
- insertContentOfFileInDb(file1.getKey(), CONTENT_EMPTY);
- setFilesInReport(file2);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore()).isZero();
- assertThat(addedFileRepository.getComponents()).contains(file2);
- verifyStatistics(context, 1, 1, 1, 0);
- }
-
- @Test
- public void execute_detects_no_move_if_content_of_file_has_no_path_in_DB() {
- prepareBranchAnalysis(ANALYSIS);
- Component file1 = fileComponent(FILE_1_REF, null);
- Component file2 = fileComponent(FILE_2_REF, CONTENT1);
- insertFiles(key -> newComponentDto(key).setPath(null), file1.getKey());
- insertContentOfFileInDb(file1.getKey(), CONTENT1);
- setFilesInReport(file2);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- assertThat(scoreMatrixDumper.scoreMatrix).isNull();
- assertThat(addedFileRepository.getComponents()).containsOnly(file2);
- verifyStatistics(context, 1, 0, 1, null);
- }
-
- @Test
- public void execute_detects_no_move_if_content_of_file_is_empty_in_report() {
- prepareBranchAnalysis(ANALYSIS);
- Component file1 = fileComponent(FILE_1_REF, null);
- Component file2 = fileComponent(FILE_2_REF, CONTENT_EMPTY);
- insertFiles(file1.getKey());
- insertContentOfFileInDb(file1.getKey(), CONTENT1);
- setFilesInReport(file2);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore()).isZero();
- assertThat(addedFileRepository.getComponents()).contains(file2);
- verifyStatistics(context, 1, 1, 1, 0);
- assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("max score in matrix is less than min required score (85). Do nothing.");
- }
-
- @Test
- public void execute_detects_no_move_if_two_added_files_have_same_content_as_the_one_in_db() {
- prepareBranchAnalysis(ANALYSIS);
- Component file1 = fileComponent(FILE_1_REF, null);
- Component file2 = fileComponent(FILE_2_REF, CONTENT1);
- Component file3 = fileComponent(FILE_3_REF, CONTENT1);
- insertFiles(file1.getKey());
- insertContentOfFileInDb(file1.getKey(), CONTENT1);
- setFilesInReport(file2, file3);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore()).isEqualTo(100);
- assertThat(addedFileRepository.getComponents()).containsOnly(file2, file3);
- verifyStatistics(context, 2, 1, 2, 0);
- }
-
- @Test
- public void execute_detects_no_move_if_two_deleted_files_have_same_content_as_the_one_added() {
- prepareBranchAnalysis(ANALYSIS);
- Component file1 = fileComponent(FILE_1_REF, null);
- Component file2 = fileComponent(FILE_2_REF, null);
- Component file3 = fileComponent(FILE_3_REF, CONTENT1);
- insertFiles(file1.getUuid(), file2.getUuid());
- insertContentOfFileInDb(file1.getUuid(), CONTENT1);
- insertContentOfFileInDb(file2.getUuid(), CONTENT1);
- setFilesInReport(file3);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore()).isEqualTo(100);
- assertThat(addedFileRepository.getComponents()).containsOnly(file3);
- verifyStatistics(context, 1, 2, 1, 0);
- }
-
- @Test
- public void execute_detects_no_move_if_two_files_are_empty_in_DB() {
- prepareBranchAnalysis(ANALYSIS);
- Component file1 = fileComponent(FILE_1_REF, null);
- Component file2 = fileComponent(FILE_2_REF, null);
- insertFiles(file1.getUuid(), file2.getUuid());
- insertContentOfFileInDb(file1.getUuid(), null);
- insertContentOfFileInDb(file2.getUuid(), null);
- setFilesInReport(file1, file2);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- assertThat(scoreMatrixDumper.scoreMatrix).isNull();
- assertThat(addedFileRepository.getComponents()).isEmpty();
- verifyStatistics(context, 2, 2, 0, null);
- }
-
- @Test
- public void execute_detects_several_moves() {
- // testing:
- // - file1 renamed to file3
- // - file2 deleted
- // - file4 untouched
- // - file5 renamed to file6 with a small change
- prepareBranchAnalysis(ANALYSIS);
- Component file1 = fileComponent(FILE_1_REF, null);
- Component file2 = fileComponent(FILE_2_REF, null);
- Component file3 = fileComponent(FILE_3_REF, CONTENT1);
- Component file4 = fileComponent(5, new String[] {"a", "b"});
- Component file5 = fileComponent(6, null);
- Component file6 = fileComponent(7, LESS_CONTENT2);
- ComponentDto[] dtos = insertFiles(file1.getUuid(), file2.getUuid(), file4.getUuid(), file5.getUuid());
- insertContentOfFileInDb(file1.getUuid(), CONTENT1);
- insertContentOfFileInDb(file2.getUuid(), LESS_CONTENT1);
- insertContentOfFileInDb(file4.getUuid(), new String[] {"e", "f", "g", "h", "i"});
- insertContentOfFileInDb(file5.getUuid(), CONTENT2);
- setFilesInReport(file3, file4, file6);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).containsOnly(file3, file6);
- MovedFilesRepository.OriginalFile originalFile2 = movedFilesRepository.getOriginalFile(file3).get();
- assertThat(originalFile2.key()).isEqualTo(dtos[0].getKey());
- assertThat(originalFile2.uuid()).isEqualTo(dtos[0].uuid());
- MovedFilesRepository.OriginalFile originalFile5 = movedFilesRepository.getOriginalFile(file6).get();
- assertThat(originalFile5.key()).isEqualTo(dtos[3].getKey());
- assertThat(originalFile5.uuid()).isEqualTo(dtos[3].uuid());
- assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore()).isGreaterThan(MIN_REQUIRED_SCORE);
- assertThat(addedFileRepository.getComponents()).isEmpty();
- verifyStatistics(context, 3, 4, 2, 2);
- }
-
- @Test
- public void execute_does_not_compute_any_distance_if_all_files_sizes_are_all_too_different() {
- prepareBranchAnalysis(ANALYSIS);
- Component file1 = fileComponent(FILE_1_REF, null);
- Component file2 = fileComponent(FILE_2_REF, null);
- Component file3 = fileComponent(FILE_3_REF, arrayOf(118));
- Component file4 = fileComponent(5, arrayOf(25));
- insertFiles(file1.getKey(), file2.getKey());
- insertContentOfFileInDb(file1.getKey(), arrayOf(100));
- insertContentOfFileInDb(file2.getKey(), arrayOf(30));
- setFilesInReport(file3, file4);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- assertThat(scoreMatrixDumper.scoreMatrix.getMaxScore()).isZero();
- verifyStatistics(context, 2, 2, 2, 0);
- }
-
- /**
- * Creates an array of {@code numberOfElements} int values as String, starting with zero.
- */
- private static String[] arrayOf(int numberOfElements) {
- return IntStream.range(0, numberOfElements).mapToObj(String::valueOf).toArray(String[]::new);
- }
-
- /**
- * JH: A bug was encountered in the algorithm and I didn't manage to forge a simpler test case.
- */
- @Test
- public void real_life_use_case() throws Exception {
- prepareBranchAnalysis(ANALYSIS);
- for (File f : FileUtils.listFiles(new File("src/test/resources/org/sonar/ce/task/projectanalysis/filemove/FileMoveDetectionStepTest/v1"), null, false)) {
- insertFiles("uuid_" + f.getName().hashCode());
- insertContentOfFileInDb("uuid_" + f.getName().hashCode(), readLines(f));
- }
-
- Map<String, Component> comps = new HashMap<>();
- int i = 1;
- for (File f : FileUtils.listFiles(new File("src/test/resources/org/sonar/ce/task/projectanalysis/filemove/FileMoveDetectionStepTest/v2"), null, false)) {
- String[] lines = readLines(f);
- Component c = builder(Component.Type.FILE, i++)
- .setUuid("uuid_" + f.getName().hashCode())
- .setKey(f.getName())
- .setName(f.getName())
- .setFileAttributes(new FileAttributes(false, null, lines.length))
- .build();
-
- comps.put(f.getName(), c);
- setFileLineHashesInReport(c, lines);
- }
-
- setFilesInReport(comps.values().toArray(new Component[0]));
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- Component makeComponentUuidAndAnalysisUuidNotNullOnDuplicationsIndex = comps.get("MakeComponentUuidAndAnalysisUuidNotNullOnDuplicationsIndex.java");
- Component migrationRb1238 = comps.get("1238_make_component_uuid_and_analysis_uuid_not_null_on_duplications_index.rb");
- Component addComponentUuidAndAnalysisUuidColumnToDuplicationsIndex = comps.get("AddComponentUuidAndAnalysisUuidColumnToDuplicationsIndex.java");
- assertThat(movedFilesRepository.getComponentsWithOriginal()).containsOnly(
- makeComponentUuidAndAnalysisUuidNotNullOnDuplicationsIndex,
- migrationRb1238,
- addComponentUuidAndAnalysisUuidColumnToDuplicationsIndex);
-
- assertThat(movedFilesRepository.getOriginalFile(makeComponentUuidAndAnalysisUuidNotNullOnDuplicationsIndex).get().uuid())
- .isEqualTo("uuid_" + "MakeComponentUuidNotNullOnDuplicationsIndex.java".hashCode());
- assertThat(movedFilesRepository.getOriginalFile(migrationRb1238).get().uuid())
- .isEqualTo("uuid_" + "1242_make_analysis_uuid_not_null_on_duplications_index.rb".hashCode());
- assertThat(movedFilesRepository.getOriginalFile(addComponentUuidAndAnalysisUuidColumnToDuplicationsIndex).get().uuid())
- .isEqualTo("uuid_" + "AddComponentUuidColumnToDuplicationsIndex.java".hashCode());
- verifyStatistics(context, comps.values().size(), 12, 6, 3);
- }
-
- private String[] readLines(File filename) throws IOException {
- return FileUtils
- .readLines(filename, StandardCharsets.UTF_8)
- .toArray(new String[0]);
- }
-
- @CheckForNull
- private FileSourceDto insertContentOfFileInDb(String uuid, @Nullable String[] content) {
- return dbTester.getDbClient().componentDao().selectByUuid(dbTester.getSession(), uuid)
- .map(file -> {
- SourceLineHashesComputer linesHashesComputer = new SourceLineHashesComputer();
- if (content != null) {
- stream(content).forEach(linesHashesComputer::addLine);
- }
- FileSourceDto fileSourceDto = new FileSourceDto()
- .setUuid(Uuids.createFast())
- .setFileUuid(file.uuid())
- .setProjectUuid(file.branchUuid())
- .setLineHashes(linesHashesComputer.getLineHashes());
- dbTester.getDbClient().fileSourceDao().insert(dbTester.getSession(), fileSourceDto);
- dbTester.commit();
- return fileSourceDto;
- }).orElse(null);
- }
-
- private void setFilesInReport(Component... files) {
- treeRootHolder.setRoot(builder(Component.Type.PROJECT, ROOT_REF)
- .setUuid(project.uuid())
- .addChildren(files)
- .build());
- }
-
- private ComponentDto[] insertFiles(String... uuids) {
- return insertFiles(this::newComponentDto, uuids);
- }
-
- private ComponentDto[] insertFiles(Function<String, ComponentDto> newComponentDto, String... uuids) {
- return stream(uuids)
- .map(newComponentDto)
- .map(dto -> dbTester.components().insertComponent(dto))
- .toArray(ComponentDto[]::new);
- }
-
- private ComponentDto newComponentDto(String uuid) {
- return ComponentTesting.newFileDto(project)
- .setKey("key_" + uuid)
- .setUuid(uuid)
- .setPath("path_" + uuid);
- }
-
- private Component fileComponent(int ref, @Nullable String[] content) {
- ReportComponent component = builder(Component.Type.FILE, ref)
- .setName("report_path" + ref)
- .setFileAttributes(new FileAttributes(false, null, content == null ? 1 : content.length))
- .build();
- if (content != null) {
- setFileLineHashesInReport(component, content);
- }
- return component;
- }
-
- private void setFileLineHashesInReport(Component file, String[] content) {
- SourceLineHashesComputer computer = new SourceLineHashesComputer();
- for (String line : content) {
- computer.addLine(line);
- }
- when(sourceLinesHash.getLineHashesMatchingDBVersion(file)).thenReturn(computer.getLineHashes());
- }
-
- private static class CapturingScoreMatrixDumper implements ScoreMatrixDumper {
- private ScoreMatrix scoreMatrix;
-
- @Override
- public void dumpAsCsv(ScoreMatrix scoreMatrix) {
- this.scoreMatrix = scoreMatrix;
- }
- }
-
- private void prepareBranchAnalysis(Analysis analysis) {
- prepareAnalysis(BRANCH, analysis);
- }
-
- private void prepareAnalysis(BranchType branch, Analysis analysis) {
- mockBranchType(branch);
- analysisMetadataHolder.setBaseAnalysis(analysis);
- }
-
- private void mockBranchType(BranchType branchType) {
- when(analysisMetadataHolder.isPullRequest()).thenReturn(branchType == PULL_REQUEST);
- }
-
- public static void verifyStatistics(TestComputationStepContext context,
- @Nullable Integer expectedReportFiles, @Nullable Integer expectedDbFiles,
- @Nullable Integer expectedAddedFiles, @Nullable Integer expectedMovedFiles) {
- context.getStatistics().assertValue("reportFiles", expectedReportFiles);
- context.getStatistics().assertValue("dbFiles", expectedDbFiles);
- context.getStatistics().assertValue("addedFiles", expectedAddedFiles);
- context.getStatistics().assertValue("movedFiles", expectedMovedFiles);
- }
-
- public static class RecordingMutableAddedFileRepository implements MutableAddedFileRepository {
- private final List<Component> components = new ArrayList<>();
-
- @Override
- public void register(Component file) {
- components.add(file);
- }
-
- @Override
- public boolean isAdded(Component component) {
- throw new UnsupportedOperationException("isAdded should not be called");
- }
-
- public List<Component> getComponents() {
- return components;
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.filemove;
-
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.Immutable;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.System2;
-import org.sonar.api.utils.log.LogTester;
-import org.sonar.ce.task.projectanalysis.analysis.Analysis;
-import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
-import org.sonar.ce.task.projectanalysis.analysis.Branch;
-import org.sonar.ce.task.projectanalysis.component.Component;
-import org.sonar.ce.task.projectanalysis.component.FileAttributes;
-import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
-import org.sonar.ce.task.projectanalysis.filemove.MovedFilesRepository.OriginalFile;
-import org.sonar.ce.task.step.TestComputationStepContext;
-import org.sonar.core.util.Uuids;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.BranchType;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentTesting;
-import org.sonar.db.source.FileSourceDto;
-import org.sonar.server.project.Project;
-
-import static java.util.function.Function.identity;
-import static java.util.stream.Collectors.toMap;
-import static java.util.stream.Collectors.toSet;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
-import static org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT;
-import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
-import static org.sonar.ce.task.projectanalysis.filemove.FileMoveDetectionStepTest.RecordingMutableAddedFileRepository;
-import static org.sonar.ce.task.projectanalysis.filemove.FileMoveDetectionStepTest.verifyStatistics;
-import static org.sonar.db.component.BranchType.BRANCH;
-import static org.sonar.db.component.BranchType.PULL_REQUEST;
-
-public class PullRequestFileMoveDetectionStepTest {
- private static final String ROOT_REF = "0";
- private static final String FILE_1_REF = "1";
- private static final String FILE_2_REF = "2";
- private static final String FILE_3_REF = "3";
- private static final String FILE_4_REF = "4";
- private static final String FILE_5_REF = "5";
- private static final String FILE_6_REF = "6";
- private static final String FILE_7_REF = "7";
- private static final String TARGET_BRANCH = "target_branch";
- private static final String BRANCH_UUID = "branch_uuid";
- private static final String SNAPSHOT_UUID = "uuid_1";
-
- private static final Analysis ANALYSIS = new Analysis.Builder()
- .setUuid(SNAPSHOT_UUID)
- .setCreatedAt(86521)
- .build();
-
- @Rule
- public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
- @Rule
- public MutableMovedFilesRepositoryRule movedFilesRepository = new MutableMovedFilesRepositoryRule();
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
- @Rule
- public LogTester logTester = new LogTester();
-
- private ComponentDto branch;
- private ComponentDto project;
-
- private final DbClient dbClient = dbTester.getDbClient();
- private final AnalysisMetadataHolderRule analysisMetadataHolder = mock(AnalysisMetadataHolderRule.class);
- private final RecordingMutableAddedFileRepository addedFileRepository = new RecordingMutableAddedFileRepository();
- private final PullRequestFileMoveDetectionStep underTest = new PullRequestFileMoveDetectionStep(analysisMetadataHolder, treeRootHolder, dbClient, movedFilesRepository, addedFileRepository);
-
- @Before
- public void setUp() throws Exception {
- project = dbTester.components().insertPrivateProject();
- branch = dbTester.components().insertProjectBranch(project, branchDto -> branchDto.setUuid(BRANCH_UUID).setKey(TARGET_BRANCH));
- treeRootHolder.setRoot(builder(Component.Type.PROJECT, Integer.parseInt(ROOT_REF)).setUuid(project.uuid()).build());
- }
-
- @Test
- public void getDescription_returns_description() {
- assertThat(underTest.getDescription()).isEqualTo("Detect file moves in Pull Request scope");
- }
-
- @Test
- public void execute_does_not_detect_any_files_if_not_in_pull_request_scope() {
- prepareAnalysis(BRANCH, ANALYSIS);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- verifyStatistics(context, null, null, null, null);
- }
-
- @Test
- public void execute_detects_no_move_if_report_has_no_file() {
- preparePullRequestAnalysis(ANALYSIS);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- assertThat(addedFileRepository.getComponents()).isEmpty();
- verifyStatistics(context, 0, null, null, null);
- }
-
- @Test
- public void execute_detects_no_move_if_target_branch_has_no_files() {
- preparePullRequestAnalysis(ANALYSIS);
- Set<FileReference> fileReferences = Set.of(FileReference.of(FILE_1_REF), FileReference.of(FILE_2_REF));
- Map<String, Component> reportFilesByUuid = initializeAnalysisReportComponents(fileReferences);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- assertThat(addedFileRepository.getComponents()).containsOnlyOnceElementsOf(reportFilesByUuid.values());
- verifyStatistics(context, 2, 0, 2, null);
- }
-
- @Test
- public void execute_detects_no_move_if_there_are_no_files_in_report() {
- preparePullRequestAnalysis(ANALYSIS);
- initializeAnalysisReportComponents(Set.of());
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- assertThat(addedFileRepository.getComponents()).isEmpty();
- verifyStatistics(context, 0, null, null, null);
- }
-
- @Test
- public void execute_detects_no_move_if_file_key_exists_in_both_database_and_report() {
- preparePullRequestAnalysis(ANALYSIS);
-
- Set<FileReference> fileReferences = Set.of(FileReference.of(FILE_1_REF), FileReference.of(FILE_2_REF));
- initializeAnalysisReportComponents(fileReferences);
- initializeTargetBranchDatabaseComponents(fileReferences);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).isEmpty();
- assertThat(addedFileRepository.getComponents()).isEmpty();
- verifyStatistics(context, 2, 2, 0, 0);
- }
-
- @Test
- public void execute_detects_renamed_file() {
- // - FILE_1_REF on target branch is renamed to FILE_2_REF on Pull Request
- preparePullRequestAnalysis(ANALYSIS);
-
- Set<FileReference> reportFileReferences = Set.of(FileReference.of(FILE_2_REF, FILE_1_REF));
- Set<FileReference> databaseFileReferences = Set.of(FileReference.of(FILE_1_REF));
-
- Map<String, Component> reportFilesByUuid = initializeAnalysisReportComponents(reportFileReferences);
- Map<String, Component> databaseFilesByUuid = initializeTargetBranchDatabaseComponents(databaseFileReferences);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(addedFileRepository.getComponents()).isEmpty();
- assertThat(movedFilesRepository.getComponentsWithOriginal()).hasSize(1);
- assertThatFileRenameHasBeenDetected(reportFilesByUuid, databaseFilesByUuid, FILE_2_REF, FILE_1_REF);
- verifyStatistics(context, 1, 1, 0, 1);
- }
-
- @Test
- public void execute_detects_several_renamed_file() {
- // - FILE_1_REF has been renamed to FILE_3_REF on Pull Request
- // - FILE_2_REF has been deleted on Pull Request
- // - FILE_4_REF has been left untouched
- // - FILE_5_REF has been renamed to FILE_6_REF on Pull Request
- // - FILE_7_REF has been added on Pull Request
- preparePullRequestAnalysis(ANALYSIS);
-
- Set<FileReference> reportFileReferences = Set.of(
- FileReference.of(FILE_3_REF, FILE_1_REF),
- FileReference.of(FILE_4_REF),
- FileReference.of(FILE_6_REF, FILE_5_REF),
- FileReference.of(FILE_7_REF));
-
- Set<FileReference> databaseFileReferences = Set.of(
- FileReference.of(FILE_1_REF),
- FileReference.of(FILE_2_REF),
- FileReference.of(FILE_4_REF),
- FileReference.of(FILE_5_REF));
-
- Map<String, Component> reportFilesByUuid = initializeAnalysisReportComponents(reportFileReferences);
- Map<String, Component> databaseFilesByUuid = initializeTargetBranchDatabaseComponents(databaseFileReferences);
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(addedFileRepository.getComponents()).hasSize(1);
- assertThat(movedFilesRepository.getComponentsWithOriginal()).hasSize(2);
- assertThatFileAdditionHasBeenDetected(reportFilesByUuid, FILE_7_REF);
- assertThatFileRenameHasBeenDetected(reportFilesByUuid, databaseFilesByUuid, FILE_3_REF, FILE_1_REF);
- assertThatFileRenameHasBeenDetected(reportFilesByUuid, databaseFilesByUuid, FILE_6_REF, FILE_5_REF);
- verifyStatistics(context, 4, 4, 1, 2);
- }
-
- private void assertThatFileAdditionHasBeenDetected(Map<String, Component> reportFilesByUuid, String fileInReportReference) {
- Component fileInReport = reportFilesByUuid.get(fileInReportReference);
-
- assertThat(addedFileRepository.getComponents()).contains(fileInReport);
- assertThat(movedFilesRepository.getOriginalPullRequestFile(fileInReport)).isEmpty();
- }
-
-
- private void assertThatFileRenameHasBeenDetected(Map<String, Component> reportFilesByUuid, Map<String, Component> databaseFilesByUuid, String fileInReportReference, String originalFileInDatabaseReference) {
- Component fileInReport = reportFilesByUuid.get(fileInReportReference);
- Component originalFileInDatabase = databaseFilesByUuid.get(originalFileInDatabaseReference);
-
- assertThat(movedFilesRepository.getComponentsWithOriginal()).contains(fileInReport);
- assertThat(movedFilesRepository.getOriginalPullRequestFile(fileInReport)).isPresent();
-
- OriginalFile detectedOriginalFile = movedFilesRepository.getOriginalPullRequestFile(fileInReport).get();
- assertThat(detectedOriginalFile.key()).isEqualTo(originalFileInDatabase.getKey());
- assertThat(detectedOriginalFile.uuid()).isEqualTo(originalFileInDatabase.getUuid());
- }
-
- private Map<String, Component> initializeTargetBranchDatabaseComponents(Set<FileReference> references) {
- Set<Component> fileComponents = createFileComponents(references);
- insertFileComponentsInDatabase(fileComponents);
- return toFileComponentsByUuidMap(fileComponents);
- }
-
- private Map<String, Component> initializeAnalysisReportComponents(Set<FileReference> refs) {
- Set<Component> fileComponents = createFileComponents(refs);
- insertFileComponentsInReport(fileComponents);
- return toFileComponentsByUuidMap(fileComponents);
- }
-
- private Map<String, Component> toFileComponentsByUuidMap(Set<Component> fileComponents) {
- return fileComponents
- .stream()
- .collect(toMap(Component::getUuid, identity()));
- }
-
- private static Set<Component> createFileComponents(Set<FileReference> references) {
- return references
- .stream()
- .map(PullRequestFileMoveDetectionStepTest::createReportFileComponent)
- .collect(toSet());
- }
-
- private static Component createReportFileComponent(FileReference fileReference) {
- return builder(FILE, Integer.parseInt(fileReference.getReference()))
- .setUuid(fileReference.getReference())
- .setName("report_path" + fileReference.getReference())
- .setFileAttributes(new FileAttributes(false, null, 1, false, composeComponentPath(fileReference.getPastReference())))
- .build();
- }
-
- private void insertFileComponentsInReport(Set<Component> files) {
- treeRootHolder
- .setRoot(builder(PROJECT, Integer.parseInt(ROOT_REF))
- .setUuid(project.uuid())
- .addChildren(files.toArray(Component[]::new))
- .build());
- }
-
- private Set<ComponentDto> insertFileComponentsInDatabase(Set<Component> files) {
- return files
- .stream()
- .map(Component::getUuid)
- .map(this::composeComponentDto)
- .peek(this::insertComponentDto)
- .peek(this::insertContentOfFileInDatabase)
- .collect(toSet());
- }
-
- private void insertComponentDto(ComponentDto component) {
- dbTester.components().insertComponent(component);
- }
-
- private ComponentDto composeComponentDto(String uuid) {
- return ComponentTesting
- .newFileDto(project)
- .setBranchUuid(branch.uuid())
- .setKey("key_" + uuid)
- .setUuid(uuid)
- .setPath(composeComponentPath(uuid));
- }
-
- @CheckForNull
- private static String composeComponentPath(@Nullable String reference) {
- return Optional.ofNullable(reference)
- .map(r -> String.join("_", "path", r))
- .orElse(null);
- }
-
- private FileSourceDto insertContentOfFileInDatabase(ComponentDto file) {
- FileSourceDto fileSourceDto = composeFileSourceDto(file);
- persistFileSourceDto(fileSourceDto);
- return fileSourceDto;
- }
-
- private static FileSourceDto composeFileSourceDto(ComponentDto file) {
- return new FileSourceDto()
- .setUuid(Uuids.createFast())
- .setFileUuid(file.uuid())
- .setProjectUuid(file.branchUuid());
- }
-
- private void persistFileSourceDto(FileSourceDto fileSourceDto) {
- dbTester.getDbClient().fileSourceDao().insert(dbTester.getSession(), fileSourceDto);
- dbTester.commit();
- }
-
- private void preparePullRequestAnalysis(Analysis analysis) {
- prepareAnalysis(PULL_REQUEST, analysis);
- }
-
- private void prepareAnalysis(BranchType branch, Analysis analysis) {
- mockBranchType(branch);
- analysisMetadataHolder.setBaseAnalysis(analysis);
- }
-
- private void mockBranchType(BranchType branchType) {
- Branch branch = mock(Branch.class);
- when(analysisMetadataHolder.getBranch()).thenReturn(branch);
- when(analysisMetadataHolder.getBranch().getTargetBranchName()).thenReturn(TARGET_BRANCH);
- when(analysisMetadataHolder.isPullRequest()).thenReturn(branchType == PULL_REQUEST);
- when(analysisMetadataHolder.getProject()).thenReturn(Project.from(project));
- }
-
- @Immutable
- private static class FileReference {
- private final String reference;
- private final String pastReference;
-
- private FileReference(String reference, @Nullable String pastReference) {
- this.reference = reference;
- this.pastReference = pastReference;
- }
-
- public String getReference() {
- return reference;
- }
-
- @CheckForNull
- public String getPastReference() {
- return pastReference;
- }
-
- public static FileReference of(String reference, String pastReference) {
- return new FileReference(reference, pastReference);
- }
-
- public static FileReference of(String reference) {
- return new FileReference(reference, null);
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.metric;
-
-import java.util.List;
-import java.util.Random;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.System2;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbTester;
-import org.sonar.db.metric.MetricDto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-public class MetricRepositoryImplTest {
- private static final String SOME_KEY = "some_key";
- private static final String SOME_UUID = "uuid";
-
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
- private DbClient dbClient = dbTester.getDbClient();
- private MetricRepositoryImpl underTest = new MetricRepositoryImpl(dbClient);
-
- @Test
- public void getByKey_throws_NPE_if_arg_is_null() {
- assertThatThrownBy(() -> underTest.getByKey(null))
- .isInstanceOf(NullPointerException.class);
- }
-
- @Test
- public void getByKey_throws_ISE_if_start_has_not_been_called() {
- assertThatThrownBy(() -> underTest.getByKey(SOME_KEY))
- .isInstanceOf(IllegalStateException.class)
- .hasMessage("Metric cache has not been initialized");
- }
-
- @Test
- public void getByKey_throws_ISE_of_Metric_does_not_exist() {
- assertThatThrownBy(() -> {
- underTest.start();
- underTest.getByKey(SOME_KEY);
- })
- .isInstanceOf(IllegalStateException.class)
- .hasMessage(String.format("Metric with key '%s' does not exist", SOME_KEY));
- }
-
- @Test
- public void getByKey_throws_ISE_of_Metric_is_disabled() {
- dbTester.measures().insertMetric(t -> t.setKey("complexity").setEnabled(false));
-
- underTest.start();
-
- assertThatThrownBy(() -> underTest.getByKey("complexity"))
- .isInstanceOf(IllegalStateException.class)
- .hasMessage(String.format("Metric with key '%s' does not exist", "complexity"));
- }
-
- @Test
- public void getByKey_find_enabled_Metrics() {
- MetricDto ncloc = dbTester.measures().insertMetric(t -> t.setKey("ncloc").setEnabled(true));
- MetricDto coverage = dbTester.measures().insertMetric(t -> t.setKey("coverage").setEnabled(true));
-
- underTest.start();
-
- assertThat(underTest.getByKey("ncloc").getUuid()).isEqualTo(ncloc.getUuid());
- assertThat(underTest.getByKey("coverage").getUuid()).isEqualTo(coverage.getUuid());
- }
-
- @Test
- public void getById_throws_ISE_if_start_has_not_been_called() {
- assertThatThrownBy(() -> underTest.getByUuid(SOME_UUID))
- .isInstanceOf(IllegalStateException.class)
- .hasMessage("Metric cache has not been initialized");
- }
-
- @Test
- public void getById_throws_ISE_of_Metric_does_not_exist() {
- underTest.start();
-
- assertThatThrownBy(() -> underTest.getByUuid(SOME_UUID))
- .isInstanceOf(IllegalStateException.class)
- .hasMessage(String.format("Metric with uuid '%s' does not exist", SOME_UUID));
- }
-
- @Test
- public void getById_throws_ISE_of_Metric_is_disabled() {
- dbTester.measures().insertMetric(t -> t.setKey("complexity").setEnabled(false));
-
- underTest.start();
-
- assertThatThrownBy(() -> underTest.getByUuid(SOME_UUID))
- .isInstanceOf(IllegalStateException.class)
- .hasMessage(String.format("Metric with uuid '%s' does not exist", SOME_UUID));
- }
-
- @Test
- public void getById_find_enabled_Metrics() {
- MetricDto ncloc = dbTester.measures().insertMetric(t -> t.setKey("ncloc").setEnabled(true));
- MetricDto coverage = dbTester.measures().insertMetric(t -> t.setKey("coverage").setEnabled(true));
-
- underTest.start();
-
- assertThat(underTest.getByUuid(ncloc.getUuid()).getKey()).isEqualTo("ncloc");
- assertThat(underTest.getByUuid(coverage.getUuid()).getKey()).isEqualTo("coverage");
- }
-
- @Test
- public void getOptionalById_throws_ISE_if_start_has_not_been_called() {
- assertThatThrownBy(() -> underTest.getOptionalByUuid(SOME_UUID))
- .isInstanceOf(IllegalStateException.class)
- .hasMessage("Metric cache has not been initialized");
- }
-
- @Test
- public void getOptionalById_returns_empty_of_Metric_does_not_exist() {
- underTest.start();
-
- assertThat(underTest.getOptionalByUuid(SOME_UUID)).isEmpty();
- }
-
- @Test
- public void getOptionalById_returns_empty_of_Metric_is_disabled() {
- dbTester.measures().insertMetric(t -> t.setKey("complexity").setEnabled(false));
-
- underTest.start();
-
- assertThat(underTest.getOptionalByUuid(SOME_UUID)).isEmpty();
- }
-
- @Test
- public void getOptionalById_find_enabled_Metrics() {
- MetricDto ncloc = dbTester.measures().insertMetric(t -> t.setKey("ncloc").setEnabled(true));
- MetricDto coverage = dbTester.measures().insertMetric(t -> t.setKey("coverage").setEnabled(true));
-
- underTest.start();
-
- assertThat(underTest.getOptionalByUuid(ncloc.getUuid()).get().getKey()).isEqualTo("ncloc");
- assertThat(underTest.getOptionalByUuid(coverage.getUuid()).get().getKey()).isEqualTo("coverage");
- }
-
- @Test
- public void get_all_metrics() {
- List<MetricDto> enabledMetrics = IntStream.range(0, 1 + new Random().nextInt(12))
- .mapToObj(i -> dbTester.measures().insertMetric(t -> t.setKey("key_enabled_" + i).setEnabled(true)))
- .toList();
- IntStream.range(0, 1 + new Random().nextInt(12))
- .forEach(i -> dbTester.measures().insertMetric(t -> t.setKey("key_disabled_" + i).setEnabled(false)));
-
- underTest.start();
- assertThat(underTest.getAll())
- .extracting(Metric::getKey)
- .containsOnly(enabledMetrics.stream().map(MetricDto::getKey).toArray(String[]::new));
- }
-
- @Test
- public void getMetricsByType_givenRatingType_returnRatingMetrics() {
- List<MetricDto> enabledMetrics = IntStream.range(0, 1 + new Random().nextInt(12))
- .mapToObj(i -> dbTester.measures().insertMetric(t -> t.setKey("key_enabled_" + i).setEnabled(true).setValueType("RATING")))
- .toList();
-
- underTest.start();
- assertThat(underTest.getMetricsByType(Metric.MetricType.RATING))
- .extracting(Metric::getKey)
- .containsOnly(enabledMetrics.stream().map(MetricDto::getKey).toArray(String[]::new));
- }
-
- @Test
- public void getMetricsByType_givenRatingTypeAndWantedMilisecType_returnEmptyList() {
- IntStream.range(0, 1 + new Random().nextInt(12))
- .mapToObj(i -> dbTester.measures().insertMetric(t -> t.setKey("key_enabled_" + i).setEnabled(true).setValueType("RATING")))
- .collect(Collectors.toList());
-
- underTest.start();
- assertThat(underTest.getMetricsByType(Metric.MetricType.MILLISEC)).isEmpty();
- }
-
- @Test
- public void getMetricsByType_givenOnlyMilisecTypeAndWantedRatingMetrics_returnEmptyList() {
- IntStream.range(0, 1 + new Random().nextInt(12))
- .mapToObj(i -> dbTester.measures().insertMetric(t -> t.setKey("key_enabled_" + i).setEnabled(true).setValueType("MILISEC")));
-
- underTest.start();
- assertThat(underTest.getMetricsByType(Metric.MetricType.RATING)).isEmpty();
- }
-
- @Test
- public void getMetricsByType_givenMetricsAreNull_throwException() {
- assertThatThrownBy(() -> underTest.getMetricsByType(Metric.MetricType.RATING))
- .isInstanceOf(IllegalStateException.class);
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.scm;
-
-import com.google.common.collect.ImmutableList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Optional;
-import javax.annotation.Nullable;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.System2;
-import org.sonar.api.utils.log.LogTester;
-import org.sonar.ce.task.projectanalysis.analysis.Analysis;
-import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
-import org.sonar.ce.task.projectanalysis.analysis.Branch;
-import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
-import org.sonar.ce.task.projectanalysis.component.Component;
-import org.sonar.ce.task.projectanalysis.component.ReferenceBranchComponentUuids;
-import org.sonar.ce.task.projectanalysis.filemove.MutableMovedFilesRepositoryRule;
-import org.sonar.ce.task.projectanalysis.period.NewCodeReferenceBranchComponentUuids;
-import org.sonar.ce.task.projectanalysis.period.Period;
-import org.sonar.ce.task.projectanalysis.period.PeriodHolderRule;
-import org.sonar.core.hash.SourceHashComputer;
-import org.sonar.core.util.Uuids;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.BranchType;
-import org.sonar.db.newcodeperiod.NewCodePeriodType;
-import org.sonar.db.protobuf.DbFileSources;
-import org.sonar.db.source.FileSourceDto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.api.utils.log.LoggerLevel.TRACE;
-import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
-
-public class ScmInfoDbLoaderTest {
- static final int FILE_REF = 1;
- static final Component FILE = builder(Component.Type.FILE, FILE_REF).setKey("FILE_KEY").setUuid("FILE_UUID").build();
- static final long DATE_1 = 123456789L;
-
- static Analysis baseProjectAnalysis = new Analysis.Builder()
- .setUuid("uuid_1")
- .setCreatedAt(123456789L)
- .build();
-
- @Rule
- public LogTester logTester = new LogTester();
- @Rule
- public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
- @Rule
- public BatchReportReaderRule reportReader = new BatchReportReaderRule();
- @Rule
- public MutableMovedFilesRepositoryRule movedFiles = new MutableMovedFilesRepositoryRule();
- @Rule
- public PeriodHolderRule periodHolder = new PeriodHolderRule();
-
- private final Branch branch = mock(Branch.class);
- private final ReferenceBranchComponentUuids referenceBranchComponentUuids = mock(ReferenceBranchComponentUuids.class);
- private final NewCodeReferenceBranchComponentUuids newCodeReferenceBranchComponentUuids = mock(NewCodeReferenceBranchComponentUuids.class);
-
- private final ScmInfoDbLoader underTest = new ScmInfoDbLoader(analysisMetadataHolder, movedFiles, dbTester.getDbClient(), referenceBranchComponentUuids,
- newCodeReferenceBranchComponentUuids, periodHolder);
-
- @Before
- public void before() {
- periodHolder.setPeriod(new Period(NewCodePeriodType.PREVIOUS_VERSION.name(), null, null));
- }
-
- @Test
- public void returns_ScmInfo_from_DB() {
- analysisMetadataHolder.setBaseAnalysis(baseProjectAnalysis);
- analysisMetadataHolder.setBranch(null);
-
- String hash = computeSourceHash(1);
- addFileSourceInDb("henry", DATE_1, "rev-1", hash);
-
- DbScmInfo scmInfo = underTest.getScmInfo(FILE).get();
- assertThat(scmInfo.getAllChangesets()).hasSize(1);
- assertThat(scmInfo.fileHash()).isEqualTo(hash);
-
- assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from DB for file 'FILE_UUID'");
- }
-
- @Test
- public void read_from_reference_branch_if_no_base() {
- analysisMetadataHolder.setBaseAnalysis(null);
- analysisMetadataHolder.setBranch(branch);
-
- String referenceFileUuid = "referenceFileUuid";
- String hash = computeSourceHash(1);
-
- when(referenceBranchComponentUuids.getComponentUuid(FILE.getKey())).thenReturn(referenceFileUuid);
- addFileSourceInDb("henry", DATE_1, "rev-1", hash, referenceFileUuid);
-
- DbScmInfo scmInfo = underTest.getScmInfo(FILE).get();
- assertThat(scmInfo.getAllChangesets()).hasSize(1);
- assertThat(scmInfo.fileHash()).isEqualTo(hash);
- assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from DB for file 'referenceFileUuid'");
- }
-
- @Test
- public void read_from_target_if_pullrequest() {
- Branch branch = mock(Branch.class);
- when(branch.getType()).thenReturn(BranchType.PULL_REQUEST);
- analysisMetadataHolder.setBaseAnalysis(null);
- analysisMetadataHolder.setBranch(branch);
-
- String targetBranchFileUuid = "targetBranchFileUuid";
- String hash = computeSourceHash(1);
-
- when(referenceBranchComponentUuids.getComponentUuid(FILE.getKey())).thenReturn(targetBranchFileUuid);
- addFileSourceInDb("henry", DATE_1, "rev-1", hash, targetBranchFileUuid);
-
- DbScmInfo scmInfo = underTest.getScmInfo(FILE).get();
- assertThat(scmInfo.getAllChangesets()).hasSize(1);
- assertThat(scmInfo.fileHash()).isEqualTo(hash);
- assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from DB for file 'targetBranchFileUuid'");
- }
-
- @Test
- public void read_from_target_if_reference_branch() {
- periodHolder.setPeriod(new Period(NewCodePeriodType.REFERENCE_BRANCH.name(), null, null));
-
- Branch branch = mock(Branch.class);
- when(branch.getType()).thenReturn(BranchType.BRANCH);
- analysisMetadataHolder.setBaseAnalysis(null);
- analysisMetadataHolder.setBranch(branch);
-
- String targetBranchFileUuid = "targetBranchFileUuid";
- String hash = computeSourceHash(1);
-
- when(newCodeReferenceBranchComponentUuids.getComponentUuid(FILE.getKey())).thenReturn(targetBranchFileUuid);
- addFileSourceInDb("henry", DATE_1, "rev-1", hash, targetBranchFileUuid);
-
- DbScmInfo scmInfo = underTest.getScmInfo(FILE).get();
- assertThat(scmInfo.getAllChangesets()).hasSize(1);
- assertThat(scmInfo.fileHash()).isEqualTo(hash);
- assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from DB for file 'targetBranchFileUuid'");
- }
-
- @Test
- public void read_from_db_if_not_exist_in_reference_branch() {
- periodHolder.setPeriod(new Period(NewCodePeriodType.REFERENCE_BRANCH.name(), null, null));
-
- Branch branch = mock(Branch.class);
- when(branch.getType()).thenReturn(BranchType.BRANCH);
- analysisMetadataHolder.setBaseAnalysis(null);
- analysisMetadataHolder.setBranch(branch);
-
- String hash = computeSourceHash(1);
-
- addFileSourceInDb("henry", DATE_1, "rev-1", hash, FILE.getUuid());
-
- DbScmInfo scmInfo = underTest.getScmInfo(FILE).get();
- assertThat(scmInfo.getAllChangesets()).hasSize(1);
- assertThat(scmInfo.fileHash()).isEqualTo(hash);
- assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from DB for file 'FILE_UUID'");
- }
-
- @Test
- public void return_empty_if_no_dto_available() {
- analysisMetadataHolder.setBaseAnalysis(baseProjectAnalysis);
- analysisMetadataHolder.setBranch(null);
-
- Optional<DbScmInfo> scmInfo = underTest.getScmInfo(FILE);
-
- assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from DB for file 'FILE_UUID'");
- assertThat(scmInfo).isEmpty();
- }
-
- @Test
- public void do_not_read_from_db_on_first_analysis_if_there_is_no_reference_branch() {
- Branch branch = mock(Branch.class);
- when(branch.getType()).thenReturn(BranchType.PULL_REQUEST);
- analysisMetadataHolder.setBaseAnalysis(null);
- analysisMetadataHolder.setBranch(branch);
-
- assertThat(underTest.getScmInfo(FILE)).isEmpty();
- assertThat(logTester.logs(TRACE)).isEmpty();
- }
-
- private static List<String> generateLines(int lineCount) {
- ImmutableList.Builder<String> builder = ImmutableList.builder();
- for (int i = 0; i < lineCount; i++) {
- builder.add("line " + i);
- }
- return builder.build();
- }
-
- private static String computeSourceHash(int lineCount) {
- SourceHashComputer sourceHashComputer = new SourceHashComputer();
- Iterator<String> lines = generateLines(lineCount).iterator();
- while (lines.hasNext()) {
- sourceHashComputer.addLine(lines.next(), lines.hasNext());
- }
- return sourceHashComputer.getHash();
- }
-
- private void addFileSourceInDb(@Nullable String author, @Nullable Long date, @Nullable String revision, String srcHash) {
- addFileSourceInDb(author, date, revision, srcHash, FILE.getUuid());
- }
-
- private void addFileSourceInDb(@Nullable String author, @Nullable Long date, @Nullable String revision, String srcHash, String fileUuid) {
- DbFileSources.Data.Builder fileDataBuilder = DbFileSources.Data.newBuilder();
- DbFileSources.Line.Builder builder = fileDataBuilder.addLinesBuilder()
- .setLine(1);
- if (author != null) {
- builder.setScmAuthor(author);
- }
- if (date != null) {
- builder.setScmDate(date);
- }
- if (revision != null) {
- builder.setScmRevision(revision);
- }
- dbTester.getDbClient().fileSourceDao().insert(dbTester.getSession(), new FileSourceDto()
- .setUuid(Uuids.createFast())
- .setLineHashes(Collections.singletonList("lineHash"))
- .setFileUuid(fileUuid)
- .setProjectUuid("PROJECT_UUID")
- .setSourceData(fileDataBuilder.build())
- .setSrcHash(srcHash));
- dbTester.commit();
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.step;
-
-import com.tngtech.java.junit.dataprovider.DataProvider;
-import com.tngtech.java.junit.dataprovider.DataProviderRunner;
-import com.tngtech.java.junit.dataprovider.UseDataProvider;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import javax.annotation.Nullable;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.sonar.api.utils.System2;
-import org.sonar.ce.task.projectanalysis.analysis.Branch;
-import org.sonar.ce.task.projectanalysis.analysis.MutableAnalysisMetadataHolderRule;
-import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
-import org.sonar.ce.task.projectanalysis.component.Component;
-import org.sonar.ce.task.projectanalysis.component.DefaultBranchImpl;
-import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolderRule;
-import org.sonar.ce.task.step.TestComputationStepContext;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.BranchType;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.SnapshotDto;
-import org.sonar.scanner.protocol.output.ScannerReport;
-import org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType;
-import org.sonar.scanner.protocol.output.ScannerReport.Component.FileStatus;
-import org.sonar.server.project.Project;
-
-import static java.util.Optional.ofNullable;
-import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
-import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.db.component.BranchDto.DEFAULT_MAIN_BRANCH_NAME;
-import static org.sonar.db.component.ComponentTesting.newDirectory;
-import static org.sonar.db.component.ComponentTesting.newFileDto;
-import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
-import static org.sonar.db.component.SnapshotTesting.newAnalysis;
-import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.FILE;
-import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.PROJECT;
-
-@RunWith(DataProviderRunner.class)
-public class BuildComponentTreeStepTest {
- private static final String NO_SCANNER_PROJECT_VERSION = null;
- private static final String NO_SCANNER_BUILD_STRING = null;
-
- private static final int ROOT_REF = 1;
- private static final int FILE_1_REF = 4;
- private static final int FILE_2_REF = 5;
- private static final int FILE_3_REF = 7;
- private static final int UNCHANGED_FILE_REF = 10;
-
- private static final String REPORT_PROJECT_KEY = "REPORT_PROJECT_KEY";
- private static final String REPORT_DIR_PATH_1 = "src/main/java/dir1";
- private static final String REPORT_FILE_PATH_1 = "src/main/java/dir1/File1.java";
- private static final String REPORT_FILE_NAME_1 = "File1.java";
- private static final String REPORT_DIR_PATH_2 = "src/main/java/dir2";
- private static final String REPORT_FILE_PATH_2 = "src/main/java/dir2/File2.java";
- private static final String REPORT_FILE_PATH_3 = "src/main/java/dir2/File3.java";
- private static final String REPORT_UNCHANGED_FILE_PATH = "src/main/File3.java";
-
- private static final long ANALYSIS_DATE = 123456789L;
-
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
- @Rule
- public BatchReportReaderRule reportReader = new BatchReportReaderRule().setMetadata(createReportMetadata(NO_SCANNER_PROJECT_VERSION, NO_SCANNER_BUILD_STRING));
- @Rule
- public MutableTreeRootHolderRule treeRootHolder = new MutableTreeRootHolderRule();
- @Rule
- public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule();
-
- private DbClient dbClient = dbTester.getDbClient();
- private BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
-
- @Test
- public void fails_if_root_component_does_not_exist_in_reportReader() {
- setAnalysisMetadataHolder();
-
- assertThatThrownBy(() -> underTest.execute(new TestComputationStepContext()))
- .isInstanceOf(NullPointerException.class);
- }
-
- @Test
- public void verify_tree_is_correctly_built() {
- setAnalysisMetadataHolder();
- reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF, FILE_2_REF, FILE_3_REF));
- reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));
- reportReader.putComponent(componentWithPath(FILE_2_REF, FILE, REPORT_FILE_PATH_2));
- reportReader.putComponent(componentWithPath(FILE_3_REF, FILE, REPORT_FILE_PATH_3));
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- Component root = treeRootHolder.getRoot();
- assertThat(root).isNotNull();
- verifyComponent(root, Component.Type.PROJECT, ROOT_REF, 1);
-
- Component dir = root.getChildren().iterator().next();
- verifyComponent(dir, Component.Type.DIRECTORY, null, 2);
-
- Component dir1 = dir.getChildren().get(0);
- verifyComponent(dir1, Component.Type.DIRECTORY, null, 1);
- verifyComponent(dir1.getChildren().get(0), Component.Type.FILE, FILE_1_REF, 0);
-
- Component dir2 = dir.getChildren().get(1);
- verifyComponent(dir2, Component.Type.DIRECTORY, null, 2);
- verifyComponent(dir2.getChildren().get(0), Component.Type.FILE, FILE_2_REF, 0);
- verifyComponent(dir2.getChildren().get(1), Component.Type.FILE, FILE_3_REF, 0);
-
- context.getStatistics().assertValue("components", 7);
- }
-
- @Test
- public void verify_tree_is_correctly_built_in_prs() {
- setAnalysisMetadataHolder(true);
- reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF, FILE_2_REF, FILE_3_REF, UNCHANGED_FILE_REF));
- reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));
- reportReader.putComponent(componentWithPath(FILE_2_REF, FILE, REPORT_FILE_PATH_2));
- reportReader.putComponent(componentWithPath(FILE_3_REF, FILE, REPORT_FILE_PATH_3));
- reportReader.putComponent(unchangedComponentWithPath(UNCHANGED_FILE_REF, FILE, REPORT_UNCHANGED_FILE_PATH));
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- // modified root
- Component mRoot = treeRootHolder.getRoot();
- verifyComponent(mRoot, Component.Type.PROJECT, ROOT_REF, 1);
-
- Component mDir = mRoot.getChildren().get(0);
- assertThat(mDir.getName()).isEqualTo("src/main/java");
- verifyComponent(mDir, Component.Type.DIRECTORY, null, 2);
-
- Component mDir1 = mDir.getChildren().get(0);
- assertThat(mDir1.getName()).isEqualTo("src/main/java/dir1");
- verifyComponent(mDir1, Component.Type.DIRECTORY, null, 1);
- verifyComponent(mDir1.getChildren().get(0), Component.Type.FILE, FILE_1_REF, 0);
-
- Component mDir2 = mDir.getChildren().get(1);
- assertThat(mDir2.getName()).isEqualTo("src/main/java/dir2");
- verifyComponent(mDir2, Component.Type.DIRECTORY, null, 2);
- verifyComponent(mDir2.getChildren().get(0), Component.Type.FILE, FILE_2_REF, 0);
- verifyComponent(mDir2.getChildren().get(1), Component.Type.FILE, FILE_3_REF, 0);
-
- // root
- Component root = treeRootHolder.getReportTreeRoot();
- verifyComponent(root, Component.Type.PROJECT, ROOT_REF, 1);
-
- Component dir = root.getChildren().get(0);
- assertThat(dir.getName()).isEqualTo("src/main");
- verifyComponent(dir, Component.Type.DIRECTORY, null, 2);
-
- Component dir1 = dir.getChildren().get(0);
- assertThat(dir1.getName()).isEqualTo("src/main/java");
- verifyComponent(dir1, Component.Type.DIRECTORY, null, 2);
- verifyComponent(dir1.getChildren().get(0), Component.Type.DIRECTORY, null, 1);
- verifyComponent(dir1.getChildren().get(1), Component.Type.DIRECTORY, null, 2);
-
- Component dir2 = dir1.getChildren().get(0);
- assertThat(dir2.getName()).isEqualTo("src/main/java/dir1");
- verifyComponent(dir2, Component.Type.DIRECTORY, null, 1);
- verifyComponent(dir2.getChildren().get(0), Component.Type.FILE, FILE_1_REF, 0);
-
- Component dir3 = dir1.getChildren().get(1);
- assertThat(dir3.getName()).isEqualTo("src/main/java/dir2");
- verifyComponent(dir3, Component.Type.DIRECTORY, null, 2);
- verifyComponent(dir3.getChildren().get(0), Component.Type.FILE, FILE_2_REF, 0);
- verifyComponent(dir3.getChildren().get(1), Component.Type.FILE, FILE_3_REF, 0);
-
- context.getStatistics().assertValue("components", 7);
- }
-
- /**
- * SONAR-13262
- */
- @Test
- public void verify_tree_is_correctly_built_in_prs_with_repeated_names() {
- setAnalysisMetadataHolder(true);
- reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF));
- reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_PROJECT_KEY + "/file.js"));
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- // modified root
- Component mRoot = treeRootHolder.getRoot();
- verifyComponent(mRoot, Component.Type.PROJECT, ROOT_REF, 1);
-
- Component dir = mRoot.getChildren().get(0);
- assertThat(dir.getName()).isEqualTo(REPORT_PROJECT_KEY);
- assertThat(dir.getShortName()).isEqualTo(REPORT_PROJECT_KEY);
-
- verifyComponent(dir, Component.Type.DIRECTORY, null, 1);
- }
-
- @Test
- public void compute_keys_and_uuids() {
- setAnalysisMetadataHolder();
- reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF));
- reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));
-
- underTest.execute(new TestComputationStepContext());
-
- verifyComponentByRef(ROOT_REF, REPORT_PROJECT_KEY, analysisMetadataHolder.getProject().getName());
- verifyComponentByKey(REPORT_PROJECT_KEY + ":" + REPORT_DIR_PATH_1, REPORT_DIR_PATH_1);
- verifyComponentByRef(FILE_1_REF, REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1, REPORT_FILE_NAME_1);
- }
-
- @Test
- public void return_existing_uuids() {
- setAnalysisMetadataHolder();
- ComponentDto project = insertComponent(newPrivateProjectDto("ABCD").setKey(REPORT_PROJECT_KEY));
- ComponentDto directory = newDirectory(project, "CDEF", REPORT_DIR_PATH_1);
- insertComponent(directory.setKey(REPORT_PROJECT_KEY + ":" + REPORT_DIR_PATH_1));
- insertComponent(newFileDto(project, directory, "DEFG")
- .setKey(REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1)
- .setPath(REPORT_FILE_PATH_1));
-
- // new structure, without modules
- reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF));
- reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));
-
- underTest.execute(new TestComputationStepContext());
-
- verifyComponentByRef(ROOT_REF, REPORT_PROJECT_KEY, analysisMetadataHolder.getProject().getName(), "ABCD");
- verifyComponentByKey(REPORT_PROJECT_KEY + ":" + REPORT_DIR_PATH_1, REPORT_DIR_PATH_1, "CDEF");
- verifyComponentByRef(FILE_1_REF, REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1, REPORT_FILE_NAME_1, "DEFG");
- }
-
- @Test
- public void generate_keys_when_using_new_branch() {
- Branch branch = mock(Branch.class);
- when(branch.getName()).thenReturn("origin/feature");
- when(branch.isMain()).thenReturn(false);
- when(branch.generateKey(any(), any())).thenReturn("generated");
- analysisMetadataHolder.setRootComponentRef(ROOT_REF)
- .setAnalysisDate(ANALYSIS_DATE)
- .setProject(Project.from(newPrivateProjectDto().setKey(REPORT_PROJECT_KEY)))
- .setBranch(branch);
- BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
- reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF));
- reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));
-
- underTest.execute(new TestComputationStepContext());
-
- verifyComponentByRef(ROOT_REF, "generated", analysisMetadataHolder.getProject().getName(), null);
- verifyComponentByRef(FILE_1_REF, "generated", REPORT_FILE_NAME_1, null);
- }
-
- @Test
- public void generate_keys_when_using_existing_branch() {
- ComponentDto projectDto = dbTester.components().insertPublicProject();
- String branchName = randomAlphanumeric(248);
- ComponentDto componentDto = dbTester.components().insertProjectBranch(projectDto, b -> b.setKey(branchName));
- Branch branch = mock(Branch.class);
- when(branch.getName()).thenReturn(branchName);
- when(branch.isMain()).thenReturn(false);
- when(branch.generateKey(any(), any())).thenReturn(componentDto.getKey());
- analysisMetadataHolder.setRootComponentRef(ROOT_REF)
- .setAnalysisDate(ANALYSIS_DATE)
- .setProject(Project.from(projectDto))
- .setBranch(branch);
- BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
- reportReader.putComponent(component(ROOT_REF, PROJECT, componentDto.getKey()));
-
- underTest.execute(new TestComputationStepContext());
-
- verifyComponentByRef(ROOT_REF, componentDto.getKey(), analysisMetadataHolder.getProject().getName(), componentDto.uuid());
- }
-
- @Test
- public void generate_keys_when_using_main_branch() {
- setAnalysisMetadataHolder();
- BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
- reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF));
- reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));
-
- underTest.execute(new TestComputationStepContext());
-
- verifyComponentByRef(ROOT_REF, REPORT_PROJECT_KEY, analysisMetadataHolder.getProject().getName(), null);
- verifyComponentByKey(REPORT_PROJECT_KEY + ":" + REPORT_DIR_PATH_1, REPORT_DIR_PATH_1);
- verifyComponentByRef(FILE_1_REF, REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1, REPORT_FILE_NAME_1, null);
- }
-
- @Test
- public void compute_keys_and_uuids_on_project_having_module_and_directory() {
- setAnalysisMetadataHolder();
- reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF, FILE_2_REF));
- reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));
- reportReader.putComponent(componentWithPath(FILE_2_REF, FILE, REPORT_FILE_PATH_2));
-
- underTest.execute(new TestComputationStepContext());
-
- verifyComponentByRef(ROOT_REF, REPORT_PROJECT_KEY, analysisMetadataHolder.getProject().getName());
- verifyComponentByKey(REPORT_PROJECT_KEY + ":" + REPORT_DIR_PATH_1, "dir1");
- verifyComponentByRef(FILE_1_REF, REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1, REPORT_FILE_NAME_1);
- verifyComponentByKey(REPORT_PROJECT_KEY + ":" + REPORT_DIR_PATH_2, "dir2");
- verifyComponentByRef(FILE_2_REF, REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_2, "File2.java");
- }
-
- @Test
- public void compute_keys_and_uuids_on_multi_modules() {
- setAnalysisMetadataHolder();
- reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF));
- reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1));
-
- underTest.execute(new TestComputationStepContext());
-
- verifyComponentByRef(ROOT_REF, REPORT_PROJECT_KEY, analysisMetadataHolder.getProject().getName());
- verifyComponentByKey(REPORT_PROJECT_KEY + ":" + REPORT_DIR_PATH_1, REPORT_DIR_PATH_1);
- verifyComponentByRef(FILE_1_REF, REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1, REPORT_FILE_NAME_1);
- }
-
- @Test
- public void set_no_base_project_snapshot_when_no_snapshot() {
- setAnalysisMetadataHolder();
- reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY));
- underTest.execute(new TestComputationStepContext());
-
- assertThat(analysisMetadataHolder.isFirstAnalysis()).isTrue();
- }
-
- @Test
- public void set_no_base_project_snapshot_when_no_last_snapshot() {
- setAnalysisMetadataHolder();
- ComponentDto project = insertComponent(newPrivateProjectDto("ABCD").setKey(REPORT_PROJECT_KEY));
- insertSnapshot(newAnalysis(project).setLast(false));
-
- reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY));
- underTest.execute(new TestComputationStepContext());
-
- assertThat(analysisMetadataHolder.isFirstAnalysis()).isTrue();
- }
-
- @Test
- public void set_base_project_snapshot_when_last_snapshot_exist() {
- setAnalysisMetadataHolder();
- ComponentDto project = insertComponent(newPrivateProjectDto("ABCD").setKey(REPORT_PROJECT_KEY));
- insertSnapshot(newAnalysis(project).setLast(true));
-
- reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY));
- underTest.execute(new TestComputationStepContext());
-
- assertThat(analysisMetadataHolder.isFirstAnalysis()).isFalse();
- }
-
- @Test
- public void set_projectVersion_to_not_provided_when_not_set_on_first_analysis() {
- setAnalysisMetadataHolder();
- reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY));
-
- underTest.execute(new TestComputationStepContext());
-
- assertThat(treeRootHolder.getReportTreeRoot().getProjectAttributes().getProjectVersion()).isEqualTo("not provided");
- }
-
- @Test
- @UseDataProvider("oneParameterNullNonNullCombinations")
- public void set_projectVersion_to_previous_analysis_when_not_set(@Nullable String previousAnalysisProjectVersion) {
- setAnalysisMetadataHolder();
- ComponentDto project = insertComponent(newPrivateProjectDto("ABCD").setKey(REPORT_PROJECT_KEY));
- insertSnapshot(newAnalysis(project).setProjectVersion(previousAnalysisProjectVersion).setLast(true));
- reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY));
-
- underTest.execute(new TestComputationStepContext());
-
- String projectVersion = treeRootHolder.getReportTreeRoot().getProjectAttributes().getProjectVersion();
- if (previousAnalysisProjectVersion == null) {
- assertThat(projectVersion).isEqualTo("not provided");
- } else {
- assertThat(projectVersion).isEqualTo(previousAnalysisProjectVersion);
- }
- }
-
- @Test
- public void set_projectVersion_when_it_is_set_on_first_analysis() {
- String scannerProjectVersion = randomAlphabetic(12);
- setAnalysisMetadataHolder();
- reportReader.setMetadata(createReportMetadata(scannerProjectVersion, NO_SCANNER_BUILD_STRING));
- reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY));
-
- underTest.execute(new TestComputationStepContext());
-
- assertThat(treeRootHolder.getReportTreeRoot().getProjectAttributes().getProjectVersion())
- .isEqualTo(scannerProjectVersion);
- }
-
- @Test
- @UseDataProvider("oneParameterNullNonNullCombinations")
- public void set_projectVersion_when_it_is_set_on_later_analysis(@Nullable String previousAnalysisProjectVersion) {
- String scannerProjectVersion = randomAlphabetic(12);
- setAnalysisMetadataHolder();
- reportReader.setMetadata(createReportMetadata(scannerProjectVersion, NO_SCANNER_BUILD_STRING));
- ComponentDto project = insertComponent(newPrivateProjectDto("ABCD").setKey(REPORT_PROJECT_KEY));
- insertSnapshot(newAnalysis(project).setProjectVersion(previousAnalysisProjectVersion).setLast(true));
- reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY));
-
- underTest.execute(new TestComputationStepContext());
-
- assertThat(treeRootHolder.getReportTreeRoot().getProjectAttributes().getProjectVersion())
- .isEqualTo(scannerProjectVersion);
- }
-
- @Test
- @UseDataProvider("oneParameterNullNonNullCombinations")
- public void set_buildString(@Nullable String buildString) {
- String projectVersion = randomAlphabetic(7);
- setAnalysisMetadataHolder();
- reportReader.setMetadata(createReportMetadata(projectVersion, buildString));
- reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY));
-
- underTest.execute(new TestComputationStepContext());
-
- assertThat(treeRootHolder.getReportTreeRoot().getProjectAttributes().getBuildString()).isEqualTo(Optional.ofNullable(buildString));
- }
-
- @DataProvider
- public static Object[][] oneParameterNullNonNullCombinations() {
- return new Object[][] {
- {null},
- {randomAlphabetic(7)}
- };
- }
-
- private void verifyComponent(Component component, Component.Type type, @Nullable Integer componentRef, int size) {
- assertThat(component.getType()).isEqualTo(type);
- assertThat(component.getReportAttributes().getRef()).isEqualTo(componentRef);
- assertThat(component.getChildren()).hasSize(size);
- }
-
- private void verifyComponentByRef(int ref, String key, String shortName) {
- verifyComponentByRef(ref, key, shortName, null);
- }
-
- private void verifyComponentByKey(String key, String shortName) {
- verifyComponentByKey(key, shortName, null);
- }
-
- private void verifyComponentByKey(String key, String shortName, @Nullable String uuid) {
- Map<String, Component> componentsByKey = indexAllComponentsInTreeByKey(treeRootHolder.getRoot());
- Component component = componentsByKey.get(key);
- assertThat(component.getKey()).isEqualTo(key);
- assertThat(component.getReportAttributes().getRef()).isNull();
- assertThat(component.getShortName()).isEqualTo(shortName);
- if (uuid != null) {
- assertThat(component.getUuid()).isEqualTo(uuid);
- } else {
- assertThat(component.getUuid()).isNotNull();
- }
- }
-
- private void verifyComponentByRef(int ref, String key, String shortName, @Nullable String uuid) {
- Map<Integer, Component> componentsByRef = indexAllComponentsInTreeByRef(treeRootHolder.getRoot());
- Component component = componentsByRef.get(ref);
- assertThat(component.getKey()).isEqualTo(key);
- assertThat(component.getShortName()).isEqualTo(shortName);
- if (uuid != null) {
- assertThat(component.getUuid()).isEqualTo(uuid);
- } else {
- assertThat(component.getUuid()).isNotNull();
- }
- }
-
- private static ScannerReport.Component component(int componentRef, ComponentType componentType, String key, int... children) {
- return component(componentRef, componentType, key, FileStatus.CHANGED, null, children);
- }
-
- private static ScannerReport.Component unchangedComponentWithPath(int componentRef, ComponentType componentType, String path, int... children) {
- return component(componentRef, componentType, REPORT_PROJECT_KEY + ":" + path, FileStatus.SAME, path, children);
- }
-
- private static ScannerReport.Component componentWithPath(int componentRef, ComponentType componentType, String path, int... children) {
- return component(componentRef, componentType, REPORT_PROJECT_KEY + ":" + path, FileStatus.CHANGED, path, children);
- }
-
- private static ScannerReport.Component component(int componentRef, ComponentType componentType, String key, FileStatus status, @Nullable String path, int... children) {
- ScannerReport.Component.Builder builder = ScannerReport.Component.newBuilder()
- .setType(componentType)
- .setRef(componentRef)
- .setName(key)
- .setStatus(status)
- .setLines(1)
- .setKey(key);
- if (path != null) {
- builder.setProjectRelativePath(path);
- }
- for (int child : children) {
- builder.addChildRef(child);
- }
- return builder.build();
- }
-
- private static Map<Integer, Component> indexAllComponentsInTreeByRef(Component root) {
- Map<Integer, Component> componentsByRef = new HashMap<>();
- feedComponentByRef(root, componentsByRef);
- return componentsByRef;
- }
-
- private static Map<String, Component> indexAllComponentsInTreeByKey(Component root) {
- Map<String, Component> componentsByKey = new HashMap<>();
- feedComponentByKey(root, componentsByKey);
- return componentsByKey;
- }
-
- private static void feedComponentByKey(Component component, Map<String, Component> map) {
- map.put(component.getKey(), component);
- for (Component child : component.getChildren()) {
- feedComponentByKey(child, map);
- }
- }
-
- private static void feedComponentByRef(Component component, Map<Integer, Component> map) {
- if (component.getReportAttributes().getRef() != null) {
- map.put(component.getReportAttributes().getRef(), component);
- }
- for (Component child : component.getChildren()) {
- feedComponentByRef(child, map);
- }
- }
-
- private ComponentDto insertComponent(ComponentDto component) {
- return dbTester.components().insertComponent(component);
- }
-
- private SnapshotDto insertSnapshot(SnapshotDto snapshot) {
- dbClient.snapshotDao().insert(dbTester.getSession(), snapshot);
- dbTester.getSession().commit();
- return snapshot;
- }
-
- private void setAnalysisMetadataHolder() {
- setAnalysisMetadataHolder(false);
- }
-
- private void setAnalysisMetadataHolder(boolean isPr) {
- Branch branch = isPr ? new PrBranch(DEFAULT_MAIN_BRANCH_NAME) : new DefaultBranchImpl(DEFAULT_MAIN_BRANCH_NAME);
- analysisMetadataHolder.setRootComponentRef(ROOT_REF)
- .setAnalysisDate(ANALYSIS_DATE)
- .setBranch(branch)
- .setProject(Project.from(newPrivateProjectDto().setKey(REPORT_PROJECT_KEY).setName(REPORT_PROJECT_KEY)));
- }
-
- public static ScannerReport.Metadata createReportMetadata(@Nullable String projectVersion, @Nullable String buildString) {
- ScannerReport.Metadata.Builder builder = ScannerReport.Metadata.newBuilder()
- .setProjectKey(REPORT_PROJECT_KEY)
- .setRootComponentRef(ROOT_REF);
- ofNullable(projectVersion).ifPresent(builder::setProjectVersion);
- ofNullable(buildString).ifPresent(builder::setBuildString);
- return builder.build();
- }
-
- private static class PrBranch extends DefaultBranchImpl {
- public PrBranch(String branch) {
- super(branch);
- }
-
- @Override
- public BranchType getType() {
- return BranchType.PULL_REQUEST;
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.step;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.sonar.api.utils.System2;
-import org.sonar.ce.task.projectanalysis.analysis.Analysis;
-import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
-import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
-import org.sonar.ce.task.projectanalysis.component.Component;
-import org.sonar.ce.task.projectanalysis.component.FileAttributes;
-import org.sonar.ce.task.projectanalysis.component.ReportComponent;
-import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
-import org.sonar.ce.task.projectanalysis.duplication.CrossProjectDuplicationStatusHolder;
-import org.sonar.ce.task.projectanalysis.duplication.IntegrateCrossProjectDuplications;
-import org.sonar.ce.task.step.ComputationStep;
-import org.sonar.ce.task.step.TestComputationStepContext;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentTesting;
-import org.sonar.db.component.SnapshotDto;
-import org.sonar.db.component.SnapshotTesting;
-import org.sonar.db.duplication.DuplicationUnitDto;
-import org.sonar.duplications.block.Block;
-import org.sonar.duplications.block.ByteArray;
-import org.sonar.scanner.protocol.output.ScannerReport;
-
-import static java.util.Arrays.asList;
-import static java.util.Collections.singletonList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
-import static org.mockito.Mockito.when;
-import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
-import static org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT;
-
-public class LoadCrossProjectDuplicationsRepositoryStepTest {
-
-
- private static final String XOO_LANGUAGE = "xoo";
- private static final int PROJECT_REF = 1;
- private static final int FILE_REF = 2;
- private static final String CURRENT_FILE_KEY = "FILE_KEY";
-
- private static final Component CURRENT_FILE = ReportComponent.builder(FILE, FILE_REF)
- .setKey(CURRENT_FILE_KEY)
- .setFileAttributes(new FileAttributes(false, XOO_LANGUAGE, 1))
- .build();
-
- @Rule
- public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(
- ReportComponent.builder(PROJECT, PROJECT_REF)
- .addChildren(CURRENT_FILE).build());
-
- @Rule
- public BatchReportReaderRule batchReportReader = new BatchReportReaderRule();
-
- @Rule
- public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
-
- private CrossProjectDuplicationStatusHolder crossProjectDuplicationStatusHolder = mock(CrossProjectDuplicationStatusHolder.class);
-
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
- private DbClient dbClient = dbTester.getDbClient();
- private DbSession dbSession = dbTester.getSession();
- private IntegrateCrossProjectDuplications integrateCrossProjectDuplications = mock(IntegrateCrossProjectDuplications.class);
- private Analysis baseProjectAnalysis;
-
- private ComputationStep underTest = new LoadCrossProjectDuplicationsRepositoryStep(treeRootHolder, batchReportReader, analysisMetadataHolder, crossProjectDuplicationStatusHolder,
- integrateCrossProjectDuplications, dbClient);
-
- @Before
- public void setUp() {
- ComponentDto project = ComponentTesting.newPrivateProjectDto();
- dbTester.components().insertComponent(project);
- SnapshotDto projectSnapshot = SnapshotTesting.newAnalysis(project);
- dbClient.snapshotDao().insert(dbSession, projectSnapshot);
- dbSession.commit();
-
- baseProjectAnalysis = new Analysis.Builder()
- .setUuid(projectSnapshot.getUuid())
- .setCreatedAt(projectSnapshot.getCreatedAt())
- .build();
- }
-
- @Test
- public void call_compute_cpd_on_one_duplication() {
- when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(true);
- analysisMetadataHolder.setBaseAnalysis(baseProjectAnalysis);
-
- ComponentDto otherProject = createProject("OTHER_PROJECT_KEY");
- SnapshotDto otherProjectSnapshot = createProjectSnapshot(otherProject);
-
- ComponentDto otherFile = createFile("OTHER_FILE_KEY", otherProject);
-
- String hash = "a8998353e96320ec";
- DuplicationUnitDto duplicate = new DuplicationUnitDto()
- .setHash(hash)
- .setStartLine(40)
- .setEndLine(55)
- .setIndexInFile(0)
- .setAnalysisUuid(otherProjectSnapshot.getUuid())
- .setComponentUuid(otherFile.uuid());
- dbClient.duplicationDao().insert(dbSession, duplicate);
- dbSession.commit();
-
- ScannerReport.CpdTextBlock originBlock = ScannerReport.CpdTextBlock.newBuilder()
- .setHash(hash)
- .setStartLine(30)
- .setEndLine(45)
- .setStartTokenIndex(0)
- .setEndTokenIndex(10)
- .build();
- batchReportReader.putDuplicationBlocks(FILE_REF, singletonList(originBlock));
-
- underTest.execute(new TestComputationStepContext());
-
- verify(integrateCrossProjectDuplications).computeCpd(CURRENT_FILE,
- singletonList(
- new Block.Builder()
- .setResourceId(CURRENT_FILE_KEY)
- .setBlockHash(new ByteArray(hash))
- .setIndexInFile(0)
- .setLines(originBlock.getStartLine(), originBlock.getEndLine())
- .setUnit(originBlock.getStartTokenIndex(), originBlock.getEndTokenIndex())
- .build()),
- singletonList(
- new Block.Builder()
- .setResourceId(otherFile.getKey())
- .setBlockHash(new ByteArray(hash))
- .setIndexInFile(duplicate.getIndexInFile())
- .setLines(duplicate.getStartLine(), duplicate.getEndLine())
- .build()));
- }
-
- @Test
- public void call_compute_cpd_on_many_duplication() {
- when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(true);
- analysisMetadataHolder.setBaseAnalysis(baseProjectAnalysis);
-
- ComponentDto otherProject = createProject("OTHER_PROJECT_KEY");
- SnapshotDto otherProjectSnapshot = createProjectSnapshot(otherProject);
-
- ComponentDto otherFile = createFile("OTHER_FILE_KEY", otherProject);
-
- ScannerReport.CpdTextBlock originBlock1 = ScannerReport.CpdTextBlock.newBuilder()
- .setHash("a8998353e96320ec")
- .setStartLine(30)
- .setEndLine(45)
- .setStartTokenIndex(0)
- .setEndTokenIndex(10)
- .build();
- ScannerReport.CpdTextBlock originBlock2 = ScannerReport.CpdTextBlock.newBuilder()
- .setHash("b1234353e96320ff")
- .setStartLine(10)
- .setEndLine(25)
- .setStartTokenIndex(5)
- .setEndTokenIndex(15)
- .build();
- batchReportReader.putDuplicationBlocks(FILE_REF, asList(originBlock1, originBlock2));
-
- DuplicationUnitDto duplicate1 = new DuplicationUnitDto()
- .setHash(originBlock1.getHash())
- .setStartLine(40)
- .setEndLine(55)
- .setIndexInFile(0)
- .setAnalysisUuid(otherProjectSnapshot.getUuid())
- .setComponentUuid(otherFile.uuid());
-
- DuplicationUnitDto duplicate2 = new DuplicationUnitDto()
- .setHash(originBlock2.getHash())
- .setStartLine(20)
- .setEndLine(35)
- .setIndexInFile(1)
- .setAnalysisUuid(otherProjectSnapshot.getUuid())
- .setComponentUuid(otherFile.uuid());
- dbClient.duplicationDao().insert(dbSession, duplicate1);
- dbClient.duplicationDao().insert(dbSession, duplicate2);
- dbSession.commit();
-
- underTest.execute(new TestComputationStepContext());
-
- ArgumentCaptor<List<Block>> originBlocks = ArgumentCaptor.forClass(List.class);
- ArgumentCaptor<List<Block>> duplicationBlocks = ArgumentCaptor.forClass(List.class);
-
- verify(integrateCrossProjectDuplications).computeCpd(eq(CURRENT_FILE), originBlocks.capture(), duplicationBlocks.capture());
-
- Map<Integer, Block> originBlocksByIndex = blocksByIndexInFile(originBlocks.getValue());
- assertThat(originBlocksByIndex).containsExactly(
- Map.entry(0, new Block.Builder()
- .setResourceId(CURRENT_FILE_KEY)
- .setBlockHash(new ByteArray(originBlock1.getHash()))
- .setIndexInFile(0)
- .setLines(originBlock1.getStartLine(), originBlock1.getEndLine())
- .setUnit(originBlock1.getStartTokenIndex(), originBlock1.getEndTokenIndex())
- .build()),
- Map.entry(1, new Block.Builder()
- .setResourceId(CURRENT_FILE_KEY)
- .setBlockHash(new ByteArray(originBlock2.getHash()))
- .setIndexInFile(1)
- .setLines(originBlock2.getStartLine(), originBlock2.getEndLine())
- .setUnit(originBlock2.getStartTokenIndex(), originBlock2.getEndTokenIndex())
- .build()));
-
- Map<Integer, Block> duplicationBlocksByIndex = blocksByIndexInFile(duplicationBlocks.getValue());
- assertThat(duplicationBlocksByIndex).containsExactly(
- Map.entry(0, new Block.Builder()
- .setResourceId(otherFile.getKey())
- .setBlockHash(new ByteArray(originBlock1.getHash()))
- .setIndexInFile(duplicate1.getIndexInFile())
- .setLines(duplicate1.getStartLine(), duplicate1.getEndLine())
- .build()),
- Map.entry(1, new Block.Builder()
- .setResourceId(otherFile.getKey())
- .setBlockHash(new ByteArray(originBlock2.getHash()))
- .setIndexInFile(duplicate2.getIndexInFile())
- .setLines(duplicate2.getStartLine(), duplicate2.getEndLine())
- .build()));
- }
-
- @Test
- public void nothing_to_do_when_cross_project_duplication_is_disabled() {
- when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(false);
- analysisMetadataHolder.setBaseAnalysis(baseProjectAnalysis);
-
- ComponentDto otherProject = createProject("OTHER_PROJECT_KEY");
- SnapshotDto otherProjectSnapshot = createProjectSnapshot(otherProject);
-
- ComponentDto otherFIle = createFile("OTHER_FILE_KEY", otherProject);
-
- String hash = "a8998353e96320ec";
- DuplicationUnitDto duplicate = new DuplicationUnitDto()
- .setHash(hash)
- .setStartLine(40)
- .setEndLine(55)
- .setIndexInFile(0)
- .setAnalysisUuid(otherProjectSnapshot.getUuid())
- .setComponentUuid(otherFIle.uuid());
- dbClient.duplicationDao().insert(dbSession, duplicate);
- dbSession.commit();
-
- ScannerReport.CpdTextBlock originBlock = ScannerReport.CpdTextBlock.newBuilder()
- .setHash(hash)
- .setStartLine(30)
- .setEndLine(45)
- .setStartTokenIndex(0)
- .setEndTokenIndex(10)
- .build();
- batchReportReader.putDuplicationBlocks(FILE_REF, singletonList(originBlock));
-
- underTest.execute(new TestComputationStepContext());
-
- verifyNoInteractions(integrateCrossProjectDuplications);
- }
-
- @Test
- public void nothing_to_do_when_no_cpd_text_blocks_found() {
- when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(true);
- analysisMetadataHolder.setBaseAnalysis(baseProjectAnalysis);
-
- batchReportReader.putDuplicationBlocks(FILE_REF, Collections.emptyList());
-
- underTest.execute(new TestComputationStepContext());
-
- verifyNoInteractions(integrateCrossProjectDuplications);
- }
-
- @Test
- public void nothing_to_do_when_cpd_text_blocks_exists_but_no_duplicated_found() {
- when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(true);
- analysisMetadataHolder.setBaseAnalysis(baseProjectAnalysis);
-
- ScannerReport.CpdTextBlock originBlock = ScannerReport.CpdTextBlock.newBuilder()
- .setHash("a8998353e96320ec")
- .setStartLine(30)
- .setEndLine(45)
- .setStartTokenIndex(0)
- .setEndTokenIndex(10)
- .build();
- batchReportReader.putDuplicationBlocks(FILE_REF, singletonList(originBlock));
-
- underTest.execute(new TestComputationStepContext());
-
- verifyNoInteractions(integrateCrossProjectDuplications);
- }
-
- private ComponentDto createProject(String projectKey) {
- ComponentDto project = ComponentTesting.newPrivateProjectDto().setKey(projectKey);
- return dbTester.components().insertComponent(project);
- }
-
- private SnapshotDto createProjectSnapshot(ComponentDto project) {
- SnapshotDto projectSnapshot = SnapshotTesting.newAnalysis(project);
- dbClient.snapshotDao().insert(dbSession, projectSnapshot);
- dbSession.commit();
- return projectSnapshot;
- }
-
- private ComponentDto createFile(String fileKey, ComponentDto project) {
- ComponentDto file = ComponentTesting.newFileDto(project, null)
- .setKey(fileKey)
- .setLanguage(XOO_LANGUAGE);
- dbClient.componentDao().insert(dbSession, file);
- dbSession.commit();
- return file;
- }
-
- private static Map<Integer, Block> blocksByIndexInFile(List<Block> blocks) {
- Map<Integer, Block> blocksByIndexInFile = new HashMap<>();
- for (Block block : blocks) {
- blocksByIndexInFile.put(block.getIndexInFile(), block);
- }
- return blocksByIndexInFile;
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.step;
-
-import com.google.common.collect.ImmutableList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Optional;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.System2;
-import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
-import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
-import org.sonar.ce.task.step.TestComputationStepContext;
-import org.sonar.core.util.CloseableIterator;
-import org.sonar.core.util.UuidFactoryFast;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.AnalysisPropertyDto;
-import org.sonar.scanner.protocol.output.ScannerReport;
-
-import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.tuple;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class PersistAnalysisPropertiesStepTest {
- private static final String SNAPSHOT_UUID = randomAlphanumeric(40);
- private static final String SMALL_VALUE1 = randomAlphanumeric(50);
- private static final String SMALL_VALUE2 = randomAlphanumeric(50);
- private static final String SMALL_VALUE3 = randomAlphanumeric(50);
- private static final String BIG_VALUE = randomAlphanumeric(5000);
- private static final String VALUE_PREFIX_FOR_PR_PROPERTIES = "pr_";
- private static final List<ScannerReport.ContextProperty> PROPERTIES = Arrays.asList(
- newContextProperty("key1", "value1"),
- newContextProperty("key2", "value1"),
- newContextProperty("sonar.analysis", SMALL_VALUE1),
- newContextProperty("sonar.analysis.branch", SMALL_VALUE2),
- newContextProperty("sonar.analysis.empty_string", ""),
- newContextProperty("sonar.analysis.big_value", BIG_VALUE),
- newContextProperty("sonar.analysis.", SMALL_VALUE3),
- newContextProperty("sonar.pullrequest", VALUE_PREFIX_FOR_PR_PROPERTIES + SMALL_VALUE1),
- newContextProperty("sonar.pullrequest.branch", VALUE_PREFIX_FOR_PR_PROPERTIES + SMALL_VALUE2),
- newContextProperty("sonar.pullrequest.empty_string", ""),
- newContextProperty("sonar.pullrequest.big_value", VALUE_PREFIX_FOR_PR_PROPERTIES + BIG_VALUE),
- newContextProperty("sonar.pullrequest.", VALUE_PREFIX_FOR_PR_PROPERTIES + SMALL_VALUE3));
- private static final String SCM_REV_ID = "sha1";
-
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
- private BatchReportReader batchReportReader = mock(BatchReportReader.class);
- private AnalysisMetadataHolder analysisMetadataHolder = mock(AnalysisMetadataHolder.class);
- private PersistAnalysisPropertiesStep underTest = new PersistAnalysisPropertiesStep(dbTester.getDbClient(), analysisMetadataHolder, batchReportReader,
- UuidFactoryFast.getInstance());
-
- @Test
- public void persist_should_stores_sonarDotAnalysisDot_and_sonarDotPullRequestDot_properties() {
- when(batchReportReader.readContextProperties()).thenReturn(CloseableIterator.from(PROPERTIES.iterator()));
- when(analysisMetadataHolder.getUuid()).thenReturn(SNAPSHOT_UUID);
- when(analysisMetadataHolder.getScmRevision()).thenReturn(Optional.of(SCM_REV_ID));
-
- underTest.execute(new TestComputationStepContext());
-
- assertThat(dbTester.countRowsOfTable("analysis_properties")).isEqualTo(8);
- List<AnalysisPropertyDto> propertyDtos = dbTester.getDbClient()
- .analysisPropertiesDao().selectByAnalysisUuid(dbTester.getSession(), SNAPSHOT_UUID);
-
- assertThat(propertyDtos)
- .extracting(AnalysisPropertyDto::getAnalysisUuid, AnalysisPropertyDto::getKey, AnalysisPropertyDto::getValue)
- .containsExactlyInAnyOrder(
- tuple(SNAPSHOT_UUID, "sonar.analysis.branch", SMALL_VALUE2),
- tuple(SNAPSHOT_UUID, "sonar.analysis.empty_string", ""),
- tuple(SNAPSHOT_UUID, "sonar.analysis.big_value", BIG_VALUE),
- tuple(SNAPSHOT_UUID, "sonar.analysis.", SMALL_VALUE3),
- tuple(SNAPSHOT_UUID, "sonar.pullrequest.branch", VALUE_PREFIX_FOR_PR_PROPERTIES + SMALL_VALUE2),
- tuple(SNAPSHOT_UUID, "sonar.pullrequest.empty_string", ""),
- tuple(SNAPSHOT_UUID, "sonar.pullrequest.big_value", VALUE_PREFIX_FOR_PR_PROPERTIES + BIG_VALUE),
- tuple(SNAPSHOT_UUID, "sonar.pullrequest.", VALUE_PREFIX_FOR_PR_PROPERTIES + SMALL_VALUE3));
- }
-
- @Test
- public void persist_filtering_of_properties_is_case_sensitive() {
- when(analysisMetadataHolder.getScmRevision()).thenReturn(Optional.of(SCM_REV_ID));
- when(batchReportReader.readContextProperties()).thenReturn(CloseableIterator.from(ImmutableList.of(
- newContextProperty("sonar.ANALYSIS.foo", "foo"),
- newContextProperty("sonar.anaLysis.bar", "bar"),
- newContextProperty("sonar.anaLYSIS.doo", "doh"),
- newContextProperty("sonar.PULLREQUEST.foo", "foo"),
- newContextProperty("sonar.pullRequest.bar", "bar"),
- newContextProperty("sonar.pullREQUEST.doo", "doh")).iterator()));
- when(analysisMetadataHolder.getUuid()).thenReturn(SNAPSHOT_UUID);
-
- underTest.execute(new TestComputationStepContext());
-
- assertThat(dbTester.countRowsOfTable("analysis_properties")).isZero();
- }
-
- @Test
- public void persist_should_store_nothing_if_there_are_no_context_properties() {
- when(analysisMetadataHolder.getScmRevision()).thenReturn(Optional.of(SCM_REV_ID));
- when(batchReportReader.readContextProperties()).thenReturn(CloseableIterator.emptyCloseableIterator());
- when(analysisMetadataHolder.getUuid()).thenReturn(SNAPSHOT_UUID);
-
- underTest.execute(new TestComputationStepContext());
-
- assertThat(dbTester.countRowsOfTable("analysis_properties")).isZero();
- }
-
- @Test
- public void verify_description_value() {
- assertThat(underTest.getDescription()).isEqualTo("Persist analysis properties");
- }
-
- private static ScannerReport.ContextProperty newContextProperty(String key, String value) {
- return ScannerReport.ContextProperty.newBuilder()
- .setKey(key)
- .setValue(value)
- .build();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.step;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.System2;
-import org.sonar.ce.task.projectanalysis.analysis.Analysis;
-import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
-import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
-import org.sonar.ce.task.projectanalysis.component.Component;
-import org.sonar.ce.task.projectanalysis.component.ReportComponent;
-import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
-import org.sonar.ce.task.projectanalysis.duplication.CrossProjectDuplicationStatusHolder;
-import org.sonar.ce.task.step.ComputationStep;
-import org.sonar.ce.task.step.TestComputationStepContext;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbTester;
-import org.sonar.scanner.protocol.output.ScannerReport;
-
-import static java.util.Collections.singletonList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class PersistCrossProjectDuplicationIndexStepTest {
-
- private static final int FILE_1_REF = 2;
- private static final int FILE_2_REF = 3;
- private static final String FILE_2_UUID = "file2";
-
- private static final Component FILE_1 = ReportComponent.builder(Component.Type.FILE, FILE_1_REF).build();
- private static final Component FILE_2 = ReportComponent.builder(Component.Type.FILE, FILE_2_REF)
- .setStatus(Component.Status.SAME).setUuid(FILE_2_UUID).build();
-
- private static final Component PROJECT = ReportComponent.builder(Component.Type.PROJECT, 1)
- .addChildren(FILE_1)
- .addChildren(FILE_2)
- .build();
-
- private static final ScannerReport.CpdTextBlock CPD_TEXT_BLOCK = ScannerReport.CpdTextBlock.newBuilder()
- .setHash("a8998353e96320ec")
- .setStartLine(30)
- .setEndLine(45)
- .build();
- private static final String ANALYSIS_UUID = "analysis uuid";
- private static final String BASE_ANALYSIS_UUID = "base analysis uuid";
-
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
- @Rule
- public BatchReportReaderRule reportReader = new BatchReportReaderRule();
- @Rule
- public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(PROJECT);
- @Rule
- public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
-
- private CrossProjectDuplicationStatusHolder crossProjectDuplicationStatusHolder = mock(CrossProjectDuplicationStatusHolder.class);
- private Analysis baseAnalysis = mock(Analysis.class);
- private DbClient dbClient = dbTester.getDbClient();
-
- private ComputationStep underTest;
-
- @Before
- public void setUp() {
- when(baseAnalysis.getUuid()).thenReturn(BASE_ANALYSIS_UUID);
- analysisMetadataHolder.setUuid(ANALYSIS_UUID);
- analysisMetadataHolder.setBaseAnalysis(baseAnalysis);
- underTest = new PersistCrossProjectDuplicationIndexStep(crossProjectDuplicationStatusHolder, dbClient, treeRootHolder, analysisMetadataHolder, reportReader);
- }
-
- @Test
- public void persist_cpd_text_block() {
- when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(true);
- reportReader.putDuplicationBlocks(FILE_1_REF, singletonList(CPD_TEXT_BLOCK));
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- Map<String, Object> dto = dbTester.selectFirst("select HASH, START_LINE, END_LINE, INDEX_IN_FILE, COMPONENT_UUID, ANALYSIS_UUID from duplications_index");
- assertThat(dto)
- .containsEntry("HASH", CPD_TEXT_BLOCK.getHash())
- .containsEntry("START_LINE", 30L)
- .containsEntry("END_LINE", 45L)
- .containsEntry("INDEX_IN_FILE", 0L)
- .containsEntry("COMPONENT_UUID", FILE_1.getUuid())
- .containsEntry("ANALYSIS_UUID", ANALYSIS_UUID);
- context.getStatistics().assertValue("inserts", 1);
- }
-
- @Test
- public void persist_many_cpd_text_blocks() {
- when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(true);
- reportReader.putDuplicationBlocks(FILE_1_REF, Arrays.asList(
- CPD_TEXT_BLOCK,
- ScannerReport.CpdTextBlock.newBuilder()
- .setHash("b1234353e96320ff")
- .setStartLine(20)
- .setEndLine(15)
- .build()));
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- List<Map<String, Object>> dtos = dbTester.select("select HASH, START_LINE, END_LINE, INDEX_IN_FILE, COMPONENT_UUID, ANALYSIS_UUID from duplications_index");
- assertThat(dtos).extracting("HASH").containsOnly(CPD_TEXT_BLOCK.getHash(), "b1234353e96320ff");
- assertThat(dtos).extracting("START_LINE").containsOnly(30L, 20L);
- assertThat(dtos).extracting("END_LINE").containsOnly(45L, 15L);
- assertThat(dtos).extracting("INDEX_IN_FILE").containsOnly(0L, 1L);
- assertThat(dtos).extracting("COMPONENT_UUID").containsOnly(FILE_1.getUuid());
- assertThat(dtos).extracting("ANALYSIS_UUID").containsOnly(ANALYSIS_UUID);
- context.getStatistics().assertValue("inserts", 2);
- }
-
- @Test
- public void nothing_to_persist_when_no_cpd_text_blocks_in_report() {
- when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(true);
- reportReader.putDuplicationBlocks(FILE_1_REF, Collections.emptyList());
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(dbTester.countRowsOfTable("duplications_index")).isZero();
- context.getStatistics().assertValue("inserts", 0);
- }
-
- @Test
- public void nothing_to_do_when_cross_project_duplication_is_disabled() {
- when(crossProjectDuplicationStatusHolder.isEnabled()).thenReturn(false);
- reportReader.putDuplicationBlocks(FILE_1_REF, singletonList(CPD_TEXT_BLOCK));
-
- TestComputationStepContext context = new TestComputationStepContext();
- underTest.execute(context);
-
- assertThat(dbTester.countRowsOfTable("duplications_index")).isZero();
- context.getStatistics().assertValue("inserts", null);
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.step;
-
-import com.google.common.collect.ImmutableList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.System2;
-import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
-import org.sonar.ce.task.projectanalysis.component.Component;
-import org.sonar.ce.task.projectanalysis.component.ReportComponent;
-import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
-import org.sonar.ce.task.projectanalysis.event.Event;
-import org.sonar.ce.task.projectanalysis.event.EventRepository;
-import org.sonar.ce.task.step.ComputationStep;
-import org.sonar.ce.task.step.TestComputationStepContext;
-import org.sonar.core.util.UuidFactory;
-import org.sonar.core.util.UuidFactoryImpl;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.event.EventDto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.ce.task.projectanalysis.component.Component.Type.DIRECTORY;
-import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
-import static org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT;
-import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
-import static org.sonar.db.event.EventDto.CATEGORY_ALERT;
-import static org.sonar.db.event.EventDto.CATEGORY_PROFILE;
-import static org.sonar.db.event.EventDto.CATEGORY_VERSION;
-
-public class PersistEventsStepTest extends BaseStepTest {
-
- private static final long NOW = 1225630680000L;
- private static final ReportComponent ROOT = builder(PROJECT, 1)
- .setUuid("ABCD")
- .setProjectVersion("version_1")
- .addChildren(
- builder(DIRECTORY, 2)
- .setUuid("BCDE")
- .addChildren(
- builder(DIRECTORY, 3)
- .setUuid("Q")
- .addChildren(
- builder(FILE, 4)
- .setUuid("Z")
- .build())
- .build())
- .build())
- .build();
- private static final String ANALYSIS_UUID = "uuid_1";
-
- System2 system2 = mock(System2.class);
-
- @Rule
- public DbTester dbTester = DbTester.create(system2);
- @Rule
- public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
- @Rule
- public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
-
- private Date someDate = new Date(150000000L);
-
- private EventRepository eventRepository = mock(EventRepository.class);
- private UuidFactory uuidFactory = UuidFactoryImpl.INSTANCE;
-
- private PersistEventsStep underTest;
-
- @Before
- public void setup() {
- analysisMetadataHolder.setAnalysisDate(someDate.getTime()).setUuid(ANALYSIS_UUID);
- underTest = new PersistEventsStep(dbTester.getDbClient(), system2, treeRootHolder, analysisMetadataHolder, eventRepository, uuidFactory);
- when(eventRepository.getEvents(any(Component.class))).thenReturn(Collections.emptyList());
- }
-
- @Override
- protected ComputationStep step() {
- return underTest;
- }
-
- @Test
- public void create_version_event() {
- when(system2.now()).thenReturn(NOW);
- Component project = builder(PROJECT, 1)
- .setUuid("ABCD")
- .setProjectVersion("1.0")
- .addChildren(
- builder(DIRECTORY, 2)
- .setUuid("BCDE")
- .addChildren(
- builder(DIRECTORY, 3)
- .setUuid("Q")
- .addChildren(
- builder(FILE, 4)
- .setUuid("Z")
- .build())
- .build())
- .build())
- .build();
- treeRootHolder.setRoot(project);
-
- underTest.execute(new TestComputationStepContext());
-
- assertThat(dbTester.countRowsOfTable(dbTester.getSession(), "events")).isOne();
- List<EventDto> eventDtos = dbTester.getDbClient().eventDao().selectByComponentUuid(dbTester.getSession(), ROOT.getUuid());
- assertThat(eventDtos).hasSize(1);
- EventDto eventDto = eventDtos.iterator().next();
- assertThat(eventDto.getComponentUuid()).isEqualTo(ROOT.getUuid());
- assertThat(eventDto.getName()).isEqualTo("1.0");
- assertThat(eventDto.getDescription()).isNull();
- assertThat(eventDto.getCategory()).isEqualTo(CATEGORY_VERSION);
- assertThat(eventDto.getData()).isNull();
- assertThat(eventDto.getDate()).isEqualTo(analysisMetadataHolder.getAnalysisDate());
- assertThat(eventDto.getCreatedAt()).isEqualTo(NOW);
- }
-
- @Test
- public void persist_alert_events_on_root() {
- when(system2.now()).thenReturn(NOW);
- treeRootHolder.setRoot(ROOT);
- Event alert = Event.createAlert("Failed", null, "Open issues > 0");
- when(eventRepository.getEvents(ROOT)).thenReturn(ImmutableList.of(alert));
-
- underTest.execute(new TestComputationStepContext());
-
- assertThat(dbTester.countRowsOfTable(dbTester.getSession(), "events")).isEqualTo(2);
- List<EventDto> eventDtos = dbTester.getDbClient().eventDao().selectByComponentUuid(dbTester.getSession(), ROOT.getUuid());
- assertThat(eventDtos)
- .extracting(EventDto::getCategory)
- .containsOnly(CATEGORY_ALERT, CATEGORY_VERSION);
- EventDto eventDto = eventDtos.stream().filter(t -> CATEGORY_ALERT.equals(t.getCategory())).findAny().get();
- assertThat(eventDto.getComponentUuid()).isEqualTo(ROOT.getUuid());
- assertThat(eventDto.getName()).isEqualTo(alert.getName());
- assertThat(eventDto.getDescription()).isEqualTo(alert.getDescription());
- assertThat(eventDto.getCategory()).isEqualTo(CATEGORY_ALERT);
- assertThat(eventDto.getData()).isNull();
- assertThat(eventDto.getDate()).isEqualTo(analysisMetadataHolder.getAnalysisDate());
- assertThat(eventDto.getCreatedAt()).isEqualTo(NOW);
- }
-
- @Test
- public void persist_profile_events_on_root() {
- when(system2.now()).thenReturn(NOW);
- treeRootHolder.setRoot(ROOT);
- Event profile = Event.createProfile("foo", null, "bar");
- when(eventRepository.getEvents(ROOT)).thenReturn(ImmutableList.of(profile));
-
- underTest.execute(new TestComputationStepContext());
-
- assertThat(dbTester.countRowsOfTable(dbTester.getSession(), "events")).isEqualTo(2);
- List<EventDto> eventDtos = dbTester.getDbClient().eventDao().selectByComponentUuid(dbTester.getSession(), ROOT.getUuid());
- assertThat(eventDtos)
- .extracting(EventDto::getCategory)
- .containsOnly(CATEGORY_PROFILE, CATEGORY_VERSION);
- EventDto eventDto = eventDtos.stream().filter(t -> CATEGORY_PROFILE.equals(t.getCategory())).findAny().get();
- assertThat(eventDto.getComponentUuid()).isEqualTo(ROOT.getUuid());
- assertThat(eventDto.getName()).isEqualTo(profile.getName());
- assertThat(eventDto.getDescription()).isEqualTo(profile.getDescription());
- assertThat(eventDto.getCategory()).isEqualTo(EventDto.CATEGORY_PROFILE);
- assertThat(eventDto.getData()).isNull();
- assertThat(eventDto.getDate()).isEqualTo(analysisMetadataHolder.getAnalysisDate());
- assertThat(eventDto.getCreatedAt()).isEqualTo(NOW);
- }
-
- @Test
- public void keep_one_event_by_version() {
- ComponentDto projectDto = dbTester.components().insertPublicProject();
- EventDto[] existingEvents = new EventDto[] {
- dbTester.events().insertEvent(newVersionEventDto(projectDto, 120_000_000L, "1.3-SNAPSHOT")),
- dbTester.events().insertEvent(newVersionEventDto(projectDto, 130_000_000L, "1.4")),
- dbTester.events().insertEvent(newVersionEventDto(projectDto, 140_000_000L, "1.5-SNAPSHOT"))
- };
-
- Component project = builder(PROJECT, 1)
- .setUuid(projectDto.uuid())
- .setProjectVersion("1.5-SNAPSHOT")
- .addChildren(
- builder(DIRECTORY, 2)
- .setUuid("BCDE")
- .addChildren(
- builder(DIRECTORY, 3)
- .setUuid("Q")
- .addChildren(
- builder(FILE, 4)
- .setUuid("Z")
- .build())
- .build())
- .build())
- .build();
- treeRootHolder.setRoot(project);
-
- underTest.execute(new TestComputationStepContext());
-
- assertThat(dbTester.countRowsOfTable(dbTester.getSession(), "events")).isEqualTo(3);
- List<EventDto> eventDtos = dbTester.getDbClient().eventDao().selectByComponentUuid(dbTester.getSession(), projectDto.uuid());
- assertThat(eventDtos).hasSize(3);
- assertThat(eventDtos)
- .extracting(EventDto::getName)
- .containsOnly("1.3-SNAPSHOT", "1.4", "1.5-SNAPSHOT");
- assertThat(eventDtos)
- .extracting(EventDto::getUuid)
- .contains(existingEvents[0].getUuid(), existingEvents[1].getUuid())
- .doesNotContain(existingEvents[2].getUuid());
- }
-
- private EventDto newVersionEventDto(ComponentDto project, long date, String name) {
- return new EventDto().setUuid(uuidFactory.create()).setComponentUuid(project.uuid())
- .setAnalysisUuid("analysis_uuid")
- .setCategory(CATEGORY_VERSION)
- .setName(name).setDate(date).setCreatedAt(date);
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.taskprocessor;
-
-import java.util.Optional;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.System2;
-import org.sonar.ce.task.CeTask;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.BranchDto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.sonar.db.component.BranchType.BRANCH;
-
-public class IgnoreOrphanBranchStepTest {
-
- private String BRANCH_UUID = "branch_uuid";
-
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
- private CeTask.Component component = new CeTask.Component(BRANCH_UUID, "component key", "component name");
- private CeTask ceTask = new CeTask.Builder()
- .setType("type")
- .setUuid("uuid")
- .setComponent(component)
- .setMainComponent(component)
- .build();
-
- private DbClient dbClient = dbTester.getDbClient();
- private IgnoreOrphanBranchStep underTest = new IgnoreOrphanBranchStep(ceTask, dbClient);
-
- @Test
- public void execute() {
- BranchDto branchDto = new BranchDto()
- .setBranchType(BRANCH)
- .setKey("branchName")
- .setUuid(BRANCH_UUID)
- .setProjectUuid("project_uuid")
- .setNeedIssueSync(true);
- dbClient.branchDao().insert(dbTester.getSession(), branchDto);
- dbTester.commit();
-
- underTest.execute(() -> null);
-
- Optional<BranchDto> branch = dbClient.branchDao().selectByUuid(dbTester.getSession(), BRANCH_UUID);
- assertThat(branch.get().isNeedIssueSync()).isFalse();
- assertThat(branch.get().isExcludeFromPurge()).isFalse();
- }
-
- @Test
- public void execute_on_already_indexed_branch() {
- BranchDto branchDto = new BranchDto()
- .setBranchType(BRANCH)
- .setKey("branchName")
- .setUuid(BRANCH_UUID)
- .setProjectUuid("project_uuid")
- .setNeedIssueSync(false);
- dbClient.branchDao().insert(dbTester.getSession(), branchDto);
- dbTester.commit();
-
- underTest.execute(() -> null);
-
- Optional<BranchDto> branch = dbClient.branchDao().selectByUuid(dbTester.getSession(), BRANCH_UUID);
- assertThat(branch.get().isNeedIssueSync()).isFalse();
- assertThat(branch.get().isExcludeFromPurge()).isFalse();
- }
-
- @Test
- public void fail_if_missing_main_component_in_task() {
- CeTask ceTask = new CeTask.Builder()
- .setType("type")
- .setUuid("uuid")
- .setComponent(null)
- .setMainComponent(null)
- .build();
- IgnoreOrphanBranchStep underTest = new IgnoreOrphanBranchStep(ceTask, dbClient);
-
- assertThatThrownBy(() -> underTest.execute(() -> null))
- .isInstanceOf(UnsupportedOperationException.class)
- .hasMessage("main component not found in task");
- }
-
- @Test
- public void verify_step_description() {
- assertThat(underTest.getDescription()).isEqualTo("Ignore orphan component");
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectexport.component;
-
-import com.google.common.collect.ImmutableSet;
-import com.sonarsource.governance.projectdump.protobuf.ProjectDump;
-import java.util.Date;
-import java.util.List;
-import org.junit.After;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.resources.Qualifiers;
-import org.sonar.api.resources.Scopes;
-import org.sonar.api.utils.System2;
-import org.sonar.api.utils.log.LogTester;
-import org.sonar.api.utils.log.LoggerLevel;
-import org.sonar.ce.task.projectexport.steps.DumpElement;
-import org.sonar.ce.task.projectexport.steps.FakeDumpWriter;
-import org.sonar.ce.task.projectexport.steps.ProjectHolder;
-import org.sonar.ce.task.step.TestComputationStepContext;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.ComponentDto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.assertj.core.api.Assertions.tuple;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.db.component.ComponentDto.UUID_PATH_OF_ROOT;
-import static org.sonar.db.component.ComponentDto.UUID_PATH_SEPARATOR;
-
-public class ExportComponentsStepTest {
-
- private static final String PROJECT_UUID = "PROJECT_UUID";
- private static final ComponentDto PROJECT = new ComponentDto()
- // no id yet
- .setScope(Scopes.PROJECT)
- .setQualifier(Qualifiers.PROJECT)
- .setKey("the_project")
- .setName("The Project")
- .setDescription("The project description")
- .setEnabled(true)
- .setUuid(PROJECT_UUID)
- .setUuidPath(UUID_PATH_OF_ROOT)
- .setCreatedAt(new Date(1596749115856L))
- .setBranchUuid(PROJECT_UUID);
-
- private static final String FILE_UUID = "FILE_UUID";
- private static final String FILE_UUID_PATH = PROJECT_UUID + FILE_UUID + UUID_PATH_SEPARATOR;
- private static final ComponentDto FILE = new ComponentDto()
- // no id yet
- .setScope(Scopes.FILE)
- .setQualifier(Qualifiers.FILE)
- .setKey("the_file")
- .setName("The File")
- .setUuid(FILE_UUID)
- .setUuidPath(FILE_UUID_PATH)
- .setEnabled(true)
- .setCreatedAt(new Date(1596749148406L))
- .setBranchUuid(PROJECT_UUID);
-
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
- @Rule
- public LogTester logTester = new LogTester();
-
- private final FakeDumpWriter dumpWriter = new FakeDumpWriter();
- private final ProjectHolder projectHolder = mock(ProjectHolder.class);
- private final MutableComponentRepository componentRepository = new ComponentRepositoryImpl();
- private final ExportComponentsStep underTest = new ExportComponentsStep(dbTester.getDbClient(), projectHolder, componentRepository, dumpWriter);
-
- @After
- public void tearDown() {
- dbTester.getSession().close();
- }
-
- @Test
- public void export_components_including_project() {
- dbTester.components().insertPublicProject(PROJECT);
- dbTester.getDbClient().componentDao().insert(dbTester.getSession(), FILE);
- dbTester.commit();
- when(projectHolder.projectDto()).thenReturn(dbTester.components().getProjectDto(PROJECT));
-
- underTest.execute(new TestComputationStepContext());
-
- assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("2 components exported");
- List<ProjectDump.Component> components = dumpWriter.getWrittenMessagesOf(DumpElement.COMPONENTS);
- assertThat(components).extracting(ProjectDump.Component::getQualifier, ProjectDump.Component::getUuid, ProjectDump.Component::getUuidPath)
- .containsExactlyInAnyOrder(
- tuple(Qualifiers.FILE, FILE_UUID, FILE_UUID_PATH),
- tuple(Qualifiers.PROJECT, PROJECT_UUID, UUID_PATH_OF_ROOT));
- }
-
- @Test
- public void execute_register_all_components_uuids_as_their_id_in_ComponentRepository() {
- dbTester.components().insertPublicProject(PROJECT);
- dbTester.getDbClient().componentDao().insert(dbTester.getSession(), FILE);
- dbTester.commit();
- when(projectHolder.projectDto()).thenReturn(dbTester.components().getProjectDto(PROJECT));
-
- underTest.execute(new TestComputationStepContext());
-
- assertThat(ImmutableSet.of(
- componentRepository.getRef(PROJECT.uuid()),
- componentRepository.getRef(FILE.uuid()))).containsExactlyInAnyOrder(1L, 2L);
- }
-
- @Test
- public void throws_ISE_if_error() {
- dbTester.components().insertPublicProject(PROJECT);
- dbTester.getDbClient().componentDao().insert(dbTester.getSession(), FILE);
- dbTester.commit();
- when(projectHolder.projectDto()).thenReturn(dbTester.components().getProjectDto(PROJECT));
- dumpWriter.failIfMoreThan(1, DumpElement.COMPONENTS);
-
- assertThatThrownBy(() -> underTest.execute(new TestComputationStepContext()))
- .isInstanceOf(IllegalStateException.class)
- .hasMessage("Component Export failed after processing 1 components successfully");
- }
-
- @Test
- public void getDescription_is_defined() {
- assertThat(underTest.getDescription()).isEqualTo("Export components");
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectexport.steps;
-
-import com.sonarsource.governance.projectdump.protobuf.ProjectDump;
-import java.util.List;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.System2;
-import org.sonar.api.utils.log.LogTester;
-import org.sonar.api.utils.log.LoggerLevel;
-import org.sonar.ce.task.projectexport.component.ComponentRepositoryImpl;
-import org.sonar.ce.task.step.TestComputationStepContext;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.BranchDto;
-import org.sonar.db.component.BranchType;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.SnapshotDto;
-import org.sonar.db.measure.MeasureDto;
-import org.sonar.db.metric.MetricDto;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.db.component.ComponentDto.UUID_PATH_OF_ROOT;
-import static org.sonar.db.component.ComponentDto.UUID_PATH_SEPARATOR;
-import static org.sonar.db.component.SnapshotDto.STATUS_PROCESSED;
-import static org.sonar.db.component.SnapshotDto.STATUS_UNPROCESSED;
-
-public class ExportMeasuresStepTest {
-
- private static final ComponentDto PROJECT = new ComponentDto()
- .setKey("project_key")
- .setUuid("project_uuid")
- .setBranchUuid("project_uuid")
- .setUuidPath(UUID_PATH_OF_ROOT)
- .setEnabled(true);
- private static final ComponentDto FILE = new ComponentDto()
- .setKey("file_key")
- .setUuid("file_uuid")
- .setBranchUuid("project_uuid")
- .setUuidPath(UUID_PATH_OF_ROOT + PROJECT.uuid() + UUID_PATH_SEPARATOR)
- .setEnabled(true);
- private static final ComponentDto ANOTHER_PROJECT = new ComponentDto()
- .setKey("another_project_key")
- .setUuid("another_project_uuid")
- .setBranchUuid("another_project_uuid")
- .setUuidPath(UUID_PATH_OF_ROOT)
- .setEnabled(true);
-
- private static final MetricDto NCLOC = new MetricDto()
- .setUuid("3")
- .setKey("ncloc")
- .setShortName("Lines of code")
- .setEnabled(true);
-
- private static final MetricDto DISABLED_METRIC = new MetricDto()
- .setUuid("4")
- .setKey("coverage")
- .setShortName("Coverage")
- .setEnabled(false);
-
- private static final MetricDto NEW_NCLOC = new MetricDto()
- .setUuid("5")
- .setKey("new_ncloc")
- .setShortName("New Lines of code")
- .setEnabled(true);
-
- private static final List<BranchDto> BRANCHES = newArrayList(
- new BranchDto()
- .setBranchType(BranchType.BRANCH)
- .setKey("master")
- .setUuid(PROJECT.uuid())
- .setProjectUuid(PROJECT.uuid()));
-
-
- @Rule
- public LogTester logTester = new LogTester();
-
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
- private ComponentRepositoryImpl componentRepository = new ComponentRepositoryImpl();
- private MutableMetricRepository metricRepository = new MutableMetricRepositoryImpl();
- private ProjectHolder projectHolder = mock(ProjectHolder.class);
- private FakeDumpWriter dumpWriter = new FakeDumpWriter();
- private ExportMeasuresStep underTest = new ExportMeasuresStep(dbTester.getDbClient(), projectHolder, componentRepository, metricRepository, dumpWriter);
-
- @Before
- public void setUp() {
- String projectUuid = dbTester.components().insertPublicProject(PROJECT).uuid();
- componentRepository.register(1, projectUuid, false);
- dbTester.getDbClient().componentDao().insert(dbTester.getSession(), FILE, ANOTHER_PROJECT);
- dbTester.getDbClient().metricDao().insert(dbTester.getSession(), NCLOC, DISABLED_METRIC, NEW_NCLOC);
- dbTester.commit();
- when(projectHolder.projectDto()).thenReturn(dbTester.components().getProjectDto(PROJECT));
- when(projectHolder.branches()).thenReturn(BRANCHES);
- }
-
- @Test
- public void export_zero_measures() {
- underTest.execute(new TestComputationStepContext());
-
- assertThat(dumpWriter.getWrittenMessagesOf(DumpElement.MEASURES)).isEmpty();
- assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("0 measures exported");
- assertThat(metricRepository.getRefByUuid()).isEmpty();
- }
-
- @Test
- public void export_measures() {
- SnapshotDto firstAnalysis = insertSnapshot("U_1", PROJECT, STATUS_PROCESSED);
- insertMeasure(firstAnalysis, PROJECT, new MeasureDto().setValue(100.0).setMetricUuid(NCLOC.getUuid()));
- SnapshotDto secondAnalysis = insertSnapshot("U_2", PROJECT, STATUS_PROCESSED);
- insertMeasure(secondAnalysis, PROJECT, new MeasureDto().setValue(110.0).setMetricUuid(NCLOC.getUuid()));
- SnapshotDto anotherProjectAnalysis = insertSnapshot("U_3", ANOTHER_PROJECT, STATUS_PROCESSED);
- insertMeasure(anotherProjectAnalysis, ANOTHER_PROJECT, new MeasureDto().setValue(500.0).setMetricUuid(NCLOC.getUuid()));
- dbTester.commit();
-
- underTest.execute(new TestComputationStepContext());
-
- List<ProjectDump.Measure> exportedMeasures = dumpWriter.getWrittenMessagesOf(DumpElement.MEASURES);
- assertThat(exportedMeasures).hasSize(2);
- assertThat(exportedMeasures).extracting(ProjectDump.Measure::getAnalysisUuid).containsOnly(firstAnalysis.getUuid(), secondAnalysis.getUuid());
- assertThat(exportedMeasures).extracting(ProjectDump.Measure::getMetricRef).containsOnly(0);
- assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("2 measures exported");
- assertThat(metricRepository.getRefByUuid()).containsOnlyKeys(NCLOC.getUuid());
- }
-
- @Test
- public void do_not_export_measures_on_unprocessed_snapshots() {
- SnapshotDto firstAnalysis = insertSnapshot("U_1", PROJECT, STATUS_UNPROCESSED);
- insertMeasure(firstAnalysis, PROJECT, new MeasureDto().setValue(100.0).setMetricUuid(NCLOC.getUuid()));
- dbTester.commit();
-
- underTest.execute(new TestComputationStepContext());
-
- List<ProjectDump.Measure> exportedMeasures = dumpWriter.getWrittenMessagesOf(DumpElement.MEASURES);
- assertThat(exportedMeasures).isEmpty();
- }
-
- @Test
- public void do_not_export_measures_on_disabled_metrics() {
- SnapshotDto firstAnalysis = insertSnapshot("U_1", PROJECT, STATUS_PROCESSED);
- insertMeasure(firstAnalysis, PROJECT, new MeasureDto().setValue(100.0).setMetricUuid(DISABLED_METRIC.getUuid()));
- dbTester.commit();
-
- underTest.execute(new TestComputationStepContext());
-
- List<ProjectDump.Measure> exportedMeasures = dumpWriter.getWrittenMessagesOf(DumpElement.MEASURES);
- assertThat(exportedMeasures).isEmpty();
- }
-
- @Test
- public void test_exported_fields() {
- SnapshotDto analysis = insertSnapshot("U_1", PROJECT, STATUS_PROCESSED);
- MeasureDto dto = new MeasureDto()
- .setMetricUuid(NCLOC.getUuid())
- .setValue(100.0)
- .setData("data")
- .setAlertStatus("OK")
- .setAlertText("alert text");
- insertMeasure(analysis, PROJECT, dto);
- dbTester.commit();
-
- underTest.execute(new TestComputationStepContext());
-
- List<ProjectDump.Measure> exportedMeasures = dumpWriter.getWrittenMessagesOf(DumpElement.MEASURES);
- ProjectDump.Measure measure = exportedMeasures.get(0);
- assertThat(measure.getAlertStatus()).isEqualTo(dto.getAlertStatus());
- assertThat(measure.getAlertText()).isEqualTo(dto.getAlertText());
- assertThat(measure.getDoubleValue().getValue()).isEqualTo(dto.getValue());
- assertThat(measure.getTextValue()).isEqualTo(dto.getData());
- assertThat(measure.getMetricRef()).isZero();
- assertThat(measure.getAnalysisUuid()).isEqualTo(analysis.getUuid());
- assertThat(measure.getVariation1().getValue()).isZero();
- }
-
- @Test
- public void test_exported_fields_new_metric() {
- SnapshotDto analysis = insertSnapshot("U_1", PROJECT, STATUS_PROCESSED);
- MeasureDto dto = new MeasureDto()
- .setMetricUuid(NEW_NCLOC.getUuid())
- .setValue(100.0)
- .setData("data")
- .setAlertStatus("OK")
- .setAlertText("alert text");
- insertMeasure(analysis, PROJECT, dto);
- dbTester.commit();
-
- underTest.execute(new TestComputationStepContext());
-
- List<ProjectDump.Measure> exportedMeasures = dumpWriter.getWrittenMessagesOf(DumpElement.MEASURES);
- ProjectDump.Measure measure = exportedMeasures.get(0);
- assertThat(measure.getAlertStatus()).isEqualTo(dto.getAlertStatus());
- assertThat(measure.getAlertText()).isEqualTo(dto.getAlertText());
- assertThat(measure.getDoubleValue().getValue()).isZero();
- assertThat(measure.getTextValue()).isEqualTo(dto.getData());
- assertThat(measure.getMetricRef()).isZero();
- assertThat(measure.getAnalysisUuid()).isEqualTo(analysis.getUuid());
- assertThat(measure.getVariation1().getValue()).isEqualTo(dto.getValue());
- }
-
- @Test
- public void test_null_exported_fields() {
- SnapshotDto analysis = insertSnapshot("U_1", PROJECT, STATUS_PROCESSED);
- insertMeasure(analysis, PROJECT, new MeasureDto().setMetricUuid(NCLOC.getUuid()));
- dbTester.commit();
-
- underTest.execute(new TestComputationStepContext());
-
- ProjectDump.Measure measure = dumpWriter.getWrittenMessagesOf(DumpElement.MEASURES).get(0);
- assertThat(measure.getAlertStatus()).isEmpty();
- assertThat(measure.getAlertText()).isEmpty();
- assertThat(measure.hasDoubleValue()).isFalse();
- assertThat(measure.getTextValue()).isEmpty();
- assertThat(measure.hasVariation1()).isFalse();
- }
-
- @Test
- public void test_getDescription() {
- assertThat(underTest.getDescription()).isEqualTo("Export measures");
- }
-
- private SnapshotDto insertSnapshot(String snapshotUuid, ComponentDto project, String status) {
- SnapshotDto snapshot = new SnapshotDto()
- .setUuid(snapshotUuid)
- .setComponentUuid(project.uuid())
- .setStatus(status)
- .setLast(true);
- dbTester.getDbClient().snapshotDao().insert(dbTester.getSession(), snapshot);
- return snapshot;
- }
-
- private void insertMeasure(SnapshotDto analysisDto, ComponentDto componentDto, MeasureDto measureDto) {
- measureDto
- .setAnalysisUuid(analysisDto.getUuid())
- .setComponentUuid(componentDto.uuid());
- dbTester.getDbClient().measureDao().insert(dbTester.getSession(), measureDto);
- }
-}
+++ /dev/null
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2016 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-#
-# SonarQube 6.0
-#
-class AddComponentUuidToDuplicationsIndex < ActiveRecord::Migration
-
- def self.up
- execute_java_migration('org.sonar.db.version.v60.AddComponentUuidColumnToDuplicationsIndex')
- end
-end
+++ /dev/null
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2016 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-#
-# SonarQube 6.0
-#
-class PopulateComponentUuidOfDuplicationsIndex < ActiveRecord::Migration
-
- def self.up
- execute_java_migration('org.sonar.db.version.v60.PopulateComponentUuidOfDuplicationsIndex')
- end
-end
+++ /dev/null
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2016 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-#
-# SonarQube 6.0
-#
-class DeleteOrphanDuplicationsIndexRowsWithoutComponent < ActiveRecord::Migration
-
- def self.up
- execute_java_migration('org.sonar.db.version.v60.DeleteOrphanDuplicationsIndexRowsWithoutComponent')
- end
-end
+++ /dev/null
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2016 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-#
-# SonarQube 6.0
-#
-class MakeComponentUuidNotNullOnDuplicationsIndex < ActiveRecord::Migration
-
- def self.up
- execute_java_migration('org.sonar.db.version.v60.MakeComponentUuidNotNullOnDuplicationsIndex')
- end
-end
+++ /dev/null
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2016 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-#
-# SonarQube 6.0
-#
-class AddAnalysisUuidToDuplicationsIndex < ActiveRecord::Migration
-
- def self.up
- execute_java_migration('org.sonar.db.version.v60.AddAnalysisUuidColumnToDuplicationsIndex')
- end
-end
+++ /dev/null
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2016 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-#
-# SonarQube 6.0
-#
-class PopulateAnalysisUuidOfDuplicationsIndex < ActiveRecord::Migration
-
- def self.up
- execute_java_migration('org.sonar.db.version.v60.PopulateAnalysisUuidOfDuplicationsIndex')
- end
-end
+++ /dev/null
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2016 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-#
-# SonarQube 6.0
-#
-class DeleteOrphanDuplicationsIndexRowsWithoutAnalysis < ActiveRecord::Migration
-
- def self.up
- execute_java_migration('org.sonar.db.version.v60.DeleteOrphanDuplicationsIndexRowsWithoutAnalysis')
- end
-end
+++ /dev/null
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2016 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-#
-# SonarQube 6.0
-#
-class MakeAnalysisUuidNotNullOnDuplicationsIndex < ActiveRecord::Migration
-
- def self.up
- execute_java_migration('org.sonar.db.version.v60.MakeAnalysisUuidNotNullOnDuplicationsIndex')
-
- add_index :duplications_index, [:analysis_uuid, :component_uuid], :name => 'duplication_analysis_component'
- end
-end
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.filemove.FileMoveDetectionStepTest.v1;
-
-import java.sql.SQLException;
-import org.sonar.db.Database;
-import org.sonar.db.version.AddColumnsBuilder;
-
-import static org.sonar.db.version.VarcharColumnDef.UUID_VARCHAR_SIZE;
-import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder;
-
-public class AddAnalysisUuidColumnToDuplicationsIndex extends DdlChange {
-
- private static final String TABLE_DUPLICATIONS_INDEX = "duplications_index";
-
- public AddAnalysisUuidColumnToDuplicationsIndex(Database db) {
- super(db);
- }
-
- @Override
- public void execute(Context context) throws SQLException {
- context.execute(new AddColumnsBuilder(getDatabase().getDialect(), TABLE_DUPLICATIONS_INDEX)
- .addColumn(newVarcharColumnDefBuilder().setColumnName("analysis_uuid").setLimit(UUID_VARCHAR_SIZE).setIsNullable(true).build())
- .build());
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.filemove.FileMoveDetectionStepTest.v1;
-
-import java.sql.SQLException;
-import org.sonar.db.Database;
-import org.sonar.db.version.AddColumnsBuilder;
-
-import static org.sonar.db.version.VarcharColumnDef.UUID_VARCHAR_SIZE;
-import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder;
-
-public class AddComponentUuidAndAnalysisUuidColumnToDuplicationsIndex extends DdlChange {
-
- private static final String TABLE_PUBLICATIONS_INDEX = "duplications_index";
-
- public AddComponentUuidAndAnalysisUuidColumnToDuplicationsIndex(Database db) {
- super(db);
- }
-
- @Override
- public void execute(Context context) throws SQLException {
- context.execute(new AddColumnsBuilder(getDatabase().getDialect(), TABLE_PUBLICATIONS_INDEX)
- .addColumn(newVarcharColumnDefBuilder().setColumnName("component_uuid").setLimit(UUID_VARCHAR_SIZE).setIsNullable(true).build())
- .addColumn(newVarcharColumnDefBuilder().setColumnName("analysis_uuid").setLimit(UUID_VARCHAR_SIZE).setIsNullable(true).build())
- .build());
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.filemove.FileMoveDetectionStepTest.v1;
-
-import java.sql.SQLException;
-import org.sonar.db.Database;
-import org.sonar.server.platform.db.migration.step.MassUpdate;
-
-public class DeleteOrphanDuplicationsIndexRowsWithoutComponent extends BaseDataChange {
-
- public DeleteOrphanDuplicationsIndexRowsWithoutComponent(Database db) {
- super(db);
- }
-
- @Override
- public void execute(Context context) throws SQLException {
- MassUpdate massUpdate = context.prepareMassUpdate();
- massUpdate.select("SELECT id from duplications_index where component_uuid is null");
- massUpdate.update("DELETE from duplications_index WHERE id=?");
- massUpdate.rowPluralName("resources_index entries");
- massUpdate.execute((row, update) -> {
- update.setLong(1, row.getLong(1));
- return true;
- });
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.filemove.FileMoveDetectionStepTest.v1;
-
-import java.sql.SQLException;
-import org.sonar.db.Database;
-import org.sonar.db.version.AlterColumnsBuilder;
-
-import static org.sonar.db.version.VarcharColumnDef.UUID_VARCHAR_SIZE;
-import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder;
-
-public class MakeComponentUuidNotNullOnDuplicationsIndex extends DdlChange {
-
- private static final String TABLE_DUPLICATIONS_INDEX = "duplications_index";
-
- public MakeComponentUuidNotNullOnDuplicationsIndex(Database db) {
- super(db);
- }
-
- @Override
- public void execute(Context context) throws SQLException {
- context.execute(new AlterColumnsBuilder(getDatabase().getDialect(), TABLE_DUPLICATIONS_INDEX)
- .updateColumn(newVarcharColumnDefBuilder().setColumnName("component_uuid").setLimit(UUID_VARCHAR_SIZE).setIsNullable(false).build())
- .build());
- }
-
-}
+++ /dev/null
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2016 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-#
-# SonarQube 6.0
-#
-class AddComponentUuidAndAnalysisUuidToDuplicationsIndex < ActiveRecord::Migration
-
- def self.up
- execute_java_migration('org.sonar.db.version.v60.AddComponentUuidAndAnalysisUuidColumnToDuplicationsIndex')
- end
-end
+++ /dev/null
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2016 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-#
-# SonarQube 6.0
-#
-class PopulateComponentUuidAndAnalysisUuidOfDuplicationsIndex < ActiveRecord::Migration
-
- def self.up
- execute_java_migration('org.sonar.db.version.v60.PopulateComponentUuidAndAnalysisUuidOfDuplicationsIndex')
- end
-end
+++ /dev/null
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2016 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-#
-# SonarQube 6.0
-#
-class DeleteOrphanDuplicationsIndexRowsWithoutComponentOrAnalysis < ActiveRecord::Migration
-
- def self.up
- execute_java_migration('org.sonar.db.version.v60.DeleteOrphanDuplicationsIndexRowsWithoutComponentOrAnalysis')
- end
-end
+++ /dev/null
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2016 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-#
-# SonarQube 6.0
-#
-class MakeComponentUuidAndAnalysisUuidNotNullOnDuplicationsIndex < ActiveRecord::Migration
-
- def self.up
- execute_java_migration('org.sonar.db.version.v60.MakeComponentUuidAndAnalysisUuidNotNullOnDuplicationsIndex')
-
- add_index :duplications_index, [:analysis_uuid, :component_uuid], :name => 'duplication_analysis_component'
- end
-end
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.filemove.FileMoveDetectionStepTest.v2;
-
-import java.sql.SQLException;
-import org.sonar.db.Database;
-import org.sonar.db.version.AddColumnsBuilder;
-
-import static org.sonar.db.version.VarcharColumnDef.UUID_VARCHAR_SIZE;
-import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder;
-
-public class AddComponentUuidColumnToDuplicationsIndex extends DdlChange {
-
- private static final String TABLE_PUBLICATIONS_INDEX = "duplications_index";
-
- public AddComponentUuidColumnToDuplicationsIndex(Database db) {
- super(db);
- }
-
- @Override
- public void execute(Context context) throws SQLException {
- context.execute(new AddColumnsBuilder(getDatabase().getDialect(), TABLE_PUBLICATIONS_INDEX)
- .addColumn(newVarcharColumnDefBuilder().setColumnName("component_uuid").setLimit(UUID_VARCHAR_SIZE).setIsNullable(true).build())
- .build());
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.ce.task.projectanalysis.filemove.FileMoveDetectionStepTest.v2;
-
-import java.sql.SQLException;
-import org.sonar.db.Database;
-import org.sonar.db.version.AlterColumnsBuilder;
-
-import static org.sonar.db.version.VarcharColumnDef.UUID_VARCHAR_SIZE;
-import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder;
-
-public class MakeComponentUuidAndAnalysisUuidNotNullOnDuplicationsIndex extends DdlChange {
-
- private static final String TABLE_DUPLICATIONS_INDEX = "duplications_index";
-
- public MakeComponentUuidAndAnalysisUuidNotNullOnDuplicationsIndex(Database db) {
- super(db);
- }
-
- @Override
- public void execute(Context context) throws SQLException {
- context.execute(new AlterColumnsBuilder(getDatabase().getDialect(), TABLE_DUPLICATIONS_INDEX)
- .updateColumn(newVarcharColumnDefBuilder().setColumnName("component_uuid").setLimit(UUID_VARCHAR_SIZE).setIsNullable(false).build())
- .updateColumn(newVarcharColumnDefBuilder().setColumnName("analysis_uuid").setLimit(UUID_VARCHAR_SIZE).setIsNullable(false).build())
- .build());
- }
-
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.plugins;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.Plugin;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.core.plugin.PluginType;
+import org.sonar.db.DbTester;
+import org.sonar.db.plugin.PluginDto;
+import org.sonar.server.plugins.PluginFilesAndMd5.FileAndMd5;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DetectPluginChangeIT {
+ @Rule
+ public DbTester dbTester = DbTester.create();
+
+ private final ServerPluginRepository pluginRepository = new ServerPluginRepository();
+ private final DetectPluginChange detectPluginChange = new DetectPluginChange(pluginRepository, dbTester.getDbClient());
+
+ @Test
+ public void detect_changed_plugin() {
+ addPluginToDb("plugin1", "hash1", PluginDto.Type.BUNDLED);
+ addPluginToFs("plugin1", "hash2", PluginType.BUNDLED);
+
+ detectPluginChange.start();
+ assertThat(detectPluginChange.anyPluginChanged()).isTrue();
+ }
+
+ @Test
+ public void detect_changed_type() {
+ addPluginToDb("plugin1", "hash1", PluginDto.Type.BUNDLED);
+ addPluginToFs("plugin1", "hash1", PluginType.EXTERNAL);
+
+ detectPluginChange.start();
+ assertThat(detectPluginChange.anyPluginChanged()).isTrue();
+ }
+
+ @Test
+ public void detect_new_plugin() {
+ addPluginToDb("plugin1", "hash1", PluginDto.Type.BUNDLED);
+ addPluginToFs("plugin2", "hash1", PluginType.BUNDLED);
+
+ detectPluginChange.start();
+ assertThat(detectPluginChange.anyPluginChanged()).isTrue();
+ }
+
+ @Test
+ public void detect_removed_plugin() {
+ addPluginToDb("plugin1", "hash1", PluginDto.Type.BUNDLED, true);
+ addPluginToFs("plugin1", "hash1", PluginType.BUNDLED);
+
+ detectPluginChange.start();
+ assertThat(detectPluginChange.anyPluginChanged()).isTrue();
+ }
+
+ @Test
+ public void detect_missing_plugin() {
+ addPluginToDb("plugin1", "hash1", PluginDto.Type.BUNDLED);
+
+ detectPluginChange.start();
+ assertThat(detectPluginChange.anyPluginChanged()).isTrue();
+ }
+
+ @Test
+ public void detect_no_changes() {
+ addPluginToDb("plugin1", "hash1", PluginDto.Type.BUNDLED);
+ addPluginToFs("plugin1", "hash1", PluginType.BUNDLED);
+ addPluginToDb("plugin2", "hash2", PluginDto.Type.EXTERNAL);
+ addPluginToFs("plugin2", "hash2", PluginType.EXTERNAL);
+
+ detectPluginChange.start();
+ assertThat(detectPluginChange.anyPluginChanged()).isFalse();
+ }
+
+ @Test
+ public void fail_if_start_twice() {
+ detectPluginChange.start();
+ assertThrows(IllegalStateException.class, detectPluginChange::start);
+ }
+
+ @Test
+ public void fail_if_not_started() {
+ assertThrows(NullPointerException.class, detectPluginChange::anyPluginChanged);
+ }
+
+ private void addPluginToDb(String key, String hash, PluginDto.Type type) {
+ addPluginToDb(key, hash, type, false);
+ }
+
+ private void addPluginToDb(String key, String hash, PluginDto.Type type, boolean removed) {
+ dbTester.pluginDbTester().insertPlugin(p -> p.setKee(key).setFileHash(hash).setType(type).setRemoved(removed));
+ }
+
+ private void addPluginToFs(String key, String hash, PluginType type) {
+ PluginInfo pluginInfo = new PluginInfo(key);
+ Plugin plugin = mock(Plugin.class);
+ FileAndMd5 fileAndMd5 = mock(FileAndMd5.class);
+ when(fileAndMd5.getMd5()).thenReturn(hash);
+ ServerPlugin serverPlugin = new ServerPlugin(pluginInfo, type, plugin, fileAndMd5, null);
+ pluginRepository.addPlugin(serverPlugin);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.rule;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Consumer;
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleQuery;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.rule.RuleDao;
+import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.RuleParamDto;
+import org.sonar.db.rule.RuleTesting;
+
+import static java.util.stream.Collectors.toList;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class CachingRuleFinderIT {
+ @org.junit.Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private final DbClient dbClient = dbTester.getDbClient();
+ private final AlwaysIncreasingSystem2 system2 = new AlwaysIncreasingSystem2();
+ private RuleDto[] ruleDtos;
+ private RuleParamDto[] ruleParams;
+ private CachingRuleFinder underTest;
+ private RuleDescriptionFormatter ruleDescriptionFormatter = new RuleDescriptionFormatter();
+
+ @Before()
+ public void setUp() {
+ Consumer<RuleDto> setUpdatedAt = rule -> rule.setUpdatedAt(system2.now());
+ this.ruleDtos = new RuleDto[] {
+ dbTester.rules().insert(setUpdatedAt),
+ dbTester.rules().insert(setUpdatedAt),
+ dbTester.rules().insert(setUpdatedAt),
+ dbTester.rules().insert(setUpdatedAt),
+ dbTester.rules().insert(setUpdatedAt),
+ dbTester.rules().insert(setUpdatedAt)
+ };
+ this.ruleParams = Arrays.stream(ruleDtos)
+ .map(rule -> dbTester.rules().insertRuleParam(rule))
+ .toArray(RuleParamDto[]::new);
+
+ underTest = new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
+
+ // delete all data from DB to ensure tests rely on cache exclusively
+ dbTester.executeUpdateSql("delete from rules");
+ dbTester.executeUpdateSql("delete from rules_parameters");
+ assertThat(dbTester.countRowsOfTable("rules")).isZero();
+ assertThat(dbTester.countRowsOfTable("rules_parameters")).isZero();
+ }
+
+ @Test
+ public void constructor_reads_rules_from_DB() {
+ DbClient dbClient = mock(DbClient.class);
+ DbSession dbSession = mock(DbSession.class);
+ RuleDao ruleDao = mock(RuleDao.class);
+ when(dbClient.openSession(anyBoolean())).thenReturn(dbSession);
+ when(dbClient.ruleDao()).thenReturn(ruleDao);
+
+ new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
+
+ verify(dbClient).openSession(anyBoolean());
+ verify(ruleDao).selectAll(dbSession);
+ verify(ruleDao).selectAllRuleParams(dbSession);
+ verifyNoMoreInteractions(ruleDao);
+ }
+
+ @Test
+ public void constructor_reads_parameters_from_DB() {
+ DbClient dbClient = mock(DbClient.class);
+ DbSession dbSession = mock(DbSession.class);
+ RuleDao ruleDao = mock(RuleDao.class);
+ when(dbClient.openSession(anyBoolean())).thenReturn(dbSession);
+ when(dbClient.ruleDao()).thenReturn(ruleDao);
+ List<RuleKey> ruleKeys = Arrays.asList(RuleKey.of("A", "B"), RuleKey.of("C", "D"), RuleKey.of("E", "F"));
+ when(ruleDao.selectAll(dbSession)).thenReturn(ruleKeys.stream().map(RuleTesting::newRule).collect(toList()));
+
+ new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
+
+ verify(ruleDao).selectAllRuleParams(dbSession);
+ }
+
+ @Test
+ public void findByKey_returns_all_loaded_rules() {
+ for (int i = 0; i < ruleDtos.length; i++) {
+ RuleDto ruleDto = ruleDtos[i];
+ RuleParamDto ruleParam = ruleParams[i];
+
+ org.sonar.api.rules.Rule rule = underTest.findByKey(ruleDto.getKey());
+ verifyRule(rule, ruleDto, ruleParam);
+ assertThat(underTest.findByKey(ruleDto.getRepositoryKey(), ruleDto.getRuleKey()))
+ .isSameAs(rule);
+ }
+ }
+
+ @Test
+ public void findByKey_returns_null_when_RuleKey_is_null() {
+ assertThat(underTest.findByKey(null)).isNull();
+ }
+
+ @Test
+ public void findByKey_returns_null_when_repository_key_is_null() {
+ assertThat(underTest.findByKey(null, randomAlphabetic(2))).isNull();
+ }
+
+ @Test
+ public void findByKey_returns_null_when_key_is_null() {
+ assertThat(underTest.findByKey(randomAlphabetic(2), null)).isNull();
+ }
+
+ @Test
+ public void findByKey_returns_null_when_both_repository_key_and_key_are_null() {
+ assertThat(underTest.findByKey(null, null)).isNull();
+ }
+
+ @Test
+ public void find_returns_null_when_RuleQuery_is_empty() {
+ assertThat(underTest.find(null)).isNull();
+ }
+
+ @Test
+ public void find_returns_most_recent_rule_when_RuleQuery_has_no_non_null_field() {
+ Rule rule = underTest.find(RuleQuery.create());
+
+ assertThat(toRuleKey(rule)).isEqualTo(ruleDtos[5].getKey());
+ }
+
+ @Test
+ public void find_searches_by_exact_match_of_repository_key_and_returns_most_recent_rule() {
+ String repoKey = "ABCD";
+ RuleDto[] sameRepoKey = {
+ dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setUpdatedAt(system2.now()))
+ };
+ RuleDto otherRule = dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()));
+
+ CachingRuleFinder underTest = new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
+
+ assertThat(toRuleKey(underTest.find(RuleQuery.create().withRepositoryKey(repoKey))))
+ .isEqualTo(sameRepoKey[1].getKey());
+ assertThat(toRuleKey(underTest.find(RuleQuery.create().withRepositoryKey(otherRule.getRepositoryKey()))))
+ .isEqualTo(otherRule.getKey());
+ assertThat(underTest.find(RuleQuery.create().withRepositoryKey(repoKey.toLowerCase())))
+ .isNull();
+ assertThat(underTest.find(RuleQuery.create().withRepositoryKey(randomAlphabetic(3))))
+ .isNull();
+ }
+
+ @Test
+ public void find_searches_by_exact_match_of_ruleKey_and_returns_most_recent_rule() {
+ String ruleKey = "ABCD";
+ RuleDto[] sameRuleKey = {
+ dbTester.rules().insert(rule -> rule.setRuleKey(ruleKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setRuleKey(ruleKey).setUpdatedAt(system2.now()))
+ };
+ RuleDto otherRule = dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()));
+
+ CachingRuleFinder underTest = new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
+
+ assertThat(toRuleKey(underTest.find(RuleQuery.create().withKey(ruleKey))))
+ .isEqualTo(sameRuleKey[1].getKey());
+ assertThat(toRuleKey(underTest.find(RuleQuery.create().withKey(otherRule.getRuleKey()))))
+ .isEqualTo(otherRule.getKey());
+ assertThat(underTest.find(RuleQuery.create().withKey(ruleKey.toLowerCase())))
+ .isNull();
+ assertThat(underTest.find(RuleQuery.create().withKey(randomAlphabetic(3))))
+ .isNull();
+ }
+
+ @Test
+ public void find_searches_by_exact_match_of_configKey_and_returns_most_recent_rule() {
+ String configKey = "ABCD";
+ RuleDto[] sameConfigKey = {
+ dbTester.rules().insert(rule -> rule.setConfigKey(configKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setConfigKey(configKey).setUpdatedAt(system2.now()))
+ };
+ RuleDto otherRule = dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()));
+
+ CachingRuleFinder underTest = new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
+
+ assertThat(toRuleKey(underTest.find(RuleQuery.create().withConfigKey(configKey))))
+ .isEqualTo(sameConfigKey[1].getKey());
+ assertThat(toRuleKey(underTest.find(RuleQuery.create().withConfigKey(otherRule.getConfigKey()))))
+ .isEqualTo(otherRule.getKey());
+ assertThat(underTest.find(RuleQuery.create().withConfigKey(configKey.toLowerCase())))
+ .isNull();
+ assertThat(underTest.find(RuleQuery.create().withConfigKey(randomAlphabetic(3))))
+ .isNull();
+ }
+
+ @Test
+ public void find_searches_by_exact_match_and_match_on_all_criterias_and_returns_most_recent_match() {
+ String repoKey = "ABCD";
+ String ruleKey = "EFGH";
+ String configKey = "IJKL";
+ RuleDto[] rules = {
+ dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setRuleKey(ruleKey).setConfigKey(configKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setRuleKey(ruleKey).setConfigKey(configKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setConfigKey(configKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()))
+ };
+ RuleQuery allQuery = RuleQuery.create().withRepositoryKey(repoKey).withKey(ruleKey).withConfigKey(configKey);
+ RuleQuery ruleAndConfigKeyQuery = RuleQuery.create().withKey(ruleKey).withConfigKey(configKey);
+ RuleQuery repoAndConfigKeyQuery = RuleQuery.create().withRepositoryKey(repoKey).withConfigKey(configKey);
+ RuleQuery repoAndKeyQuery = RuleQuery.create().withRepositoryKey(repoKey).withKey(ruleKey);
+ RuleQuery configKeyQuery = RuleQuery.create().withConfigKey(configKey);
+ RuleQuery ruleKeyQuery = RuleQuery.create().withKey(ruleKey);
+ RuleQuery repoKeyQuery = RuleQuery.create().withRepositoryKey(repoKey);
+
+ CachingRuleFinder underTest = new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
+
+ assertThat(toRuleKey(underTest.find(allQuery))).isEqualTo(rules[0].getKey());
+ assertThat(toRuleKey(underTest.find(ruleAndConfigKeyQuery))).isEqualTo(rules[1].getKey());
+ assertThat(toRuleKey(underTest.find(repoAndConfigKeyQuery))).isEqualTo(rules[2].getKey());
+ assertThat(toRuleKey(underTest.find(repoAndKeyQuery))).isEqualTo(rules[0].getKey());
+ assertThat(toRuleKey(underTest.find(repoKeyQuery))).isEqualTo(rules[2].getKey());
+ assertThat(toRuleKey(underTest.find(ruleKeyQuery))).isEqualTo(rules[1].getKey());
+ assertThat(toRuleKey(underTest.find(configKeyQuery))).isEqualTo(rules[2].getKey());
+ }
+
+ @Test
+ public void findAll_returns_empty_when_RuleQuery_is_empty() {
+ assertThat(underTest.findAll(null)).isEmpty();
+ }
+
+ @Test
+ public void findAll_returns_all_rules_when_RuleQuery_has_no_non_null_field() {
+ assertThat(underTest.findAll(RuleQuery.create()))
+ .extracting(CachingRuleFinderIT::toRuleKey)
+ .containsOnly(Arrays.stream(ruleDtos).map(RuleDto::getKey).toArray(RuleKey[]::new));
+ }
+
+ @Test
+ public void findAll_returns_all_rules_with_exact_same_repository_key_and_order_them_most_recent_first() {
+ String repoKey = "ABCD";
+ long currentTimeMillis = System.currentTimeMillis();
+ RuleDto[] sameRepoKey = {
+ dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setUpdatedAt(currentTimeMillis + system2.now())),
+ dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setUpdatedAt(currentTimeMillis + system2.now()))
+ };
+ RuleDto otherRule = dbTester.rules().insert(rule -> rule.setUpdatedAt(currentTimeMillis + system2.now()));
+
+ CachingRuleFinder underTest = new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
+
+ assertThat(underTest.findAll(RuleQuery.create().withRepositoryKey(repoKey)))
+ .extracting(CachingRuleFinderIT::toRuleKey)
+ .containsExactly(sameRepoKey[1].getKey(), sameRepoKey[0].getKey());
+ assertThat(underTest.findAll(RuleQuery.create().withRepositoryKey(otherRule.getRepositoryKey())))
+ .extracting(CachingRuleFinderIT::toRuleKey)
+ .containsExactly(otherRule.getKey());
+ assertThat(underTest.findAll(RuleQuery.create().withRepositoryKey(repoKey.toLowerCase())))
+ .isEmpty();
+ assertThat(underTest.findAll(RuleQuery.create().withRepositoryKey(randomAlphabetic(3))))
+ .isEmpty();
+ }
+
+ @Test
+ public void findAll_returns_all_rules_with_exact_same_rulekey_and_order_them_most_recent_first() {
+ String ruleKey = "ABCD";
+ RuleDto[] sameRuleKey = {
+ dbTester.rules().insert(rule -> rule.setRuleKey(ruleKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setRuleKey(ruleKey).setUpdatedAt(system2.now()))
+ };
+ RuleDto otherRule = dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()));
+
+ CachingRuleFinder underTest = new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
+
+ assertThat(underTest.findAll(RuleQuery.create().withKey(ruleKey)))
+ .extracting(CachingRuleFinderIT::toRuleKey)
+ .containsExactly(sameRuleKey[1].getKey(), sameRuleKey[0].getKey());
+ assertThat(underTest.findAll(RuleQuery.create().withKey(otherRule.getRuleKey())))
+ .extracting(CachingRuleFinderIT::toRuleKey)
+ .containsExactly(otherRule.getKey());
+ assertThat(underTest.findAll(RuleQuery.create().withKey(ruleKey.toLowerCase())))
+ .isEmpty();
+ assertThat(underTest.findAll(RuleQuery.create().withKey(randomAlphabetic(3))))
+ .isEmpty();
+ }
+
+ @Test
+ public void findAll_returns_all_rules_with_exact_same_configkey_and_order_them_most_recent_first() {
+ String configKey = "ABCD";
+ RuleDto[] sameConfigKey = {
+ dbTester.rules().insert(rule -> rule.setConfigKey(configKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setConfigKey(configKey).setUpdatedAt(system2.now()))
+ };
+ RuleDto otherRule = dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()));
+
+ CachingRuleFinder underTest = new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
+
+ assertThat(underTest.findAll(RuleQuery.create().withConfigKey(configKey)))
+ .extracting(CachingRuleFinderIT::toRuleKey)
+ .containsExactly(sameConfigKey[1].getKey(), sameConfigKey[0].getKey());
+ assertThat(underTest.findAll(RuleQuery.create().withConfigKey(otherRule.getConfigKey())))
+ .extracting(CachingRuleFinderIT::toRuleKey)
+ .containsExactly(otherRule.getKey());
+ assertThat(underTest.findAll(RuleQuery.create().withConfigKey(configKey.toLowerCase())))
+ .isEmpty();
+ assertThat(underTest.findAll(RuleQuery.create().withConfigKey(randomAlphabetic(3))))
+ .isEmpty();
+ }
+
+ @Test
+ public void findAll_returns_all_rules_which_match_exactly_all_criteria_and_order_then_by_most_recent_first() {
+ String repoKey = "ABCD";
+ String ruleKey = "EFGH";
+ String configKey = "IJKL";
+ RuleDto[] rules = {
+ dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setRuleKey(ruleKey).setConfigKey(configKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setRuleKey(ruleKey).setConfigKey(configKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setConfigKey(configKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()))
+ };
+ RuleQuery allQuery = RuleQuery.create().withRepositoryKey(repoKey).withKey(ruleKey).withConfigKey(configKey);
+ RuleQuery ruleAndConfigKeyQuery = RuleQuery.create().withKey(ruleKey).withConfigKey(configKey);
+ RuleQuery repoAndConfigKeyQuery = RuleQuery.create().withRepositoryKey(repoKey).withConfigKey(configKey);
+ RuleQuery repoAndKeyQuery = RuleQuery.create().withRepositoryKey(repoKey).withKey(ruleKey);
+ RuleQuery configKeyQuery = RuleQuery.create().withConfigKey(configKey);
+ RuleQuery ruleKeyQuery = RuleQuery.create().withKey(ruleKey);
+ RuleQuery repoKeyQuery = RuleQuery.create().withRepositoryKey(repoKey);
+
+ CachingRuleFinder underTest = new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
+
+ assertThat(underTest.findAll(allQuery))
+ .extracting(CachingRuleFinderIT::toRuleKey)
+ .containsExactly(rules[0].getKey());
+ assertThat(underTest.findAll(ruleAndConfigKeyQuery))
+ .extracting(CachingRuleFinderIT::toRuleKey)
+ .containsExactly(rules[1].getKey(), rules[0].getKey());
+ assertThat(underTest.findAll(repoAndConfigKeyQuery))
+ .extracting(CachingRuleFinderIT::toRuleKey)
+ .containsExactly(rules[2].getKey(), rules[0].getKey());
+ assertThat(underTest.findAll(repoAndKeyQuery))
+ .extracting(CachingRuleFinderIT::toRuleKey)
+ .containsExactly(rules[0].getKey());
+ assertThat(underTest.findAll(repoKeyQuery))
+ .extracting(CachingRuleFinderIT::toRuleKey)
+ .containsExactly(rules[2].getKey(), rules[0].getKey());
+ assertThat(underTest.findAll(ruleKeyQuery))
+ .extracting(CachingRuleFinderIT::toRuleKey)
+ .containsExactly(rules[1].getKey(), rules[0].getKey());
+ assertThat(underTest.findAll(configKeyQuery))
+ .extracting(CachingRuleFinderIT::toRuleKey)
+ .containsExactly(rules[2].getKey(), rules[1].getKey(), rules[0].getKey());
+ }
+
+ @Test
+ public void findDtoByKey_finds_rules() {
+ for(RuleDto dto : ruleDtos) {
+ assertThat(underTest.findDtoByKey(dto.getKey())).contains(dto);
+ }
+ }
+
+ @Test
+ public void findDtoByUuid_finds_rules() {
+ for(RuleDto dto : ruleDtos) {
+ assertThat(underTest.findDtoByUuid(dto.getUuid())).contains(dto);
+ }
+ }
+
+ @Test
+ public void findDtoByKey_returns_empty_if_rule_not_found() {
+ assertThat(underTest.findDtoByKey(RuleKey.of("unknown", "unknown"))).isEmpty();
+ }
+
+ @Test
+ public void findDtoByUuid_returns_empty_if_rule_not_found() {
+ assertThat(underTest.findDtoByUuid("unknown")).isEmpty();
+ }
+
+ @Test
+ public void findAll_returns_all_rules() {
+ assertThat(underTest.findAll()).containsOnly(ruleDtos);
+ }
+
+ private static RuleKey toRuleKey(Rule rule) {
+ return RuleKey.of(rule.getRepositoryKey(), rule.getKey());
+ }
+
+ private void verifyRule(@Nullable Rule rule, RuleDto ruleDto, RuleParamDto ruleParam) {
+ assertThat(rule).isNotNull();
+
+ assertThat(rule.getName()).isEqualTo(ruleDto.getName());
+ assertThat(rule.getLanguage()).isEqualTo(ruleDto.getLanguage());
+ assertThat(rule.getKey()).isEqualTo(ruleDto.getRuleKey());
+ assertThat(rule.getConfigKey()).isEqualTo(ruleDto.getConfigKey());
+ assertThat(rule.isTemplate()).isEqualTo(ruleDto.isTemplate());
+ assertThat(rule.getCreatedAt().getTime()).isEqualTo(ruleDto.getCreatedAt());
+ assertThat(rule.getUpdatedAt().getTime()).isEqualTo(ruleDto.getUpdatedAt());
+ assertThat(rule.getRepositoryKey()).isEqualTo(ruleDto.getRepositoryKey());
+ assertThat(rule.getSeverity().name()).isEqualTo(ruleDto.getSeverityString());
+ assertThat(rule.getSystemTags()).isEqualTo(ruleDto.getSystemTags().toArray(new String[0]));
+ assertThat(rule.getTags()).isEmpty();
+ assertThat(rule.getDescription()).isEqualTo(ruleDto.getDefaultRuleDescriptionSection().getContent());
+
+ assertThat(rule.getParams()).hasSize(1);
+ org.sonar.api.rules.RuleParam param = rule.getParams().iterator().next();
+ assertThat(param.getRule()).isSameAs(rule);
+ assertThat(param.getKey()).isEqualTo(ruleParam.getName());
+ assertThat(param.getDescription()).isEqualTo(ruleParam.getDescription());
+ assertThat(param.getType()).isEqualTo(ruleParam.getType());
+ assertThat(param.getDefaultValue()).isEqualTo(ruleParam.getDefaultValue());
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.plugins;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.Plugin;
-import org.sonar.core.platform.PluginInfo;
-import org.sonar.core.plugin.PluginType;
-import org.sonar.db.DbTester;
-import org.sonar.db.plugin.PluginDto;
-import org.sonar.server.plugins.PluginFilesAndMd5.FileAndMd5;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.assertThrows;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class DetectPluginChangeTest {
- @Rule
- public DbTester dbTester = DbTester.create();
-
- private final ServerPluginRepository pluginRepository = new ServerPluginRepository();
- private final DetectPluginChange detectPluginChange = new DetectPluginChange(pluginRepository, dbTester.getDbClient());
-
- @Test
- public void detect_changed_plugin() {
- addPluginToDb("plugin1", "hash1", PluginDto.Type.BUNDLED);
- addPluginToFs("plugin1", "hash2", PluginType.BUNDLED);
-
- detectPluginChange.start();
- assertThat(detectPluginChange.anyPluginChanged()).isTrue();
- }
-
- @Test
- public void detect_changed_type() {
- addPluginToDb("plugin1", "hash1", PluginDto.Type.BUNDLED);
- addPluginToFs("plugin1", "hash1", PluginType.EXTERNAL);
-
- detectPluginChange.start();
- assertThat(detectPluginChange.anyPluginChanged()).isTrue();
- }
-
- @Test
- public void detect_new_plugin() {
- addPluginToDb("plugin1", "hash1", PluginDto.Type.BUNDLED);
- addPluginToFs("plugin2", "hash1", PluginType.BUNDLED);
-
- detectPluginChange.start();
- assertThat(detectPluginChange.anyPluginChanged()).isTrue();
- }
-
- @Test
- public void detect_removed_plugin() {
- addPluginToDb("plugin1", "hash1", PluginDto.Type.BUNDLED, true);
- addPluginToFs("plugin1", "hash1", PluginType.BUNDLED);
-
- detectPluginChange.start();
- assertThat(detectPluginChange.anyPluginChanged()).isTrue();
- }
-
- @Test
- public void detect_missing_plugin() {
- addPluginToDb("plugin1", "hash1", PluginDto.Type.BUNDLED);
-
- detectPluginChange.start();
- assertThat(detectPluginChange.anyPluginChanged()).isTrue();
- }
-
- @Test
- public void detect_no_changes() {
- addPluginToDb("plugin1", "hash1", PluginDto.Type.BUNDLED);
- addPluginToFs("plugin1", "hash1", PluginType.BUNDLED);
- addPluginToDb("plugin2", "hash2", PluginDto.Type.EXTERNAL);
- addPluginToFs("plugin2", "hash2", PluginType.EXTERNAL);
-
- detectPluginChange.start();
- assertThat(detectPluginChange.anyPluginChanged()).isFalse();
- }
-
- @Test
- public void fail_if_start_twice() {
- detectPluginChange.start();
- assertThrows(IllegalStateException.class, detectPluginChange::start);
- }
-
- @Test
- public void fail_if_not_started() {
- assertThrows(NullPointerException.class, detectPluginChange::anyPluginChanged);
- }
-
- private void addPluginToDb(String key, String hash, PluginDto.Type type) {
- addPluginToDb(key, hash, type, false);
- }
-
- private void addPluginToDb(String key, String hash, PluginDto.Type type, boolean removed) {
- dbTester.pluginDbTester().insertPlugin(p -> p.setKee(key).setFileHash(hash).setType(type).setRemoved(removed));
- }
-
- private void addPluginToFs(String key, String hash, PluginType type) {
- PluginInfo pluginInfo = new PluginInfo(key);
- Plugin plugin = mock(Plugin.class);
- FileAndMd5 fileAndMd5 = mock(FileAndMd5.class);
- when(fileAndMd5.getMd5()).thenReturn(hash);
- ServerPlugin serverPlugin = new ServerPlugin(pluginInfo, type, plugin, fileAndMd5, null);
- pluginRepository.addPlugin(serverPlugin);
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.rule;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.function.Consumer;
-import javax.annotation.Nullable;
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.rules.RuleQuery;
-import org.sonar.api.utils.System2;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.DbTester;
-import org.sonar.db.rule.RuleDao;
-import org.sonar.db.rule.RuleDto;
-import org.sonar.db.rule.RuleParamDto;
-import org.sonar.db.rule.RuleTesting;
-
-import static java.util.stream.Collectors.toList;
-import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-public class CachingRuleFinderTest {
- @org.junit.Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
- private final DbClient dbClient = dbTester.getDbClient();
- private final AlwaysIncreasingSystem2 system2 = new AlwaysIncreasingSystem2();
- private RuleDto[] ruleDtos;
- private RuleParamDto[] ruleParams;
- private CachingRuleFinder underTest;
- private RuleDescriptionFormatter ruleDescriptionFormatter = new RuleDescriptionFormatter();
-
- @Before()
- public void setUp() {
- Consumer<RuleDto> setUpdatedAt = rule -> rule.setUpdatedAt(system2.now());
- this.ruleDtos = new RuleDto[] {
- dbTester.rules().insert(setUpdatedAt),
- dbTester.rules().insert(setUpdatedAt),
- dbTester.rules().insert(setUpdatedAt),
- dbTester.rules().insert(setUpdatedAt),
- dbTester.rules().insert(setUpdatedAt),
- dbTester.rules().insert(setUpdatedAt)
- };
- this.ruleParams = Arrays.stream(ruleDtos)
- .map(rule -> dbTester.rules().insertRuleParam(rule))
- .toArray(RuleParamDto[]::new);
-
- underTest = new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
-
- // delete all data from DB to ensure tests rely on cache exclusively
- dbTester.executeUpdateSql("delete from rules");
- dbTester.executeUpdateSql("delete from rules_parameters");
- assertThat(dbTester.countRowsOfTable("rules")).isZero();
- assertThat(dbTester.countRowsOfTable("rules_parameters")).isZero();
- }
-
- @Test
- public void constructor_reads_rules_from_DB() {
- DbClient dbClient = mock(DbClient.class);
- DbSession dbSession = mock(DbSession.class);
- RuleDao ruleDao = mock(RuleDao.class);
- when(dbClient.openSession(anyBoolean())).thenReturn(dbSession);
- when(dbClient.ruleDao()).thenReturn(ruleDao);
-
- new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
-
- verify(dbClient).openSession(anyBoolean());
- verify(ruleDao).selectAll(dbSession);
- verify(ruleDao).selectAllRuleParams(dbSession);
- verifyNoMoreInteractions(ruleDao);
- }
-
- @Test
- public void constructor_reads_parameters_from_DB() {
- DbClient dbClient = mock(DbClient.class);
- DbSession dbSession = mock(DbSession.class);
- RuleDao ruleDao = mock(RuleDao.class);
- when(dbClient.openSession(anyBoolean())).thenReturn(dbSession);
- when(dbClient.ruleDao()).thenReturn(ruleDao);
- List<RuleKey> ruleKeys = Arrays.asList(RuleKey.of("A", "B"), RuleKey.of("C", "D"), RuleKey.of("E", "F"));
- when(ruleDao.selectAll(dbSession)).thenReturn(ruleKeys.stream().map(RuleTesting::newRule).collect(toList()));
-
- new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
-
- verify(ruleDao).selectAllRuleParams(dbSession);
- }
-
- @Test
- public void findByKey_returns_all_loaded_rules() {
- for (int i = 0; i < ruleDtos.length; i++) {
- RuleDto ruleDto = ruleDtos[i];
- RuleParamDto ruleParam = ruleParams[i];
-
- org.sonar.api.rules.Rule rule = underTest.findByKey(ruleDto.getKey());
- verifyRule(rule, ruleDto, ruleParam);
- assertThat(underTest.findByKey(ruleDto.getRepositoryKey(), ruleDto.getRuleKey()))
- .isSameAs(rule);
- }
- }
-
- @Test
- public void findByKey_returns_null_when_RuleKey_is_null() {
- assertThat(underTest.findByKey(null)).isNull();
- }
-
- @Test
- public void findByKey_returns_null_when_repository_key_is_null() {
- assertThat(underTest.findByKey(null, randomAlphabetic(2))).isNull();
- }
-
- @Test
- public void findByKey_returns_null_when_key_is_null() {
- assertThat(underTest.findByKey(randomAlphabetic(2), null)).isNull();
- }
-
- @Test
- public void findByKey_returns_null_when_both_repository_key_and_key_are_null() {
- assertThat(underTest.findByKey(null, null)).isNull();
- }
-
- @Test
- public void find_returns_null_when_RuleQuery_is_empty() {
- assertThat(underTest.find(null)).isNull();
- }
-
- @Test
- public void find_returns_most_recent_rule_when_RuleQuery_has_no_non_null_field() {
- Rule rule = underTest.find(RuleQuery.create());
-
- assertThat(toRuleKey(rule)).isEqualTo(ruleDtos[5].getKey());
- }
-
- @Test
- public void find_searches_by_exact_match_of_repository_key_and_returns_most_recent_rule() {
- String repoKey = "ABCD";
- RuleDto[] sameRepoKey = {
- dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setUpdatedAt(system2.now())),
- dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setUpdatedAt(system2.now()))
- };
- RuleDto otherRule = dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()));
-
- CachingRuleFinder underTest = new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
-
- assertThat(toRuleKey(underTest.find(RuleQuery.create().withRepositoryKey(repoKey))))
- .isEqualTo(sameRepoKey[1].getKey());
- assertThat(toRuleKey(underTest.find(RuleQuery.create().withRepositoryKey(otherRule.getRepositoryKey()))))
- .isEqualTo(otherRule.getKey());
- assertThat(underTest.find(RuleQuery.create().withRepositoryKey(repoKey.toLowerCase())))
- .isNull();
- assertThat(underTest.find(RuleQuery.create().withRepositoryKey(randomAlphabetic(3))))
- .isNull();
- }
-
- @Test
- public void find_searches_by_exact_match_of_ruleKey_and_returns_most_recent_rule() {
- String ruleKey = "ABCD";
- RuleDto[] sameRuleKey = {
- dbTester.rules().insert(rule -> rule.setRuleKey(ruleKey).setUpdatedAt(system2.now())),
- dbTester.rules().insert(rule -> rule.setRuleKey(ruleKey).setUpdatedAt(system2.now()))
- };
- RuleDto otherRule = dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()));
-
- CachingRuleFinder underTest = new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
-
- assertThat(toRuleKey(underTest.find(RuleQuery.create().withKey(ruleKey))))
- .isEqualTo(sameRuleKey[1].getKey());
- assertThat(toRuleKey(underTest.find(RuleQuery.create().withKey(otherRule.getRuleKey()))))
- .isEqualTo(otherRule.getKey());
- assertThat(underTest.find(RuleQuery.create().withKey(ruleKey.toLowerCase())))
- .isNull();
- assertThat(underTest.find(RuleQuery.create().withKey(randomAlphabetic(3))))
- .isNull();
- }
-
- @Test
- public void find_searches_by_exact_match_of_configKey_and_returns_most_recent_rule() {
- String configKey = "ABCD";
- RuleDto[] sameConfigKey = {
- dbTester.rules().insert(rule -> rule.setConfigKey(configKey).setUpdatedAt(system2.now())),
- dbTester.rules().insert(rule -> rule.setConfigKey(configKey).setUpdatedAt(system2.now()))
- };
- RuleDto otherRule = dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()));
-
- CachingRuleFinder underTest = new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
-
- assertThat(toRuleKey(underTest.find(RuleQuery.create().withConfigKey(configKey))))
- .isEqualTo(sameConfigKey[1].getKey());
- assertThat(toRuleKey(underTest.find(RuleQuery.create().withConfigKey(otherRule.getConfigKey()))))
- .isEqualTo(otherRule.getKey());
- assertThat(underTest.find(RuleQuery.create().withConfigKey(configKey.toLowerCase())))
- .isNull();
- assertThat(underTest.find(RuleQuery.create().withConfigKey(randomAlphabetic(3))))
- .isNull();
- }
-
- @Test
- public void find_searches_by_exact_match_and_match_on_all_criterias_and_returns_most_recent_match() {
- String repoKey = "ABCD";
- String ruleKey = "EFGH";
- String configKey = "IJKL";
- RuleDto[] rules = {
- dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setRuleKey(ruleKey).setConfigKey(configKey).setUpdatedAt(system2.now())),
- dbTester.rules().insert(rule -> rule.setRuleKey(ruleKey).setConfigKey(configKey).setUpdatedAt(system2.now())),
- dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setConfigKey(configKey).setUpdatedAt(system2.now())),
- dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()))
- };
- RuleQuery allQuery = RuleQuery.create().withRepositoryKey(repoKey).withKey(ruleKey).withConfigKey(configKey);
- RuleQuery ruleAndConfigKeyQuery = RuleQuery.create().withKey(ruleKey).withConfigKey(configKey);
- RuleQuery repoAndConfigKeyQuery = RuleQuery.create().withRepositoryKey(repoKey).withConfigKey(configKey);
- RuleQuery repoAndKeyQuery = RuleQuery.create().withRepositoryKey(repoKey).withKey(ruleKey);
- RuleQuery configKeyQuery = RuleQuery.create().withConfigKey(configKey);
- RuleQuery ruleKeyQuery = RuleQuery.create().withKey(ruleKey);
- RuleQuery repoKeyQuery = RuleQuery.create().withRepositoryKey(repoKey);
-
- CachingRuleFinder underTest = new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
-
- assertThat(toRuleKey(underTest.find(allQuery))).isEqualTo(rules[0].getKey());
- assertThat(toRuleKey(underTest.find(ruleAndConfigKeyQuery))).isEqualTo(rules[1].getKey());
- assertThat(toRuleKey(underTest.find(repoAndConfigKeyQuery))).isEqualTo(rules[2].getKey());
- assertThat(toRuleKey(underTest.find(repoAndKeyQuery))).isEqualTo(rules[0].getKey());
- assertThat(toRuleKey(underTest.find(repoKeyQuery))).isEqualTo(rules[2].getKey());
- assertThat(toRuleKey(underTest.find(ruleKeyQuery))).isEqualTo(rules[1].getKey());
- assertThat(toRuleKey(underTest.find(configKeyQuery))).isEqualTo(rules[2].getKey());
- }
-
- @Test
- public void findAll_returns_empty_when_RuleQuery_is_empty() {
- assertThat(underTest.findAll(null)).isEmpty();
- }
-
- @Test
- public void findAll_returns_all_rules_when_RuleQuery_has_no_non_null_field() {
- assertThat(underTest.findAll(RuleQuery.create()))
- .extracting(CachingRuleFinderTest::toRuleKey)
- .containsOnly(Arrays.stream(ruleDtos).map(RuleDto::getKey).toArray(RuleKey[]::new));
- }
-
- @Test
- public void findAll_returns_all_rules_with_exact_same_repository_key_and_order_them_most_recent_first() {
- String repoKey = "ABCD";
- long currentTimeMillis = System.currentTimeMillis();
- RuleDto[] sameRepoKey = {
- dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setUpdatedAt(currentTimeMillis + system2.now())),
- dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setUpdatedAt(currentTimeMillis + system2.now()))
- };
- RuleDto otherRule = dbTester.rules().insert(rule -> rule.setUpdatedAt(currentTimeMillis + system2.now()));
-
- CachingRuleFinder underTest = new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
-
- assertThat(underTest.findAll(RuleQuery.create().withRepositoryKey(repoKey)))
- .extracting(CachingRuleFinderTest::toRuleKey)
- .containsExactly(sameRepoKey[1].getKey(), sameRepoKey[0].getKey());
- assertThat(underTest.findAll(RuleQuery.create().withRepositoryKey(otherRule.getRepositoryKey())))
- .extracting(CachingRuleFinderTest::toRuleKey)
- .containsExactly(otherRule.getKey());
- assertThat(underTest.findAll(RuleQuery.create().withRepositoryKey(repoKey.toLowerCase())))
- .isEmpty();
- assertThat(underTest.findAll(RuleQuery.create().withRepositoryKey(randomAlphabetic(3))))
- .isEmpty();
- }
-
- @Test
- public void findAll_returns_all_rules_with_exact_same_rulekey_and_order_them_most_recent_first() {
- String ruleKey = "ABCD";
- RuleDto[] sameRuleKey = {
- dbTester.rules().insert(rule -> rule.setRuleKey(ruleKey).setUpdatedAt(system2.now())),
- dbTester.rules().insert(rule -> rule.setRuleKey(ruleKey).setUpdatedAt(system2.now()))
- };
- RuleDto otherRule = dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()));
-
- CachingRuleFinder underTest = new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
-
- assertThat(underTest.findAll(RuleQuery.create().withKey(ruleKey)))
- .extracting(CachingRuleFinderTest::toRuleKey)
- .containsExactly(sameRuleKey[1].getKey(), sameRuleKey[0].getKey());
- assertThat(underTest.findAll(RuleQuery.create().withKey(otherRule.getRuleKey())))
- .extracting(CachingRuleFinderTest::toRuleKey)
- .containsExactly(otherRule.getKey());
- assertThat(underTest.findAll(RuleQuery.create().withKey(ruleKey.toLowerCase())))
- .isEmpty();
- assertThat(underTest.findAll(RuleQuery.create().withKey(randomAlphabetic(3))))
- .isEmpty();
- }
-
- @Test
- public void findAll_returns_all_rules_with_exact_same_configkey_and_order_them_most_recent_first() {
- String configKey = "ABCD";
- RuleDto[] sameConfigKey = {
- dbTester.rules().insert(rule -> rule.setConfigKey(configKey).setUpdatedAt(system2.now())),
- dbTester.rules().insert(rule -> rule.setConfigKey(configKey).setUpdatedAt(system2.now()))
- };
- RuleDto otherRule = dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()));
-
- CachingRuleFinder underTest = new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
-
- assertThat(underTest.findAll(RuleQuery.create().withConfigKey(configKey)))
- .extracting(CachingRuleFinderTest::toRuleKey)
- .containsExactly(sameConfigKey[1].getKey(), sameConfigKey[0].getKey());
- assertThat(underTest.findAll(RuleQuery.create().withConfigKey(otherRule.getConfigKey())))
- .extracting(CachingRuleFinderTest::toRuleKey)
- .containsExactly(otherRule.getKey());
- assertThat(underTest.findAll(RuleQuery.create().withConfigKey(configKey.toLowerCase())))
- .isEmpty();
- assertThat(underTest.findAll(RuleQuery.create().withConfigKey(randomAlphabetic(3))))
- .isEmpty();
- }
-
- @Test
- public void findAll_returns_all_rules_which_match_exactly_all_criteria_and_order_then_by_most_recent_first() {
- String repoKey = "ABCD";
- String ruleKey = "EFGH";
- String configKey = "IJKL";
- RuleDto[] rules = {
- dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setRuleKey(ruleKey).setConfigKey(configKey).setUpdatedAt(system2.now())),
- dbTester.rules().insert(rule -> rule.setRuleKey(ruleKey).setConfigKey(configKey).setUpdatedAt(system2.now())),
- dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setConfigKey(configKey).setUpdatedAt(system2.now())),
- dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()))
- };
- RuleQuery allQuery = RuleQuery.create().withRepositoryKey(repoKey).withKey(ruleKey).withConfigKey(configKey);
- RuleQuery ruleAndConfigKeyQuery = RuleQuery.create().withKey(ruleKey).withConfigKey(configKey);
- RuleQuery repoAndConfigKeyQuery = RuleQuery.create().withRepositoryKey(repoKey).withConfigKey(configKey);
- RuleQuery repoAndKeyQuery = RuleQuery.create().withRepositoryKey(repoKey).withKey(ruleKey);
- RuleQuery configKeyQuery = RuleQuery.create().withConfigKey(configKey);
- RuleQuery ruleKeyQuery = RuleQuery.create().withKey(ruleKey);
- RuleQuery repoKeyQuery = RuleQuery.create().withRepositoryKey(repoKey);
-
- CachingRuleFinder underTest = new CachingRuleFinder(dbClient, ruleDescriptionFormatter);
-
- assertThat(underTest.findAll(allQuery))
- .extracting(CachingRuleFinderTest::toRuleKey)
- .containsExactly(rules[0].getKey());
- assertThat(underTest.findAll(ruleAndConfigKeyQuery))
- .extracting(CachingRuleFinderTest::toRuleKey)
- .containsExactly(rules[1].getKey(), rules[0].getKey());
- assertThat(underTest.findAll(repoAndConfigKeyQuery))
- .extracting(CachingRuleFinderTest::toRuleKey)
- .containsExactly(rules[2].getKey(), rules[0].getKey());
- assertThat(underTest.findAll(repoAndKeyQuery))
- .extracting(CachingRuleFinderTest::toRuleKey)
- .containsExactly(rules[0].getKey());
- assertThat(underTest.findAll(repoKeyQuery))
- .extracting(CachingRuleFinderTest::toRuleKey)
- .containsExactly(rules[2].getKey(), rules[0].getKey());
- assertThat(underTest.findAll(ruleKeyQuery))
- .extracting(CachingRuleFinderTest::toRuleKey)
- .containsExactly(rules[1].getKey(), rules[0].getKey());
- assertThat(underTest.findAll(configKeyQuery))
- .extracting(CachingRuleFinderTest::toRuleKey)
- .containsExactly(rules[2].getKey(), rules[1].getKey(), rules[0].getKey());
- }
-
- @Test
- public void findDtoByKey_finds_rules() {
- for(RuleDto dto : ruleDtos) {
- assertThat(underTest.findDtoByKey(dto.getKey())).contains(dto);
- }
- }
-
- @Test
- public void findDtoByUuid_finds_rules() {
- for(RuleDto dto : ruleDtos) {
- assertThat(underTest.findDtoByUuid(dto.getUuid())).contains(dto);
- }
- }
-
- @Test
- public void findDtoByKey_returns_empty_if_rule_not_found() {
- assertThat(underTest.findDtoByKey(RuleKey.of("unknown", "unknown"))).isEmpty();
- }
-
- @Test
- public void findDtoByUuid_returns_empty_if_rule_not_found() {
- assertThat(underTest.findDtoByUuid("unknown")).isEmpty();
- }
-
- @Test
- public void findAll_returns_all_rules() {
- assertThat(underTest.findAll()).containsOnly(ruleDtos);
- }
-
- private static RuleKey toRuleKey(Rule rule) {
- return RuleKey.of(rule.getRepositoryKey(), rule.getKey());
- }
-
- private void verifyRule(@Nullable Rule rule, RuleDto ruleDto, RuleParamDto ruleParam) {
- assertThat(rule).isNotNull();
-
- assertThat(rule.getName()).isEqualTo(ruleDto.getName());
- assertThat(rule.getLanguage()).isEqualTo(ruleDto.getLanguage());
- assertThat(rule.getKey()).isEqualTo(ruleDto.getRuleKey());
- assertThat(rule.getConfigKey()).isEqualTo(ruleDto.getConfigKey());
- assertThat(rule.isTemplate()).isEqualTo(ruleDto.isTemplate());
- assertThat(rule.getCreatedAt().getTime()).isEqualTo(ruleDto.getCreatedAt());
- assertThat(rule.getUpdatedAt().getTime()).isEqualTo(ruleDto.getUpdatedAt());
- assertThat(rule.getRepositoryKey()).isEqualTo(ruleDto.getRepositoryKey());
- assertThat(rule.getSeverity().name()).isEqualTo(ruleDto.getSeverityString());
- assertThat(rule.getSystemTags()).isEqualTo(ruleDto.getSystemTags().toArray(new String[0]));
- assertThat(rule.getTags()).isEmpty();
- assertThat(rule.getDescription()).isEqualTo(ruleDto.getDefaultRuleDescriptionSection().getContent());
-
- assertThat(rule.getParams()).hasSize(1);
- org.sonar.api.rules.RuleParam param = rule.getParams().iterator().next();
- assertThat(param.getRule()).isSameAs(rule);
- assertThat(param.getKey()).isEqualTo(ruleParam.getName());
- assertThat(param.getDescription()).isEqualTo(ruleParam.getDescription());
- assertThat(param.getType()).isEqualTo(ruleParam.getType());
- assertThat(param.getDefaultValue()).isEqualTo(ruleParam.getDefaultValue());
- }
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.config.internal.Settings;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.server.setting.SettingsChangeNotifier;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class PersistentSettingsIT {
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+ private Settings delegate = new MapSettings();
+ private SettingsChangeNotifier changeNotifier = mock(SettingsChangeNotifier.class);
+ private PersistentSettings underTest = new PersistentSettings(delegate, dbTester.getDbClient(), changeNotifier);
+
+ @Test
+ public void insert_property_into_database_and_notify_extensions() {
+ assertThat(underTest.getString("foo")).isNull();
+
+ underTest.saveProperty("foo", "bar");
+
+ assertThat(underTest.getString("foo")).isEqualTo("bar");
+ assertThat(dbTester.getDbClient().propertiesDao().selectGlobalProperty("foo").getValue()).isEqualTo("bar");
+ verify(changeNotifier).onGlobalPropertyChange("foo", "bar");
+ }
+
+ @Test
+ public void delete_property_from_database_and_notify_extensions() {
+ underTest.saveProperty("foo", "bar");
+ underTest.saveProperty("foo", null);
+
+ assertThat(underTest.getString("foo")).isNull();
+ assertThat(dbTester.getDbClient().propertiesDao().selectGlobalProperty("foo")).isNull();
+ verify(changeNotifier).onGlobalPropertyChange("foo", null);
+ }
+
+ @Test
+ public void getSettings_returns_delegate() {
+ assertThat(underTest.getSettings()).isSameAs(delegate);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.property.PropertyDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class StartupMetadataPersisterIT {
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private StartupMetadata metadata = new StartupMetadata(123_456_789L);
+ private StartupMetadataPersister underTest = new StartupMetadataPersister(metadata, dbTester.getDbClient());
+
+ @Test
+ public void persist_metadata_at_startup() {
+ underTest.start();
+
+ assertPersistedProperty(CoreProperties.SERVER_STARTTIME, DateUtils.formatDateTime(metadata.getStartedAt()));
+
+ underTest.stop();
+ }
+
+ private void assertPersistedProperty(String propertyKey, String expectedValue) {
+ PropertyDto prop = dbTester.getDbClient().propertiesDao().selectGlobalProperty(dbTester.getSession(), propertyKey);
+ assertThat(prop.getValue()).isEqualTo(expectedValue);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.serverid;
+
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.SonarEdition;
+import org.sonar.api.SonarQubeSide;
+import org.sonar.api.internal.SonarRuntimeImpl;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.Version;
+import org.sonar.core.platform.ServerId;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.property.PropertyDto;
+import org.sonar.server.platform.NodeInformation;
+import org.sonar.server.property.InternalProperties;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.Assertions.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.SonarQubeSide.COMPUTE_ENGINE;
+import static org.sonar.api.SonarQubeSide.SERVER;
+import static org.sonar.core.platform.ServerId.DATABASE_ID_LENGTH;
+import static org.sonar.core.platform.ServerId.NOT_UUID_DATASET_ID_LENGTH;
+import static org.sonar.core.platform.ServerId.UUID_DATASET_ID_LENGTH;
+
+@RunWith(DataProviderRunner.class)
+public class ServerIdManagerIT {
+
+ private static final ServerId WITH_DATABASE_ID_SERVER_ID = ServerId.of(randomAlphanumeric(DATABASE_ID_LENGTH), randomAlphanumeric(NOT_UUID_DATASET_ID_LENGTH));
+ private static final String CHECKSUM_1 = randomAlphanumeric(12);
+
+ @Rule
+ public final DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private final ServerIdChecksum serverIdChecksum = mock(ServerIdChecksum.class);
+ private final ServerIdFactory serverIdFactory = mock(ServerIdFactory.class);
+ private final DbClient dbClient = dbTester.getDbClient();
+ private final DbSession dbSession = dbTester.getSession();
+ private final NodeInformation nodeInformation = mock(NodeInformation.class);
+ private ServerIdManager underTest;
+
+ @After
+ public void tearDown() {
+ if (underTest != null) {
+ underTest.stop();
+ }
+ }
+
+ @Test
+ public void web_leader_persists_new_server_id_if_missing() {
+ mockCreateNewServerId();
+ mockChecksumOf(WITH_DATABASE_ID_SERVER_ID, CHECKSUM_1);
+ when(nodeInformation.isStartupLeader()).thenReturn(true);
+
+ test(SERVER);
+
+ verifyDb(CHECKSUM_1);
+ verifyCreateNewServerIdFromScratch();
+ }
+
+ @Test
+ public void web_leader_persists_new_server_id_if_value_is_empty() {
+ insertServerId("");
+ mockCreateNewServerId();
+ mockChecksumOf(WITH_DATABASE_ID_SERVER_ID, CHECKSUM_1);
+ when(nodeInformation.isStartupLeader()).thenReturn(true);
+
+ test(SERVER);
+
+ verifyDb(CHECKSUM_1);
+ verifyCreateNewServerIdFromScratch();
+ }
+
+ @Test
+ public void web_leader_keeps_existing_server_id_if_valid() {
+ insertServerId(WITH_DATABASE_ID_SERVER_ID);
+ insertChecksum(CHECKSUM_1);
+ mockChecksumOf(WITH_DATABASE_ID_SERVER_ID, CHECKSUM_1);
+ when(nodeInformation.isStartupLeader()).thenReturn(true);
+
+ test(SERVER);
+
+ verifyDb(CHECKSUM_1);
+ }
+
+ @Test
+ public void web_leader_creates_server_id_from_current_serverId_with_databaseId_if_checksum_fails() {
+ ServerId currentServerId = ServerId.of(randomAlphanumeric(DATABASE_ID_LENGTH), randomAlphanumeric(UUID_DATASET_ID_LENGTH));
+ insertServerId(currentServerId);
+ insertChecksum("does_not_match_WITH_DATABASE_ID_SERVER_ID");
+ mockChecksumOf(currentServerId, "matches_WITH_DATABASE_ID_SERVER_ID");
+ mockCreateNewServerIdFrom(currentServerId);
+ mockChecksumOf(WITH_DATABASE_ID_SERVER_ID, CHECKSUM_1);
+ when(nodeInformation.isStartupLeader()).thenReturn(true);
+
+ test(SERVER);
+
+ verifyDb(CHECKSUM_1);
+ verifyCreateNewServerIdFrom(currentServerId);
+ }
+
+ @Test
+ public void web_leader_generates_missing_checksum_for_current_serverId_with_databaseId() {
+ insertServerId(WITH_DATABASE_ID_SERVER_ID);
+ mockChecksumOf(WITH_DATABASE_ID_SERVER_ID, CHECKSUM_1);
+ when(nodeInformation.isStartupLeader()).thenReturn(true);
+
+ test(SERVER);
+
+ verifyDb(CHECKSUM_1);
+ }
+
+ @Test
+ public void web_follower_does_not_fail_if_server_id_matches_checksum() {
+ insertServerId(WITH_DATABASE_ID_SERVER_ID);
+ insertChecksum(CHECKSUM_1);
+ mockChecksumOf(WITH_DATABASE_ID_SERVER_ID, CHECKSUM_1);
+ when(nodeInformation.isStartupLeader()).thenReturn(false);
+
+ test(SERVER);
+
+ // no changes
+ verifyDb(CHECKSUM_1);
+ }
+
+ @Test
+ public void web_follower_fails_if_server_id_is_missing() {
+ when(nodeInformation.isStartupLeader()).thenReturn(false);
+
+ expectMissingServerIdException(() -> test(SERVER));
+ }
+
+ @Test
+ public void web_follower_fails_if_server_id_is_empty() {
+ insertServerId("");
+ when(nodeInformation.isStartupLeader()).thenReturn(false);
+
+ expectEmptyServerIdException(() -> test(SERVER));
+ }
+
+ @Test
+ public void web_follower_fails_if_checksum_does_not_match() {
+ String dbChecksum = "boom";
+ insertServerId(WITH_DATABASE_ID_SERVER_ID);
+ insertChecksum(dbChecksum);
+ mockChecksumOf(WITH_DATABASE_ID_SERVER_ID, CHECKSUM_1);
+ when(nodeInformation.isStartupLeader()).thenReturn(false);
+
+ try {
+ test(SERVER);
+ fail("An ISE should have been raised");
+ } catch (IllegalStateException e) {
+ assertThat(e.getMessage()).isEqualTo("Server ID is invalid");
+ // no changes
+ verifyDb(dbChecksum);
+ }
+ }
+
+ @Test
+ public void compute_engine_does_not_fail_if_server_id_is_valid() {
+ insertServerId(WITH_DATABASE_ID_SERVER_ID);
+ insertChecksum(CHECKSUM_1);
+ mockChecksumOf(WITH_DATABASE_ID_SERVER_ID, CHECKSUM_1);
+
+ test(COMPUTE_ENGINE);
+
+ // no changes
+ verifyDb(CHECKSUM_1);
+ }
+
+ @Test
+ public void compute_engine_fails_if_server_id_is_missing() {
+ expectMissingServerIdException(() -> test(COMPUTE_ENGINE));
+ }
+
+ @Test
+ public void compute_engine_fails_if_server_id_is_empty() {
+ insertServerId("");
+
+ expectEmptyServerIdException(() -> test(COMPUTE_ENGINE));
+ }
+
+ @Test
+ public void compute_engine_fails_if_server_id_is_invalid() {
+ String dbChecksum = "boom";
+ insertServerId(WITH_DATABASE_ID_SERVER_ID);
+ insertChecksum(dbChecksum);
+ mockChecksumOf(WITH_DATABASE_ID_SERVER_ID, CHECKSUM_1);
+
+ try {
+ test(SERVER);
+ fail("An ISE should have been raised");
+ } catch (IllegalStateException e) {
+ assertThat(e.getMessage()).isEqualTo("Server ID is invalid");
+ // no changes
+ verifyDb(dbChecksum);
+ }
+ }
+
+ private void expectEmptyServerIdException(ThrowingCallable callback) {
+ assertThatThrownBy(callback)
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Property sonar.core.id is empty in database");
+ }
+
+ private void expectMissingServerIdException(ThrowingCallable callback) {
+ assertThatThrownBy(callback)
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Property sonar.core.id is missing in database");
+ }
+
+ private void verifyDb(String expectedChecksum) {
+ assertThat(dbClient.propertiesDao().selectGlobalProperty(dbSession, CoreProperties.SERVER_ID))
+ .extracting(PropertyDto::getValue)
+ .isEqualTo(ServerIdManagerIT.WITH_DATABASE_ID_SERVER_ID.toString());
+ assertThat(dbClient.internalPropertiesDao().selectByKey(dbSession, InternalProperties.SERVER_ID_CHECKSUM))
+ .hasValue(expectedChecksum);
+ }
+
+ private void mockCreateNewServerId() {
+ when(serverIdFactory.create()).thenReturn(WITH_DATABASE_ID_SERVER_ID);
+ when(serverIdFactory.create(any())).thenThrow(new IllegalStateException("new ServerId should not be created from current server id"));
+ }
+
+ private void mockCreateNewServerIdFrom(ServerId currentServerId) {
+ when(serverIdFactory.create()).thenThrow(new IllegalStateException("new ServerId should be created from current server id"));
+ when(serverIdFactory.create(currentServerId)).thenReturn(ServerIdManagerIT.WITH_DATABASE_ID_SERVER_ID);
+ }
+
+ private void verifyCreateNewServerIdFromScratch() {
+ verify(serverIdFactory).create();
+ }
+
+ private void verifyCreateNewServerIdFrom(ServerId currentServerId) {
+ verify(serverIdFactory).create(currentServerId);
+ }
+
+ private void mockChecksumOf(ServerId serverId, String checksum1) {
+ when(serverIdChecksum.computeFor(serverId.toString())).thenReturn(checksum1);
+ }
+
+ private void insertServerId(ServerId serverId) {
+ insertServerId(serverId.toString());
+ }
+
+ private void insertServerId(String serverId) {
+ dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto().setKey(CoreProperties.SERVER_ID).setValue(serverId),
+ null, null, null, null);
+ dbSession.commit();
+ }
+
+ private void insertChecksum(String value) {
+ dbClient.internalPropertiesDao().save(dbSession, InternalProperties.SERVER_ID_CHECKSUM, value);
+ dbSession.commit();
+ }
+
+ private void test(SonarQubeSide side) {
+ underTest = new ServerIdManager(serverIdChecksum, serverIdFactory, dbClient, SonarRuntimeImpl
+ .forSonarQube(Version.create(6, 7), side, SonarEdition.COMMUNITY), nodeInformation);
+ underTest.start();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.startup;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.stream.IntStream;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.Metrics;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.SequenceUuidFactory;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.metric.MetricDto;
+
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
+
+public class RegisterMetricsIT {
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private final UuidFactory uuidFactory = new SequenceUuidFactory();
+ private final DbClient dbClient = dbTester.getDbClient();
+ private final RegisterMetrics register = new RegisterMetrics(dbClient, uuidFactory);
+
+ /**
+ * Insert new metrics, including custom metrics
+ */
+ @Test
+ public void insert_new_metrics() {
+ Metric m1 = new Metric.Builder("m1", "One", Metric.ValueType.FLOAT)
+ .setDescription("desc1")
+ .setDirection(1)
+ .setQualitative(true)
+ .setDomain("domain1")
+ .setUserManaged(false)
+ .create();
+ Metric custom = new Metric.Builder("custom", "Custom", Metric.ValueType.FLOAT)
+ .setDescription("This is a custom metric")
+ .setUserManaged(true)
+ .create();
+
+ register.register(asList(m1, custom));
+
+ Map<String, MetricDto> metricsByKey = selectAllMetrics();
+ assertThat(metricsByKey).hasSize(2);
+ assertEquals(m1, metricsByKey.get("m1"));
+ assertEquals(custom, metricsByKey.get("custom"));
+ }
+
+ /**
+ * Update existing metrics
+ */
+ @Test
+ public void update_metrics() {
+ dbTester.measures().insertMetric(t -> t.setKey("m1")
+ .setShortName("name")
+ .setValueType(Metric.ValueType.INT.name())
+ .setDescription("old desc")
+ .setDomain("old domain")
+ .setShortName("old short name")
+ .setQualitative(false)
+ .setEnabled(true)
+ .setOptimizedBestValue(false)
+ .setDirection(1)
+ .setHidden(false));
+
+ Metric m1 = new Metric.Builder("m1", "New name", Metric.ValueType.FLOAT)
+ .setDescription("new description")
+ .setDirection(-1)
+ .setQualitative(true)
+ .setDomain("new domain")
+ .setUserManaged(false)
+ .setDecimalScale(3)
+ .setHidden(true)
+ .create();
+ register.register(asList(m1));
+
+ Map<String, MetricDto> metricsByKey = selectAllMetrics();
+ assertThat(metricsByKey).hasSize(1);
+ assertEquals(m1, metricsByKey.get("m1"));
+ }
+
+ @Test
+ public void disable_undefined_metrics() {
+ Random random = new Random();
+ int count = 1 + random.nextInt(10);
+ IntStream.range(0, count)
+ .forEach(t -> dbTester.measures().insertMetric(m -> m.setEnabled(random.nextBoolean())));
+
+ register.register(Collections.emptyList());
+
+ assertThat(selectAllMetrics().values().stream())
+ .extracting(MetricDto::isEnabled)
+ .containsOnly(IntStream.range(0, count).mapToObj(t -> false).toArray(Boolean[]::new));
+ }
+
+ @Test
+ public void enable_disabled_metrics() {
+ MetricDto enabledMetric = dbTester.measures().insertMetric(t -> t.setEnabled(true));
+ MetricDto disabledMetric = dbTester.measures().insertMetric(t -> t.setEnabled(false));
+
+ register.register(asList(builderOf(enabledMetric).create(), builderOf(disabledMetric).create()));
+
+ assertThat(selectAllMetrics().values())
+ .extracting(MetricDto::isEnabled)
+ .containsOnly(true, true);
+ }
+
+ @Test
+ public void insert_core_metrics() {
+ register.start();
+
+ assertThat(dbTester.countRowsOfTable("metrics")).isEqualTo(CoreMetrics.getMetrics().size());
+ }
+
+ @Test
+ public void fail_if_duplicated_plugin_metrics() {
+ Metrics plugin1 = new TestMetrics(new Metric.Builder("m1", "In first plugin", Metric.ValueType.FLOAT).create());
+ Metrics plugin2 = new TestMetrics(new Metric.Builder("m1", "In second plugin", Metric.ValueType.FLOAT).create());
+
+ assertThatThrownBy(() -> new RegisterMetrics(dbClient, uuidFactory, new Metrics[] {plugin1, plugin2}).start())
+ .isInstanceOf(IllegalStateException.class);
+ }
+
+ @Test
+ public void fail_if_plugin_duplicates_core_metric() {
+ Metrics plugin = new TestMetrics(new Metric.Builder("ncloc", "In plugin", Metric.ValueType.FLOAT).create());
+
+ assertThatThrownBy(() -> new RegisterMetrics(dbClient, uuidFactory, new Metrics[] {plugin}).start())
+ .isInstanceOf(IllegalStateException.class);
+ }
+
+ private static class TestMetrics implements Metrics {
+ private final List<Metric> metrics;
+
+ public TestMetrics(Metric... metrics) {
+ this.metrics = asList(metrics);
+ }
+
+ @Override
+ public List<Metric> getMetrics() {
+ return metrics;
+ }
+ }
+
+ private Map<String, MetricDto> selectAllMetrics() {
+ return dbTester.getDbClient().metricDao().selectAll(dbTester.getSession())
+ .stream()
+ .collect(uniqueIndex(MetricDto::getKey));
+ }
+
+ private void assertEquals(Metric expected, MetricDto actual) {
+ assertThat(actual.getKey()).isEqualTo(expected.getKey());
+ assertThat(actual.getShortName()).isEqualTo(expected.getName());
+ assertThat(actual.getValueType()).isEqualTo(expected.getType().name());
+ assertThat(actual.getDescription()).isEqualTo(expected.getDescription());
+ assertThat(actual.getDirection()).isEqualTo(expected.getDirection());
+ assertThat(actual.isQualitative()).isEqualTo(expected.getQualitative());
+ }
+
+ private static Metric.Builder builderOf(MetricDto enabledMetric) {
+ return new Metric.Builder(enabledMetric.getKey(), enabledMetric.getShortName(), Metric.ValueType.valueOf(enabledMetric.getValueType()))
+ .setDescription(enabledMetric.getDescription())
+ .setDirection(enabledMetric.getDirection())
+ .setQualitative(enabledMetric.isQualitative())
+ .setQualitative(enabledMetric.isQualitative())
+ .setDomain(enabledMetric.getDomain())
+ .setHidden(enabledMetric.isHidden());
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.startup;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.utils.System2;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.plugin.PluginDto;
+import org.sonar.db.plugin.PluginDto.Type;
+import org.sonar.server.plugins.PluginFilesAndMd5;
+import org.sonar.core.plugin.PluginType;
+import org.sonar.server.plugins.ServerPlugin;
+import org.sonar.server.plugins.ServerPluginRepository;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
+
+public class RegisterPluginsIT {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private final long now = 12345L;
+ private final DbClient dbClient = dbTester.getDbClient();
+ private final ServerPluginRepository serverPluginRepository = new ServerPluginRepository();
+ private final UuidFactory uuidFactory = mock(UuidFactory.class);
+ private final System2 system2 = mock(System2.class);
+ private final RegisterPlugins register = new RegisterPlugins(serverPluginRepository, dbClient, uuidFactory, system2);
+
+
+ @Before
+ public void setUp() {
+ when(system2.now()).thenReturn(now);
+ }
+
+ /**
+ * Insert new plugins
+ */
+ @Test
+ public void insert_new_plugins() throws IOException {
+ addPlugin("java", null);
+ addPlugin("javacustom", "java");
+ when(uuidFactory.create()).thenReturn("a").thenReturn("b").thenThrow(new IllegalStateException("Should be called only twice"));
+ register.start();
+
+ Map<String, PluginDto> pluginsByKey = selectAllPlugins();
+ assertThat(pluginsByKey).hasSize(2);
+ verify(pluginsByKey.get("java"), Type.BUNDLED, null, "93f725a07423fe1c889f448b33d21f46", false, now, now);
+ verify(pluginsByKey.get("javacustom"), Type.BUNDLED, "java", "bf8b3d4cb91efc5f9ef0bcd02f128ebf", false, now, now);
+
+ register.stop();
+ }
+
+ @Test
+ public void update_removed_plugins() throws IOException {
+ // will be flagged as removed
+ dbClient.pluginDao().insert(dbTester.getSession(), new PluginDto()
+ .setUuid("a")
+ .setKee("java")
+ .setBasePluginKey(null)
+ .setFileHash("bd451e47a1aa76e73da0359cef63dd63")
+ .setType(Type.BUNDLED)
+ .setCreatedAt(1L)
+ .setUpdatedAt(1L));
+ // already flagged as removed
+ dbClient.pluginDao().insert(dbTester.getSession(), new PluginDto()
+ .setUuid("b")
+ .setKee("java2")
+ .setBasePluginKey(null)
+ .setFileHash("bd451e47a1aa76e73da0359cef63dd63")
+ .setType(Type.BUNDLED)
+ .setRemoved(true)
+ .setCreatedAt(1L)
+ .setUpdatedAt(1L));
+ dbClient.pluginDao().insert(dbTester.getSession(), new PluginDto()
+ .setUuid("c")
+ .setKee("csharp")
+ .setBasePluginKey(null)
+ .setFileHash("a20d785dbacb8f41a3b7392aa7d03b78")
+ .setType(Type.EXTERNAL)
+ .setCreatedAt(1L)
+ .setUpdatedAt(1L));
+ dbTester.commit();
+
+ addPlugin("csharp", PluginType.EXTERNAL, null);
+ register.start();
+
+ Map<String, PluginDto> pluginsByKey = selectAllPlugins();
+ assertThat(pluginsByKey).hasSize(3);
+ verify(pluginsByKey.get("java"), Type.BUNDLED, null, "bd451e47a1aa76e73da0359cef63dd63", true, 1L, now);
+ verify(pluginsByKey.get("java2"), Type.BUNDLED, null, "bd451e47a1aa76e73da0359cef63dd63", true, 1L, 1L);
+ verify(pluginsByKey.get("csharp"), Type.EXTERNAL, null, "a20d785dbacb8f41a3b7392aa7d03b78", false, 1L, 1L);
+ }
+
+ @Test
+ public void re_add_previously_removed_plugin() throws IOException {
+ dbClient.pluginDao().insert(dbTester.getSession(), new PluginDto()
+ .setUuid("c")
+ .setKee("csharp")
+ .setBasePluginKey(null)
+ .setFileHash("a20d785dbacb8f41a3b7392aa7d03b78")
+ .setType(Type.EXTERNAL)
+ .setRemoved(true)
+ .setCreatedAt(1L)
+ .setUpdatedAt(1L));
+ dbTester.commit();
+
+ addPlugin("csharp", PluginType.EXTERNAL, null);
+ register.start();
+
+ Map<String, PluginDto> pluginsByKey = selectAllPlugins();
+ assertThat(pluginsByKey).hasSize(1);
+ verify(pluginsByKey.get("csharp"), Type.EXTERNAL, null, "a20d785dbacb8f41a3b7392aa7d03b78", false, 1L, now);
+ }
+
+ /**
+ * Update existing plugins, only when checksum is different and don't remove uninstalled plugins
+ */
+ @Test
+ public void update_changed_plugins() throws IOException {
+ dbClient.pluginDao().insert(dbTester.getSession(), new PluginDto()
+ .setUuid("a")
+ .setKee("java")
+ .setBasePluginKey(null)
+ .setFileHash("bd451e47a1aa76e73da0359cef63dd63")
+ .setType(Type.BUNDLED)
+ .setCreatedAt(1L)
+ .setUpdatedAt(1L));
+ dbClient.pluginDao().insert(dbTester.getSession(), new PluginDto()
+ .setUuid("b")
+ .setKee("javacustom")
+ .setBasePluginKey("java")
+ .setFileHash("de9b2de3ddc0680904939686c0dba5be")
+ .setType(Type.BUNDLED)
+ .setCreatedAt(1L)
+ .setUpdatedAt(1L));
+ dbClient.pluginDao().insert(dbTester.getSession(), new PluginDto()
+ .setUuid("c")
+ .setKee("csharp")
+ .setBasePluginKey(null)
+ .setFileHash("a20d785dbacb8f41a3b7392aa7d03b78")
+ .setType(Type.EXTERNAL)
+ .setCreatedAt(1L)
+ .setUpdatedAt(1L));
+ dbClient.pluginDao().insert(dbTester.getSession(), new PluginDto()
+ .setUuid("d")
+ .setKee("new-measures")
+ .setBasePluginKey(null)
+ .setFileHash("6d24712cf701c41ce5eaa948e0bd6d22")
+ .setType(Type.EXTERNAL)
+ .setCreatedAt(1L)
+ .setUpdatedAt(1L));
+
+ dbTester.commit();
+
+ addPlugin("javacustom", PluginType.BUNDLED, "java2");
+ // csharp plugin type changed
+ addPlugin("csharp", PluginType.BUNDLED, null);
+
+ register.start();
+
+ Map<String, PluginDto> pluginsByKey = selectAllPlugins();
+ assertThat(pluginsByKey).hasSize(4);
+ verify(pluginsByKey.get("java"), Type.BUNDLED, null, "bd451e47a1aa76e73da0359cef63dd63", true, 1L, now);
+ verify(pluginsByKey.get("javacustom"), Type.BUNDLED, "java2", "bf8b3d4cb91efc5f9ef0bcd02f128ebf", false, 1L, now);
+ verify(pluginsByKey.get("csharp"), Type.BUNDLED, null, "a20d785dbacb8f41a3b7392aa7d03b78", false, 1L, now);
+ verify(pluginsByKey.get("new-measures"), Type.EXTERNAL, null, "6d24712cf701c41ce5eaa948e0bd6d22", true, 1L, now);
+ }
+
+ private ServerPlugin addPlugin(String key, @Nullable String basePlugin) throws IOException {
+ return addPlugin(key, PluginType.BUNDLED, basePlugin);
+ }
+
+ private ServerPlugin addPlugin(String key, PluginType type, @Nullable String basePlugin) throws IOException {
+ File file = createPluginFile(key);
+ PluginFilesAndMd5.FileAndMd5 jar = new PluginFilesAndMd5.FileAndMd5(file);
+ PluginInfo info = new PluginInfo(key)
+ .setBasePlugin(basePlugin)
+ .setJarFile(file);
+ ServerPlugin serverPlugin = new ServerPlugin(info, type, null, jar, null);
+ serverPluginRepository.addPlugin(serverPlugin);
+ return serverPlugin;
+ }
+
+ private File createPluginFile(String key) throws IOException {
+ File pluginJar = temp.newFile();
+ FileUtils.write(pluginJar, key, StandardCharsets.UTF_8);
+ return pluginJar;
+ }
+
+ private Map<String, PluginDto> selectAllPlugins() {
+ return dbTester.getDbClient().pluginDao().selectAll(dbTester.getSession()).stream()
+ .collect(uniqueIndex(PluginDto::getKee));
+ }
+
+ private void verify(PluginDto pluginDto, Type type, @Nullable String basePluginKey, String fileHash, boolean removed, @Nullable Long createdAt, long updatedAt) {
+ assertThat(pluginDto.getBasePluginKey()).isEqualTo(basePluginKey);
+ assertThat(pluginDto.getType()).isEqualTo(type);
+ assertThat(pluginDto.getFileHash()).isEqualTo(fileHash);
+ assertThat(pluginDto.isRemoved()).isEqualTo(removed);
+ assertThat(pluginDto.getCreatedAt()).isEqualTo(createdAt);
+ assertThat(pluginDto.getUpdatedAt()).isEqualTo(updatedAt);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.startup;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sonar.api.SonarEdition;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.ce.CeTaskMessageDto;
+import org.sonar.db.ce.CeTaskMessageType;
+import org.sonar.db.user.UserDismissedMessageDto;
+import org.sonar.db.user.UserDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(DataProviderRunner.class)
+public class UpgradeSuggestionsCleanerIT {
+ private static final String TASK_UUID = "b8d564dd-4ceb-4dba-8a3d-5fafa2d72cdf";
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private final SonarRuntime sonarRuntime = mock(SonarRuntime.class);
+ private final UpgradeSuggestionsCleaner underTest = new UpgradeSuggestionsCleaner(dbTester.getDbClient(), sonarRuntime);
+
+ private UserDto user;
+
+ @Before
+ public void setup() {
+ user = dbTester.users().insertUser();
+ }
+
+ @DataProvider
+ public static Object[][] editionsWithCleanup() {
+ return new Object[][] {
+ {SonarEdition.DEVELOPER},
+ {SonarEdition.ENTERPRISE},
+ {SonarEdition.DATACENTER}
+ };
+ }
+
+ @DataProvider
+ public static Object[][] allEditions() {
+ return new Object[][] {
+ {SonarEdition.COMMUNITY},
+ {SonarEdition.DEVELOPER},
+ {SonarEdition.ENTERPRISE},
+ {SonarEdition.DATACENTER}
+ };
+ }
+
+ @Test
+ @UseDataProvider("editionsWithCleanup")
+ public void start_cleans_up_obsolete_upgrade_suggestions(SonarEdition edition) {
+ when(sonarRuntime.getEdition()).thenReturn(edition);
+ insertCeTaskMessage("ctm1", CeTaskMessageType.GENERIC, "msg1");
+ insertCeTaskMessage("ctm2", CeTaskMessageType.GENERIC, "msg2");
+ insertCeTaskMessage("ctm3", CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE, "upgrade-msg-1");
+ insertInUserDismissedMessages("u1", CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+ insertInUserDismissedMessages("u2", CeTaskMessageType.GENERIC);
+
+ underTest.start();
+ underTest.stop();
+
+ assertThat(dbTester.getDbClient().ceTaskMessageDao().selectByTask(dbTester.getSession(), TASK_UUID))
+ .extracting(CeTaskMessageDto::getUuid)
+ .containsExactly("ctm1", "ctm2");
+ assertThat(dbTester.getDbClient().userDismissedMessagesDao().selectByUser(dbTester.getSession(), user))
+ .extracting(UserDismissedMessageDto::getUuid)
+ .containsExactly("u2");
+ }
+
+ @Test
+ public void start_does_nothing_in_community_edition() {
+ when(sonarRuntime.getEdition()).thenReturn(SonarEdition.COMMUNITY);
+ insertCeTaskMessage("ctm1", CeTaskMessageType.GENERIC, "msg1");
+ insertCeTaskMessage("ctm2", CeTaskMessageType.GENERIC, "msg2");
+ insertCeTaskMessage("ctm3", CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE, "upgrade-msg-1");
+ insertInUserDismissedMessages("u1", CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+ insertInUserDismissedMessages("u2", CeTaskMessageType.GENERIC);
+
+ underTest.start();
+
+ assertThat(dbTester.getDbClient().ceTaskMessageDao().selectByTask(dbTester.getSession(), TASK_UUID))
+ .extracting(CeTaskMessageDto::getUuid)
+ .containsExactly("ctm1", "ctm2", "ctm3");
+ assertThat(dbTester.getDbClient().userDismissedMessagesDao().selectByUser(dbTester.getSession(), user))
+ .extracting(UserDismissedMessageDto::getUuid)
+ .containsExactlyInAnyOrder("u1", "u2");
+ }
+
+ @Test
+ @UseDataProvider("allEditions")
+ public void start_does_nothing_when_no_suggest_upgrade_messages(SonarEdition edition) {
+ when(sonarRuntime.getEdition()).thenReturn(edition);
+
+ underTest.start();
+
+ assertThat(dbTester.getDbClient().ceTaskMessageDao().selectByTask(dbTester.getSession(), TASK_UUID)).isEmpty();
+ assertThat(dbTester.getDbClient().userDismissedMessagesDao().selectByUser(dbTester.getSession(), user)).isEmpty();
+ }
+
+ private void insertCeTaskMessage(String uuid, CeTaskMessageType messageType, String msg) {
+ CeTaskMessageDto dto = new CeTaskMessageDto()
+ .setUuid(uuid)
+ .setMessage(msg)
+ .setType(messageType)
+ .setTaskUuid(TASK_UUID);
+ dbTester.getDbClient().ceTaskMessageDao().insert(dbTester.getSession(), dto);
+ dbTester.getSession().commit();
+ }
+
+ private void insertInUserDismissedMessages(String uuid, CeTaskMessageType messageType) {
+ UserDismissedMessageDto dto = new UserDismissedMessageDto()
+ .setUuid(uuid)
+ .setUserUuid(user.getUuid())
+ .setProjectUuid("PROJECT_1")
+ .setCeMessageType(messageType);
+ dbTester.getDbClient().userDismissedMessagesDao().insert(dbTester.getSession(), dto);
+ dbTester.getSession().commit();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.webhook;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Random;
+import java.util.Set;
+import java.util.function.Supplier;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactoryFast;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.AnalysisPropertyDto;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.project.ProjectDto;
+import org.sonar.server.qualitygate.EvaluatedQualityGate;
+import org.sonar.server.qualitygate.changeevent.QGChangeEvent;
+import org.sonar.server.qualitygate.changeevent.QGChangeEventListener;
+
+import static java.util.Arrays.stream;
+import static java.util.Collections.emptySet;
+import static java.util.stream.Stream.concat;
+import static java.util.stream.Stream.of;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+import static org.sonar.core.util.stream.MoreCollectors.toArrayList;
+import static org.sonar.db.component.BranchType.BRANCH;
+
+@RunWith(DataProviderRunner.class)
+public class WebhookQGChangeEventListenerIT {
+
+ private static final Set<QGChangeEventListener.ChangedIssue> CHANGED_ISSUES_ARE_IGNORED = emptySet();
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private DbClient dbClient = dbTester.getDbClient();
+
+ private EvaluatedQualityGate newQualityGate = mock(EvaluatedQualityGate.class);
+ private WebHooks webHooks = mock(WebHooks.class);
+ private WebhookPayloadFactory webhookPayloadFactory = mock(WebhookPayloadFactory.class);
+ private DbClient spiedOnDbClient = Mockito.spy(dbClient);
+ private WebhookQGChangeEventListener underTest = new WebhookQGChangeEventListener(webHooks, webhookPayloadFactory, spiedOnDbClient);
+ private DbClient mockedDbClient = mock(DbClient.class);
+ private WebhookQGChangeEventListener mockedUnderTest = new WebhookQGChangeEventListener(webHooks, webhookPayloadFactory, mockedDbClient);
+
+ @Test
+ @UseDataProvider("allCombinationsOfStatuses")
+ public void onIssueChanges_has_no_effect_if_no_webhook_is_configured(Metric.Level previousStatus, Metric.Level newStatus) {
+ Configuration configuration1 = mock(Configuration.class);
+ when(newQualityGate.getStatus()).thenReturn(newStatus);
+ QGChangeEvent qualityGateEvent = newQGChangeEvent(configuration1, previousStatus, newQualityGate);
+ mockWebhookDisabled(qualityGateEvent.getProject());
+
+ mockedUnderTest.onIssueChanges(qualityGateEvent, CHANGED_ISSUES_ARE_IGNORED);
+
+ verify(webHooks).isEnabled(qualityGateEvent.getProject());
+ verifyNoInteractions(webhookPayloadFactory, mockedDbClient);
+ }
+
+ @DataProvider
+ public static Object[][] allCombinationsOfStatuses() {
+ Metric.Level[] levelsAndNull = concat(of((Metric.Level) null), stream(Metric.Level.values()))
+ .toArray(Metric.Level[]::new);
+ Object[][] res = new Object[levelsAndNull.length * levelsAndNull.length][2];
+ int i = 0;
+ for (Metric.Level previousStatus : levelsAndNull) {
+ for (Metric.Level newStatus : levelsAndNull) {
+ res[i][0] = previousStatus;
+ res[i][1] = newStatus;
+ i++;
+ }
+ }
+ return res;
+ }
+
+ @Test
+ public void onIssueChanges_has_no_effect_if_event_has_neither_previousQGStatus_nor_qualityGate() {
+ Configuration configuration = mock(Configuration.class);
+ QGChangeEvent qualityGateEvent = newQGChangeEvent(configuration, null, null);
+ mockWebhookEnabled(qualityGateEvent.getProject());
+
+ underTest.onIssueChanges(qualityGateEvent, CHANGED_ISSUES_ARE_IGNORED);
+
+ verifyNoInteractions(webhookPayloadFactory, mockedDbClient);
+ }
+
+ @Test
+ public void onIssueChanges_has_no_effect_if_event_has_same_status_in_previous_and_new_QG() {
+ Configuration configuration = mock(Configuration.class);
+ Metric.Level previousStatus = randomLevel();
+ when(newQualityGate.getStatus()).thenReturn(previousStatus);
+ QGChangeEvent qualityGateEvent = newQGChangeEvent(configuration, previousStatus, newQualityGate);
+ mockWebhookEnabled(qualityGateEvent.getProject());
+
+ underTest.onIssueChanges(qualityGateEvent, CHANGED_ISSUES_ARE_IGNORED);
+
+ verifyNoInteractions(webhookPayloadFactory, mockedDbClient);
+ }
+
+ @Test
+ @UseDataProvider("newQGorNot")
+ public void onIssueChanges_calls_webhook_for_changeEvent_with_webhook_enabled(@Nullable EvaluatedQualityGate newQualityGate) {
+ ProjectAndBranch projectBranch = insertBranch(BRANCH, "foo");
+ SnapshotDto analysis = insertAnalysisTask(projectBranch);
+ Configuration configuration = mock(Configuration.class);
+ mockPayloadSupplierConsumedByWebhooks();
+ Map<String, String> properties = new HashMap<>();
+ properties.put("sonar.analysis.test1", randomAlphanumeric(50));
+ properties.put("sonar.analysis.test2", randomAlphanumeric(5000));
+ insertPropertiesFor(analysis.getUuid(), properties);
+ QGChangeEvent qualityGateEvent = newQGChangeEvent(projectBranch, analysis, configuration, newQualityGate);
+ mockWebhookEnabled(qualityGateEvent.getProject());
+
+ underTest.onIssueChanges(qualityGateEvent, CHANGED_ISSUES_ARE_IGNORED);
+
+ ProjectAnalysis projectAnalysis = verifyWebhookCalledAndExtractPayloadFactoryArgument(projectBranch, analysis, qualityGateEvent.getProject());
+ assertThat(projectAnalysis).isEqualTo(
+ new ProjectAnalysis(
+ new Project(projectBranch.project.getUuid(), projectBranch.project.getKey(), projectBranch.project.getName()),
+ null,
+ new Analysis(analysis.getUuid(), analysis.getCreatedAt(), analysis.getRevision()),
+ new Branch(false, "foo", Branch.Type.BRANCH),
+ newQualityGate,
+ null,
+ properties));
+ }
+
+ @Test
+ @UseDataProvider("newQGorNot")
+ public void onIssueChanges_calls_webhook_on_main_branch(@Nullable EvaluatedQualityGate newQualityGate) {
+ ProjectAndBranch mainBranch = insertMainBranch();
+ SnapshotDto analysis = insertAnalysisTask(mainBranch);
+ Configuration configuration = mock(Configuration.class);
+ QGChangeEvent qualityGateEvent = newQGChangeEvent(mainBranch, analysis, configuration, newQualityGate);
+ mockWebhookEnabled(qualityGateEvent.getProject());
+
+ underTest.onIssueChanges(qualityGateEvent, CHANGED_ISSUES_ARE_IGNORED);
+
+ verifyWebhookCalled(mainBranch, analysis, qualityGateEvent.getProject());
+ }
+
+ @Test
+ public void onIssueChanges_calls_webhook_on_branch() {
+ onIssueChangesCallsWebhookOnBranch(BRANCH);
+ }
+
+ @Test
+ public void onIssueChanges_calls_webhook_on_pr() {
+ onIssueChangesCallsWebhookOnBranch(BranchType.PULL_REQUEST);
+ }
+
+ public void onIssueChangesCallsWebhookOnBranch(BranchType branchType) {
+ ProjectAndBranch nonMainBranch = insertBranch(branchType, "foo");
+ SnapshotDto analysis = insertAnalysisTask(nonMainBranch);
+ Configuration configuration = mock(Configuration.class);
+ QGChangeEvent qualityGateEvent = newQGChangeEvent(nonMainBranch, analysis, configuration, null);
+ mockWebhookEnabled(qualityGateEvent.getProject());
+
+ underTest.onIssueChanges(qualityGateEvent, CHANGED_ISSUES_ARE_IGNORED);
+
+ verifyWebhookCalled(nonMainBranch, analysis, qualityGateEvent.getProject());
+ }
+
+ @DataProvider
+ public static Object[][] newQGorNot() {
+ EvaluatedQualityGate newQualityGate = mock(EvaluatedQualityGate.class);
+ return new Object[][] {
+ {null},
+ {newQualityGate}
+ };
+ }
+
+ private void mockWebhookEnabled(ProjectDto... projects) {
+ for (ProjectDto dto : projects) {
+ when(webHooks.isEnabled(dto)).thenReturn(true);
+ }
+ }
+
+ private void mockWebhookDisabled(ProjectDto... projects) {
+ for (ProjectDto dto : projects) {
+ when(webHooks.isEnabled(dto)).thenReturn(false);
+ }
+ }
+
+ private void mockPayloadSupplierConsumedByWebhooks() {
+ Mockito.doAnswer(invocationOnMock -> {
+ Supplier<WebhookPayload> supplier = (Supplier<WebhookPayload>) invocationOnMock.getArguments()[1];
+ supplier.get();
+ return null;
+ }).when(webHooks)
+ .sendProjectAnalysisUpdate(any(), any());
+ }
+
+ private void insertPropertiesFor(String snapshotUuid, Map<String, String> properties) {
+ List<AnalysisPropertyDto> analysisProperties = properties.entrySet().stream()
+ .map(entry -> new AnalysisPropertyDto()
+ .setUuid(UuidFactoryFast.getInstance().create())
+ .setAnalysisUuid(snapshotUuid)
+ .setKey(entry.getKey())
+ .setValue(entry.getValue()))
+ .collect(toArrayList(properties.size()));
+ dbTester.getDbClient().analysisPropertiesDao().insert(dbTester.getSession(), analysisProperties);
+ dbTester.getSession().commit();
+ }
+
+ private SnapshotDto insertAnalysisTask(ProjectAndBranch projectAndBranch) {
+ return dbTester.components().insertSnapshot(projectAndBranch.getBranch());
+ }
+
+ private ProjectAnalysis verifyWebhookCalledAndExtractPayloadFactoryArgument(ProjectAndBranch projectAndBranch, SnapshotDto analysis, ProjectDto project) {
+ verifyWebhookCalled(projectAndBranch, analysis, project);
+
+ return extractPayloadFactoryArguments(1).iterator().next();
+ }
+
+ private void verifyWebhookCalled(ProjectAndBranch projectAndBranch, SnapshotDto analysis, ProjectDto project) {
+ verify(webHooks).isEnabled(project);
+ verify(webHooks).sendProjectAnalysisUpdate(
+ eq(new WebHooks.Analysis(projectAndBranch.uuid(), analysis.getUuid(), null)),
+ any());
+ }
+
+ private List<ProjectAnalysis> extractPayloadFactoryArguments(int time) {
+ ArgumentCaptor<ProjectAnalysis> projectAnalysisCaptor = ArgumentCaptor.forClass(ProjectAnalysis.class);
+ verify(webhookPayloadFactory, Mockito.times(time)).create(projectAnalysisCaptor.capture());
+ return projectAnalysisCaptor.getAllValues();
+ }
+
+ public ProjectAndBranch insertMainBranch() {
+ ProjectDto project = dbTester.components().insertPrivateProjectDto();
+ BranchDto branch = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), project.getUuid()).get();
+ dbTester.commit();
+ return new ProjectAndBranch(project, branch);
+ }
+
+ public ProjectAndBranch insertBranch(BranchType type, String branchKey) {
+ ProjectDto project = dbTester.components().insertPrivateProjectDto();
+ BranchDto branch = dbTester.components().insertProjectBranch(project, b -> b.setKey(branchKey).setBranchType(type));
+ return new ProjectAndBranch(project, branch);
+ }
+
+ public ProjectAndBranch insertBranch(ProjectDto project, BranchType type, String branchKey) {
+ BranchDto branch = dbTester.components().insertProjectBranch(project, b -> b.setKey(branchKey).setBranchType(type));
+ return new ProjectAndBranch(project, branch);
+ }
+
+ private static class ProjectAndBranch {
+ private final ProjectDto project;
+ private final BranchDto branch;
+
+ private ProjectAndBranch(ProjectDto project, BranchDto branch) {
+ this.project = project;
+ this.branch = branch;
+ }
+
+ public ProjectDto getProject() {
+ return project;
+ }
+
+ public BranchDto getBranch() {
+ return branch;
+ }
+
+ public String uuid() {
+ return project.getUuid();
+ }
+
+ }
+
+ private static QGChangeEvent newQGChangeEvent(Configuration configuration, @Nullable Metric.Level previousQQStatus, @Nullable EvaluatedQualityGate evaluatedQualityGate) {
+ return new QGChangeEvent(new ProjectDto(), new BranchDto(), new SnapshotDto(), configuration, previousQQStatus, () -> Optional.ofNullable(evaluatedQualityGate));
+ }
+
+ private static QGChangeEvent newQGChangeEvent(ProjectAndBranch branch, SnapshotDto analysis, Configuration configuration, @Nullable EvaluatedQualityGate evaluatedQualityGate) {
+ Metric.Level previousStatus = randomLevel();
+ if (evaluatedQualityGate != null) {
+ Metric.Level otherLevel = stream(Metric.Level.values())
+ .filter(s -> s != previousStatus)
+ .toArray(Metric.Level[]::new)[new Random().nextInt(Metric.Level.values().length - 1)];
+ when(evaluatedQualityGate.getStatus()).thenReturn(otherLevel);
+ }
+ return new QGChangeEvent(branch.project, branch.branch, analysis, configuration, previousStatus, () -> Optional.ofNullable(evaluatedQualityGate));
+ }
+
+ private static Metric.Level randomLevel() {
+ return Metric.Level.values()[new Random().nextInt(Metric.Level.values().length)];
+ }
+
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.platform;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.config.internal.MapSettings;
-import org.sonar.api.config.internal.Settings;
-import org.sonar.api.utils.System2;
-import org.sonar.db.DbTester;
-import org.sonar.server.setting.SettingsChangeNotifier;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-public class PersistentSettingsTest {
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
- private Settings delegate = new MapSettings();
- private SettingsChangeNotifier changeNotifier = mock(SettingsChangeNotifier.class);
- private PersistentSettings underTest = new PersistentSettings(delegate, dbTester.getDbClient(), changeNotifier);
-
- @Test
- public void insert_property_into_database_and_notify_extensions() {
- assertThat(underTest.getString("foo")).isNull();
-
- underTest.saveProperty("foo", "bar");
-
- assertThat(underTest.getString("foo")).isEqualTo("bar");
- assertThat(dbTester.getDbClient().propertiesDao().selectGlobalProperty("foo").getValue()).isEqualTo("bar");
- verify(changeNotifier).onGlobalPropertyChange("foo", "bar");
- }
-
- @Test
- public void delete_property_from_database_and_notify_extensions() {
- underTest.saveProperty("foo", "bar");
- underTest.saveProperty("foo", null);
-
- assertThat(underTest.getString("foo")).isNull();
- assertThat(dbTester.getDbClient().propertiesDao().selectGlobalProperty("foo")).isNull();
- verify(changeNotifier).onGlobalPropertyChange("foo", null);
- }
-
- @Test
- public void getSettings_returns_delegate() {
- assertThat(underTest.getSettings()).isSameAs(delegate);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.platform;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.utils.DateUtils;
-import org.sonar.api.utils.System2;
-import org.sonar.db.DbTester;
-import org.sonar.db.property.PropertyDto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class StartupMetadataPersisterTest {
-
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
- private StartupMetadata metadata = new StartupMetadata(123_456_789L);
- private StartupMetadataPersister underTest = new StartupMetadataPersister(metadata, dbTester.getDbClient());
-
- @Test
- public void persist_metadata_at_startup() {
- underTest.start();
-
- assertPersistedProperty(CoreProperties.SERVER_STARTTIME, DateUtils.formatDateTime(metadata.getStartedAt()));
-
- underTest.stop();
- }
-
- private void assertPersistedProperty(String propertyKey, String expectedValue) {
- PropertyDto prop = dbTester.getDbClient().propertiesDao().selectGlobalProperty(dbTester.getSession(), propertyKey);
- assertThat(prop.getValue()).isEqualTo(expectedValue);
- }
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.monitoring;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.SonarQubeSide;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+import org.sonar.server.platform.db.migration.version.DatabaseVersion;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.process.systeminfo.SystemInfoUtils.attribute;
+
+public class DbConnectionSectionIT {
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private final DatabaseVersion databaseVersion = mock(DatabaseVersion.class);
+ private final SonarRuntime runtime = mock(SonarRuntime.class);
+ private final DbConnectionSection underTest = new DbConnectionSection(databaseVersion, dbTester.getDbClient(), runtime);
+
+ @Test
+ public void jmx_name_is_not_empty() {
+ assertThat(underTest.name()).isEqualTo("Database");
+ }
+
+ @Test
+ public void pool_info() {
+ ProtobufSystemInfo.Section section = underTest.toProtobuf();
+ assertThat(attribute(section, "Pool Total Connections").getLongValue()).isNotNegative();
+ assertThat(attribute(section, "Pool Active Connections").getLongValue()).isNotNegative();
+ assertThat(attribute(section, "Pool Idle Connections").getLongValue()).isNotNegative();
+ assertThat(attribute(section, "Pool Max Connections").getLongValue()).isNotNegative();
+ assertThat(attribute(section, "Pool Min Idle Connections")).isNotNull();
+ assertThat(attribute(section, "Pool Max Lifetime (ms)")).isNotNull();
+ }
+
+ @Test
+ public void section_name_depends_on_runtime_side() {
+ when(runtime.getSonarQubeSide()).thenReturn(SonarQubeSide.COMPUTE_ENGINE);
+ assertThat(underTest.toProtobuf().getName()).isEqualTo("Compute Engine Database Connection");
+
+ when(runtime.getSonarQubeSide()).thenReturn(SonarQubeSide.SERVER);
+ assertThat(underTest.toProtobuf().getName()).isEqualTo("Web Database Connection");
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.platform.monitoring;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.SonarQubeSide;
-import org.sonar.api.SonarRuntime;
-import org.sonar.api.utils.System2;
-import org.sonar.db.DbTester;
-import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
-import org.sonar.server.platform.db.migration.version.DatabaseVersion;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.process.systeminfo.SystemInfoUtils.attribute;
-
-public class DbConnectionSectionTest {
-
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
- private final DatabaseVersion databaseVersion = mock(DatabaseVersion.class);
- private final SonarRuntime runtime = mock(SonarRuntime.class);
- private final DbConnectionSection underTest = new DbConnectionSection(databaseVersion, dbTester.getDbClient(), runtime);
-
- @Test
- public void jmx_name_is_not_empty() {
- assertThat(underTest.name()).isEqualTo("Database");
- }
-
- @Test
- public void pool_info() {
- ProtobufSystemInfo.Section section = underTest.toProtobuf();
- assertThat(attribute(section, "Pool Total Connections").getLongValue()).isNotNegative();
- assertThat(attribute(section, "Pool Active Connections").getLongValue()).isNotNegative();
- assertThat(attribute(section, "Pool Idle Connections").getLongValue()).isNotNegative();
- assertThat(attribute(section, "Pool Max Connections").getLongValue()).isNotNegative();
- assertThat(attribute(section, "Pool Min Idle Connections")).isNotNull();
- assertThat(attribute(section, "Pool Max Lifetime (ms)")).isNotNull();
- }
-
- @Test
- public void section_name_depends_on_runtime_side() {
- when(runtime.getSonarQubeSide()).thenReturn(SonarQubeSide.COMPUTE_ENGINE);
- assertThat(underTest.toProtobuf().getName()).isEqualTo("Compute Engine Database Connection");
-
- when(runtime.getSonarQubeSide()).thenReturn(SonarQubeSide.SERVER);
- assertThat(underTest.toProtobuf().getName()).isEqualTo("Web Database Connection");
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.platform.serverid;
-
-import com.tngtech.java.junit.dataprovider.DataProviderRunner;
-import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
-import org.junit.After;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.SonarEdition;
-import org.sonar.api.SonarQubeSide;
-import org.sonar.api.internal.SonarRuntimeImpl;
-import org.sonar.api.utils.System2;
-import org.sonar.api.utils.Version;
-import org.sonar.core.platform.ServerId;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.DbTester;
-import org.sonar.db.property.PropertyDto;
-import org.sonar.server.platform.NodeInformation;
-import org.sonar.server.property.InternalProperties;
-
-import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.assertj.core.api.Assertions.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.sonar.api.SonarQubeSide.COMPUTE_ENGINE;
-import static org.sonar.api.SonarQubeSide.SERVER;
-import static org.sonar.core.platform.ServerId.DATABASE_ID_LENGTH;
-import static org.sonar.core.platform.ServerId.NOT_UUID_DATASET_ID_LENGTH;
-import static org.sonar.core.platform.ServerId.UUID_DATASET_ID_LENGTH;
-
-@RunWith(DataProviderRunner.class)
-public class ServerIdManagerTest {
-
- private static final ServerId WITH_DATABASE_ID_SERVER_ID = ServerId.of(randomAlphanumeric(DATABASE_ID_LENGTH), randomAlphanumeric(NOT_UUID_DATASET_ID_LENGTH));
- private static final String CHECKSUM_1 = randomAlphanumeric(12);
-
- @Rule
- public final DbTester dbTester = DbTester.create(System2.INSTANCE);
-
- private final ServerIdChecksum serverIdChecksum = mock(ServerIdChecksum.class);
- private final ServerIdFactory serverIdFactory = mock(ServerIdFactory.class);
- private final DbClient dbClient = dbTester.getDbClient();
- private final DbSession dbSession = dbTester.getSession();
- private final NodeInformation nodeInformation = mock(NodeInformation.class);
- private ServerIdManager underTest;
-
- @After
- public void tearDown() {
- if (underTest != null) {
- underTest.stop();
- }
- }
-
- @Test
- public void web_leader_persists_new_server_id_if_missing() {
- mockCreateNewServerId();
- mockChecksumOf(WITH_DATABASE_ID_SERVER_ID, CHECKSUM_1);
- when(nodeInformation.isStartupLeader()).thenReturn(true);
-
- test(SERVER);
-
- verifyDb(CHECKSUM_1);
- verifyCreateNewServerIdFromScratch();
- }
-
- @Test
- public void web_leader_persists_new_server_id_if_value_is_empty() {
- insertServerId("");
- mockCreateNewServerId();
- mockChecksumOf(WITH_DATABASE_ID_SERVER_ID, CHECKSUM_1);
- when(nodeInformation.isStartupLeader()).thenReturn(true);
-
- test(SERVER);
-
- verifyDb(CHECKSUM_1);
- verifyCreateNewServerIdFromScratch();
- }
-
- @Test
- public void web_leader_keeps_existing_server_id_if_valid() {
- insertServerId(WITH_DATABASE_ID_SERVER_ID);
- insertChecksum(CHECKSUM_1);
- mockChecksumOf(WITH_DATABASE_ID_SERVER_ID, CHECKSUM_1);
- when(nodeInformation.isStartupLeader()).thenReturn(true);
-
- test(SERVER);
-
- verifyDb(CHECKSUM_1);
- }
-
- @Test
- public void web_leader_creates_server_id_from_current_serverId_with_databaseId_if_checksum_fails() {
- ServerId currentServerId = ServerId.of(randomAlphanumeric(DATABASE_ID_LENGTH), randomAlphanumeric(UUID_DATASET_ID_LENGTH));
- insertServerId(currentServerId);
- insertChecksum("does_not_match_WITH_DATABASE_ID_SERVER_ID");
- mockChecksumOf(currentServerId, "matches_WITH_DATABASE_ID_SERVER_ID");
- mockCreateNewServerIdFrom(currentServerId);
- mockChecksumOf(WITH_DATABASE_ID_SERVER_ID, CHECKSUM_1);
- when(nodeInformation.isStartupLeader()).thenReturn(true);
-
- test(SERVER);
-
- verifyDb(CHECKSUM_1);
- verifyCreateNewServerIdFrom(currentServerId);
- }
-
- @Test
- public void web_leader_generates_missing_checksum_for_current_serverId_with_databaseId() {
- insertServerId(WITH_DATABASE_ID_SERVER_ID);
- mockChecksumOf(WITH_DATABASE_ID_SERVER_ID, CHECKSUM_1);
- when(nodeInformation.isStartupLeader()).thenReturn(true);
-
- test(SERVER);
-
- verifyDb(CHECKSUM_1);
- }
-
- @Test
- public void web_follower_does_not_fail_if_server_id_matches_checksum() {
- insertServerId(WITH_DATABASE_ID_SERVER_ID);
- insertChecksum(CHECKSUM_1);
- mockChecksumOf(WITH_DATABASE_ID_SERVER_ID, CHECKSUM_1);
- when(nodeInformation.isStartupLeader()).thenReturn(false);
-
- test(SERVER);
-
- // no changes
- verifyDb(CHECKSUM_1);
- }
-
- @Test
- public void web_follower_fails_if_server_id_is_missing() {
- when(nodeInformation.isStartupLeader()).thenReturn(false);
-
- expectMissingServerIdException(() -> test(SERVER));
- }
-
- @Test
- public void web_follower_fails_if_server_id_is_empty() {
- insertServerId("");
- when(nodeInformation.isStartupLeader()).thenReturn(false);
-
- expectEmptyServerIdException(() -> test(SERVER));
- }
-
- @Test
- public void web_follower_fails_if_checksum_does_not_match() {
- String dbChecksum = "boom";
- insertServerId(WITH_DATABASE_ID_SERVER_ID);
- insertChecksum(dbChecksum);
- mockChecksumOf(WITH_DATABASE_ID_SERVER_ID, CHECKSUM_1);
- when(nodeInformation.isStartupLeader()).thenReturn(false);
-
- try {
- test(SERVER);
- fail("An ISE should have been raised");
- } catch (IllegalStateException e) {
- assertThat(e.getMessage()).isEqualTo("Server ID is invalid");
- // no changes
- verifyDb(dbChecksum);
- }
- }
-
- @Test
- public void compute_engine_does_not_fail_if_server_id_is_valid() {
- insertServerId(WITH_DATABASE_ID_SERVER_ID);
- insertChecksum(CHECKSUM_1);
- mockChecksumOf(WITH_DATABASE_ID_SERVER_ID, CHECKSUM_1);
-
- test(COMPUTE_ENGINE);
-
- // no changes
- verifyDb(CHECKSUM_1);
- }
-
- @Test
- public void compute_engine_fails_if_server_id_is_missing() {
- expectMissingServerIdException(() -> test(COMPUTE_ENGINE));
- }
-
- @Test
- public void compute_engine_fails_if_server_id_is_empty() {
- insertServerId("");
-
- expectEmptyServerIdException(() -> test(COMPUTE_ENGINE));
- }
-
- @Test
- public void compute_engine_fails_if_server_id_is_invalid() {
- String dbChecksum = "boom";
- insertServerId(WITH_DATABASE_ID_SERVER_ID);
- insertChecksum(dbChecksum);
- mockChecksumOf(WITH_DATABASE_ID_SERVER_ID, CHECKSUM_1);
-
- try {
- test(SERVER);
- fail("An ISE should have been raised");
- } catch (IllegalStateException e) {
- assertThat(e.getMessage()).isEqualTo("Server ID is invalid");
- // no changes
- verifyDb(dbChecksum);
- }
- }
-
- private void expectEmptyServerIdException(ThrowingCallable callback) {
- assertThatThrownBy(callback)
- .isInstanceOf(IllegalStateException.class)
- .hasMessage("Property sonar.core.id is empty in database");
- }
-
- private void expectMissingServerIdException(ThrowingCallable callback) {
- assertThatThrownBy(callback)
- .isInstanceOf(IllegalStateException.class)
- .hasMessage("Property sonar.core.id is missing in database");
- }
-
- private void verifyDb(String expectedChecksum) {
- assertThat(dbClient.propertiesDao().selectGlobalProperty(dbSession, CoreProperties.SERVER_ID))
- .extracting(PropertyDto::getValue)
- .isEqualTo(ServerIdManagerTest.WITH_DATABASE_ID_SERVER_ID.toString());
- assertThat(dbClient.internalPropertiesDao().selectByKey(dbSession, InternalProperties.SERVER_ID_CHECKSUM))
- .hasValue(expectedChecksum);
- }
-
- private void mockCreateNewServerId() {
- when(serverIdFactory.create()).thenReturn(WITH_DATABASE_ID_SERVER_ID);
- when(serverIdFactory.create(any())).thenThrow(new IllegalStateException("new ServerId should not be created from current server id"));
- }
-
- private void mockCreateNewServerIdFrom(ServerId currentServerId) {
- when(serverIdFactory.create()).thenThrow(new IllegalStateException("new ServerId should be created from current server id"));
- when(serverIdFactory.create(currentServerId)).thenReturn(ServerIdManagerTest.WITH_DATABASE_ID_SERVER_ID);
- }
-
- private void verifyCreateNewServerIdFromScratch() {
- verify(serverIdFactory).create();
- }
-
- private void verifyCreateNewServerIdFrom(ServerId currentServerId) {
- verify(serverIdFactory).create(currentServerId);
- }
-
- private void mockChecksumOf(ServerId serverId, String checksum1) {
- when(serverIdChecksum.computeFor(serverId.toString())).thenReturn(checksum1);
- }
-
- private void insertServerId(ServerId serverId) {
- insertServerId(serverId.toString());
- }
-
- private void insertServerId(String serverId) {
- dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto().setKey(CoreProperties.SERVER_ID).setValue(serverId),
- null, null, null, null);
- dbSession.commit();
- }
-
- private void insertChecksum(String value) {
- dbClient.internalPropertiesDao().save(dbSession, InternalProperties.SERVER_ID_CHECKSUM, value);
- dbSession.commit();
- }
-
- private void test(SonarQubeSide side) {
- underTest = new ServerIdManager(serverIdChecksum, serverIdFactory, dbClient, SonarRuntimeImpl
- .forSonarQube(Version.create(6, 7), side, SonarEdition.COMMUNITY), nodeInformation);
- underTest.start();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.startup;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.stream.IntStream;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.measures.Metrics;
-import org.sonar.api.utils.System2;
-import org.sonar.core.util.SequenceUuidFactory;
-import org.sonar.core.util.UuidFactory;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbTester;
-import org.sonar.db.metric.MetricDto;
-
-import static java.util.Arrays.asList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
-
-public class RegisterMetricsTest {
-
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
- private final UuidFactory uuidFactory = new SequenceUuidFactory();
- private final DbClient dbClient = dbTester.getDbClient();
- private final RegisterMetrics register = new RegisterMetrics(dbClient, uuidFactory);
-
- /**
- * Insert new metrics, including custom metrics
- */
- @Test
- public void insert_new_metrics() {
- Metric m1 = new Metric.Builder("m1", "One", Metric.ValueType.FLOAT)
- .setDescription("desc1")
- .setDirection(1)
- .setQualitative(true)
- .setDomain("domain1")
- .setUserManaged(false)
- .create();
- Metric custom = new Metric.Builder("custom", "Custom", Metric.ValueType.FLOAT)
- .setDescription("This is a custom metric")
- .setUserManaged(true)
- .create();
-
- register.register(asList(m1, custom));
-
- Map<String, MetricDto> metricsByKey = selectAllMetrics();
- assertThat(metricsByKey).hasSize(2);
- assertEquals(m1, metricsByKey.get("m1"));
- assertEquals(custom, metricsByKey.get("custom"));
- }
-
- /**
- * Update existing metrics
- */
- @Test
- public void update_metrics() {
- dbTester.measures().insertMetric(t -> t.setKey("m1")
- .setShortName("name")
- .setValueType(Metric.ValueType.INT.name())
- .setDescription("old desc")
- .setDomain("old domain")
- .setShortName("old short name")
- .setQualitative(false)
- .setEnabled(true)
- .setOptimizedBestValue(false)
- .setDirection(1)
- .setHidden(false));
-
- Metric m1 = new Metric.Builder("m1", "New name", Metric.ValueType.FLOAT)
- .setDescription("new description")
- .setDirection(-1)
- .setQualitative(true)
- .setDomain("new domain")
- .setUserManaged(false)
- .setDecimalScale(3)
- .setHidden(true)
- .create();
- register.register(asList(m1));
-
- Map<String, MetricDto> metricsByKey = selectAllMetrics();
- assertThat(metricsByKey).hasSize(1);
- assertEquals(m1, metricsByKey.get("m1"));
- }
-
- @Test
- public void disable_undefined_metrics() {
- Random random = new Random();
- int count = 1 + random.nextInt(10);
- IntStream.range(0, count)
- .forEach(t -> dbTester.measures().insertMetric(m -> m.setEnabled(random.nextBoolean())));
-
- register.register(Collections.emptyList());
-
- assertThat(selectAllMetrics().values().stream())
- .extracting(MetricDto::isEnabled)
- .containsOnly(IntStream.range(0, count).mapToObj(t -> false).toArray(Boolean[]::new));
- }
-
- @Test
- public void enable_disabled_metrics() {
- MetricDto enabledMetric = dbTester.measures().insertMetric(t -> t.setEnabled(true));
- MetricDto disabledMetric = dbTester.measures().insertMetric(t -> t.setEnabled(false));
-
- register.register(asList(builderOf(enabledMetric).create(), builderOf(disabledMetric).create()));
-
- assertThat(selectAllMetrics().values())
- .extracting(MetricDto::isEnabled)
- .containsOnly(true, true);
- }
-
- @Test
- public void insert_core_metrics() {
- register.start();
-
- assertThat(dbTester.countRowsOfTable("metrics")).isEqualTo(CoreMetrics.getMetrics().size());
- }
-
- @Test
- public void fail_if_duplicated_plugin_metrics() {
- Metrics plugin1 = new TestMetrics(new Metric.Builder("m1", "In first plugin", Metric.ValueType.FLOAT).create());
- Metrics plugin2 = new TestMetrics(new Metric.Builder("m1", "In second plugin", Metric.ValueType.FLOAT).create());
-
- assertThatThrownBy(() -> new RegisterMetrics(dbClient, uuidFactory, new Metrics[] {plugin1, plugin2}).start())
- .isInstanceOf(IllegalStateException.class);
- }
-
- @Test
- public void fail_if_plugin_duplicates_core_metric() {
- Metrics plugin = new TestMetrics(new Metric.Builder("ncloc", "In plugin", Metric.ValueType.FLOAT).create());
-
- assertThatThrownBy(() -> new RegisterMetrics(dbClient, uuidFactory, new Metrics[] {plugin}).start())
- .isInstanceOf(IllegalStateException.class);
- }
-
- private static class TestMetrics implements Metrics {
- private final List<Metric> metrics;
-
- public TestMetrics(Metric... metrics) {
- this.metrics = asList(metrics);
- }
-
- @Override
- public List<Metric> getMetrics() {
- return metrics;
- }
- }
-
- private Map<String, MetricDto> selectAllMetrics() {
- return dbTester.getDbClient().metricDao().selectAll(dbTester.getSession())
- .stream()
- .collect(uniqueIndex(MetricDto::getKey));
- }
-
- private void assertEquals(Metric expected, MetricDto actual) {
- assertThat(actual.getKey()).isEqualTo(expected.getKey());
- assertThat(actual.getShortName()).isEqualTo(expected.getName());
- assertThat(actual.getValueType()).isEqualTo(expected.getType().name());
- assertThat(actual.getDescription()).isEqualTo(expected.getDescription());
- assertThat(actual.getDirection()).isEqualTo(expected.getDirection());
- assertThat(actual.isQualitative()).isEqualTo(expected.getQualitative());
- }
-
- private static Metric.Builder builderOf(MetricDto enabledMetric) {
- return new Metric.Builder(enabledMetric.getKey(), enabledMetric.getShortName(), Metric.ValueType.valueOf(enabledMetric.getValueType()))
- .setDescription(enabledMetric.getDescription())
- .setDirection(enabledMetric.getDirection())
- .setQualitative(enabledMetric.isQualitative())
- .setQualitative(enabledMetric.isQualitative())
- .setDomain(enabledMetric.getDomain())
- .setHidden(enabledMetric.isHidden());
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.startup;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.Map;
-import javax.annotation.Nullable;
-import org.apache.commons.io.FileUtils;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.api.utils.System2;
-import org.sonar.core.platform.PluginInfo;
-import org.sonar.core.util.UuidFactory;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbTester;
-import org.sonar.db.plugin.PluginDto;
-import org.sonar.db.plugin.PluginDto.Type;
-import org.sonar.server.plugins.PluginFilesAndMd5;
-import org.sonar.core.plugin.PluginType;
-import org.sonar.server.plugins.ServerPlugin;
-import org.sonar.server.plugins.ServerPluginRepository;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
-
-public class RegisterPluginsTest {
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
- private final long now = 12345L;
- private final DbClient dbClient = dbTester.getDbClient();
- private final ServerPluginRepository serverPluginRepository = new ServerPluginRepository();
- private final UuidFactory uuidFactory = mock(UuidFactory.class);
- private final System2 system2 = mock(System2.class);
- private final RegisterPlugins register = new RegisterPlugins(serverPluginRepository, dbClient, uuidFactory, system2);
-
-
- @Before
- public void setUp() {
- when(system2.now()).thenReturn(now);
- }
-
- /**
- * Insert new plugins
- */
- @Test
- public void insert_new_plugins() throws IOException {
- addPlugin("java", null);
- addPlugin("javacustom", "java");
- when(uuidFactory.create()).thenReturn("a").thenReturn("b").thenThrow(new IllegalStateException("Should be called only twice"));
- register.start();
-
- Map<String, PluginDto> pluginsByKey = selectAllPlugins();
- assertThat(pluginsByKey).hasSize(2);
- verify(pluginsByKey.get("java"), Type.BUNDLED, null, "93f725a07423fe1c889f448b33d21f46", false, now, now);
- verify(pluginsByKey.get("javacustom"), Type.BUNDLED, "java", "bf8b3d4cb91efc5f9ef0bcd02f128ebf", false, now, now);
-
- register.stop();
- }
-
- @Test
- public void update_removed_plugins() throws IOException {
- // will be flagged as removed
- dbClient.pluginDao().insert(dbTester.getSession(), new PluginDto()
- .setUuid("a")
- .setKee("java")
- .setBasePluginKey(null)
- .setFileHash("bd451e47a1aa76e73da0359cef63dd63")
- .setType(Type.BUNDLED)
- .setCreatedAt(1L)
- .setUpdatedAt(1L));
- // already flagged as removed
- dbClient.pluginDao().insert(dbTester.getSession(), new PluginDto()
- .setUuid("b")
- .setKee("java2")
- .setBasePluginKey(null)
- .setFileHash("bd451e47a1aa76e73da0359cef63dd63")
- .setType(Type.BUNDLED)
- .setRemoved(true)
- .setCreatedAt(1L)
- .setUpdatedAt(1L));
- dbClient.pluginDao().insert(dbTester.getSession(), new PluginDto()
- .setUuid("c")
- .setKee("csharp")
- .setBasePluginKey(null)
- .setFileHash("a20d785dbacb8f41a3b7392aa7d03b78")
- .setType(Type.EXTERNAL)
- .setCreatedAt(1L)
- .setUpdatedAt(1L));
- dbTester.commit();
-
- addPlugin("csharp", PluginType.EXTERNAL, null);
- register.start();
-
- Map<String, PluginDto> pluginsByKey = selectAllPlugins();
- assertThat(pluginsByKey).hasSize(3);
- verify(pluginsByKey.get("java"), Type.BUNDLED, null, "bd451e47a1aa76e73da0359cef63dd63", true, 1L, now);
- verify(pluginsByKey.get("java2"), Type.BUNDLED, null, "bd451e47a1aa76e73da0359cef63dd63", true, 1L, 1L);
- verify(pluginsByKey.get("csharp"), Type.EXTERNAL, null, "a20d785dbacb8f41a3b7392aa7d03b78", false, 1L, 1L);
- }
-
- @Test
- public void re_add_previously_removed_plugin() throws IOException {
- dbClient.pluginDao().insert(dbTester.getSession(), new PluginDto()
- .setUuid("c")
- .setKee("csharp")
- .setBasePluginKey(null)
- .setFileHash("a20d785dbacb8f41a3b7392aa7d03b78")
- .setType(Type.EXTERNAL)
- .setRemoved(true)
- .setCreatedAt(1L)
- .setUpdatedAt(1L));
- dbTester.commit();
-
- addPlugin("csharp", PluginType.EXTERNAL, null);
- register.start();
-
- Map<String, PluginDto> pluginsByKey = selectAllPlugins();
- assertThat(pluginsByKey).hasSize(1);
- verify(pluginsByKey.get("csharp"), Type.EXTERNAL, null, "a20d785dbacb8f41a3b7392aa7d03b78", false, 1L, now);
- }
-
- /**
- * Update existing plugins, only when checksum is different and don't remove uninstalled plugins
- */
- @Test
- public void update_changed_plugins() throws IOException {
- dbClient.pluginDao().insert(dbTester.getSession(), new PluginDto()
- .setUuid("a")
- .setKee("java")
- .setBasePluginKey(null)
- .setFileHash("bd451e47a1aa76e73da0359cef63dd63")
- .setType(Type.BUNDLED)
- .setCreatedAt(1L)
- .setUpdatedAt(1L));
- dbClient.pluginDao().insert(dbTester.getSession(), new PluginDto()
- .setUuid("b")
- .setKee("javacustom")
- .setBasePluginKey("java")
- .setFileHash("de9b2de3ddc0680904939686c0dba5be")
- .setType(Type.BUNDLED)
- .setCreatedAt(1L)
- .setUpdatedAt(1L));
- dbClient.pluginDao().insert(dbTester.getSession(), new PluginDto()
- .setUuid("c")
- .setKee("csharp")
- .setBasePluginKey(null)
- .setFileHash("a20d785dbacb8f41a3b7392aa7d03b78")
- .setType(Type.EXTERNAL)
- .setCreatedAt(1L)
- .setUpdatedAt(1L));
- dbClient.pluginDao().insert(dbTester.getSession(), new PluginDto()
- .setUuid("d")
- .setKee("new-measures")
- .setBasePluginKey(null)
- .setFileHash("6d24712cf701c41ce5eaa948e0bd6d22")
- .setType(Type.EXTERNAL)
- .setCreatedAt(1L)
- .setUpdatedAt(1L));
-
- dbTester.commit();
-
- addPlugin("javacustom", PluginType.BUNDLED, "java2");
- // csharp plugin type changed
- addPlugin("csharp", PluginType.BUNDLED, null);
-
- register.start();
-
- Map<String, PluginDto> pluginsByKey = selectAllPlugins();
- assertThat(pluginsByKey).hasSize(4);
- verify(pluginsByKey.get("java"), Type.BUNDLED, null, "bd451e47a1aa76e73da0359cef63dd63", true, 1L, now);
- verify(pluginsByKey.get("javacustom"), Type.BUNDLED, "java2", "bf8b3d4cb91efc5f9ef0bcd02f128ebf", false, 1L, now);
- verify(pluginsByKey.get("csharp"), Type.BUNDLED, null, "a20d785dbacb8f41a3b7392aa7d03b78", false, 1L, now);
- verify(pluginsByKey.get("new-measures"), Type.EXTERNAL, null, "6d24712cf701c41ce5eaa948e0bd6d22", true, 1L, now);
- }
-
- private ServerPlugin addPlugin(String key, @Nullable String basePlugin) throws IOException {
- return addPlugin(key, PluginType.BUNDLED, basePlugin);
- }
-
- private ServerPlugin addPlugin(String key, PluginType type, @Nullable String basePlugin) throws IOException {
- File file = createPluginFile(key);
- PluginFilesAndMd5.FileAndMd5 jar = new PluginFilesAndMd5.FileAndMd5(file);
- PluginInfo info = new PluginInfo(key)
- .setBasePlugin(basePlugin)
- .setJarFile(file);
- ServerPlugin serverPlugin = new ServerPlugin(info, type, null, jar, null);
- serverPluginRepository.addPlugin(serverPlugin);
- return serverPlugin;
- }
-
- private File createPluginFile(String key) throws IOException {
- File pluginJar = temp.newFile();
- FileUtils.write(pluginJar, key, StandardCharsets.UTF_8);
- return pluginJar;
- }
-
- private Map<String, PluginDto> selectAllPlugins() {
- return dbTester.getDbClient().pluginDao().selectAll(dbTester.getSession()).stream()
- .collect(uniqueIndex(PluginDto::getKee));
- }
-
- private void verify(PluginDto pluginDto, Type type, @Nullable String basePluginKey, String fileHash, boolean removed, @Nullable Long createdAt, long updatedAt) {
- assertThat(pluginDto.getBasePluginKey()).isEqualTo(basePluginKey);
- assertThat(pluginDto.getType()).isEqualTo(type);
- assertThat(pluginDto.getFileHash()).isEqualTo(fileHash);
- assertThat(pluginDto.isRemoved()).isEqualTo(removed);
- assertThat(pluginDto.getCreatedAt()).isEqualTo(createdAt);
- assertThat(pluginDto.getUpdatedAt()).isEqualTo(updatedAt);
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.startup;
-
-import com.tngtech.java.junit.dataprovider.DataProvider;
-import com.tngtech.java.junit.dataprovider.DataProviderRunner;
-import com.tngtech.java.junit.dataprovider.UseDataProvider;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.sonar.api.SonarEdition;
-import org.sonar.api.SonarRuntime;
-import org.sonar.api.utils.System2;
-import org.sonar.db.DbTester;
-import org.sonar.db.ce.CeTaskMessageDto;
-import org.sonar.db.ce.CeTaskMessageType;
-import org.sonar.db.user.UserDismissedMessageDto;
-import org.sonar.db.user.UserDto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-@RunWith(DataProviderRunner.class)
-public class UpgradeSuggestionsCleanerTest {
- private static final String TASK_UUID = "b8d564dd-4ceb-4dba-8a3d-5fafa2d72cdf";
-
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
- private final SonarRuntime sonarRuntime = mock(SonarRuntime.class);
- private final UpgradeSuggestionsCleaner underTest = new UpgradeSuggestionsCleaner(dbTester.getDbClient(), sonarRuntime);
-
- private UserDto user;
-
- @Before
- public void setup() {
- user = dbTester.users().insertUser();
- }
-
- @DataProvider
- public static Object[][] editionsWithCleanup() {
- return new Object[][] {
- {SonarEdition.DEVELOPER},
- {SonarEdition.ENTERPRISE},
- {SonarEdition.DATACENTER}
- };
- }
-
- @DataProvider
- public static Object[][] allEditions() {
- return new Object[][] {
- {SonarEdition.COMMUNITY},
- {SonarEdition.DEVELOPER},
- {SonarEdition.ENTERPRISE},
- {SonarEdition.DATACENTER}
- };
- }
-
- @Test
- @UseDataProvider("editionsWithCleanup")
- public void start_cleans_up_obsolete_upgrade_suggestions(SonarEdition edition) {
- when(sonarRuntime.getEdition()).thenReturn(edition);
- insertCeTaskMessage("ctm1", CeTaskMessageType.GENERIC, "msg1");
- insertCeTaskMessage("ctm2", CeTaskMessageType.GENERIC, "msg2");
- insertCeTaskMessage("ctm3", CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE, "upgrade-msg-1");
- insertInUserDismissedMessages("u1", CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
- insertInUserDismissedMessages("u2", CeTaskMessageType.GENERIC);
-
- underTest.start();
- underTest.stop();
-
- assertThat(dbTester.getDbClient().ceTaskMessageDao().selectByTask(dbTester.getSession(), TASK_UUID))
- .extracting(CeTaskMessageDto::getUuid)
- .containsExactly("ctm1", "ctm2");
- assertThat(dbTester.getDbClient().userDismissedMessagesDao().selectByUser(dbTester.getSession(), user))
- .extracting(UserDismissedMessageDto::getUuid)
- .containsExactly("u2");
- }
-
- @Test
- public void start_does_nothing_in_community_edition() {
- when(sonarRuntime.getEdition()).thenReturn(SonarEdition.COMMUNITY);
- insertCeTaskMessage("ctm1", CeTaskMessageType.GENERIC, "msg1");
- insertCeTaskMessage("ctm2", CeTaskMessageType.GENERIC, "msg2");
- insertCeTaskMessage("ctm3", CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE, "upgrade-msg-1");
- insertInUserDismissedMessages("u1", CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
- insertInUserDismissedMessages("u2", CeTaskMessageType.GENERIC);
-
- underTest.start();
-
- assertThat(dbTester.getDbClient().ceTaskMessageDao().selectByTask(dbTester.getSession(), TASK_UUID))
- .extracting(CeTaskMessageDto::getUuid)
- .containsExactly("ctm1", "ctm2", "ctm3");
- assertThat(dbTester.getDbClient().userDismissedMessagesDao().selectByUser(dbTester.getSession(), user))
- .extracting(UserDismissedMessageDto::getUuid)
- .containsExactlyInAnyOrder("u1", "u2");
- }
-
- @Test
- @UseDataProvider("allEditions")
- public void start_does_nothing_when_no_suggest_upgrade_messages(SonarEdition edition) {
- when(sonarRuntime.getEdition()).thenReturn(edition);
-
- underTest.start();
-
- assertThat(dbTester.getDbClient().ceTaskMessageDao().selectByTask(dbTester.getSession(), TASK_UUID)).isEmpty();
- assertThat(dbTester.getDbClient().userDismissedMessagesDao().selectByUser(dbTester.getSession(), user)).isEmpty();
- }
-
- private void insertCeTaskMessage(String uuid, CeTaskMessageType messageType, String msg) {
- CeTaskMessageDto dto = new CeTaskMessageDto()
- .setUuid(uuid)
- .setMessage(msg)
- .setType(messageType)
- .setTaskUuid(TASK_UUID);
- dbTester.getDbClient().ceTaskMessageDao().insert(dbTester.getSession(), dto);
- dbTester.getSession().commit();
- }
-
- private void insertInUserDismissedMessages(String uuid, CeTaskMessageType messageType) {
- UserDismissedMessageDto dto = new UserDismissedMessageDto()
- .setUuid(uuid)
- .setUserUuid(user.getUuid())
- .setProjectUuid("PROJECT_1")
- .setCeMessageType(messageType);
- dbTester.getDbClient().userDismissedMessagesDao().insert(dbTester.getSession(), dto);
- dbTester.getSession().commit();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.webhook;
-
-import com.tngtech.java.junit.dataprovider.DataProvider;
-import com.tngtech.java.junit.dataprovider.DataProviderRunner;
-import com.tngtech.java.junit.dataprovider.UseDataProvider;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Random;
-import java.util.Set;
-import java.util.function.Supplier;
-import javax.annotation.Nullable;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mockito;
-import org.sonar.api.config.Configuration;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.utils.System2;
-import org.sonar.core.util.UuidFactoryFast;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.AnalysisPropertyDto;
-import org.sonar.db.component.BranchDto;
-import org.sonar.db.component.BranchType;
-import org.sonar.db.component.SnapshotDto;
-import org.sonar.db.project.ProjectDto;
-import org.sonar.server.qualitygate.EvaluatedQualityGate;
-import org.sonar.server.qualitygate.changeevent.QGChangeEvent;
-import org.sonar.server.qualitygate.changeevent.QGChangeEventListener;
-
-import static java.util.Arrays.stream;
-import static java.util.Collections.emptySet;
-import static java.util.stream.Stream.concat;
-import static java.util.stream.Stream.of;
-import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
-import static org.mockito.Mockito.when;
-import static org.sonar.core.util.stream.MoreCollectors.toArrayList;
-import static org.sonar.db.component.BranchType.BRANCH;
-
-@RunWith(DataProviderRunner.class)
-public class WebhookQGChangeEventListenerTest {
-
- private static final Set<QGChangeEventListener.ChangedIssue> CHANGED_ISSUES_ARE_IGNORED = emptySet();
-
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
- private DbClient dbClient = dbTester.getDbClient();
-
- private EvaluatedQualityGate newQualityGate = mock(EvaluatedQualityGate.class);
- private WebHooks webHooks = mock(WebHooks.class);
- private WebhookPayloadFactory webhookPayloadFactory = mock(WebhookPayloadFactory.class);
- private DbClient spiedOnDbClient = Mockito.spy(dbClient);
- private WebhookQGChangeEventListener underTest = new WebhookQGChangeEventListener(webHooks, webhookPayloadFactory, spiedOnDbClient);
- private DbClient mockedDbClient = mock(DbClient.class);
- private WebhookQGChangeEventListener mockedUnderTest = new WebhookQGChangeEventListener(webHooks, webhookPayloadFactory, mockedDbClient);
-
- @Test
- @UseDataProvider("allCombinationsOfStatuses")
- public void onIssueChanges_has_no_effect_if_no_webhook_is_configured(Metric.Level previousStatus, Metric.Level newStatus) {
- Configuration configuration1 = mock(Configuration.class);
- when(newQualityGate.getStatus()).thenReturn(newStatus);
- QGChangeEvent qualityGateEvent = newQGChangeEvent(configuration1, previousStatus, newQualityGate);
- mockWebhookDisabled(qualityGateEvent.getProject());
-
- mockedUnderTest.onIssueChanges(qualityGateEvent, CHANGED_ISSUES_ARE_IGNORED);
-
- verify(webHooks).isEnabled(qualityGateEvent.getProject());
- verifyNoInteractions(webhookPayloadFactory, mockedDbClient);
- }
-
- @DataProvider
- public static Object[][] allCombinationsOfStatuses() {
- Metric.Level[] levelsAndNull = concat(of((Metric.Level) null), stream(Metric.Level.values()))
- .toArray(Metric.Level[]::new);
- Object[][] res = new Object[levelsAndNull.length * levelsAndNull.length][2];
- int i = 0;
- for (Metric.Level previousStatus : levelsAndNull) {
- for (Metric.Level newStatus : levelsAndNull) {
- res[i][0] = previousStatus;
- res[i][1] = newStatus;
- i++;
- }
- }
- return res;
- }
-
- @Test
- public void onIssueChanges_has_no_effect_if_event_has_neither_previousQGStatus_nor_qualityGate() {
- Configuration configuration = mock(Configuration.class);
- QGChangeEvent qualityGateEvent = newQGChangeEvent(configuration, null, null);
- mockWebhookEnabled(qualityGateEvent.getProject());
-
- underTest.onIssueChanges(qualityGateEvent, CHANGED_ISSUES_ARE_IGNORED);
-
- verifyNoInteractions(webhookPayloadFactory, mockedDbClient);
- }
-
- @Test
- public void onIssueChanges_has_no_effect_if_event_has_same_status_in_previous_and_new_QG() {
- Configuration configuration = mock(Configuration.class);
- Metric.Level previousStatus = randomLevel();
- when(newQualityGate.getStatus()).thenReturn(previousStatus);
- QGChangeEvent qualityGateEvent = newQGChangeEvent(configuration, previousStatus, newQualityGate);
- mockWebhookEnabled(qualityGateEvent.getProject());
-
- underTest.onIssueChanges(qualityGateEvent, CHANGED_ISSUES_ARE_IGNORED);
-
- verifyNoInteractions(webhookPayloadFactory, mockedDbClient);
- }
-
- @Test
- @UseDataProvider("newQGorNot")
- public void onIssueChanges_calls_webhook_for_changeEvent_with_webhook_enabled(@Nullable EvaluatedQualityGate newQualityGate) {
- ProjectAndBranch projectBranch = insertBranch(BRANCH, "foo");
- SnapshotDto analysis = insertAnalysisTask(projectBranch);
- Configuration configuration = mock(Configuration.class);
- mockPayloadSupplierConsumedByWebhooks();
- Map<String, String> properties = new HashMap<>();
- properties.put("sonar.analysis.test1", randomAlphanumeric(50));
- properties.put("sonar.analysis.test2", randomAlphanumeric(5000));
- insertPropertiesFor(analysis.getUuid(), properties);
- QGChangeEvent qualityGateEvent = newQGChangeEvent(projectBranch, analysis, configuration, newQualityGate);
- mockWebhookEnabled(qualityGateEvent.getProject());
-
- underTest.onIssueChanges(qualityGateEvent, CHANGED_ISSUES_ARE_IGNORED);
-
- ProjectAnalysis projectAnalysis = verifyWebhookCalledAndExtractPayloadFactoryArgument(projectBranch, analysis, qualityGateEvent.getProject());
- assertThat(projectAnalysis).isEqualTo(
- new ProjectAnalysis(
- new Project(projectBranch.project.getUuid(), projectBranch.project.getKey(), projectBranch.project.getName()),
- null,
- new Analysis(analysis.getUuid(), analysis.getCreatedAt(), analysis.getRevision()),
- new Branch(false, "foo", Branch.Type.BRANCH),
- newQualityGate,
- null,
- properties));
- }
-
- @Test
- @UseDataProvider("newQGorNot")
- public void onIssueChanges_calls_webhook_on_main_branch(@Nullable EvaluatedQualityGate newQualityGate) {
- ProjectAndBranch mainBranch = insertMainBranch();
- SnapshotDto analysis = insertAnalysisTask(mainBranch);
- Configuration configuration = mock(Configuration.class);
- QGChangeEvent qualityGateEvent = newQGChangeEvent(mainBranch, analysis, configuration, newQualityGate);
- mockWebhookEnabled(qualityGateEvent.getProject());
-
- underTest.onIssueChanges(qualityGateEvent, CHANGED_ISSUES_ARE_IGNORED);
-
- verifyWebhookCalled(mainBranch, analysis, qualityGateEvent.getProject());
- }
-
- @Test
- public void onIssueChanges_calls_webhook_on_branch() {
- onIssueChangesCallsWebhookOnBranch(BRANCH);
- }
-
- @Test
- public void onIssueChanges_calls_webhook_on_pr() {
- onIssueChangesCallsWebhookOnBranch(BranchType.PULL_REQUEST);
- }
-
- public void onIssueChangesCallsWebhookOnBranch(BranchType branchType) {
- ProjectAndBranch nonMainBranch = insertBranch(branchType, "foo");
- SnapshotDto analysis = insertAnalysisTask(nonMainBranch);
- Configuration configuration = mock(Configuration.class);
- QGChangeEvent qualityGateEvent = newQGChangeEvent(nonMainBranch, analysis, configuration, null);
- mockWebhookEnabled(qualityGateEvent.getProject());
-
- underTest.onIssueChanges(qualityGateEvent, CHANGED_ISSUES_ARE_IGNORED);
-
- verifyWebhookCalled(nonMainBranch, analysis, qualityGateEvent.getProject());
- }
-
- @DataProvider
- public static Object[][] newQGorNot() {
- EvaluatedQualityGate newQualityGate = mock(EvaluatedQualityGate.class);
- return new Object[][] {
- {null},
- {newQualityGate}
- };
- }
-
- private void mockWebhookEnabled(ProjectDto... projects) {
- for (ProjectDto dto : projects) {
- when(webHooks.isEnabled(dto)).thenReturn(true);
- }
- }
-
- private void mockWebhookDisabled(ProjectDto... projects) {
- for (ProjectDto dto : projects) {
- when(webHooks.isEnabled(dto)).thenReturn(false);
- }
- }
-
- private void mockPayloadSupplierConsumedByWebhooks() {
- Mockito.doAnswer(invocationOnMock -> {
- Supplier<WebhookPayload> supplier = (Supplier<WebhookPayload>) invocationOnMock.getArguments()[1];
- supplier.get();
- return null;
- }).when(webHooks)
- .sendProjectAnalysisUpdate(any(), any());
- }
-
- private void insertPropertiesFor(String snapshotUuid, Map<String, String> properties) {
- List<AnalysisPropertyDto> analysisProperties = properties.entrySet().stream()
- .map(entry -> new AnalysisPropertyDto()
- .setUuid(UuidFactoryFast.getInstance().create())
- .setAnalysisUuid(snapshotUuid)
- .setKey(entry.getKey())
- .setValue(entry.getValue()))
- .collect(toArrayList(properties.size()));
- dbTester.getDbClient().analysisPropertiesDao().insert(dbTester.getSession(), analysisProperties);
- dbTester.getSession().commit();
- }
-
- private SnapshotDto insertAnalysisTask(ProjectAndBranch projectAndBranch) {
- return dbTester.components().insertSnapshot(projectAndBranch.getBranch());
- }
-
- private ProjectAnalysis verifyWebhookCalledAndExtractPayloadFactoryArgument(ProjectAndBranch projectAndBranch, SnapshotDto analysis, ProjectDto project) {
- verifyWebhookCalled(projectAndBranch, analysis, project);
-
- return extractPayloadFactoryArguments(1).iterator().next();
- }
-
- private void verifyWebhookCalled(ProjectAndBranch projectAndBranch, SnapshotDto analysis, ProjectDto project) {
- verify(webHooks).isEnabled(project);
- verify(webHooks).sendProjectAnalysisUpdate(
- eq(new WebHooks.Analysis(projectAndBranch.uuid(), analysis.getUuid(), null)),
- any());
- }
-
- private List<ProjectAnalysis> extractPayloadFactoryArguments(int time) {
- ArgumentCaptor<ProjectAnalysis> projectAnalysisCaptor = ArgumentCaptor.forClass(ProjectAnalysis.class);
- verify(webhookPayloadFactory, Mockito.times(time)).create(projectAnalysisCaptor.capture());
- return projectAnalysisCaptor.getAllValues();
- }
-
- public ProjectAndBranch insertMainBranch() {
- ProjectDto project = dbTester.components().insertPrivateProjectDto();
- BranchDto branch = dbTester.getDbClient().branchDao().selectByUuid(dbTester.getSession(), project.getUuid()).get();
- dbTester.commit();
- return new ProjectAndBranch(project, branch);
- }
-
- public ProjectAndBranch insertBranch(BranchType type, String branchKey) {
- ProjectDto project = dbTester.components().insertPrivateProjectDto();
- BranchDto branch = dbTester.components().insertProjectBranch(project, b -> b.setKey(branchKey).setBranchType(type));
- return new ProjectAndBranch(project, branch);
- }
-
- public ProjectAndBranch insertBranch(ProjectDto project, BranchType type, String branchKey) {
- BranchDto branch = dbTester.components().insertProjectBranch(project, b -> b.setKey(branchKey).setBranchType(type));
- return new ProjectAndBranch(project, branch);
- }
-
- private static class ProjectAndBranch {
- private final ProjectDto project;
- private final BranchDto branch;
-
- private ProjectAndBranch(ProjectDto project, BranchDto branch) {
- this.project = project;
- this.branch = branch;
- }
-
- public ProjectDto getProject() {
- return project;
- }
-
- public BranchDto getBranch() {
- return branch;
- }
-
- public String uuid() {
- return project.getUuid();
- }
-
- }
-
- private static QGChangeEvent newQGChangeEvent(Configuration configuration, @Nullable Metric.Level previousQQStatus, @Nullable EvaluatedQualityGate evaluatedQualityGate) {
- return new QGChangeEvent(new ProjectDto(), new BranchDto(), new SnapshotDto(), configuration, previousQQStatus, () -> Optional.ofNullable(evaluatedQualityGate));
- }
-
- private static QGChangeEvent newQGChangeEvent(ProjectAndBranch branch, SnapshotDto analysis, Configuration configuration, @Nullable EvaluatedQualityGate evaluatedQualityGate) {
- Metric.Level previousStatus = randomLevel();
- if (evaluatedQualityGate != null) {
- Metric.Level otherLevel = stream(Metric.Level.values())
- .filter(s -> s != previousStatus)
- .toArray(Metric.Level[]::new)[new Random().nextInt(Metric.Level.values().length - 1)];
- when(evaluatedQualityGate.getStatus()).thenReturn(otherLevel);
- }
- return new QGChangeEvent(branch.project, branch.branch, analysis, configuration, previousStatus, () -> Optional.ofNullable(evaluatedQualityGate));
- }
-
- private static Metric.Level randomLevel() {
- return Metric.Level.values()[new Random().nextInt(Metric.Level.values().length)];
- }
-
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.permission.index;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import org.assertj.core.api.Assertions;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.Uuids;
+import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.permission.GroupPermissionDto;
+import org.sonar.db.user.GroupDto;
+import org.sonar.db.user.UserDbTester;
+import org.sonar.db.user.UserDto;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.api.resources.Qualifiers.APP;
+import static org.sonar.api.resources.Qualifiers.PROJECT;
+import static org.sonar.api.resources.Qualifiers.VIEW;
+import static org.sonar.api.web.UserRole.ADMIN;
+import static org.sonar.api.web.UserRole.USER;
+
+public class PermissionIndexerDaoIT {
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private final DbClient dbClient = dbTester.getDbClient();
+ private final DbSession dbSession = dbTester.getSession();
+ private final ComponentDbTester componentDbTester = new ComponentDbTester(dbTester);
+ private final UserDbTester userDbTester = new UserDbTester(dbTester);
+
+ private ComponentDto publicProject;
+ private ComponentDto privateProject1;
+ private ComponentDto privateProject2;
+ private ComponentDto view1;
+ private ComponentDto view2;
+ private ComponentDto application;
+ private UserDto user1;
+ private UserDto user2;
+ private GroupDto group;
+
+ private final PermissionIndexerDao underTest = new PermissionIndexerDao();
+
+ @Before
+ public void setUp() {
+ publicProject = componentDbTester.insertPublicProject();
+ privateProject1 = componentDbTester.insertPrivateProject();
+ privateProject2 = componentDbTester.insertPrivateProject();
+ view1 = componentDbTester.insertPublicPortfolio();
+ view2 = componentDbTester.insertPublicPortfolio();
+ application = componentDbTester.insertPublicApplication();
+ user1 = userDbTester.insertUser();
+ user2 = userDbTester.insertUser();
+ group = userDbTester.insertGroup();
+ }
+
+ @Test
+ public void select_all() {
+ insertTestDataForProjectsAndViews();
+
+ Collection<IndexPermissions> dtos = underTest.selectAll(dbClient, dbSession);
+ Assertions.assertThat(dtos).hasSize(6);
+
+ IndexPermissions publicProjectAuthorization = getByProjectUuid(publicProject.uuid(), dtos);
+ isPublic(publicProjectAuthorization, PROJECT);
+
+ IndexPermissions view1Authorization = getByProjectUuid(view1.uuid(), dtos);
+ isPublic(view1Authorization, VIEW);
+
+ IndexPermissions applicationAuthorization = getByProjectUuid(application.uuid(), dtos);
+ isPublic(applicationAuthorization, APP);
+
+ IndexPermissions privateProject1Authorization = getByProjectUuid(privateProject1.uuid(), dtos);
+ assertThat(privateProject1Authorization.getGroupUuids()).containsOnly(group.getUuid());
+ assertThat(privateProject1Authorization.isAllowAnyone()).isFalse();
+ assertThat(privateProject1Authorization.getUserUuids()).containsOnly(user1.getUuid(), user2.getUuid());
+ assertThat(privateProject1Authorization.getQualifier()).isEqualTo(PROJECT);
+
+ IndexPermissions privateProject2Authorization = getByProjectUuid(privateProject2.uuid(), dtos);
+ assertThat(privateProject2Authorization.getGroupUuids()).isEmpty();
+ assertThat(privateProject2Authorization.isAllowAnyone()).isFalse();
+ assertThat(privateProject2Authorization.getUserUuids()).containsOnly(user1.getUuid());
+ assertThat(privateProject2Authorization.getQualifier()).isEqualTo(PROJECT);
+
+ IndexPermissions view2Authorization = getByProjectUuid(view2.uuid(), dtos);
+ isPublic(view2Authorization, VIEW);
+ }
+
+ @Test
+ public void selectByUuids() {
+ insertTestDataForProjectsAndViews();
+
+ Map<String, IndexPermissions> dtos = underTest
+ .selectByUuids(dbClient, dbSession, asList(publicProject.uuid(), privateProject1.uuid(), privateProject2.uuid(), view1.uuid(), view2.uuid(), application.uuid()))
+ .stream()
+ .collect(MoreCollectors.uniqueIndex(IndexPermissions::getProjectUuid, Function.identity()));
+ Assertions.assertThat(dtos).hasSize(6);
+
+ IndexPermissions publicProjectAuthorization = dtos.get(publicProject.uuid());
+ isPublic(publicProjectAuthorization, PROJECT);
+
+ IndexPermissions view1Authorization = dtos.get(view1.uuid());
+ isPublic(view1Authorization, VIEW);
+
+ IndexPermissions applicationAuthorization = dtos.get(application.uuid());
+ isPublic(applicationAuthorization, APP);
+
+ IndexPermissions privateProject1Authorization = dtos.get(privateProject1.uuid());
+ assertThat(privateProject1Authorization.getGroupUuids()).containsOnly(group.getUuid());
+ assertThat(privateProject1Authorization.isAllowAnyone()).isFalse();
+ assertThat(privateProject1Authorization.getUserUuids()).containsOnly(user1.getUuid(), user2.getUuid());
+ assertThat(privateProject1Authorization.getQualifier()).isEqualTo(PROJECT);
+
+ IndexPermissions privateProject2Authorization = dtos.get(privateProject2.uuid());
+ assertThat(privateProject2Authorization.getGroupUuids()).isEmpty();
+ assertThat(privateProject2Authorization.isAllowAnyone()).isFalse();
+ assertThat(privateProject2Authorization.getUserUuids()).containsOnly(user1.getUuid());
+ assertThat(privateProject2Authorization.getQualifier()).isEqualTo(PROJECT);
+
+ IndexPermissions view2Authorization = dtos.get(view2.uuid());
+ isPublic(view2Authorization, VIEW);
+ }
+
+ @Test
+ public void selectByUuids_returns_empty_list_when_project_does_not_exist() {
+ insertTestDataForProjectsAndViews();
+
+ List<IndexPermissions> dtos = underTest.selectByUuids(dbClient, dbSession, singletonList("missing"));
+ Assertions.assertThat(dtos).isEmpty();
+ }
+
+ @Test
+ public void select_by_projects_with_high_number_of_projects() {
+ List<String> projectUuids = new ArrayList<>();
+ for (int i = 0; i < 3500; i++) {
+ ComponentDto project = dbTester.components().insertPrivateProject(Integer.toString(i));
+ projectUuids.add(project.uuid());
+ GroupPermissionDto dto = new GroupPermissionDto()
+ .setUuid(Uuids.createFast())
+ .setGroupUuid(group.getUuid())
+ .setGroupName(group.getName())
+ .setRole(USER)
+ .setComponentUuid(project.uuid())
+ .setComponentName(project.name());
+ dbClient.groupPermissionDao().insert(dbSession, dto, project, null);
+ }
+ dbSession.commit();
+
+ assertThat(underTest.selectByUuids(dbClient, dbSession, projectUuids))
+ .hasSize(3500)
+ .extracting(IndexPermissions::getProjectUuid)
+ .containsAll(projectUuids);
+ }
+
+ @Test
+ public void return_private_project_without_any_permission_when_no_permission_in_DB() {
+ List<IndexPermissions> dtos = underTest.selectByUuids(dbClient, dbSession, singletonList(privateProject1.uuid()));
+
+ // no permissions
+ Assertions.assertThat(dtos).hasSize(1);
+ IndexPermissions dto = dtos.get(0);
+ assertThat(dto.getGroupUuids()).isEmpty();
+ assertThat(dto.getUserUuids()).isEmpty();
+ assertThat(dto.isAllowAnyone()).isFalse();
+ assertThat(dto.getProjectUuid()).isEqualTo(privateProject1.uuid());
+ assertThat(dto.getQualifier()).isEqualTo(privateProject1.qualifier());
+ }
+
+ @Test
+ public void return_public_project_with_only_AllowAnyone_true_when_no_permission_in_DB() {
+ List<IndexPermissions> dtos = underTest.selectByUuids(dbClient, dbSession, singletonList(publicProject.uuid()));
+
+ Assertions.assertThat(dtos).hasSize(1);
+ IndexPermissions dto = dtos.get(0);
+ assertThat(dto.getGroupUuids()).isEmpty();
+ assertThat(dto.getUserUuids()).isEmpty();
+ assertThat(dto.isAllowAnyone()).isTrue();
+ assertThat(dto.getProjectUuid()).isEqualTo(publicProject.uuid());
+ assertThat(dto.getQualifier()).isEqualTo(publicProject.qualifier());
+ }
+
+ @Test
+ public void return_private_project_with_AllowAnyone_false_and_user_id_when_user_is_granted_USER_permission_directly() {
+ dbTester.users().insertProjectPermissionOnUser(user1, USER, privateProject1);
+ List<IndexPermissions> dtos = underTest.selectByUuids(dbClient, dbSession, singletonList(privateProject1.uuid()));
+
+ Assertions.assertThat(dtos).hasSize(1);
+ IndexPermissions dto = dtos.get(0);
+ assertThat(dto.getGroupUuids()).isEmpty();
+ assertThat(dto.getUserUuids()).containsOnly(user1.getUuid());
+ assertThat(dto.isAllowAnyone()).isFalse();
+ assertThat(dto.getProjectUuid()).isEqualTo(privateProject1.uuid());
+ assertThat(dto.getQualifier()).isEqualTo(privateProject1.qualifier());
+ }
+
+ @Test
+ public void return_private_project_with_AllowAnyone_false_and_group_id_but_not_user_id_when_user_is_granted_USER_permission_through_group() {
+ dbTester.users().insertMember(group, user1);
+ dbTester.users().insertProjectPermissionOnGroup(group, USER, privateProject1);
+ List<IndexPermissions> dtos = underTest.selectByUuids(dbClient, dbSession, singletonList(privateProject1.uuid()));
+
+ Assertions.assertThat(dtos).hasSize(1);
+ IndexPermissions dto = dtos.get(0);
+ assertThat(dto.getGroupUuids()).containsOnly(group.getUuid());
+ assertThat(dto.getUserUuids()).isEmpty();
+ assertThat(dto.isAllowAnyone()).isFalse();
+ assertThat(dto.getProjectUuid()).isEqualTo(privateProject1.uuid());
+ assertThat(dto.getQualifier()).isEqualTo(privateProject1.qualifier());
+ }
+
+ private void isPublic(IndexPermissions view1Authorization, String qualifier) {
+ assertThat(view1Authorization.getGroupUuids()).isEmpty();
+ assertThat(view1Authorization.isAllowAnyone()).isTrue();
+ assertThat(view1Authorization.getUserUuids()).isEmpty();
+ assertThat(view1Authorization.getQualifier()).isEqualTo(qualifier);
+ }
+
+ private static IndexPermissions getByProjectUuid(String projectUuid, Collection<IndexPermissions> dtos) {
+ return dtos.stream().filter(dto -> dto.getProjectUuid().equals(projectUuid)).findFirst().orElseThrow(IllegalArgumentException::new);
+ }
+
+ private void insertTestDataForProjectsAndViews() {
+ // user1 has USER access on both private projects
+ userDbTester.insertProjectPermissionOnUser(user1, ADMIN, publicProject);
+ userDbTester.insertProjectPermissionOnUser(user1, USER, privateProject1);
+ userDbTester.insertProjectPermissionOnUser(user1, USER, privateProject2);
+ userDbTester.insertProjectPermissionOnUser(user1, ADMIN, view1);
+ userDbTester.insertProjectPermissionOnUser(user1, ADMIN, application);
+
+ // user2 has USER access on privateProject1 only
+ userDbTester.insertProjectPermissionOnUser(user2, USER, privateProject1);
+ userDbTester.insertProjectPermissionOnUser(user2, ADMIN, privateProject2);
+
+ // group1 has USER access on privateProject1 only
+ userDbTester.insertProjectPermissionOnGroup(group, USER, privateProject1);
+ userDbTester.insertProjectPermissionOnGroup(group, ADMIN, privateProject1);
+ userDbTester.insertProjectPermissionOnGroup(group, ADMIN, view1);
+ userDbTester.insertProjectPermissionOnGroup(group, ADMIN, application);
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.permission.index;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-import org.assertj.core.api.Assertions;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.System2;
-import org.sonar.core.util.Uuids;
-import org.sonar.core.util.stream.MoreCollectors;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.ComponentDbTester;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.permission.GroupPermissionDto;
-import org.sonar.db.user.GroupDto;
-import org.sonar.db.user.UserDbTester;
-import org.sonar.db.user.UserDto;
-
-import static java.util.Arrays.asList;
-import static java.util.Collections.singletonList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.api.resources.Qualifiers.APP;
-import static org.sonar.api.resources.Qualifiers.PROJECT;
-import static org.sonar.api.resources.Qualifiers.VIEW;
-import static org.sonar.api.web.UserRole.ADMIN;
-import static org.sonar.api.web.UserRole.USER;
-
-public class PermissionIndexerDaoTest {
-
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
- private final DbClient dbClient = dbTester.getDbClient();
- private final DbSession dbSession = dbTester.getSession();
- private final ComponentDbTester componentDbTester = new ComponentDbTester(dbTester);
- private final UserDbTester userDbTester = new UserDbTester(dbTester);
-
- private ComponentDto publicProject;
- private ComponentDto privateProject1;
- private ComponentDto privateProject2;
- private ComponentDto view1;
- private ComponentDto view2;
- private ComponentDto application;
- private UserDto user1;
- private UserDto user2;
- private GroupDto group;
-
- private final PermissionIndexerDao underTest = new PermissionIndexerDao();
-
- @Before
- public void setUp() {
- publicProject = componentDbTester.insertPublicProject();
- privateProject1 = componentDbTester.insertPrivateProject();
- privateProject2 = componentDbTester.insertPrivateProject();
- view1 = componentDbTester.insertPublicPortfolio();
- view2 = componentDbTester.insertPublicPortfolio();
- application = componentDbTester.insertPublicApplication();
- user1 = userDbTester.insertUser();
- user2 = userDbTester.insertUser();
- group = userDbTester.insertGroup();
- }
-
- @Test
- public void select_all() {
- insertTestDataForProjectsAndViews();
-
- Collection<IndexPermissions> dtos = underTest.selectAll(dbClient, dbSession);
- Assertions.assertThat(dtos).hasSize(6);
-
- IndexPermissions publicProjectAuthorization = getByProjectUuid(publicProject.uuid(), dtos);
- isPublic(publicProjectAuthorization, PROJECT);
-
- IndexPermissions view1Authorization = getByProjectUuid(view1.uuid(), dtos);
- isPublic(view1Authorization, VIEW);
-
- IndexPermissions applicationAuthorization = getByProjectUuid(application.uuid(), dtos);
- isPublic(applicationAuthorization, APP);
-
- IndexPermissions privateProject1Authorization = getByProjectUuid(privateProject1.uuid(), dtos);
- assertThat(privateProject1Authorization.getGroupUuids()).containsOnly(group.getUuid());
- assertThat(privateProject1Authorization.isAllowAnyone()).isFalse();
- assertThat(privateProject1Authorization.getUserUuids()).containsOnly(user1.getUuid(), user2.getUuid());
- assertThat(privateProject1Authorization.getQualifier()).isEqualTo(PROJECT);
-
- IndexPermissions privateProject2Authorization = getByProjectUuid(privateProject2.uuid(), dtos);
- assertThat(privateProject2Authorization.getGroupUuids()).isEmpty();
- assertThat(privateProject2Authorization.isAllowAnyone()).isFalse();
- assertThat(privateProject2Authorization.getUserUuids()).containsOnly(user1.getUuid());
- assertThat(privateProject2Authorization.getQualifier()).isEqualTo(PROJECT);
-
- IndexPermissions view2Authorization = getByProjectUuid(view2.uuid(), dtos);
- isPublic(view2Authorization, VIEW);
- }
-
- @Test
- public void selectByUuids() {
- insertTestDataForProjectsAndViews();
-
- Map<String, IndexPermissions> dtos = underTest
- .selectByUuids(dbClient, dbSession, asList(publicProject.uuid(), privateProject1.uuid(), privateProject2.uuid(), view1.uuid(), view2.uuid(), application.uuid()))
- .stream()
- .collect(MoreCollectors.uniqueIndex(IndexPermissions::getProjectUuid, Function.identity()));
- Assertions.assertThat(dtos).hasSize(6);
-
- IndexPermissions publicProjectAuthorization = dtos.get(publicProject.uuid());
- isPublic(publicProjectAuthorization, PROJECT);
-
- IndexPermissions view1Authorization = dtos.get(view1.uuid());
- isPublic(view1Authorization, VIEW);
-
- IndexPermissions applicationAuthorization = dtos.get(application.uuid());
- isPublic(applicationAuthorization, APP);
-
- IndexPermissions privateProject1Authorization = dtos.get(privateProject1.uuid());
- assertThat(privateProject1Authorization.getGroupUuids()).containsOnly(group.getUuid());
- assertThat(privateProject1Authorization.isAllowAnyone()).isFalse();
- assertThat(privateProject1Authorization.getUserUuids()).containsOnly(user1.getUuid(), user2.getUuid());
- assertThat(privateProject1Authorization.getQualifier()).isEqualTo(PROJECT);
-
- IndexPermissions privateProject2Authorization = dtos.get(privateProject2.uuid());
- assertThat(privateProject2Authorization.getGroupUuids()).isEmpty();
- assertThat(privateProject2Authorization.isAllowAnyone()).isFalse();
- assertThat(privateProject2Authorization.getUserUuids()).containsOnly(user1.getUuid());
- assertThat(privateProject2Authorization.getQualifier()).isEqualTo(PROJECT);
-
- IndexPermissions view2Authorization = dtos.get(view2.uuid());
- isPublic(view2Authorization, VIEW);
- }
-
- @Test
- public void selectByUuids_returns_empty_list_when_project_does_not_exist() {
- insertTestDataForProjectsAndViews();
-
- List<IndexPermissions> dtos = underTest.selectByUuids(dbClient, dbSession, singletonList("missing"));
- Assertions.assertThat(dtos).isEmpty();
- }
-
- @Test
- public void select_by_projects_with_high_number_of_projects() {
- List<String> projectUuids = new ArrayList<>();
- for (int i = 0; i < 3500; i++) {
- ComponentDto project = dbTester.components().insertPrivateProject(Integer.toString(i));
- projectUuids.add(project.uuid());
- GroupPermissionDto dto = new GroupPermissionDto()
- .setUuid(Uuids.createFast())
- .setGroupUuid(group.getUuid())
- .setGroupName(group.getName())
- .setRole(USER)
- .setComponentUuid(project.uuid())
- .setComponentName(project.name());
- dbClient.groupPermissionDao().insert(dbSession, dto, project, null);
- }
- dbSession.commit();
-
- assertThat(underTest.selectByUuids(dbClient, dbSession, projectUuids))
- .hasSize(3500)
- .extracting(IndexPermissions::getProjectUuid)
- .containsAll(projectUuids);
- }
-
- @Test
- public void return_private_project_without_any_permission_when_no_permission_in_DB() {
- List<IndexPermissions> dtos = underTest.selectByUuids(dbClient, dbSession, singletonList(privateProject1.uuid()));
-
- // no permissions
- Assertions.assertThat(dtos).hasSize(1);
- IndexPermissions dto = dtos.get(0);
- assertThat(dto.getGroupUuids()).isEmpty();
- assertThat(dto.getUserUuids()).isEmpty();
- assertThat(dto.isAllowAnyone()).isFalse();
- assertThat(dto.getProjectUuid()).isEqualTo(privateProject1.uuid());
- assertThat(dto.getQualifier()).isEqualTo(privateProject1.qualifier());
- }
-
- @Test
- public void return_public_project_with_only_AllowAnyone_true_when_no_permission_in_DB() {
- List<IndexPermissions> dtos = underTest.selectByUuids(dbClient, dbSession, singletonList(publicProject.uuid()));
-
- Assertions.assertThat(dtos).hasSize(1);
- IndexPermissions dto = dtos.get(0);
- assertThat(dto.getGroupUuids()).isEmpty();
- assertThat(dto.getUserUuids()).isEmpty();
- assertThat(dto.isAllowAnyone()).isTrue();
- assertThat(dto.getProjectUuid()).isEqualTo(publicProject.uuid());
- assertThat(dto.getQualifier()).isEqualTo(publicProject.qualifier());
- }
-
- @Test
- public void return_private_project_with_AllowAnyone_false_and_user_id_when_user_is_granted_USER_permission_directly() {
- dbTester.users().insertProjectPermissionOnUser(user1, USER, privateProject1);
- List<IndexPermissions> dtos = underTest.selectByUuids(dbClient, dbSession, singletonList(privateProject1.uuid()));
-
- Assertions.assertThat(dtos).hasSize(1);
- IndexPermissions dto = dtos.get(0);
- assertThat(dto.getGroupUuids()).isEmpty();
- assertThat(dto.getUserUuids()).containsOnly(user1.getUuid());
- assertThat(dto.isAllowAnyone()).isFalse();
- assertThat(dto.getProjectUuid()).isEqualTo(privateProject1.uuid());
- assertThat(dto.getQualifier()).isEqualTo(privateProject1.qualifier());
- }
-
- @Test
- public void return_private_project_with_AllowAnyone_false_and_group_id_but_not_user_id_when_user_is_granted_USER_permission_through_group() {
- dbTester.users().insertMember(group, user1);
- dbTester.users().insertProjectPermissionOnGroup(group, USER, privateProject1);
- List<IndexPermissions> dtos = underTest.selectByUuids(dbClient, dbSession, singletonList(privateProject1.uuid()));
-
- Assertions.assertThat(dtos).hasSize(1);
- IndexPermissions dto = dtos.get(0);
- assertThat(dto.getGroupUuids()).containsOnly(group.getUuid());
- assertThat(dto.getUserUuids()).isEmpty();
- assertThat(dto.isAllowAnyone()).isFalse();
- assertThat(dto.getProjectUuid()).isEqualTo(privateProject1.uuid());
- assertThat(dto.getQualifier()).isEqualTo(privateProject1.qualifier());
- }
-
- private void isPublic(IndexPermissions view1Authorization, String qualifier) {
- assertThat(view1Authorization.getGroupUuids()).isEmpty();
- assertThat(view1Authorization.isAllowAnyone()).isTrue();
- assertThat(view1Authorization.getUserUuids()).isEmpty();
- assertThat(view1Authorization.getQualifier()).isEqualTo(qualifier);
- }
-
- private static IndexPermissions getByProjectUuid(String projectUuid, Collection<IndexPermissions> dtos) {
- return dtos.stream().filter(dto -> dto.getProjectUuid().equals(projectUuid)).findFirst().orElseThrow(IllegalArgumentException::new);
- }
-
- private void insertTestDataForProjectsAndViews() {
- // user1 has USER access on both private projects
- userDbTester.insertProjectPermissionOnUser(user1, ADMIN, publicProject);
- userDbTester.insertProjectPermissionOnUser(user1, USER, privateProject1);
- userDbTester.insertProjectPermissionOnUser(user1, USER, privateProject2);
- userDbTester.insertProjectPermissionOnUser(user1, ADMIN, view1);
- userDbTester.insertProjectPermissionOnUser(user1, ADMIN, application);
-
- // user2 has USER access on privateProject1 only
- userDbTester.insertProjectPermissionOnUser(user2, USER, privateProject1);
- userDbTester.insertProjectPermissionOnUser(user2, ADMIN, privateProject2);
-
- // group1 has USER access on privateProject1 only
- userDbTester.insertProjectPermissionOnGroup(group, USER, privateProject1);
- userDbTester.insertProjectPermissionOnGroup(group, ADMIN, privateProject1);
- userDbTester.insertProjectPermissionOnGroup(group, ADMIN, view1);
- userDbTester.insertProjectPermissionOnGroup(group, ADMIN, application);
- }
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.pushapi.scheduler.purge;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.pushevent.PushEventDto;
+import org.sonar.server.util.GlobalLockManagerImpl;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.await;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.server.pushapi.scheduler.purge.PushEventsPurgeScheduler.ENQUEUE_DELAY_IN_SECONDS;
+import static org.sonar.server.pushapi.scheduler.purge.PushEventsPurgeScheduler.INITIAL_DELAY_IN_SECONDS;
+
+public class PushEventsPurgeSchedulerAndExecutorIT {
+
+ private static final String PUSH_EVENT_UUID = "push_event_uuid";
+ private static final long INITIAL_AND_ENQUE_DELAY_MS = 1L;
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private PushEventsPurgeScheduler pushEventsPurgeScheduler;
+
+ @Before
+ public void setup() {
+ Configuration configuration = mock(Configuration.class);
+ when(configuration.getLong(INITIAL_DELAY_IN_SECONDS)).thenReturn(Optional.of(INITIAL_AND_ENQUE_DELAY_MS));
+ when(configuration.getLong(ENQUEUE_DELAY_IN_SECONDS)).thenReturn(Optional.of(INITIAL_AND_ENQUE_DELAY_MS));
+
+ GlobalLockManagerImpl lockManager = mock(GlobalLockManagerImpl.class);
+ when(lockManager.tryLock(any(), anyInt())).thenReturn(true);
+
+ DbClient dbClient = dbTester.getDbClient();
+ pushEventsPurgeScheduler = new PushEventsPurgeScheduler(
+ dbClient,
+ configuration,
+ lockManager,
+ new PushEventsPurgeExecutorServiceImpl(),
+ System2.INSTANCE
+ );
+ }
+
+ @Test
+ public void pushEventsPurgeScheduler_shouldCleanUpPeriodically() throws InterruptedException {
+ insertOldPushEvent();
+ assertThat(pushEventIsCleanedUp()).isFalse();
+
+ pushEventsPurgeScheduler.start();
+
+ await()
+ .atMost(3, TimeUnit.SECONDS)
+ .pollDelay(500, TimeUnit.MILLISECONDS)
+ .until(this::pushEventIsCleanedUp);
+ }
+
+ private void insertOldPushEvent() {
+ PushEventDto pushEventDto = new PushEventDto();
+ pushEventDto.setUuid(PUSH_EVENT_UUID);
+ pushEventDto.setName("test_event");
+ pushEventDto.setProjectUuid("test_project_uuid");
+ pushEventDto.setPayload("payload".getBytes(StandardCharsets.UTF_8));
+ pushEventDto.setCreatedAt(1656633600);
+ dbTester.getDbClient().pushEventDao().insert(dbTester.getSession(), pushEventDto);
+ dbTester.commit();
+ }
+
+ private boolean pushEventIsCleanedUp() {
+ return dbTester.getDbClient().pushEventDao().selectByUuid(dbTester.getSession(), PUSH_EVENT_UUID) == null;
+ }
+
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.pushapi.scheduler.purge;
-
-import java.nio.charset.StandardCharsets;
-import java.util.Optional;
-import java.util.concurrent.TimeUnit;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.config.Configuration;
-import org.sonar.api.utils.System2;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbTester;
-import org.sonar.db.pushevent.PushEventDto;
-import org.sonar.server.util.GlobalLockManagerImpl;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.awaitility.Awaitility.await;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.server.pushapi.scheduler.purge.PushEventsPurgeScheduler.ENQUEUE_DELAY_IN_SECONDS;
-import static org.sonar.server.pushapi.scheduler.purge.PushEventsPurgeScheduler.INITIAL_DELAY_IN_SECONDS;
-
-public class PushEventsPurgeSchedulerAndExecutorTest {
-
- private static final String PUSH_EVENT_UUID = "push_event_uuid";
- private static final long INITIAL_AND_ENQUE_DELAY_MS = 1L;
-
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
- private PushEventsPurgeScheduler pushEventsPurgeScheduler;
-
- @Before
- public void setup() {
- Configuration configuration = mock(Configuration.class);
- when(configuration.getLong(INITIAL_DELAY_IN_SECONDS)).thenReturn(Optional.of(INITIAL_AND_ENQUE_DELAY_MS));
- when(configuration.getLong(ENQUEUE_DELAY_IN_SECONDS)).thenReturn(Optional.of(INITIAL_AND_ENQUE_DELAY_MS));
-
- GlobalLockManagerImpl lockManager = mock(GlobalLockManagerImpl.class);
- when(lockManager.tryLock(any(), anyInt())).thenReturn(true);
-
- DbClient dbClient = dbTester.getDbClient();
- pushEventsPurgeScheduler = new PushEventsPurgeScheduler(
- dbClient,
- configuration,
- lockManager,
- new PushEventsPurgeExecutorServiceImpl(),
- System2.INSTANCE
- );
- }
-
- @Test
- public void pushEventsPurgeScheduler_shouldCleanUpPeriodically() throws InterruptedException {
- insertOldPushEvent();
- assertThat(pushEventIsCleanedUp()).isFalse();
-
- pushEventsPurgeScheduler.start();
-
- await()
- .atMost(3, TimeUnit.SECONDS)
- .pollDelay(500, TimeUnit.MILLISECONDS)
- .until(this::pushEventIsCleanedUp);
- }
-
- private void insertOldPushEvent() {
- PushEventDto pushEventDto = new PushEventDto();
- pushEventDto.setUuid(PUSH_EVENT_UUID);
- pushEventDto.setName("test_event");
- pushEventDto.setProjectUuid("test_project_uuid");
- pushEventDto.setPayload("payload".getBytes(StandardCharsets.UTF_8));
- pushEventDto.setCreatedAt(1656633600);
- dbTester.getDbClient().pushEventDao().insert(dbTester.getSession(), pushEventDto);
- dbTester.commit();
- }
-
- private boolean pushEventIsCleanedUp() {
- return dbTester.getDbClient().pushEventDao().selectByUuid(dbTester.getSession(), PUSH_EVENT_UUID) == null;
- }
-
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.web;
+
+import java.io.IOException;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.impl.utils.TestSystem2;
+import org.sonar.db.DbTester;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.user.ServerUserSession;
+import org.sonar.server.user.ThreadLocalUserSession;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class SonarLintConnectionFilterIT {
+ private static final String LOGIN = "user1";
+ private final TestSystem2 system2 = new TestSystem2();
+
+ @Rule
+ public DbTester dbTester = DbTester.create(system2);
+
+ @Test
+ public void update() throws IOException, ServletException {
+ system2.setNow(10_000_000L);
+ addUser(LOGIN, 1_000_000L);
+
+ runFilter(LOGIN, "SonarLint for IntelliJ");
+ assertThat(getLastUpdate(LOGIN)).isEqualTo(10_000_000L);
+ }
+
+ @Test
+ public void update_first_time() throws IOException, ServletException {
+ system2.setNow(10_000_000L);
+ addUser(LOGIN, null);
+
+ runFilter(LOGIN, "SonarLint for IntelliJ");
+ assertThat(getLastUpdate(LOGIN)).isEqualTo(10_000_000L);
+ }
+
+ @Test
+ public void only_applies_to_api() {
+ SonarLintConnectionFilter underTest = new SonarLintConnectionFilter(dbTester.getDbClient(), mock(ThreadLocalUserSession.class), system2);
+ assertThat(underTest.doGetPattern().matches("/api/test")).isTrue();
+ assertThat(underTest.doGetPattern().matches("/test")).isFalse();
+
+ }
+
+ @Test
+ public void do_nothing_if_no_sonarlint_agent() throws IOException, ServletException {
+ system2.setNow(10_000L);
+ addUser(LOGIN, 1_000L);
+
+ runFilter(LOGIN, "unknown");
+ runFilter(LOGIN, null);
+ assertThat(getLastUpdate(LOGIN)).isEqualTo(1_000L);
+ }
+
+ @Test
+ public void do_nothing_if_not_logged_in() throws IOException, ServletException {
+ system2.setNow(10_000_000L);
+ addUser("invalid", 1_000_000L);
+
+ runFilter(LOGIN, "SonarLint for IntelliJ");
+ assertThat(getLastUpdate("invalid")).isEqualTo(1_000_000L);
+ }
+
+ @Test
+ public void dont_fail_if_no_user_set() throws IOException, ServletException {
+ SonarLintConnectionFilter underTest = new SonarLintConnectionFilter(dbTester.getDbClient(), new ThreadLocalUserSession(), system2);
+ HttpServletRequest request = mock(HttpServletRequest.class);
+ when(request.getHeader("User-Agent")).thenReturn("sonarlint");
+ FilterChain chain = mock(FilterChain.class);
+ underTest.doFilter(request, mock(ServletResponse.class), chain);
+ verify(chain).doFilter(any(), any());
+ }
+
+ @Test
+ public void only_update_if_not_updated_within_1h() throws IOException, ServletException {
+ system2.setNow(2_000_000L);
+ addUser(LOGIN, 1_000_000L);
+
+ runFilter(LOGIN, "SonarLint for IntelliJ");
+ assertThat(getLastUpdate(LOGIN)).isEqualTo(1_000_000L);
+ }
+
+ private void addUser(String login, @Nullable Long lastUpdate) {
+ dbTester.users().insertUser(u -> u.setLogin(login).setLastSonarlintConnectionDate(lastUpdate));
+ }
+
+ @CheckForNull
+ private Long getLastUpdate(String login) {
+ return dbTester.getDbClient().userDao().selectByLogin(dbTester.getSession(), login).getLastSonarlintConnectionDate();
+ }
+
+ private void runFilter(String loggedInUser, @Nullable String agent) throws IOException, ServletException {
+ UserDto user = dbTester.getDbClient().userDao().selectByLogin(dbTester.getSession(), loggedInUser);
+ ThreadLocalUserSession session = new ThreadLocalUserSession();
+ session.set(new ServerUserSession(dbTester.getDbClient(), user));
+ SonarLintConnectionFilter underTest = new SonarLintConnectionFilter(dbTester.getDbClient(), session, system2);
+ HttpServletRequest request = mock(HttpServletRequest.class);
+ when(request.getHeader("User-Agent")).thenReturn(agent);
+ FilterChain chain = mock(FilterChain.class);
+ underTest.doFilter(request, mock(ServletResponse.class), chain);
+ verify(chain).doFilter(any(), any());
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.platform.web;
-
-import java.io.IOException;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.impl.utils.TestSystem2;
-import org.sonar.db.DbTester;
-import org.sonar.db.user.UserDto;
-import org.sonar.server.user.ServerUserSession;
-import org.sonar.server.user.ThreadLocalUserSession;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class SonarLintConnectionFilterTest {
- private static final String LOGIN = "user1";
- private final TestSystem2 system2 = new TestSystem2();
-
- @Rule
- public DbTester dbTester = DbTester.create(system2);
-
- @Test
- public void update() throws IOException, ServletException {
- system2.setNow(10_000_000L);
- addUser(LOGIN, 1_000_000L);
-
- runFilter(LOGIN, "SonarLint for IntelliJ");
- assertThat(getLastUpdate(LOGIN)).isEqualTo(10_000_000L);
- }
-
- @Test
- public void update_first_time() throws IOException, ServletException {
- system2.setNow(10_000_000L);
- addUser(LOGIN, null);
-
- runFilter(LOGIN, "SonarLint for IntelliJ");
- assertThat(getLastUpdate(LOGIN)).isEqualTo(10_000_000L);
- }
-
- @Test
- public void only_applies_to_api() {
- SonarLintConnectionFilter underTest = new SonarLintConnectionFilter(dbTester.getDbClient(), mock(ThreadLocalUserSession.class), system2);
- assertThat(underTest.doGetPattern().matches("/api/test")).isTrue();
- assertThat(underTest.doGetPattern().matches("/test")).isFalse();
-
- }
-
- @Test
- public void do_nothing_if_no_sonarlint_agent() throws IOException, ServletException {
- system2.setNow(10_000L);
- addUser(LOGIN, 1_000L);
-
- runFilter(LOGIN, "unknown");
- runFilter(LOGIN, null);
- assertThat(getLastUpdate(LOGIN)).isEqualTo(1_000L);
- }
-
- @Test
- public void do_nothing_if_not_logged_in() throws IOException, ServletException {
- system2.setNow(10_000_000L);
- addUser("invalid", 1_000_000L);
-
- runFilter(LOGIN, "SonarLint for IntelliJ");
- assertThat(getLastUpdate("invalid")).isEqualTo(1_000_000L);
- }
-
- @Test
- public void dont_fail_if_no_user_set() throws IOException, ServletException {
- SonarLintConnectionFilter underTest = new SonarLintConnectionFilter(dbTester.getDbClient(), new ThreadLocalUserSession(), system2);
- HttpServletRequest request = mock(HttpServletRequest.class);
- when(request.getHeader("User-Agent")).thenReturn("sonarlint");
- FilterChain chain = mock(FilterChain.class);
- underTest.doFilter(request, mock(ServletResponse.class), chain);
- verify(chain).doFilter(any(), any());
- }
-
- @Test
- public void only_update_if_not_updated_within_1h() throws IOException, ServletException {
- system2.setNow(2_000_000L);
- addUser(LOGIN, 1_000_000L);
-
- runFilter(LOGIN, "SonarLint for IntelliJ");
- assertThat(getLastUpdate(LOGIN)).isEqualTo(1_000_000L);
- }
-
- private void addUser(String login, @Nullable Long lastUpdate) {
- dbTester.users().insertUser(u -> u.setLogin(login).setLastSonarlintConnectionDate(lastUpdate));
- }
-
- @CheckForNull
- private Long getLastUpdate(String login) {
- return dbTester.getDbClient().userDao().selectByLogin(dbTester.getSession(), login).getLastSonarlintConnectionDate();
- }
-
- private void runFilter(String loggedInUser, @Nullable String agent) throws IOException, ServletException {
- UserDto user = dbTester.getDbClient().userDao().selectByLogin(dbTester.getSession(), loggedInUser);
- ThreadLocalUserSession session = new ThreadLocalUserSession();
- session.set(new ServerUserSession(dbTester.getDbClient(), user));
- SonarLintConnectionFilter underTest = new SonarLintConnectionFilter(dbTester.getDbClient(), session, system2);
- HttpServletRequest request = mock(HttpServletRequest.class);
- when(request.getHeader("User-Agent")).thenReturn(agent);
- FilterChain chain = mock(FilterChain.class);
- underTest.doFilter(request, mock(ServletResponse.class), chain);
- verify(chain).doFilter(any(), any());
- }
-}