Browse Source

SONAR-8117 Extract creation of quality gate condition into QualityGateConditionsUpdater

tags/6.2-RC1
Julien Lancelot 7 years ago
parent
commit
e34e860366
25 changed files with 1122 additions and 330 deletions
  1. 148
    0
      server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateConditionsUpdater.java
  2. 1
    0
      server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateModule.java
  3. 3
    2
      server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateUpdater.java
  4. 24
    22
      server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGates.java
  5. 31
    17
      server/sonar-server/src/main/java/org/sonar/server/qualitygate/RegisterQualityGates.java
  6. 1
    7
      server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/AppAction.java
  7. 16
    6
      server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/CreateAction.java
  8. 76
    18
      server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/CreateConditionAction.java
  9. 20
    14
      server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWs.java
  10. 198
    0
      server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateConditionsUpdaterTest.java
  11. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateModuleTest.java
  12. 6
    4
      server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateUpdaterTest.java
  13. 25
    187
      server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGatesTest.java
  14. 36
    29
      server/sonar-server/src/test/java/org/sonar/server/qualitygate/RegisterQualityGatesTest.java
  15. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/CreateActionTest.java
  16. 200
    0
      server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/CreateConditionActionTest.java
  17. 2
    22
      server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QualityGatesWsTest.java
  18. 7
    0
      sonar-db/src/main/java/org/sonar/db/metric/MetricDto.java
  19. 49
    0
      sonar-db/src/test/java/org/sonar/db/qualitygate/QualityGateDbTester.java
  20. 126
    0
      sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/CreateConditionRequest.java
  21. 18
    0
      sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/QualityGatesService.java
  22. 1
    0
      sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/QualityGatesWsParameters.java
  23. 10
    0
      sonar-ws/src/main/protobuf/ws-qualitygates.proto
  24. 91
    0
      sonar-ws/src/test/java/org/sonarqube/ws/client/qualitygate/CreateConditionRequestTest.java
  25. 31
    0
      sonar-ws/src/test/java/org/sonarqube/ws/client/qualitygate/QualityGatesServiceTest.java

+ 148
- 0
server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateConditionsUpdater.java View File

@@ -0,0 +1,148 @@
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package org.sonar.server.qualitygate;

import java.util.Collection;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.ObjectUtils;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Metric;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.qualitygate.QualityGateConditionDto;
import org.sonar.db.qualitygate.QualityGateDto;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.Errors;
import org.sonar.server.exceptions.NotFoundException;

import static java.lang.String.format;
import static org.sonar.db.qualitygate.QualityGateConditionDto.isOperatorAllowed;

public class QualityGateConditionsUpdater {

private final DbClient dbClient;

public QualityGateConditionsUpdater(DbClient dbClient) {
this.dbClient = dbClient;
}

public QualityGateConditionDto createCondition(DbSession dbSession, long qGateId, String metricKey, String operator,
@Nullable String warningThreshold, @Nullable String errorThreshold, @Nullable Integer period) {
getNonNullQgate(dbSession, qGateId);
MetricDto metric = getNonNullMetric(dbSession, metricKey);
validateCondition(metric, operator, warningThreshold, errorThreshold, period);
checkConditionDoesNotAlreadyExistOnSameMetricAndPeriod(getConditions(dbSession, qGateId, null), metric, period);

QualityGateConditionDto newCondition = new QualityGateConditionDto().setQualityGateId(qGateId)
.setMetricId(metric.getId()).setMetricKey(metric.getKey())
.setOperator(operator)
.setWarningThreshold(warningThreshold)
.setErrorThreshold(errorThreshold)
.setPeriod(period);
dbClient.gateConditionDao().insert(newCondition, dbSession);
return newCondition;
}

private QualityGateDto getNonNullQgate(DbSession dbSession, long id) {
QualityGateDto qGate = dbClient.qualityGateDao().selectById(dbSession, id);
if (qGate == null) {
throw new NotFoundException(format("There is no quality gate with id=%s", id));
}
return qGate;
}

private MetricDto getNonNullMetric(DbSession dbSession, String metricKey) {
MetricDto metric = dbClient.metricDao().selectByKey(dbSession, metricKey);
if (metric == null) {
throw new NotFoundException(format("There is no metric with key=%s", metricKey));
}
return metric;
}

private Collection<QualityGateConditionDto> getConditions(DbSession dbSession, long qGateId, @Nullable Long conditionId) {
Collection<QualityGateConditionDto> conditions = dbClient.gateConditionDao().selectForQualityGate(qGateId, dbSession);
if (conditionId == null) {
return conditions;
}
return dbClient.gateConditionDao().selectForQualityGate(qGateId, dbSession).stream()
.filter(condition -> condition.getId() != conditionId)
.collect(Collectors.toList());
}

private static void validateCondition(MetricDto metric, String operator, @Nullable String warningThreshold, @Nullable String errorThreshold, @Nullable Integer period) {
Errors errors = new Errors();
validateMetric(metric, errors);
checkOperator(metric, operator, errors);
checkThresholds(warningThreshold, errorThreshold, errors);
checkPeriod(metric, period, errors);
if (!errors.isEmpty()) {
throw new BadRequestException(errors);
}
}

private static void validateMetric(MetricDto metric, Errors errors) {
errors.check(isAlertable(metric), format("Metric '%s' cannot be used to define a condition.", metric.getKey()));
}

private static boolean isAlertable(MetricDto metric) {
return isAvailableForInit(metric) && BooleanUtils.isFalse(metric.isHidden());
}

private static boolean isAvailableForInit(MetricDto metric) {
return !metric.isDataType() && !CoreMetrics.ALERT_STATUS_KEY.equals(metric.getKey()) && !Objects.equals(Metric.ValueType.RATING.name(), metric.getValueType());
}

private static void checkOperator(MetricDto metric, String operator, Errors errors) {
Metric.ValueType valueType = Metric.ValueType.valueOf(metric.getValueType());
errors.check(isOperatorAllowed(operator, valueType), format("Operator %s is not allowed for metric type %s.", operator, metric.getValueType()));
}

private static void checkThresholds(@Nullable String warningThreshold, @Nullable String errorThreshold, Errors errors) {
errors.check(warningThreshold != null || errorThreshold != null, "At least one threshold (warning, error) must be set.");
}

private static void checkPeriod(MetricDto metric, @Nullable Integer period, Errors errors) {
if (period == null) {
errors.check(!metric.getKey().startsWith("new_"), "A period must be selected for differential metrics.");
} else {
errors.check(period == 1, "The only valid quality gate period is 1, the leak period.");
}
}

private static void checkConditionDoesNotAlreadyExistOnSameMetricAndPeriod(Collection<QualityGateConditionDto> conditions, MetricDto metric, @Nullable final Integer period) {
if (conditions.isEmpty()) {
return;
}

boolean conditionExists = conditions.stream().anyMatch(c -> c.getMetricId() == metric.getId() && ObjectUtils.equals(c.getPeriod(), period));
if (conditionExists) {
String errorMessage = period == null
? format("Condition on metric '%s' already exists.", metric.getShortName())
: format("Condition on metric '%s' over leak period already exists.", metric.getShortName());
throw new BadRequestException(errorMessage);
}
}

}

+ 1
- 0
server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateModule.java View File

