Browse Source

SONAR-20607 Add isCaycCondition attribute in 'api/qualitygates/show' endpoint

tags/10.3.0.82913
Zipeng WU 7 months ago
parent
commit
664aa30312

+ 46
- 19
server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualitygate/QualityGateCaycCheckerIT.java View File

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

import java.util.List;
import java.util.stream.IntStream;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.measures.Metric;
@@ -30,14 +29,17 @@ import org.sonar.db.metric.MetricDto;
import org.sonar.db.qualitygate.QualityGateConditionDto;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.sonar.api.measures.CoreMetrics.BLOCKER_VIOLATIONS;
import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES;
import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY;
import static org.sonar.api.measures.CoreMetrics.LINE_COVERAGE;
import static org.sonar.api.measures.CoreMetrics.NEW_COVERAGE;
import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_DENSITY;
import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_RATING;
import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_RATING;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING;
import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS;
import static org.sonar.server.qualitygate.QualityGateCaycChecker.BEST_VALUE_REQUIREMENTS;
import static org.sonar.server.qualitygate.QualityGateCaycChecker.CAYC_METRICS;
import static org.sonar.server.qualitygate.QualityGateCaycStatus.COMPLIANT;
import static org.sonar.server.qualitygate.QualityGateCaycStatus.NON_COMPLIANT;
@@ -50,41 +52,57 @@ public class QualityGateCaycCheckerIT {
QualityGateCaycChecker underTest = new QualityGateCaycChecker(db.getDbClient());

@Test
public void checkCaycCompliant() {
public void checkCaycCompliant_when_contains_all_and_only_complicant_conditions_should_return_compliant() {
String qualityGateUuid = "abcd";
CAYC_METRICS.forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue()));
assertEquals(COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid));
}

@Test
public void check_Cayc_non_compliant_with_extra_conditions() {
public void checkCaycCompliant_when_extra_conditions_should_return_over_compliant() {
String qualityGateUuid = "abcd";
CAYC_METRICS.forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue()));

// extra conditions outside of CAYC requirements
List.of(LINE_COVERAGE, DUPLICATED_LINES).forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue()));
List.of(LINE_COVERAGE, DUPLICATED_LINES).forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid,
metric.getBestValue()));

assertEquals(OVER_COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid));
}

@Test
public void check_Cayc_NonCompliant_with_lesser_threshold_value() {
public void checkCaycCompliant_when_conditions_have_lesser_threshold_value_should_return_non_compliant() {
var metrics = CAYC_METRICS.stream().map(this::insertMetric).toList();

IntStream.range(0, 4).forEach(idx -> {
String qualityGateUuid = "abcd" + idx;
for (int i = 0; i < metrics.size(); i++) {
var metric = metrics.get(i);
insertCondition(metric, qualityGateUuid, idx == i ? metric.getWorstValue() : metric.getBestValue());
String qualityGateUuid = "abcd";
for (var metric : metrics) {
if (BEST_VALUE_REQUIREMENTS.keySet().contains(metric.getKey())) {
insertCondition(metric, qualityGateUuid, metric.getBestValue() - 1);
} else {
insertCondition(metric, qualityGateUuid, metric.getBestValue());
}
assertEquals(NON_COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid));
});
}
assertEquals(NON_COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid));
}

@Test
public void isCaycCondition_when_check_compliant_condition_should_return_true() {
CAYC_METRICS.stream().map(this::toMetricDto)
.forEach(metricDto -> assertTrue(underTest.isCaycCondition(metricDto)));
}

@Test
public void isCaycCondition_when_check_non_compliant_condition_should_return_false() {
List.of(BLOCKER_VIOLATIONS, FUNCTION_COMPLEXITY)
.stream().map(this::toMetricDto)
.forEach(metricDto -> assertFalse(underTest.isCaycCondition(metricDto)));
}


