Browse Source

SONAR-10085 webhook now use shared model of QualityGate

tags/7.0-RC1
Sébastien Lesaint 6 years ago
parent
commit
dd8f1153d9

+ 3
- 2
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutor.java View File

@@ -24,7 +24,6 @@ import java.util.Date;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.ce.posttask.Analysis;
@@ -37,6 +36,7 @@ import org.sonar.api.ce.posttask.ScannerContext;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReader;
import org.sonar.server.computation.task.projectanalysis.qualitygate.Condition;
@@ -191,7 +191,8 @@ public class PostProjectAnalysisTasksExecutor implements ComputationStepExecutor

private static Collection<QualityGate.Condition> convert(Set<Condition> conditions, Map<Condition, ConditionStatus> statusPerConditions) {
return conditions.stream()
.map(new ConditionToCondition(statusPerConditions)::apply).collect(Collectors.toList());
.map(new ConditionToCondition(statusPerConditions)::apply)
.collect(MoreCollectors.toList(statusPerConditions.size()));
}

private static class ProjectAnalysisImpl implements PostProjectAnalysisTask.ProjectAnalysis {

+ 22
- 12
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPostTask.java View File

@@ -21,15 +21,18 @@ package org.sonar.server.computation.task.projectanalysis.webhook;

import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.Set;
import org.sonar.api.ce.posttask.PostProjectAnalysisTask;
import org.sonar.api.config.Configuration;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.server.computation.task.projectanalysis.component.ConfigurationRepository;
import org.sonar.server.qualitygate.Condition;
import org.sonar.server.qualitygate.EvaluatedCondition;
import org.sonar.server.qualitygate.EvaluatedQualityGate;
import org.sonar.server.webhook.Analysis;
import org.sonar.server.webhook.Branch;
import org.sonar.server.webhook.CeTask;
import org.sonar.server.webhook.Project;
import org.sonar.server.webhook.QualityGate;
import org.sonar.server.webhook.WebHooks;
import org.sonar.server.webhook.WebhookPayloadFactory;

@@ -63,16 +66,23 @@ public class WebhookPostTask implements PostProjectAnalysisTask {
Project project = new Project(projectAnalysis.getProject().getUuid(), projectAnalysis.getProject().getKey(), projectAnalysis.getProject().getName());
Analysis analysis = projectAnalysis.getAnalysis().map(a -> new Analysis(a.getAnalysisUuid(), a.getDate().getTime())).orElse(null);
Branch branch = projectAnalysis.getBranch().map(b -> new Branch(b.isMain(), b.getName().orElse(null), Branch.Type.valueOf(b.getType().name()))).orElse(null);
QualityGate qualityGate = Optional.ofNullable(projectAnalysis.getQualityGate())
.map(qg -> new QualityGate(
qg.getId(),
qg.getName(),
QualityGate.Status.valueOf(qg.getStatus().name()),
qg.getConditions().stream()
.map(c -> new QualityGate.Condition(QualityGate.EvaluationStatus.valueOf(c.getStatus().name()), c.getMetricKey(), QualityGate.Operator.valueOf(c.getOperator().name()),
c.getErrorThreshold(), c.getWarningThreshold(), c.isOnLeakPeriod(),
c.getStatus() == org.sonar.api.ce.posttask.QualityGate.EvaluationStatus.NO_VALUE ? null : c.getValue()))
.collect(Collectors.toSet())))
EvaluatedQualityGate qualityGate = Optional.ofNullable(projectAnalysis.getQualityGate())
.map(qg -> {
EvaluatedQualityGate.Builder builder = EvaluatedQualityGate.newBuilder();
Set<Condition> conditions = qg.getConditions().stream()
.map(q -> {
Condition condition = new Condition(q.getMetricKey(), Condition.Operator.valueOf(q.getOperator().name()),
q.getErrorThreshold(), q.getWarningThreshold(), q.isOnLeakPeriod());
builder.addCondition(condition,
EvaluatedCondition.EvaluationStatus.valueOf(q.getStatus().name()),
q.getStatus() == org.sonar.api.ce.posttask.QualityGate.EvaluationStatus.NO_VALUE ? null : q.getValue());
return condition;
})
.collect(MoreCollectors.toSet());
return builder.setQualityGate(new org.sonar.server.qualitygate.QualityGate(qg.getId(), qg.getName(), conditions))
.setStatus(EvaluatedQualityGate.Status.valueOf(qg.getStatus().name()))
.build();
})
.orElse(null);
Long date = projectAnalysis.getAnalysis().map(a -> a.getDate().getTime()).orElse(null);
Map<String, String> properties = projectAnalysis.getScannerContext().getProperties();

+ 31
- 23
server/sonar-server/src/main/java/org/sonar/server/issue/webhook/IssueChangeWebhookImpl.java View File

@@ -46,6 +46,10 @@ import org.sonar.server.es.Facets;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.issue.IssueQuery;
import org.sonar.server.issue.index.IssueIndex;
import org.sonar.server.qualitygate.Condition;
import org.sonar.server.qualitygate.EvaluatedCondition;
import org.sonar.server.qualitygate.EvaluatedCondition.EvaluationStatus;
import org.sonar.server.qualitygate.EvaluatedQualityGate;
import org.sonar.server.qualitygate.ShortLivingBranchQualityGate;
import org.sonar.server.rule.index.RuleIndex;
import org.sonar.server.settings.ProjectConfigurationLoader;
@@ -53,7 +57,6 @@ import org.sonar.server.webhook.Analysis;
import org.sonar.server.webhook.Branch;
import org.sonar.server.webhook.Project;
import org.sonar.server.webhook.ProjectAnalysis;
import org.sonar.server.webhook.QualityGate;
import org.sonar.server.webhook.WebHooks;
import org.sonar.server.webhook.WebhookPayload;
import org.sonar.server.webhook.WebhookPayloadFactory;
@@ -170,7 +173,7 @@ public class IssueChangeWebhookImpl implements IssueChangeWebhook {
return webhookPayloadFactory.create(projectAnalysis);
}

private QualityGate createQualityGate(ComponentDto branch, IssueIndex issueIndex) {
private EvaluatedQualityGate createQualityGate(ComponentDto branch, IssueIndex issueIndex) {
SearchResponse searchResponse = issueIndex.search(IssueQuery.builder()
.projectUuids(singletonList(branch.getMainBranchProjectUuid()))
.branchUuid(branch.uuid())
@@ -182,43 +185,48 @@ public class IssueChangeWebhookImpl implements IssueChangeWebhook {
LinkedHashMap<String, Long> typeFacet = new Facets(searchResponse, system2.getDefaultTimeZone())
.get(RuleIndex.FACET_TYPES);

Set<QualityGate.Condition> conditions = ShortLivingBranchQualityGate.CONDITIONS.stream()
.map(c -> toCondition(typeFacet, c))
EvaluatedQualityGate.Builder builder = EvaluatedQualityGate.newBuilder();
Set<Condition> conditions = ShortLivingBranchQualityGate.CONDITIONS.stream()
.map(c -> {
long measure = getMeasure(typeFacet, c);
EvaluationStatus status = measure > 0 ? EvaluationStatus.ERROR : EvaluationStatus.OK;
Condition condition = new Condition(c.getMetricKey(), toOperator(c), c.getErrorThreshold(), c.getWarnThreshold(), c.isOnLeak());
builder.addCondition(condition, status, valueOf(measure));
return condition;
})
.collect(toSet(ShortLivingBranchQualityGate.CONDITIONS.size()));
builder
.setQualityGate(
new org.sonar.server.qualitygate.QualityGate(
valueOf(ShortLivingBranchQualityGate.ID),
ShortLivingBranchQualityGate.NAME,
conditions))
.setStatus(qgStatusFrom(builder.getEvaluatedConditions()));

return new QualityGate(valueOf(ShortLivingBranchQualityGate.ID), ShortLivingBranchQualityGate.NAME, qgStatusFrom(conditions), conditions);
return builder.build();
}

private static QualityGate.Condition toCondition(LinkedHashMap<String, Long> typeFacet, ShortLivingBranchQualityGate.Condition c) {
long measure = getMeasure(typeFacet, c);
QualityGate.EvaluationStatus status = measure > 0 ? QualityGate.EvaluationStatus.ERROR : QualityGate.EvaluationStatus.OK;
return new QualityGate.Condition(status, c.getMetricKey(),
toOperator(c),
c.getErrorThreshold(), c.getWarnThreshold(), c.isOnLeak(),
valueOf(measure));
}

private static QualityGate.Operator toOperator(ShortLivingBranchQualityGate.Condition c) {
private static Condition.Operator toOperator(ShortLivingBranchQualityGate.Condition c) {
String operator = c.getOperator();
switch (operator) {
case QualityGateConditionDto.OPERATOR_GREATER_THAN:
return QualityGate.Operator.GREATER_THAN;
return Condition.Operator.GREATER_THAN;
case QualityGateConditionDto.OPERATOR_LESS_THAN:
return QualityGate.Operator.LESS_THAN;
return Condition.Operator.LESS_THAN;
case QualityGateConditionDto.OPERATOR_EQUALS:
return QualityGate.Operator.EQUALS;
return Condition.Operator.EQUALS;
case QualityGateConditionDto.OPERATOR_NOT_EQUALS:
return QualityGate.Operator.NOT_EQUALS;
return Condition.Operator.NOT_EQUALS;
default:
throw new IllegalArgumentException(format("Unsupported Condition operator '%s'", operator));
}
}

private static QualityGate.Status qgStatusFrom(Set<QualityGate.Condition> conditions) {
if (conditions.stream().anyMatch(c -> c.getStatus() == QualityGate.EvaluationStatus.ERROR)) {
return QualityGate.Status.ERROR;
private static EvaluatedQualityGate.Status qgStatusFrom(Set<EvaluatedCondition> conditions) {
if (conditions.stream().anyMatch(c -> c.getStatus() == EvaluationStatus.ERROR)) {
return EvaluatedQualityGate.Status.ERROR;
}
return QualityGate.Status.OK;
return EvaluatedQualityGate.Status.OK;
}

private static long getMeasure(LinkedHashMap<String, Long> typeFacet, ShortLivingBranchQualityGate.Condition c) {

+ 4
- 3
server/sonar-server/src/main/java/org/sonar/server/webhook/ProjectAnalysis.java View File

@@ -23,6 +23,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import javax.annotation.Nullable;
import org.sonar.server.qualitygate.EvaluatedQualityGate;

import static com.google.common.collect.ImmutableMap.copyOf;
import static java.util.Objects.requireNonNull;
@@ -31,13 +32,13 @@ public class ProjectAnalysis {
private final Project project;
private final CeTask ceTask;
private final Branch branch;
private final QualityGate qualityGate;
private final EvaluatedQualityGate qualityGate;
private final Long updatedAt;
private final Map<String, String> properties;
private final Analysis analysis;

public ProjectAnalysis(Project project, @Nullable CeTask ceTask, @Nullable Analysis analysis,
@Nullable Branch branch, @Nullable QualityGate qualityGate, @Nullable Long updatedAt,
@Nullable Branch branch, @Nullable EvaluatedQualityGate qualityGate, @Nullable Long updatedAt,
Map<String, String> properties) {
this.project = requireNonNull(project, "project can't be null");
this.ceTask = ceTask;
@@ -60,7 +61,7 @@ public class ProjectAnalysis {
return Optional.ofNullable(branch);
}

public Optional<QualityGate> getQualityGate() {
public Optional<EvaluatedQualityGate> getQualityGate() {
return Optional.ofNullable(qualityGate);
}


+ 0
- 211
server/sonar-server/src/main/java/org/sonar/server/webhook/QualityGate.java View File

@@ -1,211 +0,0 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info 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.webhook;

import com.google.common.collect.ImmutableSet;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable;

import static java.util.Objects.requireNonNull;

public final class QualityGate {
private final String id;
private final String name;
private final Status status;
private final Set<Condition> conditions;

public QualityGate(String id, String name, Status status, Set<Condition> conditions) {
this.id = requireNonNull(id, "id can't be null");
this.name = requireNonNull(name, "name can't be null");
this.status = requireNonNull(status, "status can't be null");
this.conditions = ImmutableSet.copyOf(requireNonNull(conditions, "conditions can't be null"));
}

public String getId() {
return id;
}

public String getName() {
return name;
}

public Status getStatus() {
return status;
}

public Collection<Condition> getConditions() {
return conditions;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
QualityGate that = (QualityGate) o;
return Objects.equals(id, that.id) &&
Objects.equals(name, that.name) &&
status == that.status &&
Objects.equals(conditions, that.conditions);
}

@Override
public int hashCode() {
return Objects.hash(id, name, status, conditions);
}

@Override
public String toString() {
return "QualityGate{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", status=" + status +
", conditions=" + conditions +
'}';
}

public enum Status {
OK,
WARN,
ERROR
}

public static final class Condition {
private final EvaluationStatus status;
private final String metricKey;
private final Operator operator;
private final String errorThreshold;
private final String warnThreshold;
private final boolean onLeakPeriod;
private final String value;

public Condition(EvaluationStatus status, String metricKey, Operator operator,
@Nullable String errorThreshold, @Nullable String warnThreshold,
boolean onLeakPeriod, @Nullable String value) {
this.status = requireNonNull(status, "status can't be null");
this.metricKey = requireNonNull(metricKey, "metricKey can't be null");
this.operator = requireNonNull(operator, "operator can't be null");
this.errorThreshold = errorThreshold;
this.warnThreshold = warnThreshold;
this.onLeakPeriod = onLeakPeriod;
this.value = value;
}

public EvaluationStatus getStatus() {
return status;
}

public String getMetricKey() {
return metricKey;
}

public Operator getOperator() {
return operator;
}

public Optional<String> getErrorThreshold() {
return Optional.ofNullable(errorThreshold);
}

public Optional<String> getWarningThreshold() {
return Optional.ofNullable(warnThreshold);
}

public boolean isOnLeakPeriod() {
return onLeakPeriod;
}

public Optional<String> getValue() {
return Optional.ofNullable(value);
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Condition condition = (Condition) o;
return onLeakPeriod == condition.onLeakPeriod &&
status == condition.status &&
Objects.equals(metricKey, condition.metricKey) &&
operator == condition.operator &&
Objects.equals(errorThreshold, condition.errorThreshold) &&
Objects.equals(warnThreshold, condition.warnThreshold) &&
Objects.equals(value, condition.value);
}

@Override
public int hashCode() {
return Objects.hash(status, metricKey, operator, errorThreshold, warnThreshold, onLeakPeriod, value);
}

@Override
public String toString() {
return "Condition{" +
"status=" + status +
", metricKey='" + metricKey + '\'' +
", operator=" + operator +
", errorThreshold='" + errorThreshold + '\'' +
", warnThreshold='" + warnThreshold + '\'' +
", onLeakPeriod=" + onLeakPeriod +
", value='" + value + '\'' +
'}';
}
}

/**
* Quality Gate condition operator.
*/
public enum Operator {
EQUALS, NOT_EQUALS, GREATER_THAN, LESS_THAN
}

/**
* Quality gate condition evaluation status.
*/
public enum EvaluationStatus {
/**
* No measure found or measure had no value. The condition has not been evaluated and therefor ignored in
* the computation of the Quality Gate status.
*/
NO_VALUE,
/**
* Condition evaluated as OK, neither error nor warning thresholds have been reached.
*/
OK,
/**
* Condition evaluated as WARN, only warning thresholds has been reached.
*/
WARN,
/**
* Condition evaluated as ERROR, error thresholds has been reached (and most likely warning thresholds too).
*/
ERROR
}
}

+ 9
- 5
server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookPayloadFactoryImpl.java View File

@@ -30,6 +30,9 @@ import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.platform.Server;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.server.qualitygate.Condition;
import org.sonar.server.qualitygate.EvaluatedCondition;
import org.sonar.server.qualitygate.EvaluatedQualityGate;

import static java.lang.String.format;
import static org.sonar.core.config.WebhookProperties.ANALYSIS_PROPERTY_PREFIX;
@@ -126,22 +129,23 @@ public class WebhookPayloadFactoryImpl implements WebhookPayloadFactory {
}
}

private static void writeQualityGate(JsonWriter writer, QualityGate gate) {
private static void writeQualityGate(JsonWriter writer, EvaluatedQualityGate gate) {
writer
.name("qualityGate")
.beginObject()
.prop("name", gate.getName())
.prop("name", gate.getQualityGate().getName())
.prop(PROPERTY_STATUS, gate.getStatus().toString())
.name("conditions")
.beginArray();
for (QualityGate.Condition condition : gate.getConditions()) {
for (EvaluatedCondition evaluatedCondition : gate.getEvaluatedConditions()) {
Condition condition = evaluatedCondition.getCondition();
writer
.beginObject()
.prop("metric", condition.getMetricKey())
.prop("operator", condition.getOperator().name());
condition.getValue().ifPresent(t -> writer.prop("value", t));
evaluatedCondition.getValue().ifPresent(t -> writer.prop("value", t));
writer
.prop(PROPERTY_STATUS, condition.getStatus().name())
.prop(PROPERTY_STATUS, evaluatedCondition.getStatus().name())
.prop("onLeakPeriod", condition.isOnLeakPeriod())
.prop("errorThreshold", condition.getErrorThreshold().orElse(null))
.prop("warningThreshold", condition.getWarningThreshold().orElse(null))

+ 15
- 11
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPostTaskTest.java View File

@@ -36,6 +36,9 @@ import org.sonar.api.ce.posttask.Project;
import org.sonar.api.ce.posttask.QualityGate;
import org.sonar.api.config.Configuration;
import org.sonar.server.computation.task.projectanalysis.component.ConfigurationRepository;
import org.sonar.server.qualitygate.Condition;
import org.sonar.server.qualitygate.EvaluatedCondition;
import org.sonar.server.qualitygate.EvaluatedQualityGate;
import org.sonar.server.webhook.Analysis;
import org.sonar.server.webhook.ProjectAnalysis;
import org.sonar.server.webhook.WebHooks;
@@ -139,19 +142,20 @@ public class WebhookPostTaskTest {

assertThat(supplierCaptor.getValue().get()).isSameAs(webhookPayload);

org.sonar.server.webhook.QualityGate webQualityGate = null;
EvaluatedQualityGate webQualityGate = null;
if (qualityGate != null) {
QualityGate.Condition condition = qualityGate.getConditions().iterator().next();
webQualityGate = new org.sonar.server.webhook.QualityGate(qualityGate.getId(), qualityGate.getName(),
org.sonar.server.webhook.QualityGate.Status.valueOf(qualityGate.getStatus().name()),
Collections.singleton(new org.sonar.server.webhook.QualityGate.Condition(
org.sonar.server.webhook.QualityGate.EvaluationStatus.valueOf(condition.getStatus().name()),
condition.getMetricKey(),
org.sonar.server.webhook.QualityGate.Operator.valueOf(condition.getOperator().name()),
condition.getErrorThreshold(),
condition.getWarningThreshold(),
condition.isOnLeakPeriod(),
condition.getValue())));
Condition qgCondition = new Condition(
condition.getMetricKey(),
Condition.Operator.valueOf(condition.getOperator().name()),
condition.getErrorThreshold(),
condition.getWarningThreshold(),
condition.isOnLeakPeriod());
webQualityGate = EvaluatedQualityGate.newBuilder()
.setQualityGate(new org.sonar.server.qualitygate.QualityGate(qualityGate.getId(), qualityGate.getName(), Collections.singleton(qgCondition)))
.setStatus(EvaluatedQualityGate.Status.valueOf(qualityGate.getStatus().name()))
.addCondition(qgCondition, EvaluatedCondition.EvaluationStatus.valueOf(condition.getStatus().name()), condition.getValue())
.build();
}

verify(payloadFactory).create(new ProjectAnalysis(

+ 39
- 31
server/sonar-server/src/test/java/org/sonar/server/issue/webhook/IssueChangeWebhookImplTest.java View File

@@ -49,7 +49,6 @@ import org.sonar.api.config.Configuration;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.issue.DefaultTransitions;
import org.sonar.api.issue.Issue;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.System2;
import org.sonar.core.issue.IssueChangeContext;
@@ -77,6 +76,11 @@ import org.sonar.server.issue.index.IssueIndexer;
import org.sonar.server.issue.index.IssueIteratorFactory;
import org.sonar.server.issue.webhook.IssueChangeWebhook.IssueChange;
import org.sonar.server.permission.index.AuthorizationTypeSupport;
import org.sonar.server.qualitygate.Condition;
import org.sonar.server.qualitygate.EvaluatedCondition;
import org.sonar.server.qualitygate.EvaluatedCondition.EvaluationStatus;
import org.sonar.server.qualitygate.EvaluatedQualityGate;
import org.sonar.server.qualitygate.QualityGate;
import org.sonar.server.qualitygate.ShortLivingBranchQualityGate;
import org.sonar.server.settings.ProjectConfigurationLoader;
import org.sonar.server.tester.UserSessionRule;
@@ -84,8 +88,6 @@ import org.sonar.server.webhook.Analysis;
import org.sonar.server.webhook.Branch;
import org.sonar.server.webhook.Project;
import org.sonar.server.webhook.ProjectAnalysis;
import org.sonar.server.webhook.QualityGate;
import org.sonar.server.webhook.QualityGate.EvaluationStatus;
import org.sonar.server.webhook.WebHooks;
import org.sonar.server.webhook.WebhookPayload;
import org.sonar.server.webhook.WebhookPayloadFactory;
@@ -115,7 +117,7 @@ import static org.sonar.api.measures.CoreMetrics.BUGS_KEY;
import static org.sonar.api.measures.CoreMetrics.CODE_SMELLS_KEY;
import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY;
import static org.sonar.core.util.stream.MoreCollectors.toArrayList;
import static org.sonar.server.webhook.QualityGate.Operator.GREATER_THAN;
import static org.sonar.server.qualitygate.Condition.Operator.GREATER_THAN;

@RunWith(DataProviderRunner.class)
public class IssueChangeWebhookImplTest {
@@ -148,7 +150,8 @@ public class IssueChangeWebhookImplTest {
private IssueChangeWebhookImpl underTest = new IssueChangeWebhookImpl(spiedOnDbClient, webHooks, projectConfigurationLoader, webhookPayloadFactory, issueIndex, System2.INSTANCE);
private DbClient mockedDbClient = mock(DbClient.class);
private IssueIndex spiedOnIssueIndex = spy(issueIndex);
private IssueChangeWebhookImpl mockedUnderTest = new IssueChangeWebhookImpl(mockedDbClient, webHooks, projectConfigurationLoader, webhookPayloadFactory, spiedOnIssueIndex, System2.INSTANCE);
private IssueChangeWebhookImpl mockedUnderTest = new IssueChangeWebhookImpl(mockedDbClient, webHooks, projectConfigurationLoader, webhookPayloadFactory, spiedOnIssueIndex,
System2.INSTANCE);

@Test
public void on_type_change_has_no_effect_if_SearchResponseData_has_no_issue() {
@@ -267,20 +270,25 @@ public class IssueChangeWebhookImplTest {
underTest.onChange(issueChangeData(newIssueDto(branch)), issueChange, userChangeContext);

ProjectAnalysis projectAnalysis = verifyWebhookCalledAndExtractPayloadFactoryArgument(branch, configuration, analysis);
Condition condition1 = new Condition(BUGS_KEY, GREATER_THAN, "0", null, false);
Condition condition2 = new Condition(VULNERABILITIES_KEY, GREATER_THAN, "0", null, false);
Condition condition3 = new Condition(CODE_SMELLS_KEY, GREATER_THAN, "0", null, false);
assertThat(projectAnalysis).isEqualTo(
new ProjectAnalysis(
new Project(project.uuid(), project.getKey(), project.name()),
null,
new Analysis(analysis.getUuid(), analysis.getCreatedAt()),
new Branch(false, "foo", Branch.Type.SHORT),
new QualityGate(
valueOf(ShortLivingBranchQualityGate.ID),
ShortLivingBranchQualityGate.NAME,
QualityGate.Status.OK,
ImmutableSet.of(
new QualityGate.Condition(EvaluationStatus.OK, BUGS_KEY, GREATER_THAN, "0", null, false, "0"),
new QualityGate.Condition(EvaluationStatus.OK, CoreMetrics.VULNERABILITIES_KEY, GREATER_THAN, "0", null, false, "0"),
new QualityGate.Condition(EvaluationStatus.OK, CODE_SMELLS_KEY, GREATER_THAN, "0", null, false, "0"))),
EvaluatedQualityGate.newBuilder()
.setQualityGate(new QualityGate(
valueOf(ShortLivingBranchQualityGate.ID),
ShortLivingBranchQualityGate.NAME,
ImmutableSet.of(condition1, condition2, condition3)))
.setStatus(EvaluatedQualityGate.Status.OK)
.addCondition(condition1, EvaluationStatus.OK, "0")
.addCondition(condition2, EvaluationStatus.OK, "0")
.addCondition(condition3, EvaluationStatus.OK, "0")
.build(),
null,
properties));
}
@@ -378,10 +386,10 @@ public class IssueChangeWebhookImplTest {
underTest.onChange(issueChangeData(newIssueDto(branch)), new IssueChange(randomRuleType), userChangeContext);

ProjectAnalysis projectAnalysis = verifyWebhookCalledAndExtractPayloadFactoryArgument(branch, configuration, analysis);
QualityGate qualityGate = projectAnalysis.getQualityGate().get();
assertThat(qualityGate.getStatus()).isEqualTo(QualityGate.Status.OK);
assertThat(qualityGate.getConditions())
.extracting(QualityGate.Condition::getStatus, QualityGate.Condition::getValue)
EvaluatedQualityGate qualityGate = projectAnalysis.getQualityGate().get();
assertThat(qualityGate.getStatus()).isEqualTo(EvaluatedQualityGate.Status.OK);
assertThat(qualityGate.getEvaluatedConditions())
.extracting(EvaluatedCondition::getStatus, EvaluatedCondition::getValue)
.containsOnly(Tuple.tuple(EvaluationStatus.OK, Optional.of("0")));
}

@@ -436,10 +444,10 @@ public class IssueChangeWebhookImplTest {
underTest.onChange(issueChangeData(newIssueDto(branch)), new IssueChange(randomRuleType), userChangeContext);

ProjectAnalysis projectAnalysis = verifyWebhookCalledAndExtractPayloadFactoryArgument(branch, configuration, analysis);
QualityGate qualityGate = projectAnalysis.getQualityGate().get();
assertThat(qualityGate.getStatus()).isEqualTo(QualityGate.Status.ERROR);
assertThat(qualityGate.getConditions())
.extracting(QualityGate.Condition::getMetricKey, QualityGate.Condition::getStatus, QualityGate.Condition::getValue)
EvaluatedQualityGate qualityGate = projectAnalysis.getQualityGate().get();
assertThat(qualityGate.getStatus()).isEqualTo(EvaluatedQualityGate.Status.ERROR);
assertThat(qualityGate.getEvaluatedConditions())
.extracting(s -> s.getCondition().getMetricKey(), EvaluatedCondition::getStatus, EvaluatedCondition::getValue)
.containsOnly(expectedQGConditions);
}

@@ -462,10 +470,10 @@ public class IssueChangeWebhookImplTest {
underTest.onChange(issueChangeData(newIssueDto(branch)), new IssueChange(randomRuleType), userChangeContext);

ProjectAnalysis projectAnalysis = verifyWebhookCalledAndExtractPayloadFactoryArgument(branch, configuration, analysis);
QualityGate qualityGate = projectAnalysis.getQualityGate().get();
assertThat(qualityGate.getStatus()).isEqualTo(QualityGate.Status.ERROR);
assertThat(qualityGate.getConditions())
.extracting(QualityGate.Condition::getMetricKey, QualityGate.Condition::getStatus, QualityGate.Condition::getValue)
EvaluatedQualityGate qualityGate = projectAnalysis.getQualityGate().get();
assertThat(qualityGate.getStatus()).isEqualTo(EvaluatedQualityGate.Status.ERROR);
assertThat(qualityGate.getEvaluatedConditions())
.extracting(s -> s.getCondition().getMetricKey(), EvaluatedCondition::getStatus, EvaluatedCondition::getValue)
.containsOnly(
Tuple.tuple(BUGS_KEY, EvaluationStatus.ERROR, Optional.of(valueOf(unresolvedBugs))),
Tuple.tuple(VULNERABILITIES_KEY, EvaluationStatus.ERROR, Optional.of(valueOf(unresolvedVulnerabilities))),
@@ -564,18 +572,18 @@ public class IssueChangeWebhookImplTest {
Configuration configuration2 = mock(Configuration.class);
Configuration configuration3 = mock(Configuration.class);
mockLoadProjectConfigurations(
branch1, configuration1,
branch2, configuration2,
branch3, configuration3);
branch1, configuration1,
branch2, configuration2,
branch3, configuration3);
mockWebhookEnabled(configuration1, configuration2, configuration3);
mockPayloadSupplierConsumedByWebhooks();

ComponentDao componentDaoSpy = spy(dbClient.componentDao());
when(spiedOnDbClient.componentDao()).thenReturn(componentDaoSpy);
underTest.onChange(
issueChangeData(issueDtos, branch1, branch2, branch3),
new IssueChange(randomRuleType),
userChangeContext);
issueChangeData(issueDtos, branch1, branch2, branch3),
new IssueChange(randomRuleType),
userChangeContext);

verifyWebhookCalled(branch1, configuration1, analysis1);
verifyWebhookCalled(branch2, configuration2, analysis2);

+ 21
- 8
server/sonar-server/src/test/java/org/sonar/server/webhook/ProjectAnalysisTest.java View File

@@ -24,6 +24,8 @@ import java.util.Map;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.server.qualitygate.EvaluatedQualityGate;
import org.sonar.server.qualitygate.QualityGate;

import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
@@ -37,7 +39,10 @@ public class ProjectAnalysisTest {
private final Project project = new Project("uuid", "key", "name");
private final Analysis analysis = new Analysis("analysis_uuid", 1_500L);
private final Branch branch = new Branch(true, "name", Branch.Type.SHORT);
private final QualityGate qualityGate = new QualityGate("id", "name", QualityGate.Status.WARN, emptySet());
private final EvaluatedQualityGate qualityGate = EvaluatedQualityGate.newBuilder()
.setQualityGate(new QualityGate("id", "name", emptySet()))
.setStatus(EvaluatedQualityGate.Status.WARN)
.build();
private final Map<String, String> properties = ImmutableMap.of("a", "b");
private ProjectAnalysis underTest = new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, properties);

@@ -101,7 +106,11 @@ public class ProjectAnalysisTest {
assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, null, qualityGate, 1L, properties));
assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, new Branch(false, "B", Branch.Type.SHORT), qualityGate, 1L, properties));
assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, null, 1L, properties));
assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, new QualityGate("A", "B", QualityGate.Status.WARN, emptySet()), 1L, properties));
EvaluatedQualityGate otherQualityGate = EvaluatedQualityGate.newBuilder()
.setQualityGate(new QualityGate("A", "B", emptySet()))
.setStatus(EvaluatedQualityGate.Status.WARN)
.build();
assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, otherQualityGate, 1L, properties));
assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, null, properties));
assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 2L, properties));
assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, emptyMap()));
@@ -123,17 +132,21 @@ public class ProjectAnalysisTest {
assertThat(underTest.hashCode())
.isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, new Branch(false, "B", Branch.Type.SHORT), qualityGate, 1L, properties).hashCode());
assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, null, 1L, properties).hashCode());
EvaluatedQualityGate otherQualityGate = EvaluatedQualityGate.newBuilder()
.setQualityGate(new QualityGate("A", "B", emptySet()))
.setStatus(EvaluatedQualityGate.Status.WARN)
.build();
assertThat(underTest.hashCode())
.isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, new QualityGate("A", "B", QualityGate.Status.WARN, emptySet()), 1L, properties).hashCode());
assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, null, properties).hashCode());
assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 2L, properties).hashCode());
assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, emptyMap()).hashCode());
assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, ImmutableMap.of("B", "C")).hashCode());
.isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, otherQualityGate, 1L, properties).hashCode());
assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, this.qualityGate, null, properties).hashCode());
assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, this.qualityGate, 2L, properties).hashCode());
assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, this.qualityGate, 1L, emptyMap()).hashCode());
assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, this.qualityGate, 1L, ImmutableMap.of("B", "C")).hashCode());
}

