From ed707f3ee780d6bb3cd588bedce87abb57dcc3dd Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Thu, 29 Mar 2018 17:20:25 +0200 Subject: [PATCH] SONAR-5182 Add input validation on quality gate conditions --- .../QualityGateConditionsUpdater.java | 36 ++ .../qualitygate/ws/CreateConditionAction.java | 3 +- .../qualitygate/ws/UpdateConditionAction.java | 3 +- .../QualityGateConditionsUpdaterTest.java | 384 ++++++++++++------ .../ws/CreateConditionActionTest.java | 38 ++ .../ws/UpdateConditionActionTest.java | 42 ++ .../tests/qualityGate/QualityGateTest.java | 268 +++++------- tests/src/test/java/util/ItUtils.java | 14 +- 8 files changed, 490 insertions(+), 298 deletions(-) diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateConditionsUpdater.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateConditionsUpdater.java index d91c1c837e0..0a70ed55ad6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateConditionsUpdater.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateConditionsUpdater.java @@ -38,7 +38,9 @@ import org.sonar.server.computation.task.projectanalysis.qualitymodel.Rating; import org.sonar.server.exceptions.NotFoundException; import static com.google.common.base.Strings.isNullOrEmpty; +import static java.lang.Double.parseDouble; import static java.lang.Integer.parseInt; +import static java.lang.Long.parseLong; import static java.lang.String.format; import static java.util.Arrays.stream; import static org.sonar.api.measures.Metric.ValueType.RATING; @@ -115,6 +117,9 @@ public class QualityGateConditionsUpdater { checkOperator(metric, operator, errors); checkThresholds(warningThreshold, errorThreshold, errors); checkPeriod(metric, period, errors); + + validateThresholdValues(metric, warningThreshold, errors); + validateThresholdValues(metric, errorThreshold, errors); checkRatingMetric(metric, warningThreshold, errorThreshold, period, errors); checkRequest(errors.isEmpty(), errors); } @@ -159,6 +164,37 @@ public class QualityGateConditionsUpdater { : format("Condition on metric '%s' over leak period already exists.", metric.getShortName())); } + private static void validateThresholdValues(MetricDto metric, @Nullable String value, List errors) { + if (value == null) { + return; + } + try { + ValueType valueType = ValueType.valueOf(metric.getValueType()); + switch (valueType) { + case BOOL: + case INT: + case RATING: + parseInt(value); + return; + case MILLISEC: + case WORK_DUR: + parseLong(value); + return; + case FLOAT: + case PERCENT: + parseDouble(value); + return; + case STRING: + case LEVEL: + return; + default: + throw new IllegalArgumentException(format("Unsupported value type %s. Cannot convert condition value", valueType)); + } + } catch (Exception e) { + errors.add(format("Invalid value '%s' for metric '%s'", value, metric.getShortName())); + } + } + private static void checkRatingMetric(MetricDto metric, @Nullable String warningThreshold, @Nullable String errorThreshold, @Nullable Integer period, List errors) { if (!metric.getValueType().equals(RATING.name())) { return; diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/CreateConditionAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/CreateConditionAction.java index ef9d4d6ee6d..0b40b6c776e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/CreateConditionAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/CreateConditionAction.java @@ -31,6 +31,7 @@ import org.sonar.server.qualitygate.QualityGateConditionsUpdater; import org.sonar.server.qualitygate.QualityGateFinder; import org.sonarqube.ws.Qualitygates.CreateConditionResponse; +import static com.google.common.base.Strings.emptyToNull; import static org.sonar.core.util.Protobuf.setNullable; import static org.sonar.server.qualitygate.ws.QualityGatesWs.addConditionParams; import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.ACTION_CREATE_CONDITION; @@ -89,7 +90,7 @@ public class CreateConditionAction implements QualityGatesWsAction { OrganizationDto organization = wsSupport.getOrganization(dbSession, request); QGateWithOrgDto qualityGate = qualityGateFinder.getByOrganizationAndId(dbSession, organization, gateId); wsSupport.checkCanEdit(qualityGate); - QualityGateConditionDto condition = qualityGateConditionsUpdater.createCondition(dbSession, qualityGate, metric, operator, warning, error, period); + QualityGateConditionDto condition = qualityGateConditionsUpdater.createCondition(dbSession, qualityGate, metric, operator, emptyToNull(warning), emptyToNull(error), period); CreateConditionResponse.Builder createConditionResponse = CreateConditionResponse.newBuilder() .setId(condition.getId()) .setMetric(condition.getMetricKey()) diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/UpdateConditionAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/UpdateConditionAction.java index d5365ef5423..dcdb8b6e29f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/UpdateConditionAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/UpdateConditionAction.java @@ -31,6 +31,7 @@ import org.sonar.server.qualitygate.QualityGateConditionsUpdater; import org.sonarqube.ws.Qualitygates.UpdateConditionResponse; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Strings.emptyToNull; import static org.sonar.core.util.Protobuf.setNullable; import static org.sonar.server.qualitygate.ws.QualityGatesWs.addConditionParams; import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.ACTION_UPDATE_CONDITION; @@ -88,7 +89,7 @@ public class UpdateConditionAction implements QualityGatesWsAction { QGateWithOrgDto qualityGateDto = dbClient.qualityGateDao().selectByOrganizationAndId(dbSession, organization, condition.getQualityGateId()); checkState(qualityGateDto != null, "Condition '%s' is linked to an unknown quality gate '%s'", id, condition.getQualityGateId()); wsSupport.checkCanEdit(qualityGateDto); - QualityGateConditionDto updatedCondition = qualityGateConditionsUpdater.updateCondition(dbSession, condition, metric, operator, warning, error, period); + QualityGateConditionDto updatedCondition = qualityGateConditionsUpdater.updateCondition(dbSession, condition, metric, operator, emptyToNull(warning), emptyToNull(error), period); UpdateConditionResponse.Builder updateConditionResponse = UpdateConditionResponse.newBuilder() .setId(updatedCondition.getId()) .setMetric(updatedCondition.getMetricKey()) diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateConditionsUpdaterTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateConditionsUpdaterTest.java index 74c4583f7a9..22693231eec 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateConditionsUpdaterTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateConditionsUpdaterTest.java @@ -23,30 +23,31 @@ 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 java.lang.String.format; import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY; +import static org.sonar.api.measures.CoreMetrics.SQALE_RATING_KEY; +import static org.sonar.api.measures.Metric.ValueType.BOOL; import static org.sonar.api.measures.Metric.ValueType.DATA; +import static org.sonar.api.measures.Metric.ValueType.FLOAT; import static org.sonar.api.measures.Metric.ValueType.INT; +import static org.sonar.api.measures.Metric.ValueType.MILLISEC; +import static org.sonar.api.measures.Metric.ValueType.PERCENT; 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; +import static org.sonar.api.measures.Metric.ValueType.WORK_DUR; @RunWith(DataProviderRunner.class) public class QualityGateConditionsUpdaterTest { @@ -57,234 +58,351 @@ public class QualityGateConditionsUpdaterTest { @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(db.getDefaultOrganization()); - dbClient.metricDao().insert(dbSession, coverageMetricDto, ratingMetricDto); - dbSession.commit(); - } + private QualityGateConditionsUpdater underTest = new QualityGateConditionsUpdater(db.getDbClient()); @Test public void create_warning_condition_without_period() { - QualityGateConditionDto result = underTest.createCondition(dbSession, qualityGateDto, "coverage", "LT", "90", null, null); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(PERCENT.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + + QualityGateConditionDto result = underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "LT", "90", null, null); - verifyCondition(result, coverageMetricDto.getId(), "LT", "90", null, null); + verifyCondition(result, qualityGate, metric, "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(); + public void create_error_condition_on_leak_period() { + MetricDto metric = db.measures().insertMetric(m -> m.setKey("new_coverage").setValueType(INT.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); - QualityGateConditionDto result = underTest.createCondition(dbSession, qualityGateDto, "new_coverage", "LT", null, "80", 1); + QualityGateConditionDto result = underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "LT", null, "80", 1); - verifyCondition(result, metricDto.getId(), "LT", null, "80", 1); + verifyCondition(result, qualityGate, metric, "LT", null, "80", 1); } @Test public void fail_to_create_condition_when_condition_on_same_metric_already_exist() { - dbClient.gateConditionDao().insert(new QualityGateConditionDto() - .setQualityGateId(qualityGateDto.getId()) - .setMetricId(coverageMetricDto.getId()) - .setPeriod(null), - dbSession); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(PERCENT.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + db.qualityGates().addCondition(qualityGate, metric); expectedException.expect(BadRequestException.class); - expectedException.expectMessage("Condition on metric 'Coverage' already exists."); - underTest.createCondition(dbSession, qualityGateDto, coverageMetricDto.getKey(), "LT", "90", null, null); + expectedException.expectMessage(format("Condition on metric '%s' already exists.", metric.getShortName())); + + underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "LT", "90", null, null); } @Test public void fail_to_create_condition_when_condition_on_same_metric_and_on_leak_period_already_exist() { - dbClient.gateConditionDao().insert(new QualityGateConditionDto() - .setQualityGateId(qualityGateDto.getId()) - .setMetricId(coverageMetricDto.getId()) - .setPeriod(1), - dbSession); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(PERCENT.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + db.qualityGates().addCondition(qualityGate, metric, c -> c.setPeriod(1)); expectedException.expect(BadRequestException.class); - expectedException.expectMessage("Condition on metric 'Coverage' over leak period already exists."); - underTest.createCondition(dbSession, qualityGateDto, coverageMetricDto.getKey(), "LT", "90", null, 1); + expectedException.expectMessage(format("Condition on metric '%s' over leak period already exists.", metric.getShortName())); + + underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "LT", "90", null, 1); } @Test public void fail_to_create_condition_on_missing_metric() { + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + expectedException.expect(NotFoundException.class); expectedException.expectMessage("There is no metric with key=new_coverage"); - underTest.createCondition(dbSession, qualityGateDto, "new_coverage", "LT", null, "80", 2); + + underTest.createCondition(db.getSession(), qualityGate, "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(); + MetricDto metric = db.measures().insertMetric(m -> m.setKey(metricKey).setValueType(valueType.name()).setHidden(hidden)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); expectedException.expect(BadRequestException.class); - expectedException.expectMessage("Metric '" + metricKey + "' cannot be used to define a condition."); - underTest.createCondition(dbSession, qualityGateDto, metricKey, "EQ", null, "80", null); + expectedException.expectMessage(format("Metric '%s' cannot be used to define a condition.", metric.getKey())); + + underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "EQ", null, "80", null); } @Test public void fail_to_create_condition_on_not_allowed_operator() { + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(PERCENT.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + expectedException.expect(BadRequestException.class); expectedException.expectMessage("Operator UNKNOWN is not allowed for metric type PERCENT."); - underTest.createCondition(dbSession, qualityGateDto, "coverage", "UNKNOWN", null, "80", 2); + + underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "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(); + MetricDto metric = db.measures().insertMetric(m -> m.setKey("new_coverage").setValueType(INT.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); expectedException.expect(BadRequestException.class); expectedException.expectMessage("A period must be selected for differential metrics."); - underTest.createCondition(dbSession, qualityGateDto, "new_coverage", "EQ", null, "90", null); + + underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "EQ", null, "90", null); } @Test public void fail_to_create_condition_on_invalid_period() { + MetricDto metric = db.measures().insertMetric(m -> m.setKey("new_coverage").setValueType(INT.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + expectedException.expect(BadRequestException.class); expectedException.expectMessage("The only valid quality gate period is 1, the leak period."); - underTest.createCondition(dbSession, qualityGateDto, "coverage", "EQ", null, "90", 6); + + underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "EQ", null, "90", 6); } @Test public void create_condition_on_rating_metric() { - QualityGateConditionDto result = underTest.createCondition(dbSession, qualityGateDto, ratingMetricDto.getKey(), "GT", null, "3", null); + MetricDto metric = db.measures().insertMetric(m -> m.setKey(SQALE_RATING_KEY).setValueType(RATING.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); - verifyCondition(result, ratingMetricDto.getId(), "GT", null, "3", null); + QualityGateConditionDto result = underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "GT", null, "3", null); + + verifyCondition(result, qualityGate, metric, "GT", null, "3", null); } @Test public void fail_to_create_condition_on_rating_metric_on_leak_period() { + MetricDto metric = db.measures().insertMetric(m -> m.setKey(SQALE_RATING_KEY).setValueType(RATING.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + expectedException.expect(BadRequestException.class); - expectedException.expectMessage("The metric 'Reliability Rating' cannot be used on the leak period"); - underTest.createCondition(dbSession, qualityGateDto, ratingMetricDto.getKey(), "GT", null, "3", 1); + expectedException.expectMessage(format("The metric '%s' cannot be used on the leak period", metric.getShortName())); + + underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "GT", null, "3", 1); } @Test public void fail_to_create_warning_condition_on_invalid_rating_metric() { + MetricDto metric = db.measures().insertMetric(m -> m.setKey(SQALE_RATING_KEY).setValueType(RATING.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + expectedException.expect(BadRequestException.class); expectedException.expectMessage("'6' is not a valid rating"); - underTest.createCondition(dbSession, qualityGateDto, ratingMetricDto.getKey(), "GT", "6", null, null); + + underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "GT", "6", null, null); } @Test public void fail_to_create_error_condition_on_invalid_rating_metric() { + MetricDto metric = db.measures().insertMetric(m -> m.setKey(SQALE_RATING_KEY).setValueType(RATING.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + expectedException.expect(BadRequestException.class); expectedException.expectMessage("'80' is not a valid rating"); - underTest.createCondition(dbSession, qualityGateDto, ratingMetricDto.getKey(), "GT", null, "80", null); + + underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "GT", null, "80", null); } @Test public void fail_to_create_condition_on_greater_than_E() { + MetricDto metric = db.measures().insertMetric(m -> m.setKey(SQALE_RATING_KEY).setValueType(RATING.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + expectedException.expect(BadRequestException.class); expectedException.expectMessage("There's no worse rating than E (5)"); - underTest.createCondition(dbSession, qualityGateDto, ratingMetricDto.getKey(), "GT", "5", null, null); + + underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "GT", "5", null, null); + } + + @Test + @UseDataProvider("valid_values") + public void create_warning_condition(Metric.ValueType valueType, String value) { + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(valueType.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + + QualityGateConditionDto result = underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "EQ", value, null, null); + + verifyCondition(result, qualityGate, metric, "EQ", value, null, null); + } + + @Test + @UseDataProvider("valid_values") + public void create_error_condition(Metric.ValueType valueType, String value) { + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(valueType.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + + QualityGateConditionDto result = underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "EQ", null, value, null); + + verifyCondition(result, qualityGate, metric, "EQ", null, value, null); + } + + @Test + @UseDataProvider("invalid_values") + public void fail_to_create_warning_INT_condition_when_value_is_not_an_integer(Metric.ValueType valueType, String value) { + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(valueType.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + + expectedException.expect(BadRequestException.class); + expectedException.expectMessage(format("Invalid value '%s' for metric '%s'", value, metric.getShortName())); + + underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "EQ", value, null, null); + } + + @Test + @UseDataProvider("invalid_values") + public void fail_to_create_error_INT_condition_when_value_is_not_an_integer(Metric.ValueType valueType, String value) { + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(valueType.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + + expectedException.expect(BadRequestException.class); + expectedException.expectMessage(format("Invalid value '%s' for metric '%s'", value, metric.getShortName())); + + underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "EQ", null, value, null); } @Test public void update_condition() { - QualityGateConditionDto condition = insertCondition(coverageMetricDto.getId(), "LT", null, "80", null); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(PERCENT.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric, + c -> c.setOperator("LT").setWarningThreshold(null).setErrorThreshold("80").setPeriod(null)); - QualityGateConditionDto result = underTest.updateCondition(dbSession, condition, "coverage", "GT", "60", null, 1); + QualityGateConditionDto result = underTest.updateCondition(db.getSession(), condition, metric.getKey(), "GT", "60", null, 1); - verifyCondition(result, coverageMetricDto.getId(), "GT", "60", null, 1); + verifyCondition(result, qualityGate, metric, "GT", "60", null, 1); } @Test public void update_condition_over_leak_period() { - QualityGateConditionDto condition = insertCondition(coverageMetricDto.getId(), "GT", "80", null, 1); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(PERCENT.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric, + c -> c.setOperator("GT").setWarningThreshold("80").setErrorThreshold(null).setPeriod(1)); - QualityGateConditionDto result = underTest.updateCondition(dbSession, condition, "coverage", "LT", null, "80", null); + QualityGateConditionDto result = underTest.updateCondition(db.getSession(), condition, metric.getKey(), "LT", null, "80", null); - verifyCondition(result, coverageMetricDto.getId(), "LT", null, "80", null); + verifyCondition(result, qualityGate, metric, "LT", null, "80", null); } @Test public void update_condition_on_rating_metric() { - QualityGateConditionDto condition = insertCondition(ratingMetricDto.getId(), "LT", null, "3", null); + MetricDto metric = db.measures().insertMetric(m -> m.setKey(SQALE_RATING_KEY).setValueType(RATING.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric, + c -> c.setOperator("LT").setWarningThreshold("80").setErrorThreshold(null).setPeriod(1)); - QualityGateConditionDto result = underTest.updateCondition(dbSession, condition, ratingMetricDto.getKey(), "GT", "4", null, null); + QualityGateConditionDto result = underTest.updateCondition(db.getSession(), condition, metric.getKey(), "GT", "4", null, null); - verifyCondition(result, ratingMetricDto.getId(), "GT", "4", null, null); + verifyCondition(result, qualityGate, metric, "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); + MetricDto metric = db.measures().insertMetric(m -> m.setKey(SQALE_RATING_KEY).setValueType(RATING.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric, + c -> c.setOperator("LT").setWarningThreshold(null).setErrorThreshold("3").setPeriod(null)); expectedException.expect(BadRequestException.class); - expectedException.expectMessage("The metric 'Reliability Rating' cannot be used on the leak period"); - underTest.updateCondition(dbSession, condition, ratingMetricDto.getKey(), "GT", "4", null, 1); + expectedException.expectMessage(format("The metric '%s' cannot be used on the leak period", metric.getShortName())); + + underTest.updateCondition(db.getSession(), condition, metric.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(); + MetricDto metric = db.measures().insertMetric(m -> m.setKey("not_core_rating_metric").setValueType(RATING.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric, + c -> c.setOperator("LT").setWarningThreshold(null).setErrorThreshold("3").setPeriod(null)); expectedException.expect(BadRequestException.class); - expectedException.expectMessage("The metric 'Not core rating' cannot be used"); - underTest.updateCondition(dbSession, condition, metricDto.getKey(), "GT", "4", null, 1); + expectedException.expectMessage(format("The metric '%s' cannot be used", metric.getShortName())); + + underTest.updateCondition(db.getSession(), condition, metric.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(); + MetricDto metric = db.measures().insertMetric(m -> m.setKey(metricKey).setValueType(valueType.name()).setHidden(hidden)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric, + c -> c.setOperator("LT").setWarningThreshold(null).setErrorThreshold("80").setPeriod(null)); expectedException.expect(BadRequestException.class); - expectedException.expectMessage("Metric '" + metricKey + "' cannot be used to define a condition."); - underTest.updateCondition(dbSession, condition, metricDto.getKey(), "GT", "60", null, 1); + expectedException.expectMessage(format("Metric '%s' cannot be used to define a condition.", metric.getKey())); + + underTest.updateCondition(db.getSession(), condition, metric.getKey(), "GT", "60", null, 1); } @Test public void fail_to_update_condition_when_condition_on_same_metric_already_exist() { - QualityGateConditionDto conditionNotOnLeakPeriod = insertCondition(coverageMetricDto.getId(), "GT", "80", null, null); - QualityGateConditionDto conditionOnLeakPeriod = insertCondition(coverageMetricDto.getId(), "GT", "80", null, 1); + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + QualityGateConditionDto conditionNotOnLeakPeriod = db.qualityGates().addCondition(qualityGate, metric, + c -> c.setOperator("GT").setWarningThreshold("80").setErrorThreshold(null).setPeriod(null)); + QualityGateConditionDto conditionOnLeakPeriod = db.qualityGates().addCondition(qualityGate, metric, + c -> c.setOperator("GT").setWarningThreshold("80").setErrorThreshold(null).setPeriod(1)); expectedException.expect(BadRequestException.class); - expectedException.expectMessage("Condition on metric 'Coverage' over leak period already exists."); + expectedException.expectMessage(format("Condition on metric '%s' over leak period already exists.", metric.getShortName())); + // Update condition not on leak period to be on leak period => will fail as this condition already exist - underTest.updateCondition(dbSession, conditionNotOnLeakPeriod, coverageMetricDto.getKey(), "GT", "80", null, 1); + underTest.updateCondition(db.getSession(), conditionNotOnLeakPeriod, metric.getKey(), "GT", "80", null, 1); + } + + @Test + @UseDataProvider("valid_values") + public void update_warning_condition(Metric.ValueType valueType, String value) { + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(valueType.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric, + c -> c.setOperator("LT").setWarningThreshold(null).setErrorThreshold("80").setPeriod(null)); + + QualityGateConditionDto result = underTest.updateCondition(db.getSession(), condition, metric.getKey(), "EQ", value, null, null); + + verifyCondition(result, qualityGate, metric, "EQ", value, null, null); + } + + @Test + @UseDataProvider("valid_values") + public void update_error_condition(Metric.ValueType valueType, String value) { + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(valueType.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric, + c -> c.setOperator("LT").setWarningThreshold(null).setErrorThreshold("80").setPeriod(null)); + + QualityGateConditionDto result = underTest.updateCondition(db.getSession(), condition, metric.getKey(), "EQ", null, value, null); + + verifyCondition(result, qualityGate, metric, "EQ", null, value, null); + } + + @Test + @UseDataProvider("invalid_values") + public void fail_to_update_warning_INT_condition_when_value_is_not_an_integer(Metric.ValueType valueType, String value) { + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(valueType.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric, + c -> c.setOperator("LT").setWarningThreshold(null).setErrorThreshold("80").setPeriod(null)); + + expectedException.expect(BadRequestException.class); + expectedException.expectMessage(format("Invalid value '%s' for metric '%s'", value, metric.getShortName())); + + underTest.updateCondition(db.getSession(), condition, metric.getKey(), "EQ", value, null, null); + } + + @Test + @UseDataProvider("invalid_values") + public void fail_to_update_error_INT_condition_when_value_is_not_an_integer(Metric.ValueType valueType, String value) { + MetricDto metric = db.measures().insertMetric(m -> m.setValueType(valueType.name()).setHidden(false)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization()); + QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric, + c -> c.setOperator("LT").setWarningThreshold(null).setErrorThreshold("80").setPeriod(null)); + + expectedException.expect(BadRequestException.class); + expectedException.expectMessage(format("Invalid value '%s' for metric '%s'", value, metric.getShortName())); + + underTest.updateCondition(db.getSession(), condition, metric.getKey(), "EQ", null, value, null); } @DataProvider @@ -296,30 +414,42 @@ public class QualityGateConditionsUpdaterTest { }; } - private QualityGateConditionDto insertCondition(long metricId, String operator, @Nullable String warning, @Nullable String error, + @DataProvider + public static Object[][] valid_values() { + return new Object[][] { + {INT, "10"}, + {BOOL, "1"}, + {MILLISEC, "1000"}, + {WORK_DUR, "1000"}, + {FLOAT, "5.12"}, + {PERCENT, "10.30"}, + }; + } + + @DataProvider + public static Object[][] invalid_values() { + return new Object[][] { + {INT, "ABCD"}, + {BOOL, "ABCD"}, + {MILLISEC, "ABCD"}, + {WORK_DUR, "ABCD"}, + {FLOAT, "ABCD"}, + {PERCENT, "ABCD"}, + }; + } + + private void verifyCondition(QualityGateConditionDto dto, QualityGateDto qualityGate, MetricDto metric, 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); + QualityGateConditionDto reloaded = db.getDbClient().gateConditionDao().selectById(dto.getId(), db.getSession()); + assertThat(reloaded.getQualityGateId()).isEqualTo(qualityGate.getId()); + assertThat(reloaded.getMetricId()).isEqualTo(metric.getId().longValue()); 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.getQualityGateId()).isEqualTo(qualityGate.getId()); + assertThat(dto.getMetricId()).isEqualTo(metric.getId().longValue()); assertThat(dto.getOperator()).isEqualTo(operator); assertThat(dto.getWarningThreshold()).isEqualTo(warning); assertThat(dto.getErrorThreshold()).isEqualTo(error); diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/CreateConditionActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/CreateConditionActionTest.java index f4f885a070a..01f3bc60e20 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/CreateConditionActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/CreateConditionActionTest.java @@ -147,6 +147,44 @@ public class CreateConditionActionTest { assertCondition(qualityGate, metric, "LT", "90", null, null); } + @Test + public void create_warning_condition_with_empty_string_on_error() { + OrganizationDto organization = db.organizations().insert(); + logInAsQualityGateAdmin(organization); + QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); + MetricDto metric = insertMetric(); + + ws.newRequest() + .setParam(PARAM_GATE_ID, qualityGate.getId().toString()) + .setParam(PARAM_METRIC, metric.getKey()) + .setParam(PARAM_OPERATOR, "LT") + .setParam(PARAM_WARNING, "90") + .setParam(PARAM_ERROR, "") + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + + assertCondition(qualityGate, metric, "LT", "90", null, null); + } + + @Test + public void create_error_condition_with_empty_string_on_warning() { + OrganizationDto organization = db.organizations().insert(); + logInAsQualityGateAdmin(organization); + QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); + MetricDto metric = insertMetric(); + + ws.newRequest() + .setParam(PARAM_GATE_ID, qualityGate.getId().toString()) + .setParam(PARAM_METRIC, metric.getKey()) + .setParam(PARAM_OPERATOR, "LT") + .setParam(PARAM_WARNING, "") + .setParam(PARAM_ERROR, "90") + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .execute(); + + assertCondition(qualityGate, metric, "LT", null, "90", null); + } + @Test public void fail_to_update_built_in_quality_gate() { OrganizationDto organization = db.organizations().insert(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/UpdateConditionActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/UpdateConditionActionTest.java index 5b783fc0129..f01d7f6ac91 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/UpdateConditionActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/UpdateConditionActionTest.java @@ -151,6 +151,48 @@ public class UpdateConditionActionTest { assertCondition(qualityGate, metric, "LT", "90", null, null); } + @Test + public void update_warning_condition_with_empty_string_on_error() { + OrganizationDto organization = db.organizations().insert(); + userSession.addPermission(ADMINISTER_QUALITY_GATES, organization); + QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); + MetricDto metric = insertMetric(); + QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric, + c -> c.setOperator("GT").setWarningThreshold(null).setErrorThreshold("80").setPeriod(null)); + + ws.newRequest() + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .setParam(PARAM_ID, Long.toString(condition.getId())) + .setParam(PARAM_METRIC, metric.getKey()) + .setParam(PARAM_OPERATOR, "LT") + .setParam(PARAM_WARNING, "90") + .setParam(PARAM_ERROR, "") + .execute(); + + assertCondition(qualityGate, metric, "LT", "90", null, null); + } + + @Test + public void update_error_condition_with_empty_string_on_warning() { + OrganizationDto organization = db.organizations().insert(); + userSession.addPermission(ADMINISTER_QUALITY_GATES, organization); + QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); + MetricDto metric = insertMetric(); + QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric, + c -> c.setOperator("GT").setWarningThreshold(null).setErrorThreshold("80").setPeriod(null)); + + ws.newRequest() + .setParam(PARAM_ORGANIZATION, organization.getKey()) + .setParam(PARAM_ID, Long.toString(condition.getId())) + .setParam(PARAM_METRIC, metric.getKey()) + .setParam(PARAM_OPERATOR, "LT") + .setParam(PARAM_ERROR, "90") + .setParam(PARAM_WARNING, "") + .execute(); + + assertCondition(qualityGate, metric, "LT", null, "90", null); + } + @Test public void test_response() { OrganizationDto organization = db.organizations().insert(); diff --git a/tests/src/test/java/org/sonarqube/tests/qualityGate/QualityGateTest.java b/tests/src/test/java/org/sonarqube/tests/qualityGate/QualityGateTest.java index 7160ac819d8..05f7871f364 100644 --- a/tests/src/test/java/org/sonarqube/tests/qualityGate/QualityGateTest.java +++ b/tests/src/test/java/org/sonarqube/tests/qualityGate/QualityGateTest.java @@ -19,6 +19,7 @@ */ package org.sonarqube.tests.qualityGate; +import com.google.common.collect.ImmutableList; import com.google.gson.Gson; import com.sonar.orchestrator.Orchestrator; import com.sonar.orchestrator.build.BuildResult; @@ -34,49 +35,45 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; import org.apache.commons.io.Charsets; import org.apache.commons.io.FileUtils; -import org.junit.After; -import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; -import org.sonar.wsclient.qualitygate.NewCondition; -import org.sonar.wsclient.qualitygate.QualityGate; -import org.sonar.wsclient.qualitygate.QualityGateClient; +import org.sonarqube.qa.util.QGateTester; import org.sonarqube.qa.util.Tester; -import org.sonarqube.qa.util.TesterSession; -import org.sonarqube.ws.Ce; import org.sonarqube.ws.Measures.Measure; -import org.sonarqube.ws.MediaTypes; -import org.sonarqube.ws.Organizations.Organization; import org.sonarqube.ws.Projects.CreateWsResponse.Project; import org.sonarqube.ws.Qualitygates; import org.sonarqube.ws.Qualitygates.CreateResponse; +import org.sonarqube.ws.Qualitygates.ListWsResponse.QualityGate; import org.sonarqube.ws.Qualitygates.ProjectStatusResponse; import org.sonarqube.ws.Users; -import org.sonarqube.ws.client.GetRequest; -import org.sonarqube.ws.client.PostRequest; -import org.sonarqube.ws.client.WsResponse; +import org.sonarqube.ws.client.ce.TaskRequest; +import org.sonarqube.ws.client.metrics.CreateRequest; +import org.sonarqube.ws.client.metrics.DeleteRequest; import org.sonarqube.ws.client.permissions.AddUserRequest; import org.sonarqube.ws.client.qualitygates.CreateConditionRequest; +import org.sonarqube.ws.client.qualitygates.DeleteConditionRequest; +import org.sonarqube.ws.client.qualitygates.DestroyRequest; +import org.sonarqube.ws.client.qualitygates.ListRequest; import org.sonarqube.ws.client.qualitygates.ProjectStatusRequest; -import org.sonarqube.ws.client.qualitygates.QualitygatesService; import org.sonarqube.ws.client.qualitygates.SelectRequest; +import org.sonarqube.ws.client.qualitygates.SetAsDefaultRequest; import org.sonarqube.ws.client.qualitygates.UpdateConditionRequest; +import static java.lang.String.format; import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.groups.Tuple.tuple; import static org.sonarqube.ws.Qualitygates.ProjectStatusResponse.Status.ERROR; import static util.ItUtils.concat; +import static util.ItUtils.expectHttpError; import static util.ItUtils.extractCeTaskId; import static util.ItUtils.getMeasure; -import static util.ItUtils.newProjectKey; import static util.ItUtils.projectDir; public class QualityGateTest { private static final String TASK_STATUS_SUCCESS = "SUCCESS"; - private static final String QG_STATUS_NO_QG = "null"; private static final String QG_STATUS_OK = "OK"; private static final String QG_STATUS_ERROR = "ERROR"; private static final String QG_STATUS_WARN = "WARN"; @@ -85,114 +82,101 @@ public class QualityGateTest { public static Orchestrator orchestrator = QualityGateSuite.ORCHESTRATOR; @Rule - public Tester tester = new Tester(orchestrator) - // all the tests of QualityGateSuite must disable organizations - .disableOrganizations(); - - private QualityGate defaultGate; - - @Before - public void setUp() { - defaultGate = qgClient().list().defaultGate(); - } - - @After - public void tearDown() { - if (defaultGate != null) { - qgClient().setDefault(defaultGate.id()); - } - } + public Tester tester = new Tester(orchestrator).disableOrganizations(); @Test public void status_ok_if_empty_gate() throws Exception { - Qualitygates.CreateResponse empty = tester.qGates().generate(); - qgClient().setDefault(empty.getId()); - - String projectKey = newProjectKey(); - BuildResult buildResult = executeAnalysis(projectKey); + Project project = tester.projects().provision(); + Qualitygates.CreateResponse qualityGate = tester.qGates().generate(); + tester.qGates().associateProject(qualityGate, project); + BuildResult buildResult = executeAnalysis(project.getKey()); - verifyQGStatusInPostTask(buildResult, projectKey, TASK_STATUS_SUCCESS, QG_STATUS_OK); + verifyQGStatusInPostTask(buildResult, project.getKey(), TASK_STATUS_SUCCESS, QG_STATUS_OK); - assertThat(getGateStatusMeasure(projectKey).getValue()).isEqualTo("OK"); + assertThat(getGateStatusMeasure(project.getKey()).getValue()).isEqualTo("OK"); } @Test public void test_status_ok() throws IOException { - Qualitygates.CreateResponse simple = tester.qGates().generate(); - qgClient().setDefault(simple.getId()); - qgClient().createCondition(NewCondition.create(simple.getId()).metricKey("ncloc").operator("GT").warningThreshold("40")); - - String projectKey = newProjectKey(); - BuildResult buildResult = executeAnalysis(projectKey); + Project project = tester.projects().provision(); + Qualitygates.CreateResponse qualityGate = tester.qGates().generate(); + tester.qGates().associateProject(qualityGate, project); + tester.qGates().service().createCondition(new CreateConditionRequest().setGateId(Long.toString(qualityGate.getId())).setMetric("ncloc").setOp("GT").setWarning("40")); + BuildResult buildResult = executeAnalysis(project.getKey()); - verifyQGStatusInPostTask(buildResult, projectKey, TASK_STATUS_SUCCESS, QG_STATUS_OK); + verifyQGStatusInPostTask(buildResult, project.getKey(), TASK_STATUS_SUCCESS, QG_STATUS_OK); - assertThat(getGateStatusMeasure(projectKey).getValue()).isEqualTo("OK"); + assertThat(getGateStatusMeasure(project.getKey()).getValue()).isEqualTo("OK"); } @Test public void test_status_warning() throws IOException { - Qualitygates.CreateResponse simple = tester.qGates().generate(); - qgClient().setDefault(simple.getId()); - qgClient().createCondition(NewCondition.create(simple.getId()).metricKey("ncloc").operator("GT").warningThreshold("10")); - - String projectKey = newProjectKey(); - BuildResult buildResult = executeAnalysis(projectKey); + Project project = tester.projects().provision(); + Qualitygates.CreateResponse qualityGate = tester.qGates().generate(); + tester.qGates().associateProject(qualityGate, project); + tester.qGates().service().createCondition(new CreateConditionRequest().setGateId(Long.toString(qualityGate.getId())).setMetric("ncloc").setOp("GT").setWarning("10")); + BuildResult buildResult = executeAnalysis(project.getKey()); - verifyQGStatusInPostTask(buildResult, projectKey, TASK_STATUS_SUCCESS, QG_STATUS_WARN); + verifyQGStatusInPostTask(buildResult, project.getKey(), TASK_STATUS_SUCCESS, QG_STATUS_WARN); - assertThat(getGateStatusMeasure(projectKey).getValue()).isEqualTo("WARN"); + assertThat(getGateStatusMeasure(project.getKey()).getValue()).isEqualTo("WARN"); } @Test public void test_status_error() throws IOException { - Qualitygates.CreateResponse simple = tester.qGates().generate(); - qgClient().setDefault(simple.getId()); - qgClient().createCondition(NewCondition.create(simple.getId()).metricKey("ncloc").operator("GT").errorThreshold("10")); - - String projectKey = newProjectKey(); - BuildResult buildResult = executeAnalysis(projectKey); + Project project = tester.projects().provision(); + Qualitygates.CreateResponse qualityGate = tester.qGates().generate(); + tester.qGates().associateProject(qualityGate, project); + tester.qGates().service().createCondition(new CreateConditionRequest().setGateId(Long.toString(qualityGate.getId())).setMetric("ncloc").setOp("GT").setError("10")); + BuildResult buildResult = executeAnalysis(project.getKey()); - verifyQGStatusInPostTask(buildResult, projectKey, TASK_STATUS_SUCCESS, QG_STATUS_ERROR); + verifyQGStatusInPostTask(buildResult, project.getKey(), TASK_STATUS_SUCCESS, QG_STATUS_ERROR); - assertThat(getGateStatusMeasure(projectKey).getValue()).isEqualTo("ERROR"); + assertThat(getGateStatusMeasure(project.getKey()).getValue()).isEqualTo("ERROR"); } @Test public void use_server_settings_instead_of_default_gate() throws IOException { - Qualitygates.CreateResponse alert = tester.qGates().generate(); - qgClient().createCondition(NewCondition.create(alert.getId()).metricKey("ncloc").operator("GT").warningThreshold("10")); - Qualitygates.CreateResponse error = tester.qGates().generate(); - qgClient().createCondition(NewCondition.create(error.getId()).metricKey("ncloc").operator("GT").errorThreshold("10")); - - qgClient().setDefault(alert.getId()); - String projectKey = newProjectKey(); - orchestrator.getServer().provisionProject(projectKey, projectKey); - associateQualityGateToProject(error.getId(), projectKey); + QualityGate existingDefaultQualityGate = tester.qGates().service().list(new ListRequest()).getQualitygatesList() + .stream() + .filter(QualityGate::getIsDefault) + .findFirst() + .orElseThrow(() -> new IllegalStateException("No default quality gate found")); + try { + Qualitygates.CreateResponse defaultQualityGate = tester.qGates().generate(); + tester.qGates().service().createCondition(new CreateConditionRequest().setGateId(Long.toString(defaultQualityGate.getId())).setMetric("ncloc").setOp("GT").setWarning("10")); + tester.qGates().service().setAsDefault(new SetAsDefaultRequest().setId(Long.toString(defaultQualityGate.getId()))); - BuildResult buildResult = executeAnalysis(projectKey); + Project project = tester.projects().provision(); + Qualitygates.CreateResponse qualityGate = tester.qGates().generate(); + tester.qGates().service().createCondition(new CreateConditionRequest().setGateId(Long.toString(qualityGate.getId())).setMetric("ncloc").setOp("GT").setError("10")); + tester.qGates().associateProject(qualityGate, project); - verifyQGStatusInPostTask(buildResult, projectKey, TASK_STATUS_SUCCESS, QG_STATUS_ERROR); + BuildResult buildResult = executeAnalysis(project.getKey()); - assertThat(getGateStatusMeasure(projectKey).getValue()).isEqualTo("ERROR"); + verifyQGStatusInPostTask(buildResult, project.getKey(), TASK_STATUS_SUCCESS, QG_STATUS_ERROR); + assertThat(getGateStatusMeasure(project.getKey()).getValue()).isEqualTo("ERROR"); + } finally { + tester.qGates().service().setAsDefault(new SetAsDefaultRequest().setId(Long.toString(existingDefaultQualityGate.getId()))); + } } @Test public void conditions_on_multiple_metric_types() throws IOException { - Qualitygates.CreateResponse allTypes = tester.qGates().generate(); - qgClient().createCondition(NewCondition.create(allTypes.getId()).metricKey("ncloc").operator("GT").warningThreshold("10")); - qgClient().createCondition(NewCondition.create(allTypes.getId()).metricKey("duplicated_lines_density").operator("GT").warningThreshold("20")); - qgClient().setDefault(allTypes.getId()); - - String projectKey = newProjectKey(); - BuildResult buildResult = executeAnalysis(projectKey, "sonar.cpd.xoo.minimumLines", "2", "sonar.cpd.xoo.minimumTokens", "5"); + Project project = tester.projects().provision(); + Qualitygates.CreateResponse qualityGate = tester.qGates().generate(); + tester.qGates().associateProject(qualityGate, project); + tester.qGates().service().createCondition(new CreateConditionRequest().setGateId(Long.toString(qualityGate.getId())).setMetric("ncloc").setOp("GT").setWarning("10")); + tester.qGates().service() + .createCondition(new CreateConditionRequest().setGateId(Long.toString(qualityGate.getId())).setMetric("duplicated_lines_density").setOp("GT").setWarning("20")); + BuildResult buildResult = executeAnalysis(project.getKey(), "sonar.cpd.xoo.minimumLines", "2", "sonar.cpd.xoo.minimumTokens", "5"); - verifyQGStatusInPostTask(buildResult, projectKey, TASK_STATUS_SUCCESS, QG_STATUS_WARN); + verifyQGStatusInPostTask(buildResult, project.getKey(), TASK_STATUS_SUCCESS, QG_STATUS_WARN); - Measure alertStatus = getGateStatusMeasure(projectKey); + Measure alertStatus = getGateStatusMeasure(project.getKey()); assertThat(alertStatus.getValue()).isEqualTo("WARN"); - String qualityGateDetailJson = getMeasure(orchestrator, projectKey, "quality_gate_details").getValue(); + String qualityGateDetailJson = getMeasure(orchestrator, project.getKey(), "quality_gate_details").getValue(); assertThat(QualityGateDetails.parse(qualityGateDetailJson).getConditions()) .extracting(QualityGateDetails.Conditions::getMetric, QualityGateDetails.Conditions::getOp, QualityGateDetails.Conditions::getWarning) .contains(tuple("ncloc", "GT", "10"), tuple("duplicated_lines_density", "GT", "20")); @@ -200,19 +184,18 @@ public class QualityGateTest { @Test public void ad_hoc_build_break_strategy() throws IOException { - Qualitygates.CreateResponse simple = tester.qGates().generate(); - qgClient().setDefault(simple.getId()); - qgClient().createCondition(NewCondition.create(simple.getId()).metricKey("ncloc").operator("GT").errorThreshold("7")); - - String projectKey = newProjectKey(); - BuildResult buildResult = executeAnalysis(projectKey); + Project project = tester.projects().provision(); + Qualitygates.CreateResponse qualityGate = tester.qGates().generate(); + tester.qGates().associateProject(qualityGate, project); + tester.qGates().service().createCondition(new CreateConditionRequest().setGateId(Long.toString(qualityGate.getId())).setMetric("ncloc").setOp("GT").setError("7")); + BuildResult buildResult = executeAnalysis(project.getKey()); - verifyQGStatusInPostTask(buildResult, projectKey, TASK_STATUS_SUCCESS, QG_STATUS_ERROR); + verifyQGStatusInPostTask(buildResult, project.getKey(), TASK_STATUS_SUCCESS, QG_STATUS_ERROR); String taskId = getTaskIdInLocalReport(projectDir("qualitygate/xoo-sample")); - String analysisId = getAnalysisId(taskId); + String analysisId = tester.wsClient().ce().task(new TaskRequest().setId(taskId)).getTask().getAnalysisId(); - ProjectStatusResponse projectStatusWsResponse = tester.wsClient().qualitygates().projectStatus(new ProjectStatusRequest().setAnalysisId(analysisId)); + ProjectStatusResponse projectStatusWsResponse = tester.qGates().service().projectStatus(new ProjectStatusRequest().setAnalysisId(analysisId)); ProjectStatusResponse.ProjectStatus projectStatus = projectStatusWsResponse.getProjectStatus(); assertThat(projectStatus.getStatus()).isEqualTo(ERROR); assertThat(projectStatus.getConditionsCount()).isEqualTo(1); @@ -223,52 +206,59 @@ public class QualityGateTest { @Test public void does_not_fail_when_condition_is_on_removed_metric() throws Exception { - // create project Project project = tester.projects().provision(); - String projectKey = project.getKey(); - - // create custom metric String customMetricKey = randomAlphabetic(10); - createCustomIntMetric(customMetricKey); + tester.wsClient().metrics().create(new CreateRequest().setKey(customMetricKey).setName(customMetricKey).setType("INT")); try { // create quality gate - Qualitygates.CreateResponse simple = tester.qGates().generate(); - Long qualityGateId = simple.getId(); - qgClient().createCondition(NewCondition.create(qualityGateId).metricKey(customMetricKey).operator("GT").warningThreshold("40")); - + Qualitygates.CreateResponse qualityGate = tester.qGates().generate(); + Long qualityGateId = qualityGate.getId(); + tester.qGates().service().createCondition(new CreateConditionRequest().setGateId(Long.toString(qualityGate.getId())).setMetric(customMetricKey).setOp("GT").setWarning("40")); // delete custom metric - deleteCustomMetric(customMetricKey); + tester.wsClient().metrics().delete(new DeleteRequest().setKeys(ImmutableList.of(customMetricKey))); // run analysis - tester.wsClient().qualitygates().select(new SelectRequest().setProjectKey(projectKey).setGateId(String.valueOf(qualityGateId))); - BuildResult buildResult = executeAnalysis(projectKey); + tester.qGates().service().select(new SelectRequest().setProjectKey(project.getKey()).setGateId(String.valueOf(qualityGateId))); + BuildResult buildResult = executeAnalysis(project.getKey()); // verify quality gate - verifyQGStatusInPostTask(buildResult, projectKey, TASK_STATUS_SUCCESS, QG_STATUS_OK); - assertThat(getGateStatusMeasure(projectKey).getValue()).isEqualTo("OK"); + verifyQGStatusInPostTask(buildResult, project.getKey(), TASK_STATUS_SUCCESS, QG_STATUS_OK); + assertThat(getGateStatusMeasure(project.getKey()).getValue()).isEqualTo("OK"); } finally { - deleteCustomMetric(customMetricKey); + tester.wsClient().metrics().delete(new DeleteRequest().setKeys(ImmutableList.of(customMetricKey))); } } @Test public void administrate_quality_gate_with_gateadmin_permission() { // user is quality gate admin of default organization - Organization organization = tester.organizations().getDefaultOrganization(); - Users.CreateWsResponse.User user = tester.users().generateMember(organization); - tester.wsClient().permissions().addUser(new AddUserRequest().setLogin(user.getLogin()).setPermission("gateadmin").setOrganization(organization.getKey())); - TesterSession qGateAdminTester = tester.as(user.getLogin()); - QualitygatesService qGateService = qGateAdminTester.qGates().service(); + Users.CreateWsResponse.User user = tester.users().generate(); + tester.wsClient().permissions().addUser(new AddUserRequest().setLogin(user.getLogin()).setPermission("gateadmin")); + QGateTester qGateAdminTester = tester.as(user.getLogin()).qGates(); + // perform administration operations - CreateResponse qualityGate = qGateAdminTester.qGates().generate(); - Qualitygates.CreateConditionResponse condition = qGateService.createCondition(new CreateConditionRequest() + CreateResponse qualityGate = qGateAdminTester.generate(); + Qualitygates.CreateConditionResponse condition = qGateAdminTester.service().createCondition(new CreateConditionRequest() .setGateId(String.valueOf(qualityGate.getId())).setMetric("coverage").setOp("LT").setError("90")); - qGateService.updateCondition(new UpdateConditionRequest() + qGateAdminTester.service().updateCondition(new UpdateConditionRequest() .setId(String.valueOf(condition.getId())).setMetric("coverage").setOp("LT").setError("90").setWarning("80")); - qGateAdminTester.wsClient().wsConnector().call(new PostRequest("api/qualitygates/set_as_default").setParam("id", qualityGate.getId())); - qGateAdminTester.wsClient().wsConnector().call(new PostRequest("api/qualitygates/delete_condition").setParam("id", condition.getId())); - qGateAdminTester.wsClient().wsConnector().call(new PostRequest("api/qualitygates/unset_default").setParam("id", qualityGate.getId())); - qGateAdminTester.wsClient().wsConnector().call(new PostRequest("api/qualitygates/destroy").setParam("id", qualityGate.getId())); + qGateAdminTester.service().deleteCondition(new DeleteConditionRequest().setId(Long.toString(condition.getId()))); + qGateAdminTester.service().destroy(new DestroyRequest().setId(Long.toString(qualityGate.getId()))); + } + + @Test + public void fail_to_create_and_update_conditions_when_using_invalid_values() { + Qualitygates.CreateResponse qualityGate = tester.qGates().generate(); + + expectHttpError(400, + format("Invalid value 'INVALID' for metric 'ncloc'"), + () -> tester.qGates().service().createCondition(new CreateConditionRequest().setGateId(Long.toString(qualityGate.getId())).setMetric("ncloc").setOp("GT").setWarning("INVALID"))); + expectHttpError(400, + format("User '%s' is not member of organization '%s'"), + () -> tester.qGates().service().createCondition(new CreateConditionRequest().setGateId(Long.toString(qualityGate.getId())).setMetric("sqale_index").setOp("GT").setWarning("10d"))); + expectHttpError(400, + format("User '%s' is not member of organization '%s'"), + () -> tester.qGates().service().createCondition(new CreateConditionRequest().setGateId(Long.toString(qualityGate.getId())).setMetric("coverage").setOp("GT").setWarning("10%"))); } private BuildResult executeAnalysis(String projectKey, String... keyValueProperties) { @@ -287,16 +277,6 @@ public class QualityGateTest { .contains("QualityGate[" + qgStatus + "]"); } - private String getAnalysisId(String taskId) throws IOException { - WsResponse activity = tester.wsClient() - .wsConnector() - .call(new GetRequest("api/ce/task") - .setParam("id", taskId) - .setMediaType(MediaTypes.PROTOBUF)); - Ce.TaskResponse activityWsResponse = Ce.TaskResponse.parseFrom(activity.contentStream()); - return activityWsResponse.getTask().getAnalysisId(); - } - private String getTaskIdInLocalReport(File projectDirectory) throws IOException { File metadata = new File(projectDirectory, ".sonar/report-task.txt"); assertThat(metadata).exists().isFile(); @@ -312,18 +292,6 @@ public class QualityGateTest { return getMeasure(orchestrator, projectKey, "alert_status"); } - private QualityGateClient qgClient() { - return orchestrator.getServer().adminWsClient().qualityGateClient(); - } - - private void associateQualityGateToProject(long qGateId, String projectKey) { - tester.wsClient().wsConnector() - .call(new PostRequest("api/qualitygates/select") - .setParam("gateId", qGateId) - .setParam("projectKey", projectKey)) - .failIfNotSuccessful(); - } - private static List extractPosttaskPluginLogs(String taskUuid, Iterable ceLogs) { return StreamSupport.stream(ceLogs.spliterator(), false) .filter(s -> s.contains("POSTASKPLUGIN: finished()")) @@ -331,20 +299,6 @@ public class QualityGateTest { .collect(Collectors.toList()); } - private void createCustomIntMetric(String metricKey) { - tester.wsClient().wsConnector().call(new PostRequest("api/metrics/create") - .setParam("key", metricKey) - .setParam("name", metricKey) - .setParam("type", "INT")) - .failIfNotSuccessful(); - } - - private void deleteCustomMetric(String metricKey) { - tester.wsClient().wsConnector().call(new PostRequest("api/metrics/delete") - .setParam("keys", metricKey)) - .failIfNotSuccessful(); - } - static class QualityGateDetails { private String level; diff --git a/tests/src/test/java/util/ItUtils.java b/tests/src/test/java/util/ItUtils.java index a5c763180de..13f268a4697 100644 --- a/tests/src/test/java/util/ItUtils.java +++ b/tests/src/test/java/util/ItUtils.java @@ -85,7 +85,6 @@ import static com.sonar.orchestrator.container.Server.ADMIN_PASSWORD; import static java.lang.Double.parseDouble; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; -import static java.util.Locale.ENGLISH; import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; @@ -314,7 +313,7 @@ public class ItUtils { } /** - * @deprecated no more needed as already done by n by {@link Tester#after()} + * @deprecated no more needed as already done by {@link Tester#after()} */ @Deprecated public static void resetEmailSettings(Orchestrator orchestrator) { @@ -323,7 +322,7 @@ public class ItUtils { } /** - * @deprecated no more needed as already done by n by {@link Tester#after()} + * @deprecated no more needed as already done by {@link Tester#after()} */ @Deprecated public static void resetPeriod(Orchestrator orchestrator) { @@ -432,10 +431,6 @@ public class ItUtils { .build().call(httpRequest); } - public static String newOrganizationKey() { - return randomAlphabetic(32).toLowerCase(ENGLISH); - } - public static String newProjectKey() { return "key-" + randomAlphabetic(100); } @@ -499,11 +494,6 @@ public class ItUtils { return sdf.format(d); } - public static String formatDateTime(Date d) { - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); - return sdf.format(d); - } - public static String extractCeTaskId(BuildResult buildResult) { List taskIds = extractCeTaskIds(buildResult); checkState(taskIds.size() == 1, "More than one task id retrieved from logs"); -- 2.39.5