diff options
Diffstat (limited to 'server')
10 files changed, 475 insertions, 6 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/LoadPeriodsStepIT.java b/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/LoadPeriodsStepIT.java index b0aed6b3f10..d0c0c7c8c3d 100644 --- a/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/LoadPeriodsStepIT.java +++ b/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/LoadPeriodsStepIT.java @@ -62,7 +62,6 @@ import static org.apache.commons.lang3.RandomStringUtils.secure; 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.verifyNoInteractions; @@ -198,7 +197,6 @@ public class LoadPeriodsStepIT extends BaseStepTest { underTest.execute(new TestComputationStepContext()); assertPeriod(NewCodePeriodType.REFERENCE_BRANCH, newCodeReferenceBranch, null); - verify(ceTaskMessages).add(any(CeTaskMessages.Message.class)); } @Test diff --git a/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/PersistReferenceBranchPeriodStepIT.java b/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/PersistReferenceBranchPeriodStepIT.java new file mode 100644 index 00000000000..0ede18e3ccf --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/PersistReferenceBranchPeriodStepIT.java @@ -0,0 +1,112 @@ +/* + * SonarQube + * Copyright (C) 2009-2025 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.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule; +import org.sonar.ce.task.projectanalysis.analysis.TestBranch; +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.period.Period; +import org.sonar.ce.task.projectanalysis.period.PeriodHolderRule; +import org.sonar.ce.task.projectanalysis.period.PeriodOrigin; +import org.sonar.ce.task.step.ComputationStep.Context; +import org.sonar.db.DbTester; +import org.sonar.db.component.BranchDto; +import org.sonar.db.component.ProjectData; +import org.sonar.db.newcodeperiod.NewCodePeriodDto; +import org.sonar.server.project.Project; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.Mockito.mock; +import static org.sonar.db.newcodeperiod.NewCodePeriodType.REFERENCE_BRANCH; + +class PersistReferenceBranchPeriodStepIT { + + private static final String BRANCH_NAME = "feature"; + + @RegisterExtension + private final PeriodHolderRule periodHolder = new PeriodHolderRule(); + + @RegisterExtension + private final AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule(); + + @RegisterExtension + private final TreeRootHolderRule treeRootHolderRule = new TreeRootHolderRule(); + + @RegisterExtension + private final DbTester db = DbTester.create(); + + private final PersistReferenceBranchPeriodStep persistReferenceBranchPeriodStep = new PersistReferenceBranchPeriodStep( + periodHolder, analysisMetadataHolder, db.getDbClient(), treeRootHolderRule); + + private ProjectData projectData; + private String branchUuid; + + @BeforeEach + void setUp() { + projectData = db.components().insertPrivateProject(); + BranchDto branchDto = db.components().insertProjectBranch(projectData.getProjectDto(), branch -> branch.setKey(BRANCH_NAME)); + branchUuid = branchDto.getUuid(); + + analysisMetadataHolder.setProject(new Project(projectData.projectUuid(), projectData.projectKey(), projectData.projectKey(), null, List.of())); + analysisMetadataHolder.setBranch(new TestBranch(BRANCH_NAME)); + periodHolder.setPeriod(new Period(REFERENCE_BRANCH.name(), "main", null)); + periodHolder.setPeriodOrigin(PeriodOrigin.SCANNER); + + ReportComponent reportComponent = ReportComponent + .builder(Component.Type.PROJECT, 1) + .setUuid(branchUuid) + .setKey(branchDto.getKey()) + .build(); + treeRootHolderRule.setRoot(reportComponent); + } + + @Test + void execute_shouldPersistReferenceBranchPeriod() { + + persistReferenceBranchPeriodStep.execute(mock(Context.class)); + + NewCodePeriodDto newCodePeriodDto = db.getDbClient().newCodePeriodDao().selectByBranch(db.getSession(), projectData.projectUuid(), branchUuid) + .orElseGet(() -> fail("No new code period found for branch")); + assertThat(newCodePeriodDto.getBranchUuid()).isEqualTo(branchUuid); + assertThat(newCodePeriodDto.getType()).isEqualTo(REFERENCE_BRANCH); + assertThat(newCodePeriodDto.getValue()).isEqualTo("main"); + } + + @Test + void execute_shouldUpdateReferenceBranchPeriod() { + db.newCodePeriods().insert(projectData.projectUuid(), branchUuid, REFERENCE_BRANCH, "old_value"); + + persistReferenceBranchPeriodStep.execute(mock(Context.class)); + + NewCodePeriodDto newCodePeriodDto = db.getDbClient().newCodePeriodDao().selectByBranch(db.getSession(), projectData.projectUuid(), branchUuid) + .orElseGet(() -> fail("No new code period found for branch")); + assertThat(newCodePeriodDto.getBranchUuid()).isEqualTo(branchUuid); + assertThat(newCodePeriodDto.getType()).isEqualTo(REFERENCE_BRANCH); + assertThat(newCodePeriodDto.getValue()).isEqualTo("main"); + } + +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodHolder.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodHolder.java index 1705c9fb3bb..9fe7dadb9f5 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodHolder.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodHolder.java @@ -51,4 +51,10 @@ public interface PeriodHolder { */ Period getPeriod(); + /** + * Retrieve the context from which this period is coming from. For example, it can be coming from a scanner parameter, a global setting, etc. + * See {@link PeriodOrigin} for the possible values. + */ + PeriodOrigin getPeriodOrigin(); + } diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderImpl.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderImpl.java index 86ea07a18e2..2150f3f5539 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderImpl.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderImpl.java @@ -29,6 +29,7 @@ public class PeriodHolderImpl implements PeriodHolder { @CheckForNull private Period period = null; private boolean initialized = false; + private PeriodOrigin periodOrigin = null; /** * Initializes the periods in the holder. @@ -41,6 +42,10 @@ public class PeriodHolderImpl implements PeriodHolder { this.initialized = true; } + public void setPeriodOrigin(PeriodOrigin periodOrigin) { + this.periodOrigin = periodOrigin; + } + @Override public boolean hasPeriod() { checkHolderIsInitialized(); @@ -60,6 +65,11 @@ public class PeriodHolderImpl implements PeriodHolder { return period; } + @Override + public PeriodOrigin getPeriodOrigin() { + return periodOrigin; + } + private void checkHolderIsInitialized() { checkState(initialized, "Period have not been initialized yet"); } diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodOrigin.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodOrigin.java new file mode 100644 index 00000000000..6e2847fe480 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodOrigin.java @@ -0,0 +1,25 @@ +/* + * SonarQube + * Copyright (C) 2009-2025 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.period; + +public enum PeriodOrigin { + SCANNER, + SETTINGS +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadPeriodsStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadPeriodsStep.java index 8d9026c42af..45831d3bc6f 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadPeriodsStep.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadPeriodsStep.java @@ -22,13 +22,13 @@ package org.sonar.ce.task.projectanalysis.step; import java.util.Optional; import org.sonar.api.utils.System2; import org.sonar.ce.task.log.CeTaskMessages; -import org.sonar.ce.task.log.CeTaskMessages.Message; import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder; import org.sonar.ce.task.projectanalysis.component.TreeRootHolder; import org.sonar.ce.task.projectanalysis.period.NewCodePeriodResolver; import org.sonar.ce.task.projectanalysis.period.Period; import org.sonar.ce.task.projectanalysis.period.PeriodHolder; import org.sonar.ce.task.projectanalysis.period.PeriodHolderImpl; +import org.sonar.ce.task.projectanalysis.period.PeriodOrigin; import org.sonar.ce.task.step.ComputationStep; import org.sonar.db.DbClient; import org.sonar.db.DbSession; @@ -89,6 +89,8 @@ public class LoadPeriodsStep implements ComputationStep { .map(b -> new NewCodePeriodDto().setType(REFERENCE_BRANCH).setValue(b)) .orElse(null); + PeriodOrigin periodOrigin = newCodePeriod == null ? PeriodOrigin.SETTINGS : PeriodOrigin.SCANNER; + try (DbSession dbSession = dbClient.openSession(false)) { Optional<NewCodePeriodDto> branchSpecificSetting = getBranchSetting(dbSession, projectUuid, branchUuid); @@ -102,13 +104,11 @@ public class LoadPeriodsStep implements ComputationStep { periodsHolder.setPeriod(null); return; } - } else if (branchSpecificSetting.isPresent()) { - ceTaskMessages.add(new Message("A scanner parameter is defining a new code reference branch, but this conflicts with the New Code Period" - + " setting of your branch. Please check your project configuration. You should use either one or the other but not both.", system2.now())); } Period period = resolver.resolve(dbSession, branchUuid, newCodePeriod, projectVersion); periodsHolder.setPeriod(period); + periodsHolder.setPeriodOrigin(periodOrigin); } } diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistReferenceBranchPeriodStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistReferenceBranchPeriodStep.java new file mode 100644 index 00000000000..96f471946ae --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistReferenceBranchPeriodStep.java @@ -0,0 +1,113 @@ +/* + * SonarQube + * Copyright (C) 2009-2025 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.Objects; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder; +import org.sonar.ce.task.projectanalysis.component.TreeRootHolder; +import org.sonar.ce.task.projectanalysis.period.PeriodHolder; +import org.sonar.ce.task.projectanalysis.period.PeriodOrigin; +import org.sonar.ce.task.step.ComputationStep; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.newcodeperiod.NewCodePeriodDto; +import org.sonar.db.newcodeperiod.NewCodePeriodType; + +public class PersistReferenceBranchPeriodStep implements ComputationStep { + + private static final Logger LOGGER = LoggerFactory.getLogger(PersistReferenceBranchPeriodStep.class); + + private final PeriodHolder periodHolder; + private final AnalysisMetadataHolder analysisMetadataHolder; + private final DbClient dbClient; + private final TreeRootHolder treeRootHolder; + + public PersistReferenceBranchPeriodStep(PeriodHolder periodHolder, AnalysisMetadataHolder analysisMetadataHolder, DbClient dbClient, TreeRootHolder treeRootHolder) { + this.periodHolder = periodHolder; + this.analysisMetadataHolder = analysisMetadataHolder; + this.dbClient = dbClient; + this.treeRootHolder = treeRootHolder; + } + + @Override + public String getDescription() { + return "Persist or update reference branch new code period"; + } + + @Override + public void execute(Context context) { + if (shouldExecute()) { + executePersistPeriodStep(); + } + } + + private boolean shouldExecute() { + return analysisMetadataHolder.isBranch() && periodHolder.hasPeriod() + && periodHolder.getPeriodOrigin() == PeriodOrigin.SCANNER; + } + + void executePersistPeriodStep() { + try (DbSession dbSession = dbClient.openSession(false)) { + String projectUuid = analysisMetadataHolder.getProject().getUuid(); + String branchUuid = treeRootHolder.getRoot().getUuid(); + + dbClient.newCodePeriodDao() + .selectByBranch(dbSession, projectUuid, branchUuid) + .ifPresentOrElse( + existingNewCodePeriod -> updateNewCodePeriodIfNeeded(dbSession, existingNewCodePeriod), + () -> createNewCodePeriod(dbSession, branchUuid) + ); + } + } + + private void updateNewCodePeriodIfNeeded(DbSession dbSession, NewCodePeriodDto newCodePeriodDto) { + if (shouldUpdateNewCodePeriod(newCodePeriodDto)) { + LOGGER.debug("Updating reference branch new code period '{}' for project '{}' and branch '{}'", + periodHolder.getPeriod().getModeParameter(), analysisMetadataHolder.getProject().getName(), analysisMetadataHolder.getBranch().getName()); + newCodePeriodDto.setValue(periodHolder.getPeriod().getModeParameter()); + newCodePeriodDto.setType(NewCodePeriodType.REFERENCE_BRANCH); + dbClient.newCodePeriodDao().update(dbSession, newCodePeriodDto); + dbSession.commit(); + } + } + + private boolean shouldUpdateNewCodePeriod(NewCodePeriodDto existingNewCodePeriod) { + return existingNewCodePeriod.getType() != NewCodePeriodType.REFERENCE_BRANCH + || !Objects.equals(existingNewCodePeriod.getValue(), periodHolder.getPeriod().getModeParameter()); + } + + private void createNewCodePeriod(DbSession dbSession, String branchUuid) { + LOGGER.debug("Persisting reference branch new code period '{}' for project '{}' and branch '{}'", + periodHolder.getPeriod().getModeParameter(), analysisMetadataHolder.getProject().getName(), analysisMetadataHolder.getBranch().getName()); + dbClient.newCodePeriodDao().insert(dbSession, buildNewCodePeriodDto(branchUuid)); + dbSession.commit(); + } + + private NewCodePeriodDto buildNewCodePeriodDto(String branchUuid) { + return new NewCodePeriodDto() + .setProjectUuid(analysisMetadataHolder.getProject().getUuid()) + .setBranchUuid(branchUuid) + .setType(NewCodePeriodType.REFERENCE_BRANCH) + .setValue(periodHolder.getPeriod().getModeParameter()); + } + +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java index 8cbcbbf421a..b121df6e657 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java @@ -106,6 +106,7 @@ public class ReportComputationSteps extends AbstractComputationSteps { // Persist data PersistScannerAnalysisCacheStep.class, PersistComponentsStep.class, + PersistReferenceBranchPeriodStep.class, PersistAnalysisStep.class, PersistAnalysisPropertiesStep.class, PersistProjectMeasuresStep.class, diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderRule.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderRule.java index f47424267a9..7c9b454e8c2 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderRule.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderRule.java @@ -73,4 +73,14 @@ public class PeriodHolderRule implements TestRule, PeriodHolder, AfterEachCallba return delegate.getPeriod(); } + @Override + public PeriodOrigin getPeriodOrigin() { + return delegate.getPeriodOrigin(); + } + + public PeriodHolderRule setPeriodOrigin(PeriodOrigin periodOrigin) { + delegate.setPeriodOrigin(periodOrigin); + return this; + } + } diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistReferenceBranchPeriodStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistReferenceBranchPeriodStepTest.java new file mode 100644 index 00000000000..84c3cc6d42d --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistReferenceBranchPeriodStepTest.java @@ -0,0 +1,194 @@ +/* + * SonarQube + * Copyright (C) 2009-2025 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.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mockito.ArgumentCaptor; +import org.slf4j.event.Level; +import org.sonar.api.testfixtures.log.LogTesterJUnit5; +import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder; +import org.sonar.ce.task.projectanalysis.analysis.TestBranch; +import org.sonar.ce.task.projectanalysis.component.Component; +import org.sonar.ce.task.projectanalysis.component.TreeRootHolder; +import org.sonar.ce.task.projectanalysis.period.Period; +import org.sonar.ce.task.projectanalysis.period.PeriodHolder; +import org.sonar.ce.task.projectanalysis.period.PeriodOrigin; +import org.sonar.ce.task.step.ComputationStep; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.newcodeperiod.NewCodePeriodDao; +import org.sonar.db.newcodeperiod.NewCodePeriodDto; +import org.sonar.db.newcodeperiod.NewCodePeriodType; +import org.sonar.server.project.Project; + +import static java.util.Collections.emptyList; +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.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.slf4j.event.Level.DEBUG; + +class PersistReferenceBranchPeriodStepTest { + + private static final String MAIN_BRANCH = "main"; + private static final String FEATURE_BRANCH = "feature"; + private static final String BRANCH_UUID = "branch-uuid"; + private static final String PROJECT_NAME = "project-name"; + private static final String PROJECT_UUID = "project-uuid"; + + private final PeriodHolder periodHolder = mock(PeriodHolder.class); + + private final AnalysisMetadataHolder analysisMetadataHolder = mock(AnalysisMetadataHolder.class); + + private final DbClient dbClient = mock(DbClient.class); + + private final TreeRootHolder treeRootHolder = mock(TreeRootHolder.class); + + @RegisterExtension + private final LogTesterJUnit5 logs = new LogTesterJUnit5().setLevel(Level.DEBUG); + + private final PersistReferenceBranchPeriodStep persistReferenceBranchPeriodStep = new PersistReferenceBranchPeriodStep( + periodHolder, analysisMetadataHolder, dbClient, treeRootHolder); + + private final DbSession dbSession = mock(DbSession.class); + private final NewCodePeriodDao newCodePeriodeDao = mock(NewCodePeriodDao.class); + + private final ComputationStep.Context context = mock(ComputationStep.Context.class); + + @BeforeEach + void setUp() { + Project project = new Project(PROJECT_UUID, "project-key", PROJECT_NAME, "project-description", emptyList()); + when(analysisMetadataHolder.isBranch()).thenReturn(true); + when(analysisMetadataHolder.getProject()).thenReturn(project); + when(analysisMetadataHolder.getBranch()).thenReturn(new TestBranch(FEATURE_BRANCH)); + + when(periodHolder.hasPeriod()).thenReturn(true); + Period period = new Period(NewCodePeriodType.REFERENCE_BRANCH.name(), MAIN_BRANCH, null); + when(periodHolder.getPeriod()).thenReturn(period); + when(periodHolder.getPeriodOrigin()).thenReturn(PeriodOrigin.SCANNER); + + when(dbClient.openSession(false)).thenReturn(dbSession); + when(dbClient.newCodePeriodDao()).thenReturn(newCodePeriodeDao); + + Component root = mock(Component.class); + when(treeRootHolder.getRoot()).thenReturn(root); + when(root.getUuid()).thenReturn(BRANCH_UUID); + + } + + @Test + void getDescription() { + assertThat(persistReferenceBranchPeriodStep.getDescription()).isEqualTo("Persist or update reference branch new code period"); + } + + @Test + void execute_shouldDoNothing_whenNotABranch() { + when(analysisMetadataHolder.isBranch()).thenReturn(false); + verifyExecuteNotCalled(); + } + + @Test + void execute_shouldDoNothing_whenNoPeriods() { + when(periodHolder.hasPeriod()).thenReturn(false); + verifyExecuteNotCalled(); + } + + @Test + void execute_shouldDoNothing_whenNotReferenceBranchPeriod() { + Period period = new Period("not-ref-branch", MAIN_BRANCH, null); + when(periodHolder.getPeriod()).thenReturn(period); + when(periodHolder.getPeriodOrigin()).thenReturn(PeriodOrigin.SETTINGS); + verifyExecuteNotCalled(); + } + + private void verifyExecuteNotCalled() { + PersistReferenceBranchPeriodStep spyStep = spy(persistReferenceBranchPeriodStep); + + spyStep.execute(context); + + verify(spyStep, never()).executePersistPeriodStep(); + } + + @Test + void execute_shouldCreateNewCodePeriod_whenItDoesNotExists() { + NewCodePeriodDto expectedNewCodePeriod = new NewCodePeriodDto() + .setBranchUuid(BRANCH_UUID) + .setProjectUuid(PROJECT_UUID) + .setType(NewCodePeriodType.REFERENCE_BRANCH) + .setValue(MAIN_BRANCH); + when(newCodePeriodeDao.selectByBranch(dbSession, PROJECT_UUID, BRANCH_UUID)).thenReturn(Optional.empty()); + + persistReferenceBranchPeriodStep.execute(context); + + assertThat(logs.logs(DEBUG)).contains( + String.format("Persisting reference branch new code period '%s' for project '%s' and branch '%s'",MAIN_BRANCH, PROJECT_NAME, FEATURE_BRANCH)); + ArgumentCaptor<NewCodePeriodDto> newCodePeriodCaptor = ArgumentCaptor.forClass(NewCodePeriodDto.class); + verify(newCodePeriodeDao).insert(eq(dbSession), newCodePeriodCaptor.capture()); + assertThat(newCodePeriodCaptor.getValue()).usingRecursiveComparison().isEqualTo(expectedNewCodePeriod); + } + + @Test + void execute_shouldUpdateNewCodePeriod_whenItExistsAndItChanged() { + NewCodePeriodDto expectedNewCodePeriod = new NewCodePeriodDto() + .setBranchUuid(BRANCH_UUID) + .setProjectUuid(PROJECT_UUID) + .setType(NewCodePeriodType.REFERENCE_BRANCH) + .setValue(MAIN_BRANCH); + var newCodePeriodInBase = new NewCodePeriodDto() + .setBranchUuid(BRANCH_UUID) + .setProjectUuid(PROJECT_UUID) + .setType(NewCodePeriodType.REFERENCE_BRANCH) + .setValue("old-value"); + + when(newCodePeriodeDao.selectByBranch(dbSession, PROJECT_UUID, BRANCH_UUID)).thenReturn(Optional.of(newCodePeriodInBase)); + + persistReferenceBranchPeriodStep.execute(context); + + assertThat(logs.logs(DEBUG)).contains( + String.format("Updating reference branch new code period '%s' for project '%s' and branch '%s'", MAIN_BRANCH ,PROJECT_NAME, FEATURE_BRANCH)); + ArgumentCaptor<NewCodePeriodDto> newCodePeriodCaptor = ArgumentCaptor.forClass(NewCodePeriodDto.class); + verify(newCodePeriodeDao).update(eq(dbSession), newCodePeriodCaptor.capture()); + assertThat(newCodePeriodCaptor.getValue()).usingRecursiveComparison().isEqualTo(expectedNewCodePeriod); + } + + @Test + void execute_shouldDoNothing_whenItExistsAndItDidNotChanged() { + NewCodePeriodDto expectedNewCodePeriod = new NewCodePeriodDto() + .setBranchUuid(BRANCH_UUID) + .setProjectUuid(PROJECT_UUID) + .setType(NewCodePeriodType.REFERENCE_BRANCH) + .setValue(MAIN_BRANCH); + + when(newCodePeriodeDao.selectByBranch(dbSession, PROJECT_UUID, BRANCH_UUID)).thenReturn(Optional.of(expectedNewCodePeriod)); + + persistReferenceBranchPeriodStep.execute(context); + + verify(newCodePeriodeDao, never()).update(any(), any()); + verify(newCodePeriodeDao, never()).insert(any(), any()); + } + +} |