@Test
public void verify_toString() {
assertThat(underTest.toString()).isEqualTo(
"ProjectAnalysis{project=Project{uuid='uuid', key='key', name='name'}, ceTask=CeTask{id='id', status=SUCCESS}, branch=Branch{main=true, name='name', type=SHORT}, qualityGate=QualityGate{id='id', name='name', status=WARN, conditions=[]}, updatedAt=1, properties={a=b}, analysis=Analysis{uuid='analysis_uuid', date=1500}}");
"ProjectAnalysis{project=Project{uuid='uuid', key='key', name='name'}, ceTask=CeTask{id='id', status=SUCCESS}, branch=Branch{main=true, name='name', type=SHORT}, qualityGate=EvaluatedQualityGate{qualityGate=QualityGate{id=id, name='name', conditions=[]}, status=WARN, evaluatedConditions=[]}, updatedAt=1, properties={a=b}, analysis=Analysis{uuid='analysis_uuid', date=1500}}");
}
}

+ 0
- 122
server/sonar-server/src/test/java/org/sonar/server/webhook/QualityGateTest.java View File

@@ -1,122 +0,0 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info 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.webhook;

import java.util.Random;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import static java.util.Collections.singleton;
import static org.assertj.core.api.Assertions.assertThat;

public class QualityGateTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();

