/* * 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.qualitygate; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; import javax.annotation.Nullable; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.sonar.api.measures.Metric; 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.metric.MetricDto; import org.sonar.db.qualitygate.QualityGateConditionDto; import org.sonar.db.qualitygate.QualityGateDbTester; import org.sonar.db.qualitygate.QualityGateDto; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.NotFoundException; import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY; import static org.sonar.api.measures.Metric.ValueType.DATA; import static org.sonar.api.measures.Metric.ValueType.INT; import static org.sonar.api.measures.Metric.ValueType.RATING; import static org.sonar.db.metric.MetricTesting.newMetricDto; import static org.sonar.server.computation.task.projectanalysis.metric.Metric.MetricType.PERCENT; @RunWith(DataProviderRunner.class) public class QualityGateConditionsUpdaterTest { @Rule public ExpectedException expectedException = ExpectedException.none(); @Rule public DbTester db = DbTester.create(System2.INSTANCE); DbClient dbClient = db.getDbClient(); DbSession dbSession = db.getSession(); QualityGateDbTester qualityGateDbTester = new QualityGateDbTester(db); QualityGateDto qualityGateDto; MetricDto coverageMetricDto = newMetricDto() .setKey("coverage") .setShortName("Coverage") .setValueType(PERCENT.name()) .setHidden(false); MetricDto ratingMetricDto = newMetricDto() .setKey("reliability_rating") .setShortName("Reliability Rating") .setValueType(RATING.name()) .setHidden(false); QualityGateConditionsUpdater underTest = new QualityGateConditionsUpdater(dbClient); @Before public void setUp() throws Exception { qualityGateDto = qualityGateDbTester.insertQualityGate(); dbClient.metricDao().insert(dbSession, coverageMetricDto, ratingMetricDto); dbSession.commit(); } @Test public void create_warning_condition_without_period() { QualityGateConditionDto result = underTest.createCondition(dbSession, qualityGateDto.getId(), "coverage", "LT", "90", null, null); verifyCondition(result, coverageMetricDto.getId(), "LT", "90", null, null); } @Test public void create_error_condition_with_period() { MetricDto metricDto = dbClient.metricDao().insert(dbSession, newMetricDto() .setKey("new_coverage") .setValueType(INT.name()) .setHidden(false)); dbSession.commit(); QualityGateConditionDto result = underTest.createCondition(dbSession, qualityGateDto.getId(), "new_coverage", "LT", null, "80", 1); verifyCondition(result, metricDto.getId(), "LT", null, "80", 1); } @Test public void fail_to_create_condition_when_condition_on_same_metric_already_exist() throws Exception { dbClient.gateConditionDao().insert(new QualityGateConditionDto() .setQualityGateId(qualityGateDto.getId()) .setMetricId(coverageMetricDto.getId()) .setPeriod(null), dbSession); expectedException.expect(BadRequestException.class); expectedException.expectMessage("Condition on metric 'Coverage' already exists."); underTest.createCondition(dbSession, qualityGateDto.getId(), coverageMetricDto.getKey(), "LT", "90", null, null); } @Test public void fail_to_create_condition_when_condition_on_same_metric_and_on_leak_period_already_exist() throws Exception { dbClient.gateConditionDao().insert(new QualityGateConditionDto() .setQualityGateId(qualityGateDto.getId()) .setMetricId(coverageMetricDto.getId()) .setPeriod(1), dbSession); expectedException.expect(BadRequestException.class); expectedException.expectMessage("Condition on metric 'Coverage' over leak period already exists."); underTest.createCondition(dbSession, qualityGateDto.getId(), coverageMetricDto.getKey(), "LT", "90", null, 1); } @Test public void fail_to_create_condition_on_missing_metric() { expectedException.expect(NotFoundException.class); expectedException.expectMessage("There is no metric with key=new_coverage"); underTest.createCondition(dbSession, qualityGateDto.getId(), "new_coverage", "LT", null, "80", 2); } @Test @UseDataProvider("invalid_metrics") public void fail_to_create_condition_on_invalid_metric(String metricKey, Metric.ValueType valueType, boolean hidden) { dbClient.metricDao().insert(dbSession, newMetricDto() .setKey(metricKey) .setValueType(valueType.name()) .setHidden(hidden)); dbSession.commit(); expectedException.expect(BadRequestException.class); expectedException.expectMessage("Metric '" + metricKey + "' cannot be used to define a condition."); underTest.createCondition(dbSession, qualityGateDto.getId(), metricKey, "EQ", null, "80", null); } @Test public void fail_to_create_condition_on_not_allowed_operator() { expectedException.expect(BadRequestException.class); expectedException.expectMessage("Operator UNKNOWN is not allowed for metric type PERCENT."); underTest.createCondition(dbSession, qualityGateDto.getId(), "coverage", "UNKNOWN", null, "80", 2); } @Test public void fail_to_create_condition_on_missing_period() { dbClient.metricDao().insert(dbSession, newMetricDto() .setKey("new_coverage") .setValueType(INT.name()) .setHidden(false)); dbSession.commit(); expectedException.expect(BadRequestException.class); expectedException.expectMessage("A period must be selected for differential metrics."); underTest.createCondition(dbSession, qualityGateDto.getId(), "new_coverage", "EQ", null, "90", null); } @Test public void fail_to_create_condition_on_invalid_period() { expectedException.expect(BadRequestException.class); expectedException.expectMessage("The only valid quality gate period is 1, the leak period."); underTest.createCondition(dbSession, qualityGateDto.getId(), "coverage", "EQ", null, "90", 6); } @Test public void create_condition_on_rating_metric() { QualityGateConditionDto result = underTest.createCondition(dbSession, qualityGateDto.getId(), ratingMetricDto.getKey(), "GT", null, "3", null); verifyCondition(result, ratingMetricDto.getId(), "GT", null, "3", null); } @Test public void fail_to_create_condition_on_rating_metric_on_leak_period() { expectedException.expect(BadRequestException.class); expectedException.expectMessage("The metric 'Reliability Rating' cannot be used on the leak period"); underTest.createCondition(dbSession, qualityGateDto.getId(), ratingMetricDto.getKey(), "GT", null, "3", 1); } @Test public void fail_to_create_warning_condition_on_invalid_rating_metric() { expectedException.expect(BadRequestException.class); expectedException.expectMessage("'6' is not a valid rating"); underTest.createCondition(dbSession, qualityGateDto.getId(), ratingMetricDto.getKey(), "GT", "6", null, null); } @Test public void fail_to_create_error_condition_on_invalid_rating_metric() { expectedException.expect(BadRequestException.class); expectedException.expectMessage("'80' is not a valid rating"); underTest.createCondition(dbSession, qualityGateDto.getId(), ratingMetricDto.getKey(), "GT", null, "80", null); } @Test public void fail_to_create_condition_on_greater_than_E() { expectedException.expect(BadRequestException.class); expectedException.expectMessage("There's no worse rating than E (5)"); underTest.createCondition(dbSession, qualityGateDto.getId(), ratingMetricDto.getKey(), "GT", "5", null, null); } @Test public void update_condition() { QualityGateConditionDto condition = insertCondition(coverageMetricDto.getId(), "LT", null, "80", null); QualityGateConditionDto result = underTest.updateCondition(dbSession, condition.getId(), "coverage", "GT", "60", null, 1); verifyCondition(result, coverageMetricDto.getId(), "GT", "60", null, 1); } @Test public void update_condition_over_leak_period() { QualityGateConditionDto condition = insertCondition(coverageMetricDto.getId(), "GT", "80", null, 1); QualityGateConditionDto result = underTest.updateCondition(dbSession, condition.getId(), "coverage", "LT", null, "80", null); verifyCondition(result, coverageMetricDto.getId(), "LT", null, "80", null); } @Test public void update_condition_on_rating_metric() { QualityGateConditionDto condition = insertCondition(ratingMetricDto.getId(), "LT", null, "3", null); QualityGateConditionDto result = underTest.updateCondition(dbSession, condition.getId(), ratingMetricDto.getKey(), "GT", "4", null, null); verifyCondition(result, ratingMetricDto.getId(), "GT", "4", null, null); } @Test public void fail_to_update_condition_on_rating_metric_on_leak_period() { QualityGateConditionDto condition = insertCondition(ratingMetricDto.getId(), "LT", null, "3", null); expectedException.expect(BadRequestException.class); expectedException.expectMessage("The metric 'Reliability Rating' cannot be used on the leak period"); underTest.updateCondition(dbSession, condition.getId(), ratingMetricDto.getKey(), "GT", "4", null, 1); } @Test public void fail_to_update_condition_on_rating_metric_on_not_core_rating_metric() { MetricDto metricDto = dbClient.metricDao().insert(dbSession, newMetricDto().setKey("rating_metric") .setShortName("Not core rating") .setValueType(RATING.name()).setHidden(false)); QualityGateConditionDto condition = insertCondition(metricDto.getId(), "LT", null, "3", null); dbSession.commit(); expectedException.expect(BadRequestException.class); expectedException.expectMessage("The metric 'Not core rating' cannot be used"); underTest.updateCondition(dbSession, condition.getId(), metricDto.getKey(), "GT", "4", null, 1); } @Test @UseDataProvider("invalid_metrics") public void fail_to_update_condition_on_invalid_metric(String metricKey, Metric.ValueType valueType, boolean hidden) { MetricDto metricDto = dbClient.metricDao().insert(dbSession, newMetricDto() .setKey(metricKey) .setValueType(valueType.name()) .setHidden(hidden)); QualityGateConditionDto condition = insertCondition(metricDto.getId(), "LT", null, "80", null); dbSession.commit(); expectedException.expect(BadRequestException.class); expectedException.expectMessage("Metric '" + metricKey + "' cannot be used to define a condition."); underTest.updateCondition(dbSession, condition.getId(), metricDto.getKey(), "GT", "60", null, 1); } @Test public void fail_to_update_condition_when_condition_on_same_metric_already_exist() throws Exception { QualityGateConditionDto conditionNotOnLeakPeriod = insertCondition(coverageMetricDto.getId(), "GT", "80", null, null); QualityGateConditionDto conditionOnLeakPeriod = insertCondition(coverageMetricDto.getId(), "GT", "80", null, 1); expectedException.expect(BadRequestException.class); expectedException.expectMessage("Condition on metric 'Coverage' over leak period already exists."); // Update condition not on leak period to be on leak period => will fail as this condition already exist underTest.updateCondition(dbSession, conditionNotOnLeakPeriod.getId(), coverageMetricDto.getKey(), "GT", "80", null, 1); } @DataProvider public static Object[][] invalid_metrics() { return new Object[][] { {ALERT_STATUS_KEY, INT, false}, {"data_metric", DATA, false}, {"hidden", INT, true} }; } private QualityGateConditionDto insertCondition(long metricId, String operator, @Nullable String warning, @Nullable String error, @Nullable Integer period) { QualityGateConditionDto qualityGateConditionDto = new QualityGateConditionDto().setQualityGateId(qualityGateDto.getId()) .setMetricId(metricId) .setOperator(operator) .setWarningThreshold(warning) .setErrorThreshold(error) .setPeriod(period); dbClient.gateConditionDao().insert(qualityGateConditionDto, dbSession); dbSession.commit(); return qualityGateConditionDto; } private void verifyCondition(QualityGateConditionDto dto, int metricId, String operator, @Nullable String warning, @Nullable String error, @Nullable Integer period) { QualityGateConditionDto reloaded = dbClient.gateConditionDao().selectById(dto.getId(), dbSession); assertThat(reloaded.getQualityGateId()).isEqualTo(qualityGateDto.getId()); assertThat(reloaded.getMetricId()).isEqualTo(metricId); assertThat(reloaded.getOperator()).isEqualTo(operator); assertThat(reloaded.getWarningThreshold()).isEqualTo(warning); assertThat(reloaded.getErrorThreshold()).isEqualTo(error); assertThat(reloaded.getPeriod()).isEqualTo(period); assertThat(dto.getQualityGateId()).isEqualTo(qualityGateDto.getId()); assertThat(dto.getMetricId()).isEqualTo(metricId); assertThat(dto.getOperator()).isEqualTo(operator); assertThat(dto.getWarningThreshold()).isEqualTo(warning); assertThat(dto.getErrorThreshold()).isEqualTo(error); assertThat(dto.getPeriod()).isEqualTo(period); } }