public interface QualityGateService {
- long SHORT_LIVING_BRANCHES_QUALITY_GATE = -1_963_456_987L;
-
/**
* Retrieve the {@link QualityGate} from the database with the specified id, it it exists.
*/
package org.sonar.server.computation.task.projectanalysis.qualitygate;
import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableList;
import java.util.Collection;
import java.util.Objects;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.qualitygate.QualityGateConditionDto;
import org.sonar.db.qualitygate.QualityGateDto;
import org.sonar.server.computation.task.projectanalysis.metric.MetricRepository;
+import org.sonar.server.qualitygate.ShortLivingBranchQualityGate;
-import static org.sonar.db.qualitygate.QualityGateConditionDto.OPERATOR_GREATER_THAN;
+import static org.sonar.core.util.stream.MoreCollectors.toList;
public class QualityGateServiceImpl implements QualityGateService {
@Override
public Optional<QualityGate> findById(long id) {
- if (id == SHORT_LIVING_BRANCHES_QUALITY_GATE) {
+ if (id == ShortLivingBranchQualityGate.ID) {
return Optional.of(buildShortLivingBranchHardcodedQualityGate());
}
try (DbSession dbSession = dbClient.openSession(false)) {
.map(metric -> new Condition(metric, input.getOperator(), input.getErrorThreshold(), input.getWarningThreshold(), input.getPeriod() != null))
.orElse(null))
.filter(Objects::nonNull)
- .collect(MoreCollectors.toList(dtos.size()));
+ .collect(toList(dtos.size()));
return new QualityGate(qualityGateDto.getId(), qualityGateDto.getName(), conditions);
}
private QualityGate buildShortLivingBranchHardcodedQualityGate() {
return new QualityGate(
- SHORT_LIVING_BRANCHES_QUALITY_GATE,
- "Hardcoded short living branch quality gate",
- ImmutableList.of(
- new Condition(metricRepository.getByKey(CoreMetrics.NEW_BUGS_KEY), OPERATOR_GREATER_THAN, "0", null, true),
- new Condition(metricRepository.getByKey(CoreMetrics.NEW_VULNERABILITIES_KEY), OPERATOR_GREATER_THAN, "0", null, true),
- new Condition(metricRepository.getByKey(CoreMetrics.NEW_CODE_SMELLS_KEY), OPERATOR_GREATER_THAN, "0", null, true)));
+ ShortLivingBranchQualityGate.ID,
+ ShortLivingBranchQualityGate.NAME,
+ ShortLivingBranchQualityGate.CONDITIONS.stream()
+ .map(c -> new Condition(metricRepository.getByKey(c.getMetricKey()), c.getOperator(), c.getErrorThreshold(), c.getWarnThreshold(), c.isOnLeak()))
+ .collect(toList(ShortLivingBranchQualityGate.CONDITIONS.size())));
}
}
import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGate;
import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGateService;
import org.sonar.server.computation.task.step.ComputationStep;
+import org.sonar.server.qualitygate.ShortLivingBranchQualityGate;
import static org.apache.commons.lang.StringUtils.isBlank;
@Override
public void execute() {
if (analysisMetadataHolder.isShortLivingBranch()) {
- Optional<QualityGate> qualityGate = qualityGateService.findById(QualityGateService.SHORT_LIVING_BRANCHES_QUALITY_GATE);
+ Optional<QualityGate> qualityGate = qualityGateService.findById(ShortLivingBranchQualityGate.ID);
if (qualityGate.isPresent()) {
qualityGateHolder.setQualityGate(qualityGate.get());
} else {
--- /dev/null
+/*
+ * 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.qualitygate;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import javax.annotation.CheckForNull;
+import org.sonar.api.measures.CoreMetrics;
+
+import static org.sonar.db.qualitygate.QualityGateConditionDto.OPERATOR_GREATER_THAN;
+
+/**
+ * Offers constants describing the Hardcoded Quality Gate for short living branches.
+ */
+public final class ShortLivingBranchQualityGate {
+ public static final long ID = -1_963_456_987L;
+ public static final String NAME = "Hardcoded short living branch quality gate";
+ public static final List<Condition> CONDITIONS = ImmutableList.of(
+ new Condition(CoreMetrics.BUGS_KEY, OPERATOR_GREATER_THAN, "0", false),
+ new Condition(CoreMetrics.VULNERABILITIES_KEY, OPERATOR_GREATER_THAN, "0", false),
+ new Condition(CoreMetrics.CODE_SMELLS_KEY, OPERATOR_GREATER_THAN, "0", false));
+
+ private ShortLivingBranchQualityGate() {
+ // prevents instantiation
+ }
+
+ public static final class Condition {
+ private final String metricKey;
+ private final String operator;
+ private final String errorThreshold;
+ private final boolean onLeak;
+
+ public Condition(String metricKey, String operator, String errorThreshold, boolean onLeak) {
+ this.metricKey = metricKey;
+ this.operator = operator;
+ this.errorThreshold = errorThreshold;
+ this.onLeak = onLeak;
+ }
+
+ public String getMetricKey() {
+ return metricKey;
+ }
+
+ public String getOperator() {
+ return operator;
+ }
+
+ public String getErrorThreshold() {
+ return errorThreshold;
+ }
+
+ @CheckForNull
+ public String getWarnThreshold() {
+ return null;
+ }
+
+ public boolean isOnLeak() {
+ return onLeak;
+ }
+ }
+}
import org.sonar.server.computation.task.projectanalysis.metric.Metric;
import org.sonar.server.computation.task.projectanalysis.metric.MetricImpl;
import org.sonar.server.computation.task.projectanalysis.metric.MetricRepository;
+import org.sonar.server.qualitygate.ShortLivingBranchQualityGate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
@Test
public void findById_of_hardcoded_short_living_branch_returns_hardcoded_qp() {
- MetricImpl newBugsMetric = mockMetricInRepository(CoreMetrics.NEW_BUGS_KEY);
- MetricImpl newVulnerabilitiesMetric = mockMetricInRepository(CoreMetrics.NEW_VULNERABILITIES_KEY);
- MetricImpl newCodeSmellsMetric = mockMetricInRepository(CoreMetrics.NEW_CODE_SMELLS_KEY);
+ MetricImpl bugsMetric = mockMetricInRepository(CoreMetrics.BUGS_KEY);
+ MetricImpl vulnerabilitiesMetric = mockMetricInRepository(CoreMetrics.VULNERABILITIES_KEY);
+ MetricImpl codeSmellsMetric = mockMetricInRepository(CoreMetrics.CODE_SMELLS_KEY);
- Optional<QualityGate> res = underTest.findById(QualityGateService.SHORT_LIVING_BRANCHES_QUALITY_GATE);
+ Optional<QualityGate> res = underTest.findById(ShortLivingBranchQualityGate.ID);
assertThat(res).isPresent();
QualityGate qualityGate = res.get();
- assertThat(qualityGate.getId()).isEqualTo(QualityGateService.SHORT_LIVING_BRANCHES_QUALITY_GATE);
+ assertThat(qualityGate.getId()).isEqualTo(ShortLivingBranchQualityGate.ID);
assertThat(qualityGate.getName()).isEqualTo("Hardcoded short living branch quality gate");
assertThat(qualityGate.getConditions())
.extracting(Condition::getMetric, Condition::getOperator, Condition::getErrorThreshold, Condition::getWarningThreshold, Condition::hasPeriod)
.containsOnly(
- tuple(newBugsMetric, GREATER_THAN, "0", null, true),
- tuple(newVulnerabilitiesMetric, GREATER_THAN, "0", null, true),
- tuple(newCodeSmellsMetric, GREATER_THAN, "0", null, true));
+ tuple(bugsMetric, GREATER_THAN, "0", null, false),
+ tuple(vulnerabilitiesMetric, GREATER_THAN, "0", null, false),
+ tuple(codeSmellsMetric, GREATER_THAN, "0", null, false));
}
private MetricImpl mockMetricInRepository(String metricKey) {
import org.sonar.server.computation.task.projectanalysis.qualitygate.MutableQualityGateHolderRule;
import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGate;
import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGateService;
+import org.sonar.server.qualitygate.ShortLivingBranchQualityGate;
import static java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat;
public void add_hardcoded_QG_on_short_living_branch() {
when(analysisMetadataHolder.isShortLivingBranch()).thenReturn(true);
QualityGate qualityGate = mock(QualityGate.class);
- when(qualityGateService.findById(QualityGateService.SHORT_LIVING_BRANCHES_QUALITY_GATE)).thenReturn(Optional.of(qualityGate));
+ when(qualityGateService.findById(ShortLivingBranchQualityGate.ID)).thenReturn(Optional.of(qualityGate));
underTest.execute();
*/
package org.sonar.server.computation.task.projectanalysis.webhook;
+import com.google.common.collect.ImmutableMap;
import java.util.Collections;
import java.util.Date;
+import java.util.Map;
import java.util.Random;
import java.util.function.Supplier;
import javax.annotation.Nullable;
-import org.apache.commons.lang.RandomStringUtils;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
+import org.sonar.api.ce.posttask.Branch;
import org.sonar.api.ce.posttask.CeTask;
import org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester;
import org.sonar.api.ce.posttask.Project;
import org.sonar.server.webhook.WebhookPayload;
import org.sonar.server.webhook.WebhookPayloadFactory;
-import static java.util.Collections.emptySet;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newBranchBuilder;
import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newCeTaskBuilder;
+import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newConditionBuilder;
import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newProjectBuilder;
import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newQualityGateBuilder;
import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newScannerContextBuilder;
public class WebhookPostTaskTest {
+ private final Random random = new Random();
private final Configuration configuration = mock(Configuration.class);
private final WebhookPayload webhookPayload = mock(WebhookPayload.class);
private final WebhookPayloadFactory payloadFactory = mock(WebhookPayloadFactory.class);
}
@Test
- public void call_webhooks() {
+ public void call_webhooks_when_no_analysis_not_qualitygate() {
callWebHooks(null, null);
+ }
+
+ @Test
+ public void call_webhooks_with_analysis_and_qualitygate() {
+ QualityGate.Condition condition = newConditionBuilder()
+ .setMetricKey(randomAlphanumeric(96))
+ .setOperator(QualityGate.Operator.values()[random.nextInt(QualityGate.Operator.values().length)])
+ .setErrorThreshold(randomAlphanumeric(22))
+ .setWarningThreshold(randomAlphanumeric(23))
+ .setOnLeakPeriod(random.nextBoolean())
+ .build(QualityGate.EvaluationStatus.OK, randomAlphanumeric(33));
QualityGate qualityGate = newQualityGateBuilder()
- .setName("empty")
- .setStatus(QualityGate.Status.OK)
- .setId("1")
+ .setId(randomAlphanumeric(23))
+ .setName(randomAlphanumeric(66))
+ .setStatus(QualityGate.Status.values()[random.nextInt(QualityGate.Status.values().length)])
+ .add(condition)
.build();
- callWebHooks(RandomStringUtils.randomAlphanumeric(40), qualityGate);
+
+ callWebHooks(randomAlphanumeric(40), qualityGate);
}
private void callWebHooks(@Nullable String analysisUUid, @Nullable QualityGate qualityGate) {
.setName(randomAlphanumeric(5))
.build();
CeTask ceTask = newCeTaskBuilder()
- .setStatus(CeTask.Status.values()[new Random().nextInt(CeTask.Status.values().length)])
+ .setStatus(CeTask.Status.values()[random.nextInt(CeTask.Status.values().length)])
.setId(randomAlphanumeric(6))
.build();
Date date = new Date();
+ Map<String, String> properties = ImmutableMap.of(randomAlphanumeric(17), randomAlphanumeric(18));
+ Branch branch = newBranchBuilder()
+ .setIsMain(random.nextBoolean())
+ .setType(Branch.Type.values()[random.nextInt(Branch.Type.values().length)])
+ .setName(randomAlphanumeric(29))
+ .build();
PostProjectAnalysisTaskTester.of(underTest)
.at(date)
.withCeTask(ceTask)
.withProject(project)
- .withScannerContext(newScannerContextBuilder().build())
+ .withBranch(branch)
+ .withQualityGate(qualityGate)
+ .withScannerContext(newScannerContextBuilder()
+ .addProperties(properties)
+ .build())
.withAnalysisUuid(analysisUUid)
.withQualityGate(qualityGate)
.execute();
org.sonar.server.webhook.QualityGate webQualityGate = null;
if (qualityGate != null) {
- org.sonar.server.webhook.QualityGate.Status status = org.sonar.server.webhook.QualityGate.Status.valueOf(qualityGate.getStatus().name());
- webQualityGate = new org.sonar.server.webhook.QualityGate(qualityGate.getId(), qualityGate.getName(), status, emptySet());
+ 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())));
}
verify(payloadFactory).create(new ProjectAnalysis(
- new org.sonar.server.webhook.Project(project.getUuid(), project.getKey(), project.getName()), new org.sonar.server.webhook.CeTask(ceTask.getId(),
- org.sonar.server.webhook.CeTask.Status.valueOf(ceTask.getStatus().name())),
+ new org.sonar.server.webhook.Project(project.getUuid(), project.getKey(), project.getName()),
+ new org.sonar.server.webhook.CeTask(ceTask.getId(), org.sonar.server.webhook.CeTask.Status.valueOf(ceTask.getStatus().name())),
analysisUUid == null ? null : new Analysis(analysisUUid, date.getTime()),
- null,
+ new org.sonar.server.webhook.Branch(branch.isMain(), branch.getName().get(), org.sonar.server.webhook.Branch.Type.valueOf(branch.getType().name())),
webQualityGate,
analysisUUid == null ? null : date.getTime(),
- Collections.emptyMap()));
+ properties));
}
}
--- /dev/null
+/*
+ * 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.qualitygate;
+
+import org.junit.Test;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.server.qualitygate.ShortLivingBranchQualityGate.Condition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+
+public class ShortLivingBranchQualityGateTest {
+ @Test
+ public void defines_3_conditions() {
+ assertThat(ShortLivingBranchQualityGate.CONDITIONS)
+ .extracting(Condition::getMetricKey, Condition::getOperator, Condition::getErrorThreshold, Condition::getWarnThreshold, Condition::isOnLeak)
+ .containsExactly(
+ tuple(CoreMetrics.BUGS_KEY, "GT", "0", null, false),
+ tuple(CoreMetrics.VULNERABILITIES_KEY, "GT", "0", null, false),
+ tuple(CoreMetrics.CODE_SMELLS_KEY, "GT", "0", null, false));
+ }
+
+}