private final Random random = new Random();
private boolean onLeak = random.nextBoolean();
private QualityGate.EvaluationStatus randomEvaluationStatus = QualityGate.EvaluationStatus.values()[random.nextInt(QualityGate.EvaluationStatus.values().length)];
private QualityGate.Condition condition = new QualityGate.Condition(
randomEvaluationStatus, "k", QualityGate.Operator.GREATER_THAN, "l", "m", onLeak, "val");
private QualityGate.Status randomStatus = QualityGate.Status.values()[random.nextInt(QualityGate.Status.values().length)];
private QualityGate underTest = new QualityGate("i", "j", randomStatus, singleton(condition));

@Test
public void constructor_throws_NPE_if_id_is_null() {
expectedException.expect(NullPointerException.class);
expectedException.expectMessage("id can't be null");

new QualityGate(null, "j", QualityGate.Status.WARN, singleton(condition));
}

@Test
public void constructor_throws_NPE_if_name_is_null() {
expectedException.expect(NullPointerException.class);
expectedException.expectMessage("name can't be null");

new QualityGate("i", null, QualityGate.Status.WARN, singleton(condition));
}

@Test
public void constructor_throws_NPE_if_status_is_null() {
expectedException.expect(NullPointerException.class);
expectedException.expectMessage("status can't be null");

new QualityGate("i", "j", null, singleton(condition));
}