@@ -46,6 +46,7 @@ public class QualityGateModule extends Module {
add(
QualityGates.class,
QualityGateUpdater.class,
QualityGateConditionsUpdater.class,
QgateProjectFinder.class,
// WS
QualityGatesWs.class,

+ 3
- 2
server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateUpdater.java View File

@@ -22,6 +22,7 @@ package org.sonar.server.qualitygate;

import javax.annotation.Nullable;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.qualitygate.QualityGateDto;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.Errors;
@@ -38,10 +39,10 @@ public class QualityGateUpdater {
this.dbClient = dbClient;
}

public QualityGateDto create(String name) {
public QualityGateDto create(DbSession dbSession, String name) {
validateQualityGate(null, name);
QualityGateDto newQualityGate = new QualityGateDto().setName(name);
dbClient.qualityGateDao().insert(newQualityGate);
dbClient.qualityGateDao().insert(dbSession, newQualityGate);
return newQualityGate;
}


+ 24
- 22
server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGates.java View File

@@ -140,13 +140,23 @@ public class QualityGates {
}
}

public void setDefault(@Nullable Long idToUseAsDefault) {
public void setDefault(DbSession dbSession, @Nullable Long idToUseAsDefault) {
checkPermission();
if (idToUseAsDefault == null) {
propertiesDao.deleteGlobalProperty(SONAR_QUALITYGATE_PROPERTY);
propertiesDao.deleteGlobalProperty(SONAR_QUALITYGATE_PROPERTY, dbSession);
} else {
QualityGateDto newDefault = getNonNullQgate(idToUseAsDefault);
propertiesDao.saveProperty(new PropertyDto().setKey(SONAR_QUALITYGATE_PROPERTY).setValue(newDefault.getId().toString()));
QualityGateDto newDefault = getNonNullQgate(dbSession, idToUseAsDefault);
propertiesDao.saveProperty(dbSession, new PropertyDto().setKey(SONAR_QUALITYGATE_PROPERTY).setValue(newDefault.getId().toString()));
}
}

public void setDefault(@Nullable Long idToUseAsDefault) {
DbSession dbSession = dbClient.openSession(false);
try {
setDefault(dbSession, idToUseAsDefault);
dbSession.commit();
} finally {
dbClient.closeSession(dbSession);
}
}

@@ -160,23 +170,6 @@ public class QualityGates {
}
}

public QualityGateConditionDto createCondition(long qGateId, String metricKey, String operator,
@Nullable String warningThreshold, @Nullable String errorThreshold, @Nullable Integer period) {
checkPermission();
getNonNullQgate(qGateId);
Metric metric = getNonNullMetric(metricKey);
validateCondition(metric, operator, warningThreshold, errorThreshold, period);
checkConditionDoesNotAlreadyExistOnSameMetricAndPeriod(getConditions(qGateId, null), metric, period);
QualityGateConditionDto newCondition = new QualityGateConditionDto().setQualityGateId(qGateId)
.setMetricId(metric.getId()).setMetricKey(metric.getKey())
.setOperator(operator)
.setWarningThreshold(warningThreshold)
.setErrorThreshold(errorThreshold)
.setPeriod(period);
conditionDao.insert(newCondition);
return newCondition;
}

public QualityGateConditionDto updateCondition(long condId, String metricKey, String operator,
@Nullable String warningThreshold, @Nullable String errorThreshold, @Nullable Integer period) {
checkPermission();
@@ -312,7 +305,16 @@ public class QualityGates {
}

private QualityGateDto getNonNullQgate(long id) {
QualityGateDto qGate = dao.selectById(id);
DbSession dbSession = dbClient.openSession(false);
try {
return getNonNullQgate(dbSession, id);
} finally {
dbClient.closeSession(dbSession);
}
}

private QualityGateDto getNonNullQgate(DbSession dbSession, long id) {
QualityGateDto qGate = dao.selectById(dbSession, id);
if (qGate == null) {
throw new NotFoundException("There is no quality gate with id=" + id);
}

+ 31
- 17
server/sonar-server/src/main/java/org/sonar/server/qualitygate/RegisterQualityGates.java View File

@@ -20,6 +20,8 @@
package org.sonar.server.qualitygate;

import org.picocontainer.Startable;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.loadedtemplate.LoadedTemplateDao;
import org.sonar.db.loadedtemplate.LoadedTemplateDto;
import org.sonar.db.qualitygate.QualityGateDto;
@@ -40,21 +42,32 @@ public class RegisterQualityGates implements Startable {
private static final String DEBT_ON_NEW_CODE_ERROR_THRESHOLD = "5";
private static final String NEW_COVERAGE_ERROR_THRESHOLD = "80";

private final QualityGates qualityGates;
private final DbClient dbClient;
private final QualityGateUpdater qualityGateUpdater;
private final QualityGateConditionsUpdater qualityGateConditionsUpdater;
private final LoadedTemplateDao loadedTemplateDao;
private final QualityGates qualityGates;

public RegisterQualityGates(QualityGates qualityGates, QualityGateUpdater qualityGateUpdater, LoadedTemplateDao loadedTemplateDao) {
this.qualityGates = qualityGates;
public RegisterQualityGates(DbClient dbClient, QualityGateUpdater qualityGateUpdater, QualityGateConditionsUpdater qualityGateConditionsUpdater,
LoadedTemplateDao loadedTemplateDao, QualityGates qualityGates) {
this.dbClient = dbClient;
this.qualityGateUpdater = qualityGateUpdater;
this.qualityGateConditionsUpdater = qualityGateConditionsUpdater;
this.loadedTemplateDao = loadedTemplateDao;
this.qualityGates = qualityGates;
}

@Override
public void start() {
if (shouldRegisterBuiltinQualityGate()) {
createBuiltinQualityGate();
registerBuiltinQualityGate();
DbSession dbSession = dbClient.openSession(false);
try {
if (shouldRegisterBuiltinQualityGate(dbSession)) {
createBuiltinQualityGate(dbSession);
registerBuiltinQualityGate(dbSession);
dbSession.commit();
}
} finally {
dbClient.closeSession(dbSession);
}
}

@@ -63,20 +76,21 @@ public class RegisterQualityGates implements Startable {
// do nothing
}

private boolean shouldRegisterBuiltinQualityGate() {
return loadedTemplateDao.countByTypeAndKey(LoadedTemplateDto.QUALITY_GATE_TYPE, BUILTIN_QUALITY_GATE) == 0;
private boolean shouldRegisterBuiltinQualityGate(DbSession dbSession) {
return loadedTemplateDao.countByTypeAndKey(LoadedTemplateDto.QUALITY_GATE_TYPE, BUILTIN_QUALITY_GATE, dbSession) == 0;
}

private void createBuiltinQualityGate() {
QualityGateDto builtin = qualityGateUpdater.create(BUILTIN_QUALITY_GATE);
qualityGates.createCondition(builtin.getId(), NEW_VULNERABILITIES_KEY, OPERATOR_GREATER_THAN, null, NEW_VULNERABILITIES_ERROR_THRESHOLD, LEAK_PERIOD);
qualityGates.createCondition(builtin.getId(), NEW_BUGS_KEY, OPERATOR_GREATER_THAN, null, NEW_BUGS_ERROR_THRESHOLD, LEAK_PERIOD);
qualityGates.createCondition(builtin.getId(), NEW_SQALE_DEBT_RATIO_KEY, OPERATOR_GREATER_THAN, null, DEBT_ON_NEW_CODE_ERROR_THRESHOLD, LEAK_PERIOD);
qualityGates.createCondition(builtin.getId(), NEW_COVERAGE_KEY, OPERATOR_LESS_THAN, null, NEW_COVERAGE_ERROR_THRESHOLD, LEAK_PERIOD);
qualityGates.setDefault(builtin.getId());
private void createBuiltinQualityGate(DbSession dbSession) {
QualityGateDto builtin = qualityGateUpdater.create(dbSession, BUILTIN_QUALITY_GATE);
qualityGateConditionsUpdater.createCondition(dbSession, builtin.getId(), NEW_VULNERABILITIES_KEY, OPERATOR_GREATER_THAN, null, NEW_VULNERABILITIES_ERROR_THRESHOLD,
LEAK_PERIOD);
qualityGateConditionsUpdater.createCondition(dbSession, builtin.getId(), NEW_BUGS_KEY, OPERATOR_GREATER_THAN, null, NEW_BUGS_ERROR_THRESHOLD, LEAK_PERIOD);
qualityGateConditionsUpdater.createCondition(dbSession, builtin.getId(), NEW_SQALE_DEBT_RATIO_KEY, OPERATOR_GREATER_THAN, null, DEBT_ON_NEW_CODE_ERROR_THRESHOLD, LEAK_PERIOD);
qualityGateConditionsUpdater.createCondition(dbSession, builtin.getId(), NEW_COVERAGE_KEY, OPERATOR_LESS_THAN, null, NEW_COVERAGE_ERROR_THRESHOLD, LEAK_PERIOD);
qualityGates.setDefault(dbSession, builtin.getId());
}

private void registerBuiltinQualityGate() {
loadedTemplateDao.insert(new LoadedTemplateDto(BUILTIN_QUALITY_GATE, LoadedTemplateDto.QUALITY_GATE_TYPE));
private void registerBuiltinQualityGate(DbSession dbSession) {
loadedTemplateDao.insert(new LoadedTemplateDto(BUILTIN_QUALITY_GATE, LoadedTemplateDto.QUALITY_GATE_TYPE), dbSession);
}
}

+ 1
- 7
server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/AppAction.java View File

@@ -31,8 +31,6 @@ import org.sonar.server.user.UserSession;
import org.sonarqube.ws.WsQualityGates.AppWsResponse.Metric;

import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
import static org.sonar.api.measures.Metric.ValueType.DATA;
import static org.sonar.api.measures.Metric.ValueType.DISTRIB;
import static org.sonar.core.permission.GlobalPermissions.QUALITY_GATE_ADMIN;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.WsQualityGates.AppWsResponse;
@@ -86,15 +84,11 @@ public class AppAction implements QualityGatesWsAction {
DbSession dbSession = dbClient.openSession(false);
try {
return dbClient.metricDao().selectEnabled(dbSession).stream()
.filter(metric -> !isDataType(metric) && !ALERT_STATUS_KEY.equals(metric.getKey()))
.filter(metric -> !metric.isDataType() && !ALERT_STATUS_KEY.equals(metric.getKey()))
.collect(Collectors.toList());
} finally {
dbClient.closeSession(dbSession);
}
}

private static boolean isDataType(MetricDto metric) {
return DATA.name().equals(metric.getValueType()) || DISTRIB.name().equals(metric.getValueType());
}

}

+ 16
- 6
server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/CreateAction.java View File

@@ -23,6 +23,8 @@ import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.qualitygate.QualityGateDto;
import org.sonar.server.qualitygate.QualityGateUpdater;
import org.sonar.server.user.UserSession;
@@ -34,10 +36,12 @@ import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM

public class CreateAction implements QualityGatesWsAction {

private final DbClient dbClient;
private final UserSession userSession;
private final QualityGateUpdater qualityGateUpdater;

public CreateAction(UserSession userSession, QualityGateUpdater qualityGateUpdater) {
public CreateAction(DbClient dbClient, UserSession userSession, QualityGateUpdater qualityGateUpdater) {
this.dbClient = dbClient;
this.userSession = userSession;
this.qualityGateUpdater = qualityGateUpdater;
}
@@ -59,11 +63,17 @@ public class CreateAction implements QualityGatesWsAction {
@Override
public void handle(Request request, Response response) {
userSession.checkPermission(GlobalPermissions.QUALITY_GATE_ADMIN);
QualityGateDto newQualityGate = qualityGateUpdater.create(request.mandatoryParam(PARAM_NAME));
CreateWsResponse.Builder createWsResponse = CreateWsResponse.newBuilder()
.setId(newQualityGate.getId())
.setName(newQualityGate.getName());
writeProtobuf(createWsResponse.build(), request, response);
DbSession dbSession = dbClient.openSession(false);
try {
QualityGateDto newQualityGate = qualityGateUpdater.create(dbSession, request.mandatoryParam(PARAM_NAME));
CreateWsResponse.Builder createWsResponse = CreateWsResponse.newBuilder()
.setId(newQualityGate.getId())
.setName(newQualityGate.getName());
writeProtobuf(createWsResponse.build(), request, response);
dbSession.commit();
} finally {
dbClient.closeSession(dbSession);
}
}

}

+ 76
- 18
server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/CreateConditionAction.java View File

@@ -22,46 +22,104 @@ package org.sonar.server.qualitygate.ws;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.server.qualitygate.QualityGates;
import org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.qualitygate.QualityGateConditionDto;
import org.sonar.server.qualitygate.QualityGateConditionsUpdater;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.WsQualityGates.CreateConditionWsResponse;
import org.sonarqube.ws.client.qualitygate.CreateConditionRequest;

import static org.sonar.core.permission.GlobalPermissions.QUALITY_GATE_ADMIN;
import static org.sonar.server.qualitygate.ws.QualityGatesWs.addConditionParams;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.ACTION_CREATE_CONDITION;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ERROR;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_GATE_ID;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_METRIC;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_OPERATOR;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PERIOD;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_WARNING;

public class CreateConditionAction implements QualityGatesWsAction {

private final QualityGates qualityGates;
private final UserSession userSession;
private final DbClient dbClient;
private final QualityGateConditionsUpdater qualityGateConditionsUpdater;

public CreateConditionAction(QualityGates qualityGates) {
this.qualityGates = qualityGates;
public CreateConditionAction(UserSession userSession, DbClient dbClient, QualityGateConditionsUpdater qualityGateConditionsUpdater) {
this.userSession = userSession;
this.dbClient = dbClient;
this.qualityGateConditionsUpdater = qualityGateConditionsUpdater;
}

@Override
public void define(WebService.NewController controller) {
WebService.NewAction createCondition = controller.createAction("create_condition")
WebService.NewAction createCondition = controller.createAction(ACTION_CREATE_CONDITION)
.setDescription("Add a new condition to a quality gate. Require Administer Quality Gates permission")
.setPost(true)
.setSince("4.3")
.setHandler(this);

createCondition
.createParam(QualityGatesWsParameters.PARAM_GATE_ID)
.createParam(PARAM_GATE_ID)
.setDescription("ID of the quality gate")
.setRequired(true)
.setExampleValue("1");

QualityGatesWs.addConditionParams(createCondition);
addConditionParams(createCondition);
}

@Override
public void handle(Request request, Response response) {
QualityGatesWs.writeQualityGateCondition(
qualityGates.createCondition(
QualityGatesWs.parseId(request, QualityGatesWsParameters.PARAM_GATE_ID),
request.mandatoryParam(QualityGatesWsParameters.PARAM_METRIC),
request.mandatoryParam(QualityGatesWsParameters.PARAM_OPERATOR),
request.param(QualityGatesWsParameters.PARAM_WARNING),
request.param(QualityGatesWsParameters.PARAM_ERROR),
request.paramAsInt(QualityGatesWsParameters.PARAM_PERIOD)
), response.newJsonWriter()
).close();
userSession.checkPermission(QUALITY_GATE_ADMIN);

DbSession dbSession = dbClient.openSession(false);
try {
writeProtobuf(doHandle(toWsRequest(request), dbSession), request, response);
dbSession.commit();
} finally {
dbClient.closeSession(dbSession);
}
}

private CreateConditionWsResponse doHandle(CreateConditionRequest request, DbSession dbSession){
QualityGateConditionDto condition = qualityGateConditionsUpdater.createCondition(dbSession,
request.getQualityGateId(),
request.getMetricKey(),
request.getOperator(),
request.getWarning(),
request.getError(),
request.getPeriod());

CreateConditionWsResponse.Builder response = CreateConditionWsResponse.newBuilder()
.setId(condition.getId())
.setMetric(condition.getMetricKey())
.setOp(condition.getOperator());
String warning = condition.getWarningThreshold();
if (warning != null) {
response.setWarning(warning);
}
String error = condition.getErrorThreshold();
if (error != null) {
response.setError(error);
}
Integer period = condition.getPeriod();
if (period != null) {
response.setPeriod(period);
}
return response.build();
}

private static CreateConditionRequest toWsRequest(Request request) {
return CreateConditionRequest.builder()
.setQualityGateId(request.mandatoryParamAsInt(PARAM_GATE_ID))
.setMetricKey(request.mandatoryParam(PARAM_METRIC))
.setOperator(request.mandatoryParam(PARAM_OPERATOR))
.setWarning(request.param(PARAM_WARNING))
.setError(request.param(PARAM_ERROR))
.setPeriod(request.paramAsInt(PARAM_PERIOD))
.build();
}

}

+ 20
- 14
server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWs.java View File

@@ -25,9 +25,15 @@ import org.sonar.api.utils.text.JsonWriter;
import org.sonar.db.qualitygate.QualityGateConditionDto;
import org.sonar.db.qualitygate.QualityGateDto;
import org.sonar.server.exceptions.BadRequestException;
import org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters;

import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.CONTROLLER_QUALITY_GATES;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ERROR;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ID;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_METRIC;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_NAME;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_OPERATOR;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PERIOD;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_WARNING;

public class QualityGatesWs implements WebService {
private final QualityGatesWsAction[] actions;
@@ -51,12 +57,12 @@ public class QualityGatesWs implements WebService {

static void addConditionParams(NewAction action) {
action
.createParam(QualityGatesWsParameters.PARAM_METRIC)
.createParam(PARAM_METRIC)
.setDescription("Condition metric")
.setRequired(true)
.setExampleValue("blocker_violations");

action.createParam(QualityGatesWsParameters.PARAM_OPERATOR)
action.createParam(PARAM_OPERATOR)
.setDescription("Condition operator:<br/>" +
"<ul>" +
"<li>EQ = equals</li>" +
@@ -67,15 +73,15 @@ public class QualityGatesWs implements WebService {
.setExampleValue(QualityGateConditionDto.OPERATOR_EQUALS)
.setPossibleValues(QualityGateConditionDto.ALL_OPERATORS);

action.createParam(QualityGatesWsParameters.PARAM_PERIOD)
action.createParam(PARAM_PERIOD)
.setDescription("Condition period. If not set, the absolute value is considered.")
.setPossibleValues("1");

action.createParam(QualityGatesWsParameters.PARAM_WARNING)
action.createParam(PARAM_WARNING)
.setDescription("Condition warning threshold")
.setExampleValue("5");

action.createParam(QualityGatesWsParameters.PARAM_ERROR)
action.createParam(PARAM_ERROR)
.setDescription("Condition error threshold")
.setExampleValue("10");
}
@@ -90,24 +96,24 @@ public class QualityGatesWs implements WebService {

static JsonWriter writeQualityGate(QualityGateDto qualityGate, JsonWriter writer) {
return writer.beginObject()
.prop(QualityGatesWsParameters.PARAM_ID, qualityGate.getId())
.prop(QualityGatesWsParameters.PARAM_NAME, qualityGate.getName())
.prop(PARAM_ID, qualityGate.getId())
.prop(PARAM_NAME, qualityGate.getName())
.endObject();
}

static JsonWriter writeQualityGateCondition(QualityGateConditionDto condition, JsonWriter writer) {
writer.beginObject()
.prop(QualityGatesWsParameters.PARAM_ID, condition.getId())
.prop(QualityGatesWsParameters.PARAM_METRIC, condition.getMetricKey())
.prop(QualityGatesWsParameters.PARAM_OPERATOR, condition.getOperator());
.prop(PARAM_ID, condition.getId())
.prop(PARAM_METRIC, condition.getMetricKey())
.prop(PARAM_OPERATOR, condition.getOperator());
if (condition.getWarningThreshold() != null) {
writer.prop(QualityGatesWsParameters.PARAM_WARNING, condition.getWarningThreshold());
writer.prop(PARAM_WARNING, condition.getWarningThreshold());
}
if (condition.getErrorThreshold() != null) {
writer.prop(QualityGatesWsParameters.PARAM_ERROR, condition.getErrorThreshold());
writer.prop(PARAM_ERROR, condition.getErrorThreshold());
}
if (condition.getPeriod() != null) {
writer.prop(QualityGatesWsParameters.PARAM_PERIOD, condition.getPeriod());
writer.prop(PARAM_PERIOD, condition.getPeriod());
}
writer.endObject();
return writer;

+ 198
- 0
server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateConditionsUpdaterTest.java View File

@@ -0,0 +1,198 @@
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package org.sonar.server.qualitygate;

import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.sonar.api.measures.Metric;
import org.sonar.api.utils.System2;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.qualitygate.QualityGateConditionDto;
import org.sonar.db.qualitygate.QualityGateDbTester;
import org.sonar.db.qualitygate.QualityGateDto;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.NotFoundException;

import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
import static org.sonar.api.measures.Metric.ValueType.DATA;
import static org.sonar.api.measures.Metric.ValueType.INT;
import static org.sonar.api.measures.Metric.ValueType.RATING;
import static org.sonar.db.metric.MetricTesting.newMetricDto;
import static org.sonar.server.computation.task.projectanalysis.metric.Metric.MetricType.PERCENT;

@RunWith(DataProviderRunner.class)
public class QualityGateConditionsUpdaterTest {

@Rule
public ExpectedException expectedException = ExpectedException.none();

@Rule
public DbTester db = DbTester.create(System2.INSTANCE);

DbClient dbClient = db.getDbClient();
DbSession dbSession = db.getSession();
QualityGateDbTester qualityGateDbTester = new QualityGateDbTester(db);

QualityGateDto qualityGateDto;
MetricDto coverageMetricDto = newMetricDto()
.setKey("coverage")
.setShortName("Coverage")
.setValueType(PERCENT.name())
.setHidden(false);

QualityGateConditionsUpdater underTest = new QualityGateConditionsUpdater(dbClient);

@Before
public void setUp() throws Exception {
qualityGateDto = qualityGateDbTester.insertQualityGate();

dbClient.metricDao().insert(dbSession, coverageMetricDto);
dbSession.commit();
}

@Test
public void create_warning_condition_without_period() {
QualityGateConditionDto result = underTest.createCondition(dbSession, qualityGateDto.getId(), "coverage", "LT", "90", null, null);

assertThat(result.getQualityGateId()).isEqualTo(qualityGateDto.getId());
assertThat(result.getMetricId()).isEqualTo(coverageMetricDto.getId().longValue());
assertThat(result.getOperator()).isEqualTo("LT");
assertThat(result.getWarningThreshold()).isEqualTo("90");
assertThat(result.getErrorThreshold()).isNull();
assertThat(result.getPeriod()).isNull();
assertThat(dbClient.gateConditionDao().selectById(result.getId(), dbSession)).isNotNull();
}

@Test
public void create_error_condition_with_period() {
MetricDto metricDto = dbClient.metricDao().insert(dbSession, newMetricDto()
.setKey("new_coverage")
.setValueType(INT.name())
.setHidden(false));
dbSession.commit();

QualityGateConditionDto result = underTest.createCondition(dbSession, qualityGateDto.getId(), "new_coverage", "LT", null, "80", 1);

assertThat(result.getQualityGateId()).isEqualTo(qualityGateDto.getId());
assertThat(result.getMetricId()).isEqualTo(metricDto.getId().longValue());
assertThat(result.getMetricKey()).isEqualTo("new_coverage");
assertThat(result.getOperator()).isEqualTo("LT");
assertThat(result.getWarningThreshold()).isNull();
assertThat(result.getErrorThreshold()).isEqualTo("80");
assertThat(result.getPeriod()).isEqualTo(1);
assertThat(dbClient.gateConditionDao().selectById(result.getId(), dbSession)).isNotNull();
}

@Test
public void fail_to_create_condition_when_condition_on_same_metric_already_exist() throws Exception {
dbClient.gateConditionDao().insert(new QualityGateConditionDto()
.setQualityGateId(qualityGateDto.getId())
.setMetricId(coverageMetricDto.getId())
.setPeriod(null),
dbSession);

expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Condition on metric 'Coverage' already exists.");
underTest.createCondition(dbSession, qualityGateDto.getId(), coverageMetricDto.getKey(), "LT", "90", null, null);
}

@Test
public void fail_to_create_condition_when_condition_on_same_metric_and_on_leak_period_already_exist() throws Exception {
dbClient.gateConditionDao().insert(new QualityGateConditionDto()
.setQualityGateId(qualityGateDto.getId())
.setMetricId(coverageMetricDto.getId())
.setPeriod(1),
dbSession);

expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Condition on metric 'Coverage' over leak period already exists.");
underTest.createCondition(dbSession, qualityGateDto.getId(), coverageMetricDto.getKey(), "LT", "90", null, 1);
}

@Test
public void fail_to_create_condition_on_missing_metric() {
expectedException.expect(NotFoundException.class);
expectedException.expectMessage("There is no metric with key=new_coverage");
underTest.createCondition(dbSession, qualityGateDto.getId(), "new_coverage", "LT", null, "80", 2);
}

@Test
@UseDataProvider("invalid_metrics")
public void fail_to_create_condition_on_invalid_metric(String metricKey, Metric.ValueType valueType, boolean hidden) {
dbClient.metricDao().insert(dbSession, newMetricDto()
.setKey(metricKey)
.setValueType(valueType.name())
.setHidden(hidden));
dbSession.commit();

expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Metric '" + metricKey + "' cannot be used to define a condition.");
underTest.createCondition(dbSession, qualityGateDto.getId(), metricKey, "EQ", null, "80", null);
}

@Test
public void fail_to_create_condition_on_not_allowed_operator() {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Operator UNKNOWN is not allowed for metric type PERCENT.");
underTest.createCondition(dbSession, qualityGateDto.getId(), "coverage", "UNKNOWN", null, "80", 2);
}

@Test
public void fail_to_create_condition_on_missing_period() {
dbClient.metricDao().insert(dbSession, newMetricDto()
.setKey("new_coverage")
.setValueType(INT.name())
.setHidden(false));
dbSession.commit();

expectedException.expect(BadRequestException.class);
expectedException.expectMessage("A period must be selected for differential metrics.");
underTest.createCondition(dbSession, qualityGateDto.getId(), "new_coverage", "EQ", null, "90", null);
}

@Test
public void fail_to_create_condition_on_invalid_period() {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("The only valid quality gate period is 1, the leak period.");
underTest.createCondition(dbSession, qualityGateDto.getId(), "coverage", "EQ", null, "90", 6);
}

@DataProvider
public static Object[][] invalid_metrics() {
return new Object[][] {
{ALERT_STATUS_KEY, INT, false},
{"data_metric", DATA, false},
{"hidden", INT, true},
{"rating_metric", RATING, false},
};
}

}

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateModuleTest.java View File

@@ -30,6 +30,6 @@ public class QualityGateModuleTest {
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new QualityGateModule().configure(container);
assertThat(container.size()).isEqualTo(21 + 2);
assertThat(container.size()).isEqualTo(22 + 2);
}
}

+ 6
- 4
server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateUpdaterTest.java View File

@@ -25,6 +25,7 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
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.qualitygate.QualityGateDto;
import org.sonar.server.exceptions.BadRequestException;
@@ -43,17 +44,18 @@ public class QualityGateUpdaterTest {
public DbTester db = DbTester.create(System2.INSTANCE);

DbClient dbClient = db.getDbClient();
DbSession dbSession= db.getSession();

QualityGateUpdater underTest = new QualityGateUpdater(dbClient);

@Test
public void create_quality_gate() throws Exception {
QualityGateDto result = underTest.create(QGATE_NAME);
QualityGateDto result = underTest.create(dbSession, QGATE_NAME);

assertThat(result).isNotNull();
assertThat(result.getName()).isEqualTo(QGATE_NAME);
assertThat(result.getCreatedAt()).isNotNull();
QualityGateDto reloaded = dbClient.qualityGateDao().selectByName(QGATE_NAME);
QualityGateDto reloaded = dbClient.qualityGateDao().selectByName(dbSession, QGATE_NAME);
assertThat(reloaded).isNotNull();
}

@@ -61,7 +63,7 @@ public class QualityGateUpdaterTest {
public void fail_to_create_when_name_is_empty() throws Exception {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage(format("errors.cant_be_empty", "Name"));
underTest.create("");
underTest.create(dbSession, "");
}

@Test
@@ -70,6 +72,6 @@ public class QualityGateUpdaterTest {

expectedException.expect(BadRequestException.class);
expectedException.expectMessage("errors.is_already_used");
underTest.create(QGATE_NAME);
underTest.create(dbSession, QGATE_NAME);
}
}

+ 25
- 187
server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGatesTest.java View File

@@ -62,7 +62,6 @@ import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -126,9 +125,9 @@ public class QualityGatesTest {
long id = QUALITY_GATE_ID;
final String name = "Golden";
QualityGateDto existing = new QualityGateDto().setId(id).setName(name);
when(dao.selectById(id)).thenReturn(existing);
when(dao.selectById(dbSession, id)).thenReturn(existing);
assertThat(underTest.get(id)).isEqualTo(existing);
verify(dao).selectById(id);
verify(dao).selectById(dbSession, id);
}

@Test
@@ -151,10 +150,10 @@ public class QualityGatesTest {
long id = QUALITY_GATE_ID;
String name = "SG-1";
QualityGateDto existing = new QualityGateDto().setId(id).setName("Golden");
when(dao.selectById(id)).thenReturn(existing);
when(dao.selectById(dbSession, id)).thenReturn(existing);
QualityGateDto sg1 = underTest.rename(id, name);
assertThat(sg1.getName()).isEqualTo(name);
verify(dao).selectById(id);
verify(dao).selectById(dbSession, id);
verify(dao).selectByName(name);
verify(dao).update(sg1);
}
@@ -164,10 +163,10 @@ public class QualityGatesTest {
long id = QUALITY_GATE_ID;
String name = "SG-1";
QualityGateDto existing = new QualityGateDto().setId(id).setName(name);
when(dao.selectById(id)).thenReturn(existing);
when(dao.selectById(dbSession, id)).thenReturn(existing);
QualityGateDto sg1 = underTest.rename(id, name);
assertThat(sg1.getName()).isEqualTo(name);
verify(dao).selectById(id);
verify(dao).selectById(dbSession, id);
verify(dao).selectByName(name);
verify(dao).update(sg1);
}
@@ -182,7 +181,7 @@ public class QualityGatesTest {
long id = QUALITY_GATE_ID;
String name = "SG-1";
QualityGateDto existing = new QualityGateDto().setId(id).setName("Golden");
when(dao.selectById(id)).thenReturn(existing);
when(dao.selectById(dbSession, id)).thenReturn(existing);
when(dao.selectByName(name)).thenReturn(new QualityGateDto().setId(666L).setName(name));
underTest.rename(id, name);
}
@@ -191,13 +190,13 @@ public class QualityGatesTest {
public void should_select_default_qgate() {
long defaultId = QUALITY_GATE_ID;
String defaultName = "Default Name";
when(dao.selectById(defaultId)).thenReturn(new QualityGateDto().setId(defaultId).setName(defaultName));
when(dao.selectById(dbSession, defaultId)).thenReturn(new QualityGateDto().setId(defaultId).setName(defaultName));

underTest.setDefault(defaultId);

verify(dao).selectById(defaultId);
verify(dao).selectById(dbSession, defaultId);
ArgumentCaptor<PropertyDto> propertyCaptor = ArgumentCaptor.forClass(PropertyDto.class);
verify(propertiesDao).saveProperty(propertyCaptor.capture());
verify(propertiesDao).saveProperty(any(DbSession.class), propertyCaptor.capture());

assertThat(propertyCaptor.getValue().getKey()).isEqualTo("sonar.qualitygate");
assertThat(propertyCaptor.getValue().getValue()).isEqualTo("42");
@@ -208,10 +207,10 @@ public class QualityGatesTest {
long idToDelete = QUALITY_GATE_ID;
String name = "To Delete";
QualityGateDto toDelete = new QualityGateDto().setId(idToDelete).setName(name);
when(dao.selectById(idToDelete)).thenReturn(toDelete);
when(dao.selectById(dbSession, idToDelete)).thenReturn(toDelete);
when(dbClient.openSession(false)).thenReturn(dbSession);
underTest.delete(idToDelete);
verify(dao).selectById(idToDelete);
verify(dao).selectById(dbSession, idToDelete);
verify(propertiesDao).deleteProjectProperties("sonar.qualitygate", "42", dbSession);
verify(dao).delete(toDelete, dbSession);
}
@@ -221,11 +220,11 @@ public class QualityGatesTest {
long idToDelete = QUALITY_GATE_ID;
String name = "To Delete";
QualityGateDto toDelete = new QualityGateDto().setId(idToDelete).setName(name);
when(dao.selectById(idToDelete)).thenReturn(toDelete);
when(dao.selectById(dbSession, idToDelete)).thenReturn(toDelete);
when(propertiesDao.selectGlobalProperty("sonar.qualitygate")).thenReturn(new PropertyDto().setValue("666"));
when(dbClient.openSession(false)).thenReturn(dbSession);
underTest.delete(idToDelete);
verify(dao).selectById(idToDelete);
verify(dao).selectById(dbSession, idToDelete);
verify(propertiesDao).deleteProjectProperties("sonar.qualitygate", "42", dbSession);
verify(dao).delete(toDelete, dbSession);
}
@@ -235,11 +234,11 @@ public class QualityGatesTest {
long idToDelete = QUALITY_GATE_ID;
String name = "To Delete";
QualityGateDto toDelete = new QualityGateDto().setId(idToDelete).setName(name);
when(dao.selectById(idToDelete)).thenReturn(toDelete);
when(dao.selectById(dbSession, idToDelete)).thenReturn(toDelete);
when(propertiesDao.selectGlobalProperty("sonar.qualitygate")).thenReturn(new PropertyDto().setValue("42"));
when(dbClient.openSession(false)).thenReturn(dbSession);
underTest.delete(idToDelete);
verify(dao).selectById(idToDelete);
verify(dao).selectById(dbSession, idToDelete);
verify(propertiesDao).deleteGlobalProperty("sonar.qualitygate", dbSession);
verify(propertiesDao).deleteProjectProperties("sonar.qualitygate", "42", dbSession);
verify(dao).delete(toDelete, dbSession);
@@ -261,167 +260,6 @@ public class QualityGatesTest {
assertThat(underTest.getDefault()).isNull();
}

@Test
public void should_create_warning_condition_without_period() {
long qGateId = QUALITY_GATE_ID;
String metricKey = "coverage";
String operator = "LT";
String warningThreshold = "90";
when(dao.selectById(qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
int metricId = 10;
Metric coverage = Mockito.spy(CoreMetrics.COVERAGE);
when(coverage.getId()).thenReturn(metricId);
when(metricFinder.findByKey(metricKey)).thenReturn(coverage);

QualityGateConditionDto newCondition = underTest.createCondition(qGateId, metricKey, operator, warningThreshold, null, null);
assertThat(newCondition.getQualityGateId()).isEqualTo(qGateId);
assertThat(newCondition.getMetricId()).isEqualTo((long) metricId);
assertThat(newCondition.getOperator()).isEqualTo(operator);
assertThat(newCondition.getWarningThreshold()).isEqualTo(warningThreshold);
assertThat(newCondition.getErrorThreshold()).isNull();
assertThat(newCondition.getPeriod()).isNull();
verify(conditionDao).insert(newCondition);
}

@Test
public void should_create_error_condition_with_period() {
long qGateId = QUALITY_GATE_ID;
String metricKey = "new_coverage";
String operator = "LT";
String errorThreshold = "80";
when(dao.selectById(qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
int metricId = 10;
Metric newCoverage = Mockito.spy(CoreMetrics.NEW_COVERAGE);
when(newCoverage.getId()).thenReturn(metricId);
when(metricFinder.findByKey(metricKey)).thenReturn(newCoverage);
int period = 1;

QualityGateConditionDto newCondition = underTest.createCondition(qGateId, metricKey, operator, null, errorThreshold, period);
assertThat(newCondition.getQualityGateId()).isEqualTo(qGateId);
assertThat(newCondition.getMetricId()).isEqualTo((long) metricId);
assertThat(newCondition.getMetricKey()).isEqualTo(metricKey);
assertThat(newCondition.getOperator()).isEqualTo(operator);
assertThat(newCondition.getWarningThreshold()).isNull();
assertThat(newCondition.getErrorThreshold()).isEqualTo(errorThreshold);
assertThat(newCondition.getPeriod()).isEqualTo(period);
verify(conditionDao).insert(newCondition);
}

@Test(expected = NotFoundException.class)
public void should_fail_create_condition_on_missing_metric() {
long qGateId = QUALITY_GATE_ID;
when(dao.selectById(qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
underTest.createCondition(qGateId, "new_coverage", "LT", null, "80", 2);
}

@Test(expected = BadRequestException.class)
public void should_fail_create_condition_on_alert_metric() {
long qGateId = QUALITY_GATE_ID;
when(metricFinder.findByKey(anyString())).thenReturn(CoreMetrics.ALERT_STATUS);
when(dao.selectById(qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
underTest.createCondition(qGateId, "alert_status", "EQ", null, "80", 2);
}

@Test(expected = BadRequestException.class)
public void should_fail_create_condition_on_non_data_metric() {
long qGateId = QUALITY_GATE_ID;
final Metric metric = mock(Metric.class);
when(metric.getType()).thenReturn(ValueType.DATA);
when(metricFinder.findByKey(anyString())).thenReturn(metric);
when(dao.selectById(qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
underTest.createCondition(qGateId, "alert_status", "LT", null, "80", 2);
}

@Test(expected = BadRequestException.class)
public void should_fail_create_condition_on_hidden_metric() {
long qGateId = QUALITY_GATE_ID;
final Metric metric = mock(Metric.class);
when(metric.isHidden()).thenReturn(true);
when(metric.getType()).thenReturn(ValueType.INT);
when(metricFinder.findByKey(anyString())).thenReturn(metric);
when(dao.selectById(qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
underTest.createCondition(qGateId, "alert_status", "LT", null, "80", 2);
}

@Test(expected = BadRequestException.class)
public void should_fail_create_condition_on_rating_metric() {
long qGateId = QUALITY_GATE_ID;
final Metric metric = mock(Metric.class);
when(metric.getType()).thenReturn(ValueType.RATING);
when(metricFinder.findByKey(anyString())).thenReturn(metric);
when(dao.selectById(qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
underTest.createCondition(qGateId, "alert_status", "LT", null, "80", 2);
}

@Test(expected = BadRequestException.class)
public void should_fail_create_condition_on_unallowed_operator() {
long qGateId = QUALITY_GATE_ID;
final Metric metric = mock(Metric.class);
when(metric.getType()).thenReturn(ValueType.BOOL);
when(metricFinder.findByKey(anyString())).thenReturn(metric);
when(dao.selectById(qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
underTest.createCondition(qGateId, "alert_status", "LT", null, "80", 2);
}

@Test(expected = BadRequestException.class)
public void should_fail_create_condition_on_missing_thresholds() {
long qGateId = QUALITY_GATE_ID;
final Metric metric = mock(Metric.class);
when(metric.getType()).thenReturn(ValueType.BOOL);
when(metricFinder.findByKey(anyString())).thenReturn(metric);
when(dao.selectById(qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
underTest.createCondition(qGateId, "alert_status", "EQ", null, null, 2);
}

@Test(expected = BadRequestException.class)
public void should_fail_create_condition_on_missing_period() {
long qGateId = QUALITY_GATE_ID;
final Metric metric = mock(Metric.class);
when(metric.getKey()).thenReturn("new_coverage");
when(metric.getType()).thenReturn(ValueType.BOOL);
when(metricFinder.findByKey(anyString())).thenReturn(metric);
when(dao.selectById(qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
underTest.createCondition(qGateId, "alert_status", "EQ", null, "90", null);
}

@Test(expected = BadRequestException.class)
public void should_fail_create_condition_on_invalid_period() {
long qGateId = QUALITY_GATE_ID;
final Metric metric = mock(Metric.class);
when(metric.getKey()).thenReturn("new_coverage");
when(metric.getType()).thenReturn(ValueType.BOOL);
when(metricFinder.findByKey(anyString())).thenReturn(metric);
when(dao.selectById(qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
underTest.createCondition(qGateId, "alert_status", "EQ", null, "90", 6);
}

@Test
public void fail_to_create_condition_when_condition_on_same_metric_already_exist() throws Exception {
String metricKey = "coverage";
addMetric(metricKey, "Coverage");
when(dao.selectById(QUALITY_GATE_ID)).thenReturn(new QualityGateDto().setId(QUALITY_GATE_ID));
when(conditionDao.selectForQualityGate(QUALITY_GATE_ID)).thenReturn(
singletonList(new QualityGateConditionDto().setMetricKey(metricKey).setMetricId(METRIC_ID)));

expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Condition on metric 'Coverage' already exists.");
underTest.createCondition(QUALITY_GATE_ID, metricKey, "LT", "90", null, null);
}

@Test
public void fail_to_create_condition_when_condition_on_same_metric_and_period_already_exist() throws Exception {
String metricKey = "new_coverage";
addMetric(metricKey, "New Coverage");
when(dao.selectById(QUALITY_GATE_ID)).thenReturn(new QualityGateDto().setId(QUALITY_GATE_ID));

when(conditionDao.selectForQualityGate(QUALITY_GATE_ID)).thenReturn(
singletonList(new QualityGateConditionDto().setMetricKey(metricKey).setMetricId(METRIC_ID).setPeriod(1)));

expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Condition on metric 'New Coverage' over leak period already exists.");
underTest.createCondition(QUALITY_GATE_ID, metricKey, "LT", "90", null, 1);
}

@Test
public void should_update_condition() {
String metricKey = "new_coverage";
@@ -511,9 +349,9 @@ public class QualityGatesTest {
public void should_associate_project() {
Long qGateId = QUALITY_GATE_ID;
Long projectId = 24L;
when(dao.selectById(qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
when(dao.selectById(dbSession, qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
underTest.associateProject(qGateId, projectId);
verify(dao).selectById(qGateId);
verify(dao).selectById(dbSession, qGateId);
ArgumentCaptor<PropertyDto> propertyCaptor = ArgumentCaptor.forClass(PropertyDto.class);
verify(propertiesDao).saveProperty(propertyCaptor.capture());
PropertyDto property = propertyCaptor.getValue();
@@ -528,9 +366,9 @@ public class QualityGatesTest {

Long qGateId = QUALITY_GATE_ID;
Long projectId = 24L;
when(dao.selectById(qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
when(dao.selectById(dbSession, qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
underTest.associateProject(qGateId, projectId);
verify(dao).selectById(qGateId);
verify(dao).selectById(dbSession, qGateId);
ArgumentCaptor<PropertyDto> propertyCaptor = ArgumentCaptor.forClass(PropertyDto.class);
verify(propertiesDao).saveProperty(propertyCaptor.capture());
PropertyDto property = propertyCaptor.getValue();
@@ -543,9 +381,9 @@ public class QualityGatesTest {
public void should_dissociate_project() {
Long qGateId = QUALITY_GATE_ID;
Long projectId = 24L;
when(dao.selectById(qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
when(dao.selectById(dbSession, qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
underTest.dissociateProject(qGateId, projectId);
verify(dao).selectById(qGateId);
verify(dao).selectById(dbSession, qGateId);
verify(propertiesDao).deleteProjectProperty("sonar.qualitygate", projectId);
}

@@ -555,9 +393,9 @@ public class QualityGatesTest {

Long qGateId = QUALITY_GATE_ID;
Long projectId = 24L;
when(dao.selectById(qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
when(dao.selectById(dbSession, qGateId)).thenReturn(new QualityGateDto().setId(qGateId));
underTest.dissociateProject(qGateId, projectId);
verify(dao).selectById(qGateId);
verify(dao).selectById(dbSession, qGateId);
verify(propertiesDao).deleteProjectProperty("sonar.qualitygate", projectId);
}

@@ -572,7 +410,7 @@ public class QualityGatesTest {
QualityGateConditionDto cond2 = new QualityGateConditionDto().setMetricId(metric2Id);
Collection<QualityGateConditionDto> conditions = ImmutableList.of(cond1, cond2);

when(dao.selectById(sourceId)).thenReturn(new QualityGateDto().setId(sourceId).setName("SG-1"));
when(dao.selectById(dbSession, sourceId)).thenReturn(new QualityGateDto().setId(sourceId).setName("SG-1"));
Mockito.doAnswer(invocation -> {
((QualityGateDto) invocation.getArguments()[1]).setId(destId);
return null;

+ 36
- 29
server/sonar-server/src/test/java/org/sonar/server/qualitygate/RegisterQualityGatesTest.java View File

@@ -21,26 +21,29 @@ package org.sonar.server.qualitygate;

import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
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.loadedtemplate.LoadedTemplateDao;
import org.sonar.db.loadedtemplate.LoadedTemplateDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.qualitygate.QualityGateConditionDto;
import org.sonar.db.qualitygate.QualityGateDto;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.anyInt;
import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.sonar.api.measures.CoreMetrics.NEW_BUGS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_COVERAGE_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_SQALE_DEBT_RATIO_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_VULNERABILITIES_KEY;
import static org.sonar.api.measures.Metric.ValueType.INT;
import static org.sonar.api.measures.Metric.ValueType.PERCENT;
import static org.sonar.db.metric.MetricTesting.newMetricDto;
import static org.sonar.db.qualitygate.QualityGateConditionDto.OPERATOR_GREATER_THAN;
import static org.sonar.db.qualitygate.QualityGateConditionDto.OPERATOR_LESS_THAN;

@@ -50,47 +53,51 @@ public class RegisterQualityGatesTest {
public DbTester db = DbTester.create(System2.INSTANCE);

DbClient dbClient = db.getDbClient();
DbSession dbSession = db.getSession();

QualityGates qualityGates = mock(QualityGates.class);
LoadedTemplateDao templateDao = mock(LoadedTemplateDao.class);
RegisterQualityGates task = new RegisterQualityGates(qualityGates, new QualityGateUpdater(dbClient), templateDao);
RegisterQualityGates task = new RegisterQualityGates(dbClient,
new QualityGateUpdater(dbClient),
new QualityGateConditionsUpdater(dbClient),
dbClient.loadedTemplateDao(),
qualityGates);

@Test
public void register_default_gate() {
String templateType = "QUALITY_GATE";
String templateName = "SonarQube way";
when(templateDao.countByTypeAndKey(templateType, templateName)).thenReturn(0);
MetricDto newBugs = dbClient.metricDao().insert(dbSession, newMetricDto().setKey(NEW_BUGS_KEY).setValueType(INT.name()).setHidden(false));
MetricDto newVulnerabilities = dbClient.metricDao().insert(dbSession, newMetricDto().setKey(NEW_VULNERABILITIES_KEY).setValueType(INT.name()).setHidden(false));
MetricDto newSqaleDebtRatio = dbClient.metricDao().insert(dbSession, newMetricDto().setKey(NEW_SQALE_DEBT_RATIO_KEY).setValueType(PERCENT.name()).setHidden(false));
MetricDto newCoverage = dbClient.metricDao().insert(dbSession, newMetricDto().setKey(NEW_COVERAGE_KEY).setValueType(PERCENT.name()).setHidden(false));
dbSession.commit();

task.start();

verify(templateDao).countByTypeAndKey(templateType, templateName);
verify(qualityGates).createCondition(anyInt(), eq(NEW_BUGS_KEY), eq(OPERATOR_GREATER_THAN), eq((String) null), eq("0"), eq(1));
verify(qualityGates).createCondition(anyInt(), eq(NEW_VULNERABILITIES_KEY), eq(OPERATOR_GREATER_THAN), eq((String) null), eq("0"), eq(1));
verify(qualityGates).createCondition(anyInt(), eq(NEW_SQALE_DEBT_RATIO_KEY), eq(OPERATOR_GREATER_THAN), eq((String) null), eq("5"), eq(1));
verify(qualityGates).createCondition(anyInt(), eq(NEW_COVERAGE_KEY), eq(OPERATOR_LESS_THAN), eq((String) null), eq("80"), eq(1));
verify(qualityGates).setDefault(anyLong());

assertThat(dbClient.qualityGateDao().selectByName(templateName)).isNotNull();

ArgumentCaptor<LoadedTemplateDto> templateArg = ArgumentCaptor.forClass(LoadedTemplateDto.class);
verify(templateDao).insert(templateArg.capture());
LoadedTemplateDto template = templateArg.getValue();
assertThat(template.getType()).isEqualTo(templateType);
assertThat(template.getKey()).isEqualTo(templateName);
assertThat(dbClient.loadedTemplateDao().countByTypeAndKey("QUALITY_GATE", "SonarQube way", dbSession)).isEqualTo(1);
QualityGateDto qualityGateDto = dbClient.qualityGateDao().selectByName(dbSession, "SonarQube way");
assertThat(qualityGateDto).isNotNull();
assertThat(dbClient.gateConditionDao().selectForQualityGate(qualityGateDto.getId()))
.extracting(QualityGateConditionDto::getMetricId, QualityGateConditionDto::getOperator, QualityGateConditionDto::getWarningThreshold,
QualityGateConditionDto::getErrorThreshold, QualityGateConditionDto::getPeriod)
.containsOnly(
tuple(newBugs.getId().longValue(), OPERATOR_GREATER_THAN, null, "0", 1),
tuple(newVulnerabilities.getId().longValue(), OPERATOR_GREATER_THAN, null, "0", 1),
tuple(newSqaleDebtRatio.getId().longValue(), OPERATOR_GREATER_THAN, null, "5", 1),
tuple(newCoverage.getId().longValue(), OPERATOR_LESS_THAN, null, "80", 1));
verify(qualityGates).setDefault(any(DbSession.class), anyLong());

task.stop();
}

@Test
public void does_not_register_default_gate_if_already_present() {
public void does_not_register_default_gate_if_already_executed() {
String templateType = "QUALITY_GATE";
String templateName = "SonarQube way";
when(templateDao.countByTypeAndKey(templateType, templateName)).thenReturn(1);
dbClient.loadedTemplateDao().insert(new LoadedTemplateDto(templateName, templateType), dbSession);
dbSession.commit();

task.start();

verify(templateDao).countByTypeAndKey(templateType, templateName);
assertThat(dbClient.qualityGateDao().selectAll(dbSession)).isEmpty();
verifyZeroInteractions(qualityGates);
verifyNoMoreInteractions(templateDao);
}
}

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/CreateActionTest.java View File

@@ -56,7 +56,7 @@ public class CreateActionTest {
DbClient dbClient = db.getDbClient();
DbSession dbSession = db.getSession();

CreateAction underTest = new CreateAction(userSession, new QualityGateUpdater(dbClient));
CreateAction underTest = new CreateAction(dbClient, userSession, new QualityGateUpdater(dbClient));

WsActionTester ws = new WsActionTester(underTest);


+ 200
- 0
server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/CreateConditionActionTest.java View File

@@ -0,0 +1,200 @@
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package org.sonar.server.qualitygate.ws;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.server.ws.WebService;
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.ForbiddenException;
import org.sonar.server.qualitygate.QualityGateConditionsUpdater;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.WsActionTester;
import org.sonarqube.ws.MediaTypes;
import org.sonarqube.ws.WsQualityGates.CreateConditionWsResponse;

import static org.assertj.core.api.Java6Assertions.assertThat;
import static org.sonar.core.permission.GlobalPermissions.QUALITY_GATE_ADMIN;
import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
import static org.sonar.db.metric.MetricTesting.newMetricDto;
import static org.sonar.server.computation.task.projectanalysis.metric.Metric.MetricType.PERCENT;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ERROR;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_GATE_ID;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_METRIC;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_OPERATOR;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PERIOD;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_WARNING;

public class CreateConditionActionTest {

@Rule
public ExpectedException expectedException = ExpectedException.none();

@Rule
public UserSessionRule userSession = UserSessionRule.standalone();

@Rule
public DbTester db = DbTester.create(System2.INSTANCE);

DbClient dbClient = db.getDbClient();
DbSession dbSession = db.getSession();
QualityGateDbTester qualityGateDbTester = new QualityGateDbTester(db);

CreateConditionAction underTest = new CreateConditionAction(userSession, dbClient, new QualityGateConditionsUpdater(dbClient));

QualityGateDto qualityGateDto;

MetricDto coverageMetricDto = newMetricDto()
.setKey("coverage")
.setShortName("Coverage")
.setValueType(PERCENT.name())
.setHidden(false);

WsActionTester ws = new WsActionTester(underTest);

@Before
public void setUp() throws Exception {
qualityGateDto = qualityGateDbTester.insertQualityGate();
dbClient.metricDao().insert(dbSession, coverageMetricDto);
dbSession.commit();
}

@Test
public void create_warning_condition() throws Exception {
setUserAsQualityGateAdmin();

CreateConditionWsResponse response = executeRequest(qualityGateDto.getId(), coverageMetricDto.getKey(), "LT", "90", null, null);

assertCondition(response, "LT", "90", null, null);
}

@Test
public void create_error_condition() throws Exception {
setUserAsQualityGateAdmin();

CreateConditionWsResponse response = executeRequest(qualityGateDto.getId(), coverageMetricDto.getKey(), "LT", null, "90", null);

assertCondition(response, "LT", null, "90", null);
}

@Test
public void create_condition_over_leak_period() throws Exception {
setUserAsQualityGateAdmin();

CreateConditionWsResponse response = executeRequest(qualityGateDto.getId(), coverageMetricDto.getKey(), "LT", null, "90", 1);

assertCondition(response, "LT", null, "90", 1);
}

@Test
public void fail_when_not_quality_gate_admin() throws Exception {
setUserAsNotQualityGateAdmin();

expectedException.expect(ForbiddenException.class);
executeRequest(qualityGateDto.getId(), coverageMetricDto.getKey(), "LT", "90", null, null);
}

@Test
public void test_ws_definition() {
WebService.Action action = ws.getDef();
assertThat(action).isNotNull();
assertThat(action.isInternal()).isFalse();
assertThat(action.isPost()).isTrue();
assertThat(action.responseExampleAsString()).isNull();
assertThat(action.params()).hasSize(6);
}

private void assertCondition(CreateConditionWsResponse response, String operator, @Nullable String warning, @Nullable String error, @Nullable Integer period) {
List<QualityGateConditionDto> conditionDtoList = new ArrayList<>(dbClient.gateConditionDao().selectForQualityGate(qualityGateDto.getId(), dbSession));
assertThat(conditionDtoList).hasSize(1);
QualityGateConditionDto qualityGateConditionDto = conditionDtoList.get(0);
assertThat(qualityGateConditionDto.getQualityGateId()).isEqualTo(qualityGateDto.getId());
assertThat(qualityGateConditionDto.getMetricId()).isEqualTo(coverageMetricDto.getId().longValue());
assertThat(qualityGateConditionDto.getOperator()).isEqualTo(operator);
assertThat(qualityGateConditionDto.getWarningThreshold()).isEqualTo(warning);
assertThat(qualityGateConditionDto.getErrorThreshold()).isEqualTo(error);
assertThat(qualityGateConditionDto.getPeriod()).isEqualTo(period);

assertThat(response.getId()).isEqualTo(qualityGateConditionDto.getId());
assertThat(response.getMetric()).isEqualTo(coverageMetricDto.getKey());
assertThat(response.getOp()).isEqualTo(operator);
if (warning != null) {
assertThat(response.getWarning()).isEqualTo(warning);
} else {
assertThat(response.hasWarning()).isFalse();
}
if (error != null) {
assertThat(response.getError()).isEqualTo(error);
} else {
assertThat(response.hasError()).isFalse();
}
if (period != null) {
assertThat(response.getPeriod()).isEqualTo(period);
} else {
assertThat(response.hasPeriod()).isFalse();
}
}

private CreateConditionWsResponse executeRequest(long qualityProfileId, String metricKey, String operator, @Nullable String warning, @Nullable String error,
@Nullable Integer period) {
TestRequest request = ws.newRequest()
.setMediaType(MediaTypes.PROTOBUF)
.setParam(PARAM_GATE_ID, Long.toString(qualityProfileId))
.setParam(PARAM_METRIC, metricKey)
.setParam(PARAM_OPERATOR, operator);
if (warning != null) {
request.setParam(PARAM_WARNING, warning);
}
if (error != null) {
request.setParam(PARAM_ERROR, error);
}
if (period != null) {
request.setParam(PARAM_PERIOD, Integer.toString(period));
}
try {
return CreateConditionWsResponse.parseFrom(request.execute().getInputStream());
} catch (IOException e) {
throw new IllegalStateException(e);
}
}

private void setUserAsQualityGateAdmin() {
userSession.login("project-admin").setGlobalPermissions(QUALITY_GATE_ADMIN);
}

private void setUserAsNotQualityGateAdmin() {
userSession.login("not-admin").setGlobalPermissions(SCAN_EXECUTION);
}
}

+ 2
- 22
server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QualityGatesWsTest.java View File

@@ -70,9 +70,9 @@ public class QualityGatesWsTest {

tester = new WsTester(new QualityGatesWs(
new ListAction(qGates), new ShowAction(qGates), new SearchAction(projectFinder),
new CreateAction(null, null), new CopyAction(qGates), new DestroyAction(qGates), new RenameAction(qGates),
new CreateAction(null, null, null), new CopyAction(qGates), new DestroyAction(qGates), new RenameAction(qGates),
new SetAsDefaultAction(qGates), new UnsetDefaultAction(qGates),
new CreateConditionAction(qGates), new UpdateConditionAction(qGates), new DeleteConditionAction(qGates),
new CreateConditionAction(null, null, null), new UpdateConditionAction(qGates), new DeleteConditionAction(qGates),
selectAction, new DeselectAction(qGates, mock(DbClient.class), mock(ComponentFinder.class)), new AppAction(null, null)));
}

@@ -305,26 +305,6 @@ public class QualityGatesWsTest {
tester.newGetRequest("api/qualitygates", "show").setParam("id", "12345").setParam("name", "Polop").execute();
}

@Test
public void create_condition_nominal() throws Exception {
long qGateId = 42L;
String metricKey = "coverage";
String operator = "LT";
String warningThreshold = "80";
String errorThreshold = "75";
when(qGates.createCondition(qGateId, metricKey, operator, warningThreshold, errorThreshold, null))
.thenReturn(new QualityGateConditionDto().setId(12345L).setQualityGateId(qGateId).setMetricId(10).setMetricKey(metricKey)
.setOperator(operator).setWarningThreshold(warningThreshold).setErrorThreshold(errorThreshold));
tester.newPostRequest("api/qualitygates", "create_condition")
.setParam("gateId", Long.toString(qGateId))
.setParam("metric", metricKey)
.setParam("op", operator)
.setParam("warning", warningThreshold)
.setParam("error", errorThreshold)
.execute()
.assertJson("{\"id\":12345,\"metric\":\"coverage\",\"op\":\"LT\",\"warning\":\"80\",\"error\":\"75\"}");
}

@Test
public void update_condition_nominal() throws Exception {
long condId = 12345L;

+ 7
- 0
sonar-db/src/main/java/org/sonar/db/metric/MetricDto.java View File

@@ -22,6 +22,9 @@ package org.sonar.db.metric;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

import static org.sonar.api.measures.Metric.ValueType.DATA;
import static org.sonar.api.measures.Metric.ValueType.DISTRIB;

public class MetricDto {

private Integer id;
@@ -208,4 +211,8 @@ public class MetricDto {
return this;
}

public boolean isDataType() {
return DATA.name().equals(valueType) || DISTRIB.name().equals(valueType);
}

}

+ 49
- 0
sonar-db/src/test/java/org/sonar/db/qualitygate/QualityGateDbTester.java View File

@@ -0,0 +1,49 @@
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.db.qualitygate;

import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;

import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;

public class QualityGateDbTester {

private final DbTester db;
private final DbClient dbClient;
private final DbSession dbSession;

public QualityGateDbTester(DbTester db) {
this.db = db;
this.dbClient = db.getDbClient();
this.dbSession = db.getSession();
}

public QualityGateDto insertQualityGate() {
return insertQualityGate(randomAlphanumeric(30));
}

public QualityGateDto insertQualityGate(String name) {
QualityGateDto updatedUser = dbClient.qualityGateDao().insert(dbSession, new QualityGateDto().setName(name));
db.commit();
return updatedUser;
}
}

+ 126
- 0
sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/CreateConditionRequest.java View File

@@ -0,0 +1,126 @@
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonarqube.ws.client.qualitygate;

import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.isNullOrEmpty;

public class CreateConditionRequest {

private final long qualityGateId;
private final String metricKey;
private final String operator;
private final String warning;
private final String error;
private final Integer period;

public CreateConditionRequest(Builder builder) {
this.qualityGateId = builder.qualityGateId;
this.metricKey = builder.metricKey;
this.operator = builder.operator;
this.warning = builder.warning;
this.error = builder.error;
this.period = builder.period;
}

public long getQualityGateId() {
return qualityGateId;
}

public String getMetricKey() {
return metricKey;
}

public String getOperator() {
return operator;
}

@CheckForNull
public String getWarning() {
return warning;
}

@CheckForNull
public String getError() {
return error;
}

@CheckForNull
public Integer getPeriod() {
return period;
}

public static Builder builder() {
return new Builder();
}

public static class Builder {
private long qualityGateId;
private String metricKey;
private String operator;
private String warning;
private String error;
private Integer period;

private Builder() {
// enforce factory method use
}

public Builder setQualityGateId(long qualityGateId) {
this.qualityGateId = qualityGateId;
return this;
}

public Builder setMetricKey(String metricKey) {
this.metricKey = metricKey;
return this;
}

public Builder setOperator(String operator) {
this.operator = operator;
return this;
}

public Builder setWarning(@Nullable String warning) {
this.warning = warning;
return this;
}

public Builder setError(@Nullable String error) {
this.error = error;
return this;
}

public Builder setPeriod(@Nullable Integer period) {
this.period = period;
return this;
}

public CreateConditionRequest build() {
checkArgument(qualityGateId > 0, "Quality gate id is mandatory and must not be empty");
checkArgument(!isNullOrEmpty(metricKey), "Metric key is mandatory and must not be empty");
checkArgument(!isNullOrEmpty(operator), "Operator is mandatory and must not be empty");
return new CreateConditionRequest(this);
}
}
}

+ 18
- 0
sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/QualityGatesService.java View File

@@ -19,6 +19,7 @@
*/
package org.sonarqube.ws.client.qualitygate;

import org.sonarqube.ws.WsQualityGates.CreateConditionWsResponse;
import org.sonarqube.ws.WsQualityGates.CreateWsResponse;
import org.sonarqube.ws.WsQualityGates.ProjectStatusWsResponse;
import org.sonarqube.ws.client.BaseService;
@@ -27,14 +28,20 @@ import org.sonarqube.ws.client.PostRequest;
import org.sonarqube.ws.client.WsConnector;

import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.ACTION_CREATE;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.ACTION_CREATE_CONDITION;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.ACTION_PROJECT_STATUS;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.ACTION_SELECT;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.CONTROLLER_QUALITY_GATES;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ANALYSIS_ID;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ERROR;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_GATE_ID;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_METRIC;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_NAME;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_OPERATOR;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PERIOD;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PROJECT_ID;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PROJECT_KEY;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_WARNING;

public class QualityGatesService extends BaseService {

@@ -62,4 +69,15 @@ public class QualityGatesService extends BaseService {
.setParam(PARAM_NAME, name),
CreateWsResponse.parser());
}

public CreateConditionWsResponse createCondition(CreateConditionRequest request) {
return call(new PostRequest(path(ACTION_CREATE_CONDITION))
.setParam(PARAM_GATE_ID, request.getQualityGateId())
.setParam(PARAM_METRIC, request.getMetricKey())
.setParam(PARAM_OPERATOR, request.getOperator())
.setParam(PARAM_WARNING, request.getWarning())
.setParam(PARAM_ERROR, request.getError())
.setParam(PARAM_PERIOD, request.getPeriod()),
CreateConditionWsResponse.parser());
}
}

+ 1
- 0
sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/QualityGatesWsParameters.java View File

@@ -28,6 +28,7 @@ public class QualityGatesWsParameters {
public static final String ACTION_GET_BY_PROJECT = "get_by_project";
public static final String ACTION_SELECT = "select";
public static final String ACTION_CREATE = "create";
public static final String ACTION_CREATE_CONDITION = "create_condition";

public static final String PARAM_ANALYSIS_ID = "analysisId";
public static final String PARAM_PROJECT_ID = "projectId";

+ 10
- 0
sonar-ws/src/main/protobuf/ws-qualitygates.proto View File

@@ -97,5 +97,15 @@ message CreateWsResponse {
optional string name = 2;
}

// POST api/qualitygates/create_condition
message CreateConditionWsResponse {
optional int64 id = 1;
optional string metric = 2;
optional string op = 3;
optional string warning = 4;
optional string error = 5;
optional int32 period = 6;
}




+ 91
- 0
sonar-ws/src/test/java/org/sonarqube/ws/client/qualitygate/CreateConditionRequestTest.java View File

@@ -0,0 +1,91 @@
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package org.sonarqube.ws.client.qualitygate;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import static org.assertj.core.api.Assertions.assertThat;

public class CreateConditionRequestTest {

@Rule
public ExpectedException expectedException = ExpectedException.none();

CreateConditionRequest.Builder underTest = CreateConditionRequest.builder();

@Test
public void create_condition_request() {
CreateConditionRequest result = underTest
.setQualityGateId(10)
.setMetricKey("metric")
.setOperator("LT")
.setWarning("warning")
.setError("error")
.setPeriod(1)
.build();

assertThat(result.getQualityGateId()).isEqualTo(10);
assertThat(result.getMetricKey()).isEqualTo("metric");
assertThat(result.getOperator()).isEqualTo("LT");
assertThat(result.getWarning()).isEqualTo("warning");
assertThat(result.getError()).isEqualTo("error");
assertThat(result.getPeriod()).isEqualTo(1);
}

@Test
public void fail_when_no_quality_gate() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Quality gate id is mandatory and must not be empty");

underTest
.setMetricKey("metric")
.setOperator("LT")
.setWarning("warning")
.build();
}

@Test
public void fail_when_no_metric() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Metric key is mandatory and must not be empty");

underTest
.setQualityGateId(10)
.setOperator("LT")
.setWarning("warning")
.build();
}

@Test
public void fail_when_no_operator() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Operator is mandatory and must not be empty");

underTest
.setQualityGateId(10)
.setMetricKey("metric")
.setWarning("warning")
.build();
}

}

+ 31
- 0
sonar-ws/src/test/java/org/sonarqube/ws/client/qualitygate/QualityGatesServiceTest.java View File

@@ -22,6 +22,7 @@ package org.sonarqube.ws.client.qualitygate;

import org.junit.Rule;
import org.junit.Test;
import org.sonarqube.ws.WsQualityGates.CreateConditionWsResponse;
import org.sonarqube.ws.WsQualityGates.CreateWsResponse;
import org.sonarqube.ws.WsQualityGates.ProjectStatusWsResponse;
import org.sonarqube.ws.client.GetRequest;
@@ -32,10 +33,15 @@ import org.sonarqube.ws.client.WsConnector;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ANALYSIS_ID;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ERROR;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_GATE_ID;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_METRIC;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_NAME;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_OPERATOR;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PERIOD;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PROJECT_ID;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PROJECT_KEY;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_WARNING;

public class QualityGatesServiceTest {
private static final String PROJECT_ID_VALUE = "195";
@@ -92,4 +98,29 @@ public class QualityGatesServiceTest {
.hasParam(PARAM_NAME, "Default")
.andNoOtherParam();
}

@Test
public void create_condition() {
underTest.createCondition(CreateConditionRequest.builder()
.setQualityGateId(10)
.setMetricKey("metric")
.setOperator("LT")
.setWarning("warning")
.setError("error")
.setPeriod(1)
.build());

PostRequest request = serviceTester.getPostRequest();

assertThat(serviceTester.getPostParser()).isSameAs(CreateConditionWsResponse.parser());
serviceTester.assertThat(request)
.hasPath("create_condition")
.hasParam(PARAM_GATE_ID, 10)
.hasParam(PARAM_METRIC, "metric")
.hasParam(PARAM_OPERATOR, "LT")
.hasParam(PARAM_WARNING, "warning")
.hasParam(PARAM_ERROR, "error")
.hasParam(PARAM_PERIOD, 1)
.andNoOtherParam();
}
}

Loading…
Cancel
Save