/*
* SonarQube
* Copyright (C) 2009-2017 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.computation.task.projectanalysis.step;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentCaptor;
import org.sonar.api.utils.MessageException;
import org.sonar.api.utils.System2;
import org.sonar.ce.queue.CeTask;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.server.computation.task.projectanalysis.analysis.MutableAnalysisMetadataHolderRule;
import org.sonar.server.computation.task.projectanalysis.analysis.Organization;
import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReaderRule;
import org.sonar.server.computation.task.step.ComputationStep;
import org.sonar.server.organization.BillingValidations;
import org.sonar.server.organization.BillingValidations.BillingValidationsException;
import org.sonar.server.organization.BillingValidationsProxy;
import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.organization.TestDefaultOrganizationProvider;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class LoadReportAnalysisMetadataHolderStepTest {
private static final String PROJECT_KEY = "project_key";
private static final String BRANCH = "origin/master";
private static final long ANALYSIS_DATE = 123456789L;
@Rule
public DbTester dbTester = DbTester.create(System2.INSTANCE);
@Rule
public BatchReportReaderRule reportReader = new BatchReportReaderRule();
@Rule
public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule();
@Rule
public ExpectedException expectedException = ExpectedException.none();
private DbClient dbClient = dbTester.getDbClient();
private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(dbTester);
private BillingValidationsProxy billingValidations = mock(BillingValidationsProxy.class);
private ComputationStep underTest;
@Before
public void setUp() {
CeTask defaultOrgCeTask = createCeTask(PROJECT_KEY, dbTester.getDefaultOrganization().getUuid());
underTest = createStep(defaultOrgCeTask);
}
@Test
public void set_root_component_ref() {
reportReader.setMetadata(
newBatchReportBuilder()
.setRootComponentRef(1)
.build());
underTest.execute();
assertThat(analysisMetadataHolder.getRootComponentRef()).isEqualTo(1);
}
@Test
public void set_analysis_date() {
reportReader.setMetadata(
newBatchReportBuilder()
.setAnalysisDate(ANALYSIS_DATE)
.build());
underTest.execute();
assertThat(analysisMetadataHolder.getAnalysisDate()).isEqualTo(ANALYSIS_DATE);
}
@Test
public void set_branch() {
reportReader.setMetadata(
newBatchReportBuilder()
.setBranch(BRANCH)
.build());
CeTask ceTask = createCeTask(PROJECT_KEY + ":" + BRANCH, dbTester.getDefaultOrganization().getUuid());
ComputationStep underTest = createStep(ceTask);
underTest.execute();
assertThat(analysisMetadataHolder.getBranch()).isEqualTo(BRANCH);
}
@Test
public void set_null_branch_when_nothing_in_the_report() {
reportReader.setMetadata(
newBatchReportBuilder()
.build());
underTest.execute();
assertThat(analysisMetadataHolder.getBranch()).isNull();
}
@Test
public void set_cross_project_duplication_to_true() {
reportReader.setMetadata(
newBatchReportBuilder()
.setCrossProjectDuplicationActivated(true)
.build());
underTest.execute();
assertThat(analysisMetadataHolder.isCrossProjectDuplicationEnabled()).isEqualTo(true);
}
@Test
public void set_cross_project_duplication_to_false() {
reportReader.setMetadata(
newBatchReportBuilder()
.setCrossProjectDuplicationActivated(false)
.build());
underTest.execute();
assertThat(analysisMetadataHolder.isCrossProjectDuplicationEnabled()).isEqualTo(false);
}
@Test
public void set_cross_project_duplication_to_false_when_nothing_in_the_report() {
reportReader.setMetadata(
newBatchReportBuilder()
.build());
underTest.execute();
assertThat(analysisMetadataHolder.isCrossProjectDuplicationEnabled()).isEqualTo(false);
}
@Test
public void execute_fails_with_MessageException_if_projectKey_is_null_in_CE_task() {
CeTask res = mock(CeTask.class);
when(res.getComponentUuid()).thenReturn("prj_uuid");
reportReader.setMetadata(ScannerReport.Metadata.newBuilder().build());
ComputationStep underTest = createStep(res);
expectedException.expect(MessageException.class);
expectedException.expectMessage("Compute Engine task component key is null. Project with UUID prj_uuid must have been deleted since report was uploaded. Can not proceed.");
underTest.execute();
}
@Test
public void execute_fails_with_MessageException_when_projectKey_in_report_is_different_from_componentKey_in_CE_task() {
reportReader.setMetadata(
ScannerReport.Metadata.newBuilder()
.setProjectKey("some other key")
.build());
expectedException.expect(MessageException.class);
expectedException.expectMessage("ProjectKey in report (some other key) is not consistent with projectKey under which the report as been submitted (" + PROJECT_KEY + ")");
underTest.execute();
}
@Test
public void execute_sets_analysis_date_even_if_MessageException_is_thrown_because_projectKey_is_different_from_componentKey_in_CE_task() {
reportReader.setMetadata(
ScannerReport.Metadata.newBuilder()
.setProjectKey("some other key")
.setAnalysisDate(ANALYSIS_DATE)
.build());
try {
underTest.execute();
} catch (MessageException e) {
assertThat(analysisMetadataHolder.getAnalysisDate()).isEqualTo(ANALYSIS_DATE);
}
}
@Test
public void execute_fails_with_MessageException_when_report_has_no_organizationKey_but_does_not_belong_to_the_default_organization() {
reportReader.setMetadata(
newBatchReportBuilder()
.build());
OrganizationDto nonDefaultOrganizationDto = dbTester.organizations().insert();
ComputationStep underTest = createStep(createCeTask(PROJECT_KEY, nonDefaultOrganizationDto.getUuid()));
expectedException.expect(MessageException.class);
expectedException.expectMessage("Report does not specify an OrganizationKey but it has been submitted to another organization (" +
nonDefaultOrganizationDto.getKey() + ") than the default one (" + dbTester.getDefaultOrganization().getKey() + ")");
underTest.execute();
}
@Test
public void execute_set_organization_from_ce_task_when_organizationKey_is_not_set_in_report() {
reportReader.setMetadata(
newBatchReportBuilder()
.build());
underTest.execute();
Organization organization = analysisMetadataHolder.getOrganization();
OrganizationDto defaultOrganization = dbTester.getDefaultOrganization();
assertThat(organization.getUuid()).isEqualTo(defaultOrganization.getUuid());
assertThat(organization.getKey()).isEqualTo(defaultOrganization.getKey());
assertThat(organization.getName()).isEqualTo(defaultOrganization.getName());
}
@Test
public void execute_set_organization_from_ce_task_when_organizationKey_is_set_in_report() {
reportReader.setMetadata(
newBatchReportBuilder()
.setOrganizationKey(dbTester.getDefaultOrganization().getKey())
.build());
underTest.execute();
Organization organization = analysisMetadataHolder.getOrganization();
OrganizationDto defaultOrganization = dbTester.getDefaultOrganization();
assertThat(organization.getUuid()).isEqualTo(defaultOrganization.getUuid());
assertThat(organization.getKey()).isEqualTo(defaultOrganization.getKey());
assertThat(organization.getName()).isEqualTo(defaultOrganization.getName());
}
@Test
public void execute_set_non_default_organization_from_ce_task() {
OrganizationDto nonDefaultOrganizationDto = dbTester.organizations().insert();
reportReader.setMetadata(
newBatchReportBuilder()
.setOrganizationKey(nonDefaultOrganizationDto.getKey())
.build());
ComputationStep underTest = createStep(createCeTask(PROJECT_KEY, nonDefaultOrganizationDto.getUuid()));
underTest.execute();
Organization organization = analysisMetadataHolder.getOrganization();
assertThat(organization.getUuid()).isEqualTo(nonDefaultOrganizationDto.getUuid());
assertThat(organization.getKey()).isEqualTo(nonDefaultOrganizationDto.getKey());
assertThat(organization.getName()).isEqualTo(nonDefaultOrganizationDto.getName());
}
@Test
public void execute_ensures_that_report_has_quality_profiles_matching_the_project_organization() {
OrganizationDto organization = dbTester.organizations().insert();
ScannerReport.Metadata.Builder metadataBuilder = newBatchReportBuilder();
metadataBuilder.setOrganizationKey(organization.getKey());
metadataBuilder.getMutableQprofilesPerLanguage().put("js", ScannerReport.Metadata.QProfile.newBuilder().setKey("p1").setName("Sonar way").setLanguage("js").build());
reportReader.setMetadata(metadataBuilder.build());
dbTester.qualityProfiles().insert(organization, p -> p.setLanguage("js"), p -> p.setKey("p1"));
ComputationStep underTest = createStep(createCeTask(PROJECT_KEY, organization.getUuid()));
// no errors
underTest.execute();
}
@Test
public void execute_fails_with_MessageException_when_report_has_quality_profiles_on_other_organizations() {
OrganizationDto organization1 = dbTester.organizations().insert();
OrganizationDto organization2 = dbTester.organizations().insert();
ScannerReport.Metadata.Builder metadataBuilder = newBatchReportBuilder();
metadataBuilder.setOrganizationKey(organization1.getKey());
metadataBuilder.getMutableQprofilesPerLanguage().put("js", ScannerReport.Metadata.QProfile.newBuilder().setKey("jsInOrg1").setName("Sonar way").setLanguage("js").build());
metadataBuilder.getMutableQprofilesPerLanguage().put("php", ScannerReport.Metadata.QProfile.newBuilder().setKey("phpInOrg2").setName("PHP way").setLanguage("php").build());
reportReader.setMetadata(metadataBuilder.build());
dbTester.qualityProfiles().insert(organization1, p -> p.setLanguage("js"), p -> p.setKey("jsInOrg1"));
dbTester.qualityProfiles().insert(organization2, p -> p.setLanguage("php"), p -> p.setKey("phpInOrg2"));
ComputationStep underTest = createStep(createCeTask(PROJECT_KEY, organization1.getUuid()));
expectedException.expect(MessageException.class);
expectedException.expectMessage("Quality profiles with following keys don't exist in organization [" + organization1.getKey() + "]: phpInOrg2");
underTest.execute();
}
@Test
public void execute_does_not_fail_when_report_has_a_quality_profile_that_does_not_exist_anymore() {
OrganizationDto organization = dbTester.organizations().insert();
ScannerReport.Metadata.Builder metadataBuilder = newBatchReportBuilder();
metadataBuilder.setOrganizationKey(organization.getKey());
metadataBuilder.getMutableQprofilesPerLanguage().put("js", ScannerReport.Metadata.QProfile.newBuilder().setKey("p1").setName("Sonar way").setLanguage("js").build());
reportReader.setMetadata(metadataBuilder.build());
ComputationStep underTest = createStep(createCeTask(PROJECT_KEY, organization.getUuid()));
underTest.execute();
}
@Test
public void execute_fails_with_MessageException_when_organization_is_not_allowed_to_execute_analysis() {
OrganizationDto organization = dbTester.organizations().insert();
reportReader.setMetadata(newBatchReportBuilder()
.setOrganizationKey(organization.getKey())
.build());
ComputationStep underTest = createStep(createCeTask(PROJECT_KEY, organization.getUuid()));
doThrow(new BillingValidationsException("This organization cannot execute project analysis")).when(billingValidations)
.checkOnProjectAnalysis(any(BillingValidations.Organization.class));
expectedException.expect(MessageException.class);
expectedException.expectMessage("This organization cannot execute project analysis");
underTest.execute();
}
@Test
public void execute_does_no_fails_when_organization_is_allowed_to_execute_analysis() {
OrganizationDto organization = dbTester.organizations().insert();
reportReader.setMetadata(newBatchReportBuilder()
.setOrganizationKey(organization.getKey())
.build());
ComputationStep underTest = createStep(createCeTask(PROJECT_KEY, organization.getUuid()));
underTest.execute();
ArgumentCaptor<BillingValidations.Organization> argumentCaptor = ArgumentCaptor.forClass(BillingValidations.Organization.class);
verify(billingValidations).checkOnProjectAnalysis(argumentCaptor.capture());
assertThat(argumentCaptor.getValue().getKey()).isEqualTo(organization.getKey());
assertThat(argumentCaptor.getValue().getUuid()).isEqualTo(organization.getUuid());
}
private LoadReportAnalysisMetadataHolderStep createStep(CeTask ceTask) {
return new LoadReportAnalysisMetadataHolderStep(ceTask, reportReader, analysisMetadataHolder, defaultOrganizationProvider, dbClient, billingValidations);
}
private static ScannerReport.Metadata.Builder newBatchReportBuilder() {
return ScannerReport.Metadata.newBuilder()
.setProjectKey(PROJECT_KEY);
}
private CeTask createCeTask(String projectKey, String organizationUuid) {
CeTask res = mock(CeTask.class);
when(res.getOrganizationUuid()).thenReturn(organizationUuid);
when(res.getComponentKey()).thenReturn(projectKey);
return res;
}
}