@Test
public void constructor_throws_NPE_if_conditions_is_null() {
expectedException.expect(NullPointerException.class);
expectedException.expectMessage("conditions can't be null");

new QualityGate("i", "j", QualityGate.Status.WARN, null);
}

@Test
public void condition_constructor_throws_NPE_if_evaluation_status_is_null() {
expectedException.expect(NullPointerException.class);
expectedException.expectMessage("status can't be null");

new QualityGate.Condition(null, "k", QualityGate.Operator.GREATER_THAN, "l", "m", onLeak, "val");
}

@Test
public void condition_constructor_throws_NPE_if_metricKey_is_null() {
expectedException.expect(NullPointerException.class);
expectedException.expectMessage("metricKey can't be null");

new QualityGate.Condition(randomEvaluationStatus, null, QualityGate.Operator.GREATER_THAN, "l", "m", onLeak, "val");
}

@Test
public void condition_constructor_throws_NPE_if_operator_status_is_null() {
expectedException.expect(NullPointerException.class);
expectedException.expectMessage("operator can't be null");

new QualityGate.Condition(randomEvaluationStatus, "k", null, "l", "m", onLeak, "val");
}

@Test
public void verify_getters() {
assertThat(underTest.getId()).isEqualTo("i");
assertThat(underTest.getName()).isEqualTo("j");
assertThat(underTest.getStatus()).isEqualTo(randomStatus);
assertThat(underTest.getConditions()).isEqualTo(singleton(condition));
}

