3 * Copyright (C) 2009-2017 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 javax.annotation.Nullable;
26 import org.junit.Before;
27 import org.junit.Rule;
28 import org.junit.Test;
29 import org.junit.rules.ExpectedException;
30 import org.junit.runner.RunWith;
31 import org.sonar.api.measures.Metric;
32 import org.sonar.api.utils.System2;
33 import org.sonar.db.DbClient;
34 import org.sonar.db.DbSession;
35 import org.sonar.db.DbTester;
36 import org.sonar.db.metric.MetricDto;
37 import org.sonar.db.qualitygate.QualityGateConditionDto;
38 import org.sonar.db.qualitygate.QualityGateDbTester;
39 import org.sonar.db.qualitygate.QualityGateDto;
40 import org.sonar.server.exceptions.BadRequestException;
41 import org.sonar.server.exceptions.NotFoundException;
43 import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
44 import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
45 import static org.sonar.api.measures.Metric.ValueType.DATA;
46 import static org.sonar.api.measures.Metric.ValueType.INT;
47 import static org.sonar.api.measures.Metric.ValueType.RATING;
48 import static org.sonar.db.metric.MetricTesting.newMetricDto;
49 import static org.sonar.server.computation.task.projectanalysis.metric.Metric.MetricType.PERCENT;
51 @RunWith(DataProviderRunner.class)
52 public class QualityGateConditionsUpdaterTest {
55 public ExpectedException expectedException = ExpectedException.none();
58 public DbTester db = DbTester.create(System2.INSTANCE);
60 DbClient dbClient = db.getDbClient();
61 DbSession dbSession = db.getSession();
62 QualityGateDbTester qualityGateDbTester = new QualityGateDbTester(db);
64 QualityGateDto qualityGateDto;
65 MetricDto coverageMetricDto = newMetricDto()
67 .setShortName("Coverage")
68 .setValueType(PERCENT.name())
71 MetricDto ratingMetricDto = newMetricDto()
72 .setKey("reliability_rating")
73 .setShortName("Reliability Rating")
74 .setValueType(RATING.name())
77 QualityGateConditionsUpdater underTest = new QualityGateConditionsUpdater(dbClient);
80 public void setUp() throws Exception {
81 qualityGateDto = qualityGateDbTester.insertQualityGate();
82 dbClient.metricDao().insert(dbSession, coverageMetricDto, ratingMetricDto);
87 public void create_warning_condition_without_period() {
88 QualityGateConditionDto result = underTest.createCondition(dbSession, qualityGateDto.getId(), "coverage", "LT", "90", null, null);
90 verifyCondition(result, coverageMetricDto.getId(), "LT", "90", null, null);
94 public void create_error_condition_with_period() {
95 MetricDto metricDto = dbClient.metricDao().insert(dbSession, newMetricDto()
96 .setKey("new_coverage")
97 .setValueType(INT.name())
101 QualityGateConditionDto result = underTest.createCondition(dbSession, qualityGateDto.getId(), "new_coverage", "LT", null, "80", 1);
103 verifyCondition(result, metricDto.getId(), "LT", null, "80", 1);
107 public void fail_to_create_condition_when_condition_on_same_metric_already_exist() throws Exception {
108 dbClient.gateConditionDao().insert(new QualityGateConditionDto()
109 .setQualityGateId(qualityGateDto.getId())
110 .setMetricId(coverageMetricDto.getId())
114 expectedException.expect(BadRequestException.class);
115 expectedException.expectMessage("Condition on metric 'Coverage' already exists.");
116 underTest.createCondition(dbSession, qualityGateDto.getId(), coverageMetricDto.getKey(), "LT", "90", null, null);
120 public void fail_to_create_condition_when_condition_on_same_metric_and_on_leak_period_already_exist() throws Exception {
121 dbClient.gateConditionDao().insert(new QualityGateConditionDto()
122 .setQualityGateId(qualityGateDto.getId())
123 .setMetricId(coverageMetricDto.getId())
127 expectedException.expect(BadRequestException.class);
128 expectedException.expectMessage("Condition on metric 'Coverage' over leak period already exists.");
129 underTest.createCondition(dbSession, qualityGateDto.getId(), coverageMetricDto.getKey(), "LT", "90", null, 1);
133 public void fail_to_create_condition_on_missing_metric() {
134 expectedException.expect(NotFoundException.class);
135 expectedException.expectMessage("There is no metric with key=new_coverage");
136 underTest.createCondition(dbSession, qualityGateDto.getId(), "new_coverage", "LT", null, "80", 2);
140 @UseDataProvider("invalid_metrics")
141 public void fail_to_create_condition_on_invalid_metric(String metricKey, Metric.ValueType valueType, boolean hidden) {
142 dbClient.metricDao().insert(dbSession, newMetricDto()
144 .setValueType(valueType.name())
148 expectedException.expect(BadRequestException.class);
149 expectedException.expectMessage("Metric '" + metricKey + "' cannot be used to define a condition.");
150 underTest.createCondition(dbSession, qualityGateDto.getId(), metricKey, "EQ", null, "80", null);
154 public void fail_to_create_condition_on_not_allowed_operator() {
155 expectedException.expect(BadRequestException.class);
156 expectedException.expectMessage("Operator UNKNOWN is not allowed for metric type PERCENT.");
157 underTest.createCondition(dbSession, qualityGateDto.getId(), "coverage", "UNKNOWN", null, "80", 2);
161 public void fail_to_create_condition_on_missing_period() {
162 dbClient.metricDao().insert(dbSession, newMetricDto()
163 .setKey("new_coverage")
164 .setValueType(INT.name())
168 expectedException.expect(BadRequestException.class);
169 expectedException.expectMessage("A period must be selected for differential metrics.");
170 underTest.createCondition(dbSession, qualityGateDto.getId(), "new_coverage", "EQ", null, "90", null);
174 public void fail_to_create_condition_on_invalid_period() {
175 expectedException.expect(BadRequestException.class);
176 expectedException.expectMessage("The only valid quality gate period is 1, the leak period.");
177 underTest.createCondition(dbSession, qualityGateDto.getId(), "coverage", "EQ", null, "90", 6);
181 public void create_condition_on_rating_metric() {
182 QualityGateConditionDto result = underTest.createCondition(dbSession, qualityGateDto.getId(), ratingMetricDto.getKey(), "GT", null, "3", null);
184 verifyCondition(result, ratingMetricDto.getId(), "GT", null, "3", null);
188 public void fail_to_create_condition_on_rating_metric_on_leak_period() {
189 expectedException.expect(BadRequestException.class);
190 expectedException.expectMessage("The metric 'Reliability Rating' cannot be used on the leak period");
191 underTest.createCondition(dbSession, qualityGateDto.getId(), ratingMetricDto.getKey(), "GT", null, "3", 1);
195 public void fail_to_create_warning_condition_on_invalid_rating_metric() {
196 expectedException.expect(BadRequestException.class);
197 expectedException.expectMessage("'6' is not a valid rating");
198 underTest.createCondition(dbSession, qualityGateDto.getId(), ratingMetricDto.getKey(), "GT", "6", null, null);
202 public void fail_to_create_error_condition_on_invalid_rating_metric() {
203 expectedException.expect(BadRequestException.class);
204 expectedException.expectMessage("'80' is not a valid rating");
205 underTest.createCondition(dbSession, qualityGateDto.getId(), ratingMetricDto.getKey(), "GT", null, "80", null);
209 public void fail_to_create_condition_on_greater_than_E() {
210 expectedException.expect(BadRequestException.class);
211 expectedException.expectMessage("There's no worse rating than E (5)");
212 underTest.createCondition(dbSession, qualityGateDto.getId(), ratingMetricDto.getKey(), "GT", "5", null, null);
216 public void update_condition() {
217 QualityGateConditionDto condition = insertCondition(coverageMetricDto.getId(), "LT", null, "80", null);
219 QualityGateConditionDto result = underTest.updateCondition(dbSession, condition, "coverage", "GT", "60", null, 1);
221 verifyCondition(result, coverageMetricDto.getId(), "GT", "60", null, 1);
225 public void update_condition_over_leak_period() {
226 QualityGateConditionDto condition = insertCondition(coverageMetricDto.getId(), "GT", "80", null, 1);
228 QualityGateConditionDto result = underTest.updateCondition(dbSession, condition, "coverage", "LT", null, "80", null);
230 verifyCondition(result, coverageMetricDto.getId(), "LT", null, "80", null);
234 public void update_condition_on_rating_metric() {
235 QualityGateConditionDto condition = insertCondition(ratingMetricDto.getId(), "LT", null, "3", null);
237 QualityGateConditionDto result = underTest.updateCondition(dbSession, condition, ratingMetricDto.getKey(), "GT", "4", null, null);
239 verifyCondition(result, ratingMetricDto.getId(), "GT", "4", null, null);
243 public void fail_to_update_condition_on_rating_metric_on_leak_period() {
244 QualityGateConditionDto condition = insertCondition(ratingMetricDto.getId(), "LT", null, "3", null);
246 expectedException.expect(BadRequestException.class);
247 expectedException.expectMessage("The metric 'Reliability Rating' cannot be used on the leak period");
248 underTest.updateCondition(dbSession, condition, ratingMetricDto.getKey(), "GT", "4", null, 1);
252 public void fail_to_update_condition_on_rating_metric_on_not_core_rating_metric() {
253 MetricDto metricDto = dbClient.metricDao().insert(dbSession, newMetricDto().setKey("rating_metric")
254 .setShortName("Not core rating")
255 .setValueType(RATING.name()).setHidden(false));
256 QualityGateConditionDto condition = insertCondition(metricDto.getId(), "LT", null, "3", null);
259 expectedException.expect(BadRequestException.class);
260 expectedException.expectMessage("The metric 'Not core rating' cannot be used");
261 underTest.updateCondition(dbSession, condition, metricDto.getKey(), "GT", "4", null, 1);
265 @UseDataProvider("invalid_metrics")
266 public void fail_to_update_condition_on_invalid_metric(String metricKey, Metric.ValueType valueType, boolean hidden) {
267 MetricDto metricDto = dbClient.metricDao().insert(dbSession, newMetricDto()
269 .setValueType(valueType.name())
271 QualityGateConditionDto condition = insertCondition(metricDto.getId(), "LT", null, "80", null);
274 expectedException.expect(BadRequestException.class);
275 expectedException.expectMessage("Metric '" + metricKey + "' cannot be used to define a condition.");
276 underTest.updateCondition(dbSession, condition, metricDto.getKey(), "GT", "60", null, 1);
280 public void fail_to_update_condition_when_condition_on_same_metric_already_exist() throws Exception {
281 QualityGateConditionDto conditionNotOnLeakPeriod = insertCondition(coverageMetricDto.getId(), "GT", "80", null, null);
282 QualityGateConditionDto conditionOnLeakPeriod = insertCondition(coverageMetricDto.getId(), "GT", "80", null, 1);
284 expectedException.expect(BadRequestException.class);
285 expectedException.expectMessage("Condition on metric 'Coverage' over leak period already exists.");
286 // Update condition not on leak period to be on leak period => will fail as this condition already exist
287 underTest.updateCondition(dbSession, conditionNotOnLeakPeriod, coverageMetricDto.getKey(), "GT", "80", null, 1);
291 public static Object[][] invalid_metrics() {
292 return new Object[][] {
293 {ALERT_STATUS_KEY, INT, false},
294 {"data_metric", DATA, false},
295 {"hidden", INT, true}
299 private QualityGateConditionDto insertCondition(long metricId, String operator, @Nullable String warning, @Nullable String error,
300 @Nullable Integer period) {
301 QualityGateConditionDto qualityGateConditionDto = new QualityGateConditionDto().setQualityGateId(qualityGateDto.getId())
302 .setMetricId(metricId)
303 .setOperator(operator)
304 .setWarningThreshold(warning)
305 .setErrorThreshold(error)
307 dbClient.gateConditionDao().insert(qualityGateConditionDto, dbSession);
309 return qualityGateConditionDto;
312 private void verifyCondition(QualityGateConditionDto dto, int metricId, String operator, @Nullable String warning, @Nullable String error, @Nullable Integer period) {
313 QualityGateConditionDto reloaded = dbClient.gateConditionDao().selectById(dto.getId(), dbSession);
314 assertThat(reloaded.getQualityGateId()).isEqualTo(qualityGateDto.getId());
315 assertThat(reloaded.getMetricId()).isEqualTo(metricId);
316 assertThat(reloaded.getOperator()).isEqualTo(operator);
317 assertThat(reloaded.getWarningThreshold()).isEqualTo(warning);
318 assertThat(reloaded.getErrorThreshold()).isEqualTo(error);
319 assertThat(reloaded.getPeriod()).isEqualTo(period);
321 assertThat(dto.getQualityGateId()).isEqualTo(qualityGateDto.getId());
322 assertThat(dto.getMetricId()).isEqualTo(metricId);
323 assertThat(dto.getOperator()).isEqualTo(operator);
324 assertThat(dto.getWarningThreshold()).isEqualTo(warning);
325 assertThat(dto.getErrorThreshold()).isEqualTo(error);
326 assertThat(dto.getPeriod()).isEqualTo(period);