3 * Copyright (C) 2009-2019 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.qualitygate;
22 import com.tngtech.java.junit.dataprovider.DataProvider;
23 import com.tngtech.java.junit.dataprovider.DataProviderRunner;
24 import com.tngtech.java.junit.dataprovider.UseDataProvider;
25 import org.junit.Rule;
26 import org.junit.Test;
27 import org.junit.rules.ExpectedException;
28 import org.junit.runner.RunWith;
29 import org.sonar.api.measures.Metric;
30 import org.sonar.api.utils.System2;
31 import org.sonar.db.DbTester;
32 import org.sonar.db.metric.MetricDto;
33 import org.sonar.db.qualitygate.QualityGateConditionDto;
34 import org.sonar.db.qualitygate.QualityGateDto;
35 import org.sonar.server.exceptions.BadRequestException;
36 import org.sonar.server.exceptions.NotFoundException;
38 import static java.lang.String.format;
39 import static java.lang.String.valueOf;
40 import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
41 import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
42 import static org.sonar.api.measures.CoreMetrics.SQALE_RATING_KEY;
43 import static org.sonar.api.measures.Metric.ValueType.BOOL;
44 import static org.sonar.api.measures.Metric.ValueType.DATA;
45 import static org.sonar.api.measures.Metric.ValueType.DISTRIB;
46 import static org.sonar.api.measures.Metric.ValueType.FLOAT;
47 import static org.sonar.api.measures.Metric.ValueType.INT;
48 import static org.sonar.api.measures.Metric.ValueType.MILLISEC;
49 import static org.sonar.api.measures.Metric.ValueType.PERCENT;
50 import static org.sonar.api.measures.Metric.ValueType.RATING;
51 import static org.sonar.api.measures.Metric.ValueType.STRING;
52 import static org.sonar.api.measures.Metric.ValueType.WORK_DUR;
54 @RunWith(DataProviderRunner.class)
55 public class QualityGateConditionsUpdaterTest {
58 public ExpectedException expectedException = ExpectedException.none();
61 public DbTester db = DbTester.create(System2.INSTANCE);
63 private QualityGateConditionsUpdater underTest = new QualityGateConditionsUpdater(db.getDbClient());
66 public void create_error_condition() {
67 MetricDto metric = insertMetric(INT, "new_coverage");
68 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
70 QualityGateConditionDto result = underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "LT", "80");
72 verifyCondition(result, qualityGate, metric, "LT", "80");
76 @UseDataProvider("valid_operators_and_direction")
77 public void create_condition_with_valid_operators_and_direction(String operator, int direction) {
78 MetricDto metric = db.measures().insertMetric(m -> m.setKey("key").setValueType(INT.name()).setHidden(false).setDirection(direction));
79 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
81 QualityGateConditionDto result = underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), operator, "80");
83 verifyCondition(result, qualityGate, metric, operator, "80");
87 public void create_condition_throws_NPE_if_errorThreshold_is_null() {
88 MetricDto metric = insertMetric(RATING, SQALE_RATING_KEY);
89 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
91 expectedException.expect(NullPointerException.class);
92 expectedException.expectMessage("errorThreshold can not be null");
94 underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "GT", null);
98 public void fail_to_create_condition_when_condition_on_same_metric_already_exist() {
99 MetricDto metric = insertMetric(PERCENT);
100 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
101 db.qualityGates().addCondition(qualityGate, metric);
103 expectedException.expect(BadRequestException.class);
104 expectedException.expectMessage(format("Condition on metric '%s' already exists.", metric.getShortName()));
106 underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "LT", "80");
110 public void fail_to_create_condition_on_missing_metric() {
111 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
113 expectedException.expect(NotFoundException.class);
114 expectedException.expectMessage("There is no metric with key=new_coverage");
116 underTest.createCondition(db.getSession(), qualityGate, "new_coverage", "LT", "80");
120 @UseDataProvider("invalid_metrics")
121 public void fail_to_create_condition_on_invalid_metric(String metricKey, Metric.ValueType valueType, boolean hidden) {
122 MetricDto metric = db.measures().insertMetric(m -> m.setKey(metricKey).setValueType(valueType.name()).setHidden(hidden).setDirection(0));
123 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
125 expectedException.expect(BadRequestException.class);
126 expectedException.expectMessage(format("Metric '%s' cannot be used to define a condition", metric.getKey()));
128 underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "LT", "80");
132 @UseDataProvider("invalid_operators_and_direction")
133 public void fail_to_create_condition_on_not_allowed_operator_for_metric_direction(String operator, int direction) {
134 MetricDto metric = db.measures().insertMetric(m -> m.setKey("key").setValueType(INT.name()).setHidden(false).setDirection(direction));
135 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
137 expectedException.expect(BadRequestException.class);
138 expectedException.expectMessage(format("Operator %s is not allowed for this metric.", operator));
140 underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), operator, "90");
144 public void create_condition_on_rating_metric() {
145 MetricDto metric = insertMetric(RATING, SQALE_RATING_KEY);
146 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
148 QualityGateConditionDto result = underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "GT", "3");
150 verifyCondition(result, qualityGate, metric, "GT", "3");
154 public void fail_to_create_error_condition_on_invalid_rating_metric() {
155 MetricDto metric = insertMetric(RATING, SQALE_RATING_KEY);
156 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
158 expectedException.expect(BadRequestException.class);
159 expectedException.expectMessage("'80' is not a valid rating");
161 underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "GT", "80");
165 public void fail_to_create_condition_on_greater_than_E() {
166 MetricDto metric = insertMetric(RATING, SQALE_RATING_KEY);
167 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
169 expectedException.expect(BadRequestException.class);
170 expectedException.expectMessage("There's no worse rating than E (5)");
172 underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "GT", "5");
176 @UseDataProvider("valid_values")
177 public void create_error_condition(Metric.ValueType valueType, String value) {
178 MetricDto metric = db.measures().insertMetric(m -> m.setValueType(valueType.name()).setHidden(false).setDirection(0));
179 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
181 QualityGateConditionDto result = underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "LT", value);
183 verifyCondition(result, qualityGate, metric, "LT", value);
187 @UseDataProvider("invalid_values")
188 public void fail_to_create_error_INT_condition_when_value_is_not_an_integer(Metric.ValueType valueType, String value) {
189 MetricDto metric = db.measures().insertMetric(m -> m.setValueType(valueType.name()).setHidden(false).setDirection(0));
190 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
192 expectedException.expect(BadRequestException.class);
193 expectedException.expectMessage(format("Invalid value '%s' for metric '%s'", value, metric.getShortName()));
195 underTest.createCondition(db.getSession(), qualityGate, metric.getKey(), "LT", value);
199 public void update_condition() {
200 MetricDto metric = insertMetric(PERCENT);
201 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
202 QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric,
203 c -> c.setOperator("LT").setErrorThreshold("80"));
205 QualityGateConditionDto result = underTest.updateCondition(db.getSession(), condition, metric.getKey(), "LT", "80");
207 verifyCondition(result, qualityGate, metric, "LT", "80");
211 public void update_condition_throws_NPE_if_errorThreshold_is_null() {
212 MetricDto metric = insertMetric(PERCENT);
213 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
214 QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric,
215 c -> c.setOperator("LT").setErrorThreshold("80"));
217 expectedException.expect(NullPointerException.class);
218 expectedException.expectMessage("errorThreshold can not be null");
220 underTest.updateCondition(db.getSession(), condition, metric.getKey(), "GT", null);
224 public void update_condition_on_rating_metric() {
225 MetricDto metric = insertMetric(RATING, SQALE_RATING_KEY);
226 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
227 QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric,
228 c -> c.setOperator("LT").setErrorThreshold("80"));
230 QualityGateConditionDto result = underTest.updateCondition(db.getSession(), condition, metric.getKey(), "GT", "4");
232 verifyCondition(result, qualityGate, metric, "GT", "4");
236 @UseDataProvider("update_invalid_operators_and_direction")
237 public void fail_to_update_condition_on_not_allowed_operator_for_metric_direction(String validOperator, String updatedOperator, int direction) {
238 MetricDto metric = db.measures().insertMetric(m -> m.setValueType(PERCENT.name()).setHidden(false).setDirection(direction));
239 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
240 QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric,
241 c -> c.setOperator(validOperator).setErrorThreshold("80"));
243 expectedException.expect(BadRequestException.class);
244 expectedException.expectMessage(format("Operator %s is not allowed for this metric", updatedOperator));
246 underTest.updateCondition(db.getSession(), condition, metric.getKey(), updatedOperator, "70");
250 public void fail_to_update_condition_on_rating_metric_on_leak_period() {
251 MetricDto metric = insertMetric(RATING, SQALE_RATING_KEY);
252 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
253 QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric,
254 c -> c.setOperator("LT").setErrorThreshold("3"));
256 QualityGateConditionDto result = underTest.updateCondition(db.getSession(), condition, metric.getKey(), "GT", "4");
258 verifyCondition(result, qualityGate, metric, "GT", "4");
262 public void fail_to_update_condition_on_rating_metric_on_not_core_rating_metric() {
263 MetricDto metric = insertMetric(RATING, "not_core_rating_metric");
264 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
265 QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric,
266 c -> c.setOperator("LT").setErrorThreshold("3"));
268 expectedException.expect(BadRequestException.class);
269 expectedException.expectMessage(format("The metric '%s' cannot be used", metric.getShortName()));
271 underTest.updateCondition(db.getSession(), condition, metric.getKey(), "GT", "4");
275 @UseDataProvider("invalid_metrics")
276 public void fail_to_update_condition_on_invalid_metric(String metricKey, Metric.ValueType valueType, boolean hidden) {
277 MetricDto metric = db.measures().insertMetric(m -> m.setKey(metricKey).setValueType(valueType.name()).setHidden(hidden));
278 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
279 QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric,
280 c -> c.setOperator("LT").setErrorThreshold("80"));
282 expectedException.expect(BadRequestException.class);
283 expectedException.expectMessage(format("Metric '%s' cannot be used to define a condition", metric.getKey()));
285 underTest.updateCondition(db.getSession(), condition, metric.getKey(), "GT", "60");
289 @UseDataProvider("valid_values")
290 public void update_error_condition(Metric.ValueType valueType, String value) {
291 MetricDto metric = db.measures().insertMetric(m -> m.setValueType(valueType.name()).setHidden(false).setDirection(0));
292 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
293 QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric,
294 c -> c.setOperator("LT").setErrorThreshold("80"));
296 QualityGateConditionDto result = underTest.updateCondition(db.getSession(), condition, metric.getKey(), "LT", value);
298 verifyCondition(result, qualityGate, metric, "LT", value);
302 @UseDataProvider("invalid_values")
303 public void fail_to_update_error_INT_condition_when_value_is_not_an_integer(Metric.ValueType valueType, String value) {
304 MetricDto metric = db.measures().insertMetric(m -> m.setValueType(valueType.name()).setHidden(false).setDirection(0));
305 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(db.getDefaultOrganization());
306 QualityGateConditionDto condition = db.qualityGates().addCondition(qualityGate, metric,
307 c -> c.setOperator("LT").setErrorThreshold("80"));
309 expectedException.expect(BadRequestException.class);
310 expectedException.expectMessage(format("Invalid value '%s' for metric '%s'", value, metric.getShortName()));
312 underTest.updateCondition(db.getSession(), condition, metric.getKey(), "LT", value);
316 public static Object[][] invalid_metrics() {
317 return new Object[][] {
318 {ALERT_STATUS_KEY, INT, false},
319 {"boolean", BOOL, false},
320 {"string", STRING, false},
321 {"data_metric", DATA, false},
322 {"distrib", DISTRIB, false},
323 {"hidden", INT, true}
328 public static Object[][] valid_values() {
329 return new Object[][] {
339 public static Object[][] invalid_values() {
340 return new Object[][] {
350 public static Object[][] invalid_operators_and_direction() {
351 return new Object[][] {
360 public static Object[][] update_invalid_operators_and_direction() {
361 return new Object[][] {
370 public static Object[][] valid_operators_and_direction() {
371 return new Object[][] {
379 private MetricDto insertMetric(Metric.ValueType type) {
380 return insertMetric(type, "key");
383 private MetricDto insertMetric(Metric.ValueType type, String key) {
384 return db.measures().insertMetric(m -> m
386 .setValueType(type.name())
392 private void verifyCondition(QualityGateConditionDto dto, QualityGateDto qualityGate, MetricDto metric, String operator, String error) {
393 QualityGateConditionDto reloaded = db.getDbClient().gateConditionDao().selectById(dto.getId(), db.getSession());
394 assertThat(reloaded.getQualityGateId()).isEqualTo(qualityGate.getId());
395 assertThat(reloaded.getMetricId()).isEqualTo(metric.getId().longValue());
396 assertThat(reloaded.getOperator()).isEqualTo(operator);
397 assertThat(reloaded.getErrorThreshold()).isEqualTo(error);
399 assertThat(dto.getQualityGateId()).isEqualTo(qualityGate.getId());
400 assertThat(dto.getMetricId()).isEqualTo(metric.getId().longValue());
401 assertThat(dto.getOperator()).isEqualTo(operator);
402 assertThat(dto.getErrorThreshold()).isEqualTo(error);