@Test
public void check_Cayc_NonCompliant_with_missing_metric() {
public void checkCaycCompliant_when_missing_compliant_condition_should_return_non_compliant() {
String qualityGateUuid = "abcd";
List.of(NEW_MAINTAINABILITY_RATING, NEW_RELIABILITY_RATING, NEW_SECURITY_HOTSPOTS_REVIEWED, NEW_DUPLICATED_LINES_DENSITY)
List.of(NEW_VIOLATIONS, NEW_SECURITY_HOTSPOTS_REVIEWED)
.forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue()));
assertEquals(NON_COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid));
}
@@ -92,7 +110,7 @@ public class QualityGateCaycCheckerIT {
@Test
public void existency_requirements_check_only_existency() {
String qualityGateUuid = "abcd";
List.of(NEW_MAINTAINABILITY_RATING, NEW_RELIABILITY_RATING, NEW_SECURITY_HOTSPOTS_REVIEWED, NEW_SECURITY_RATING)
List.of(NEW_VIOLATIONS, NEW_SECURITY_HOTSPOTS_REVIEWED)
.forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue()));
List.of(NEW_COVERAGE, NEW_DUPLICATED_LINES_DENSITY)
.forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getWorstValue()));
@@ -115,7 +133,16 @@ public class QualityGateCaycCheckerIT {
.setValueType(metric.getType().name())
.setHidden(false)
.setBestValue(metric.getBestValue())
.setBestValue(metric.getWorstValue())
.setWorstValue(metric.getWorstValue())
.setDirection(metric.getDirection()));
}

private MetricDto toMetricDto(Metric metric) {
return new MetricDto()
.setKey(metric.key())
.setValueType(metric.getType().name())
.setBestValue(metric.getBestValue())
.setWorstValue(metric.getWorstValue())
.setDirection(metric.getDirection());
}
}

+ 5
- 3
server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualitygate/ws/CreateActionIT.java View File