@Test
public void verify_condition_getters() {
assertThat(condition.getStatus()).isEqualTo(randomEvaluationStatus);
assertThat(condition.getMetricKey()).isEqualTo("k");
assertThat(condition.getErrorThreshold()).contains("l");
assertThat(condition.getWarningThreshold()).contains("m");
assertThat(condition.isOnLeakPeriod()).isEqualTo(onLeak);
assertThat(condition.getValue()).contains("val");

QualityGate.Condition conditionWithNulls = new QualityGate.Condition(
randomEvaluationStatus, "k", QualityGate.Operator.GREATER_THAN, null, null, onLeak, null);

assertThat(conditionWithNulls.getErrorThreshold()).isEmpty();
assertThat(conditionWithNulls.getWarningThreshold()).isEmpty();
assertThat(conditionWithNulls.getValue()).isEmpty();
}
}

+ 22
- 7
server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookPayloadFactoryImplTest.java View File

@@ -26,6 +26,10 @@ import org.junit.Before;
import org.junit.Test;
import org.sonar.api.platform.Server;
import org.sonar.api.utils.System2;
import org.sonar.server.qualitygate.Condition;
import org.sonar.server.qualitygate.EvaluatedCondition;
import org.sonar.server.qualitygate.EvaluatedQualityGate;
import org.sonar.server.qualitygate.QualityGate;

