@@ -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<String> 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<String> errors) { | |||
if (!metric.getValueType().equals(RATING.name())) { | |||
return; |
@@ -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()) |
@@ -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()) |
@@ -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); |
@@ -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(); |
@@ -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(); |
@@ -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<String> extractPosttaskPluginLogs(String taskUuid, Iterable<String> 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; |
@@ -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<String> taskIds = extractCeTaskIds(buildResult); | |||
checkState(taskIds.size() == 1, "More than one task id retrieved from logs"); |