From 1770cc66b96a2185edcd5bacd04ac154e3dfefe1 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Fri, 19 Jun 2015 17:02:11 +0200 Subject: [PATCH] SONAR-6635 Check analysis date is after latest analysis --- .../computation/step/ValidateProjectStep.java | 30 ++++++-- .../step/ValidateProjectStepTest.java | 73 +++++++++++++++++-- 2 files changed, 89 insertions(+), 14 deletions(-) diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ValidateProjectStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ValidateProjectStep.java index 58c1292fb63..37382a8e8eb 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ValidateProjectStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ValidateProjectStep.java @@ -24,6 +24,7 @@ import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.collect.FluentIterable; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Map; import javax.annotation.CheckForNull; @@ -35,6 +36,7 @@ import org.sonar.api.utils.MessageException; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.core.component.ComponentDto; import org.sonar.core.component.ComponentKeys; +import org.sonar.core.component.SnapshotDto; import org.sonar.core.persistence.DbSession; import org.sonar.server.component.db.ComponentDao; import org.sonar.server.computation.batch.BatchReportReader; @@ -43,6 +45,8 @@ import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.db.DbClient; +import static org.sonar.api.utils.DateUtils.formatDateTime; + /** * Validate project and modules. It will fail in the following cases : *
    @@ -121,9 +125,10 @@ public class ValidateProjectStep implements ComputationStep { ComponentDto baseProject = loadBaseComponent(rawProjectKey); validateWhenProvisioningEnforced(baseProject, rawProjectKey); validateProjectKey(baseProject, rawProjectKey); + validateAnalysisDate(baseProject); } - private void validateWhenProvisioningEnforced(@Nullable ComponentDto baseProject, String rawProjectKey){ + private void validateWhenProvisioningEnforced(@Nullable ComponentDto baseProject, String rawProjectKey) { if (baseProject == null) { if (preventAutomaticProjectCreation) { validationMessages.add(String.format("Unable to scan non-existing project '%s'", rawProjectKey)); @@ -131,16 +136,29 @@ public class ValidateProjectStep implements ComputationStep { } } - private void validateProjectKey(@Nullable ComponentDto baseProject, String rawProjectKey){ + private void validateProjectKey(@Nullable ComponentDto baseProject, String rawProjectKey) { if (baseProject != null && !baseProject.projectUuid().equals(baseProject.uuid())) { // Project key is already used as a module of another project ComponentDto anotherBaseProject = componentDao.selectByUuid(session, baseProject.projectUuid()); validationMessages.add(String.format("The project \"%s\" is already defined in SonarQube but as a module of project \"%s\". " - + "If you really want to stop directly analysing project \"%s\", please first delete it from SonarQube and then relaunch the analysis of project \"%s\".", + + "If you really want to stop directly analysing project \"%s\", please first delete it from SonarQube and then relaunch the analysis of project \"%s\".", rawProjectKey, anotherBaseProject.key(), anotherBaseProject.key(), rawProjectKey)); } } + private void validateAnalysisDate(@Nullable ComponentDto baseProject) { + if (baseProject != null) { + SnapshotDto snapshotDto = dbClient.snapshotDao().selectLastSnapshotByComponentId(session, baseProject.getId()); + long currentAnalysisDate = reportReader.readMetadata().getAnalysisDate(); + Long lastAnalysisDate = snapshotDto != null ? snapshotDto.getCreatedAt() : null; + if (lastAnalysisDate != null && currentAnalysisDate <= snapshotDto.getCreatedAt()) { + validationMessages.add(String.format("Date of analysis cannot be older than the date of the last known analysis on this project. Value: \"%s\". " + + "Latest analysis: \"%s\". It's only possible to rebuild the past in a chronological order.", + formatDateTime(new Date(currentAnalysisDate)), formatDateTime(new Date(lastAnalysisDate)))); + } + } + } + @Override public void visitModule(Component rawModule) { String rawProjectKey = rawProject.getKey(); @@ -156,16 +174,16 @@ public class ValidateProjectStep implements ComputationStep { validateModuleKeyIsNotAlreadyUsedInAnotherProject(baseModule, rawModuleKey); } - private void validateModuleIsNotAlreadyUsedAsProject(ComponentDto baseModule, String rawProjectKey, String rawModuleKey){ + private void validateModuleIsNotAlreadyUsedAsProject(ComponentDto baseModule, String rawProjectKey, String rawModuleKey) { if (baseModule.projectUuid().equals(baseModule.uuid())) { // module is actually a project validationMessages.add(String.format("The project \"%s\" is already defined in SonarQube but not as a module of project \"%s\". " - + "If you really want to stop directly analysing project \"%s\", please first delete it from SonarQube and then relaunch the analysis of project \"%s\".", + + "If you really want to stop directly analysing project \"%s\", please first delete it from SonarQube and then relaunch the analysis of project \"%s\".", rawModuleKey, rawProjectKey, rawModuleKey, rawProjectKey)); } } - private void validateModuleKeyIsNotAlreadyUsedInAnotherProject(ComponentDto baseModule, String rawModuleKey){ + private void validateModuleKeyIsNotAlreadyUsedInAnotherProject(ComponentDto baseModule, String rawModuleKey) { if (!baseModule.projectUuid().equals(baseModule.uuid()) && !baseModule.projectUuid().equals(rawProject.getUuid())) { ComponentDto projectModule = componentDao.selectByUuid(session, baseModule.projectUuid()); validationMessages.add(String.format("Module \"%s\" is already part of project \"%s\"", rawModuleKey, projectModule.key())); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ValidateProjectStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ValidateProjectStepTest.java index cbfab26eab7..feca62fff9d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ValidateProjectStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ValidateProjectStepTest.java @@ -25,6 +25,7 @@ import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; +import org.junit.experimental.categories.Category; import org.junit.rules.ExpectedException; import org.sonar.api.CoreProperties; import org.sonar.api.config.Settings; @@ -35,15 +36,20 @@ import org.sonar.core.component.ComponentDto; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.DbTester; import org.sonar.server.component.ComponentTesting; +import org.sonar.server.component.SnapshotTesting; import org.sonar.server.component.db.ComponentDao; +import org.sonar.server.component.db.SnapshotDao; import org.sonar.server.computation.batch.BatchReportReaderRule; import org.sonar.server.computation.batch.TreeRootHolderRule; import org.sonar.server.computation.component.Component; import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.db.DbClient; +import org.sonar.test.DbTests; +@Category(DbTests.class) public class ValidateProjectStepTest { + private static long DEFAULT_ANALYSIS_TIME = 1433131200000L; // 2015-06-01 private static final String PROJECT_KEY = "PROJECT_KEY"; private static final String MODULE_KEY = "MODULE_KEY"; @@ -67,7 +73,7 @@ public class ValidateProjectStepTest { @Before public void setUp() throws Exception { dbTester.truncateTables(); - dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new ComponentDao()); + dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new ComponentDao(), new SnapshotDao()); dbSession = dbClient.openSession(false); settings = new Settings(); @@ -81,7 +87,7 @@ public class ValidateProjectStepTest { @Test public void not_fail_if_provisioning_enforced_and_project_exists() throws Exception { - reportReader.setMetadata(BatchReport.Metadata.newBuilder().build()); + reportReader.setMetadata(BatchReport.Metadata.newBuilder().setAnalysisDate(DEFAULT_ANALYSIS_TIME).build()); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -101,7 +107,7 @@ public class ValidateProjectStepTest { thrown.expect(MessageException.class); thrown.expectMessage("Unable to scan non-existing project '" + PROJECT_KEY + "'"); - reportReader.setMetadata(BatchReport.Metadata.newBuilder().build()); + reportReader.setMetadata(BatchReport.Metadata.newBuilder().setAnalysisDate(DEFAULT_ANALYSIS_TIME).build()); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -116,7 +122,7 @@ public class ValidateProjectStepTest { @Test public void fail_if_provisioning_not_enforced_and_project_does_not_exists() throws Exception { - reportReader.setMetadata(BatchReport.Metadata.newBuilder().build()); + reportReader.setMetadata(BatchReport.Metadata.newBuilder().setAnalysisDate(DEFAULT_ANALYSIS_TIME).build()); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -132,6 +138,7 @@ public class ValidateProjectStepTest { @Test public void not_fail_on_valid_branch() throws Exception { reportReader.setMetadata(BatchReport.Metadata.newBuilder() + .setAnalysisDate(DEFAULT_ANALYSIS_TIME) .setBranch("origin/master") .build()); reportReader.putComponent(BatchReport.Component.newBuilder() @@ -151,6 +158,7 @@ public class ValidateProjectStepTest { " o \"bran#ch\" is not a valid branch name. Allowed characters are alphanumeric, '-', '_', '.' and '/'."); reportReader.setMetadata(BatchReport.Metadata.newBuilder() + .setAnalysisDate(DEFAULT_ANALYSIS_TIME) .setBranch("bran#ch") .build()); reportReader.putComponent(BatchReport.Component.newBuilder() @@ -172,7 +180,7 @@ public class ValidateProjectStepTest { " o \"Project\\Key\" is not a valid project or module key. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.\n" + " o \"Module$Key\" is not a valid project or module key. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit"); - reportReader.setMetadata(BatchReport.Metadata.newBuilder().build()); + reportReader.setMetadata(BatchReport.Metadata.newBuilder().setAnalysisDate(DEFAULT_ANALYSIS_TIME).build()); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -199,7 +207,7 @@ public class ValidateProjectStepTest { "If you really want to stop directly analysing project \"" + MODULE_KEY + "\", please first delete it from SonarQube and then relaunch the analysis of project \"" + PROJECT_KEY + "\"."); - reportReader.setMetadata(BatchReport.Metadata.newBuilder().build()); + reportReader.setMetadata(BatchReport.Metadata.newBuilder().setAnalysisDate(DEFAULT_ANALYSIS_TIME).build()); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -230,7 +238,7 @@ public class ValidateProjectStepTest { thrown.expectMessage("Validation of project failed:\n" + " o Module \"" + MODULE_KEY + "\" is already part of project \"" + anotherProjectKey + "\""); - reportReader.setMetadata(BatchReport.Metadata.newBuilder().build()); + reportReader.setMetadata(BatchReport.Metadata.newBuilder().setAnalysisDate(DEFAULT_ANALYSIS_TIME).build()); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -266,7 +274,7 @@ public class ValidateProjectStepTest { "If you really want to stop directly analysing project \"" + anotherProjectKey + "\", please first delete it from SonarQube and then relaunch the analysis of project \"" + PROJECT_KEY + "\"."); - reportReader.setMetadata(BatchReport.Metadata.newBuilder().build()); + reportReader.setMetadata(BatchReport.Metadata.newBuilder().setAnalysisDate(DEFAULT_ANALYSIS_TIME).build()); reportReader.putComponent(BatchReport.Component.newBuilder() .setRef(1) .setType(Constants.ComponentType.PROJECT) @@ -291,4 +299,53 @@ public class ValidateProjectStepTest { sut.execute(); } + + @Test + public void not_fail_if_analysis_date_is_after_last_analysis() throws Exception { + reportReader.setMetadata(BatchReport.Metadata.newBuilder() + .setAnalysisDate(DEFAULT_ANALYSIS_TIME) // 2015-06-01 + .build()); + reportReader.putComponent(BatchReport.Component.newBuilder() + .setRef(1) + .setType(Constants.ComponentType.PROJECT) + .setKey(PROJECT_KEY) + .addChildRef(2) + .build()); + + ComponentDto project = ComponentTesting.newProjectDto("ABCD").setKey(PROJECT_KEY); + dbClient.componentDao().insert(dbSession, project); + dbClient.snapshotDao().insert(dbSession, SnapshotTesting.createForProject(project).setCreatedAt(1420088400000L)); // 2015-01-01 + dbSession.commit(); + + treeRootHolder.setRoot(DumbComponent.builder(Component.Type.PROJECT, 1).setUuid("ABCD").setKey(PROJECT_KEY).build()); + + sut.execute(); + } + + @Test + public void fail_if_analysis_date_is_before_last_analysis() throws Exception { + thrown.expect(MessageException.class); + thrown.expectMessage("Validation of project failed:"); + thrown.expectMessage("Date of analysis cannot be older than the date of the last known analysis on this project. Value: "); + thrown.expectMessage("Latest analysis: "); + + reportReader.setMetadata(BatchReport.Metadata.newBuilder() + .setAnalysisDate(1420088400000L) // 2015-01-01 + .build()); + reportReader.putComponent(BatchReport.Component.newBuilder() + .setRef(1) + .setType(Constants.ComponentType.PROJECT) + .setKey(PROJECT_KEY) + .addChildRef(2) + .build()); + + ComponentDto project = ComponentTesting.newProjectDto("ABCD").setKey(PROJECT_KEY); + dbClient.componentDao().insert(dbSession, project); + dbClient.snapshotDao().insert(dbSession, SnapshotTesting.createForProject(project).setCreatedAt(1433131200000L)); // 2015-06-01 + dbSession.commit(); + + treeRootHolder.setRoot(DumbComponent.builder(Component.Type.PROJECT, 1).setUuid("ABCD").setKey(PROJECT_KEY).build()); + + sut.execute(); + } } -- 2.39.5