import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
@@ -52,9 +56,12 @@ public class WebhookPayloadFactoryImplTest {
@Test
public void create_payload_for_successful_analysis() {
CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
QualityGate gate = new QualityGate("G1", "Gate One", QualityGate.Status.WARN, singleton(new QualityGate.Condition(
QualityGate.EvaluationStatus.WARN,
"coverage", QualityGate.Operator.GREATER_THAN, "70.0", "75.0", true, "74.0")));
Condition condition = new Condition("coverage", Condition.Operator.GREATER_THAN, "70.0", "75.0", true);
EvaluatedQualityGate gate = EvaluatedQualityGate.newBuilder()
.setQualityGate(new QualityGate("G1", "Gate One", singleton(condition)))
.setStatus(EvaluatedQualityGate.Status.WARN)
.addCondition(condition, EvaluatedCondition.EvaluationStatus.WARN, "74.0")
.build();
ProjectAnalysis analysis = newAnalysis(task, gate, null, 1_500_000_000_000L, emptyMap());

WebhookPayload payload = underTest.create(analysis);
@@ -94,8 +101,13 @@ public class WebhookPayloadFactoryImplTest {
@Test
public void create_payload_with_gate_conditions_without_value() {
CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
QualityGate gate = new QualityGate("G1", "Gate One", QualityGate.Status.WARN, singleton(
new QualityGate.Condition(QualityGate.EvaluationStatus.NO_VALUE, "coverage", QualityGate.Operator.GREATER_THAN, "70.0", "75.0", false, null)));

Condition condition = new Condition("coverage", Condition.Operator.GREATER_THAN, "70.0", "75.0", false);
EvaluatedQualityGate gate = EvaluatedQualityGate.newBuilder()
.setQualityGate(new QualityGate("G1", "Gate One", singleton(condition)))
.setStatus(EvaluatedQualityGate.Status.WARN)
.addCondition(condition, EvaluatedCondition.EvaluationStatus.NO_VALUE, null)
.build();
ProjectAnalysis analysis = newAnalysis(task, gate, null, 1_500_000_000_000L, emptyMap());

WebhookPayload payload = underTest.create(analysis);
@@ -132,7 +144,10 @@ public class WebhookPayloadFactoryImplTest {
@Test
public void create_payload_with_analysis_properties() {
CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
QualityGate gate = new QualityGate("G1", "Gate One", QualityGate.Status.WARN, emptySet());
EvaluatedQualityGate gate = EvaluatedQualityGate.newBuilder()
.setQualityGate(new QualityGate("G1", "Gate One", emptySet()))
.setStatus(EvaluatedQualityGate.Status.WARN)
.build();
Map<String, String> scannerProperties = ImmutableMap.of(
"sonar.analysis.revision", "ab45d24",
"sonar.analysis.buildNumber", "B123",
@@ -287,7 +302,7 @@ public class WebhookPayloadFactoryImplTest {
"}");
}

private static ProjectAnalysis newAnalysis(@Nullable CeTask task, @Nullable QualityGate gate,
private static ProjectAnalysis newAnalysis(@Nullable CeTask task, @Nullable EvaluatedQualityGate gate,
@Nullable Branch branch, @Nullable Long analysisDate, Map<String, String> scannerProperties) {
return new ProjectAnalysis(new Project("P1_UUID", PROJECT_KEY, "Project One"), task, analysisDate == null ? null : new Analysis("A_UUID1", analysisDate), branch,
gate, analysisDate, scannerProperties);

Loading…
Cancel
Save