@@ -29,6 +29,7 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Metric;
import org.sonar.api.server.ws.WebService;
import org.sonar.core.util.UuidFactoryFast;
@@ -64,7 +65,8 @@ public class CreateActionIT {

private final DbClient dbClient = db.getDbClient();
private final DbSession dbSession = db.getSession();
private final CreateAction underTest = new CreateAction(dbClient, userSession, new QualityGateUpdater(dbClient, UuidFactoryFast.getInstance()),
private final CreateAction underTest = new CreateAction(dbClient, userSession, new QualityGateUpdater(dbClient,
UuidFactoryFast.getInstance()),
new QualityGateConditionsUpdater(dbClient));
private final WsActionTester ws = new WsActionTester(underTest);

@@ -89,7 +91,7 @@ public class CreateActionIT {
var conditions = getConditions(dbSession, qualityGateDto);

CAYC_METRICS.stream()
.map(m -> dbClient.metricDao().selectByKey(dbSession, m.getKey()))
.map(metric -> dbClient.metricDao().selectByKey(dbSession, metric.getKey()))
.forEach(metricDto -> assertThat(conditions)
.anyMatch(c -> metricDto.getUuid().equals(c.getMetricUuid()) && c.getErrorThreshold().equals(String.valueOf(getDefaultCaycValue(metricDto)))));
}
@@ -139,7 +141,7 @@ public class CreateActionIT {

@DataProvider
public static Object[][] nullOrEmpty() {
return new Object[][] {
return new Object[][]{
{null},
{""},
{" "}

+ 17
- 18
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/QualityGateCaycChecker.java View File

@@ -20,51 +20,46 @@
package org.sonar.server.qualitygate;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
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 static java.util.stream.Collectors.toUnmodifiableMap;
import static java.util.stream.Collectors.toMap;
import static org.sonar.api.measures.CoreMetrics.NEW_COVERAGE;
import static org.sonar.api.measures.CoreMetrics.NEW_COVERAGE_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_DENSITY;
import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_DENSITY_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_RATING;
import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_RATING;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING;
import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS;
import static org.sonar.server.qualitygate.QualityGateCaycStatus.COMPLIANT;
import static org.sonar.server.qualitygate.QualityGateCaycStatus.NON_COMPLIANT;
import static org.sonar.server.qualitygate.QualityGateCaycStatus.OVER_COMPLIANT;

public class QualityGateCaycChecker {

public static final List<Metric<? extends Serializable>> CAYC_METRICS = List.of(
NEW_MAINTAINABILITY_RATING,
NEW_RELIABILITY_RATING,
static final Map<String, Double> BEST_VALUE_REQUIREMENTS = Stream.of(
NEW_VIOLATIONS,
NEW_SECURITY_HOTSPOTS_REVIEWED
).collect(toMap(Metric::getKey, Metric::getBestValue));
static final Set<String> EXISTENCY_REQUIREMENTS = Set.of(
NEW_DUPLICATED_LINES_DENSITY_KEY,
NEW_COVERAGE_KEY
);
public static final Set<Metric<? extends Serializable>> CAYC_METRICS = Set.of(
NEW_VIOLATIONS,
NEW_SECURITY_HOTSPOTS_REVIEWED,
NEW_SECURITY_RATING,
NEW_DUPLICATED_LINES_DENSITY,
NEW_COVERAGE
);

private static final Set<String> EXISTENCY_REQUIREMENTS = Set.of(
NEW_DUPLICATED_LINES_DENSITY_KEY,
NEW_COVERAGE_KEY
);

private static final Map<String, Double> BEST_VALUE_REQUIREMENTS = CAYC_METRICS.stream()
.filter(metric -> !EXISTENCY_REQUIREMENTS.contains(metric.getKey()))
.collect(toUnmodifiableMap(Metric::getKey, Metric::getBestValue));

private final DbClient dbClient;

public QualityGateCaycChecker(DbClient dbClient) {
@@ -105,6 +100,10 @@ public class QualityGateCaycChecker {
.orElse(NON_COMPLIANT);
}

public boolean isCaycCondition(MetricDto metric) {
return CAYC_METRICS.stream().map(Metric::getKey).anyMatch(metric.getKey()::equals);
}

private static boolean checkMetricCaycCompliant(QualityGateConditionDto condition, MetricDto metric) {
if (EXISTENCY_REQUIREMENTS.contains(metric.getKey())) {
return true;

+ 3
- 3
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/CreateAction.java View File

@@ -111,9 +111,9 @@ public class CreateAction implements QualityGatesWsAction {
}

private void addCaycConditions(DbSession dbSession, QualityGateDto newQualityGate) {
CAYC_METRICS.forEach(m ->
qualityGateConditionsUpdater.createCondition(dbSession, newQualityGate, m.getKey(), OPERATORS_BY_DIRECTION.get(m.getDirection()).getDbValue(),
String.valueOf(getDefaultCaycValue(m)))
CAYC_METRICS.forEach(metric ->
qualityGateConditionsUpdater.createCondition(dbSession, newQualityGate, metric.getKey(), OPERATORS_BY_DIRECTION.get(metric.getDirection()).getDbValue(),
String.valueOf(getDefaultCaycValue(metric)))
);
}


+ 4
- 3
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/ShowAction.java View File

@@ -105,8 +105,8 @@ public class ShowAction implements QualityGatesWsAction {
return dbClient.metricDao().selectByUuids(dbSession, metricUuids).stream().filter(MetricDto::isEnabled).collect(Collectors.toMap(MetricDto::getUuid, Function.identity()));
}

private ShowWsResponse buildResponse(DbSession dbSession, QualityGateDto qualityGate, QualityGateDto defaultQualityGate, Collection<QualityGateConditionDto> conditions,
Map<String, MetricDto> metricsByUuid, QualityGateCaycStatus caycStatus) {
private ShowWsResponse buildResponse(DbSession dbSession, QualityGateDto qualityGate, QualityGateDto defaultQualityGate,
Collection<QualityGateConditionDto> conditions, Map<String, MetricDto> metricsByUuid, QualityGateCaycStatus caycStatus) {
return ShowWsResponse.newBuilder()
.setName(qualityGate.getName())
.setIsBuiltIn(qualityGate.isBuiltIn())
@@ -118,7 +118,7 @@ public class ShowAction implements QualityGatesWsAction {
.build();
}

private static Function<QualityGateConditionDto, ShowWsResponse.Condition> toWsCondition(Map<String, MetricDto> metricsByUuid) {
private Function<QualityGateConditionDto, ShowWsResponse.Condition> toWsCondition(Map<String, MetricDto> metricsByUuid) {
return condition -> {
String metricUuid = condition.getMetricUuid();
MetricDto metric = metricsByUuid.get(metricUuid);
@@ -126,6 +126,7 @@ public class ShowAction implements QualityGatesWsAction {
ShowWsResponse.Condition.Builder builder = ShowWsResponse.Condition.newBuilder()
.setId(condition.getUuid())
.setMetric(metric.getKey())
.setIsCaycCondition(qualityGateCaycChecker.isCaycCondition(metric))
.setOp(condition.getOperator());
ofNullable(condition.getErrorThreshold()).ifPresent(builder::setError);
return builder.build();

+ 4
- 3
sonar-ws/src/main/protobuf/ws-qualitygates.proto View File

@@ -127,10 +127,11 @@ message ShowWsResponse {
optional string caycStatus = 6;

message Condition {
optional string id = 1;
optional string metric = 2;
optional string op = 4;
required string id = 1;
required string metric = 2;
required string op = 4;
optional string error = 6;
required bool isCaycCondition = 7;
}
}


Loading…
Cancel
Save