]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10247 add and populate QGChangeEvent#getPreviousStatus
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Wed, 17 Jan 2018 10:39:05 +0000 (11:39 +0100)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Tue, 23 Jan 2018 08:36:10 +0000 (09:36 +0100)
server/sonar-server/src/main/java/org/sonar/server/measure/live/LiveMeasureComputerImpl.java
server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEvent.java
server/sonar-server/src/test/java/org/sonar/server/measure/live/LiveMeasureComputerImplTest.java
server/sonar-server/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventTest.java
server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookQGChangeEventListenerTest.java

index 3f6f986c236ea1dd0e54bc786473d0670c46aad3..4bfc67be844356c7cf40a836615f3e15b5c1e075 100644 (file)
@@ -24,10 +24,14 @@ import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
+import javax.annotation.CheckForNull;
 import org.sonar.api.config.Configuration;
+import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Metric;
+import org.sonar.api.utils.log.Loggers;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.BranchDto;
@@ -109,9 +113,9 @@ public class LiveMeasureComputerImpl implements LiveMeasureComputer {
     DebtRatingGrid debtRatingGrid = new DebtRatingGrid(config);
 
     MeasureMatrix matrix = new MeasureMatrix(components, metricsPerId.values(), dbMeasures);
+    FormulaContextImpl context = new FormulaContextImpl(matrix, debtRatingGrid);
     components.forEach(c -> {
       IssueCounter issueCounter = new IssueCounter(dbClient.issueDao().selectIssueGroupsByBaseComponent(dbSession, c, beginningOfLeakPeriod.orElse(Long.MAX_VALUE)));
-      FormulaContextImpl context = new FormulaContextImpl(matrix, debtRatingGrid);
       for (IssueMetricFormula formula : formulaFactory.getFormulas()) {
         // exclude leak formulas when leak period is not defined
         if (beginningOfLeakPeriod.isPresent() || !formula.isOnLeak()) {
@@ -131,7 +135,26 @@ public class LiveMeasureComputerImpl implements LiveMeasureComputer {
     matrix.getChanged().forEach(m -> dbClient.liveMeasureDao().insertOrUpdate(dbSession, m, null));
     projectIndexer.commitAndIndex(dbSession, singleton(project), ProjectIndexer.Cause.MEASURE_CHANGE);
 
-    return Optional.of(new QGChangeEvent(project, branch, lastAnalysis.get(), config, () -> Optional.of(evaluatedQualityGate)));
+    Metric.Level previousStatus = loadPreviousStatus(project, matrix);
+    return Optional.of(
+      new QGChangeEvent(project, branch, lastAnalysis.get(), config, previousStatus, () -> Optional.of(evaluatedQualityGate)));
+  }
+
+  @CheckForNull
+  private static Metric.Level loadPreviousStatus(ComponentDto project, MeasureMatrix matrix) {
+    return matrix.getMeasure(project, CoreMetrics.ALERT_STATUS_KEY)
+      .map(liveMeasureDto -> liveMeasureDto.getTextValue())
+      .filter(Objects::nonNull)
+      .map(m -> {
+        try {
+          return Metric.Level.valueOf(m);
+        } catch (IllegalArgumentException e) {
+          Loggers.get(LiveMeasureComputerImpl.class)
+            .trace("Failed to parse value of metric '{}'", m, e);
+          return null;
+        }
+      })
+      .orElse(null);
   }
 
   private List<ComponentDto> loadTreeOfComponents(DbSession dbSession, List<ComponentDto> touchedComponents) {
index 7b8f6a6b0d4cd505e92d180beeb49b222112883f..d0ec324184461b2d8e48cccb8b9bbe322b1b5189 100644 (file)
@@ -21,8 +21,10 @@ package org.sonar.server.qualitygate.changeevent;
 
 import java.util.Optional;
 import java.util.function.Supplier;
+import javax.annotation.Nullable;
 import javax.annotation.concurrent.Immutable;
 import org.sonar.api.config.Configuration;
+import org.sonar.api.measures.Metric;
 import org.sonar.db.component.BranchDto;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.SnapshotDto;
@@ -36,14 +38,16 @@ public class QGChangeEvent {
   private final BranchDto branch;
   private final SnapshotDto analysis;
   private final Configuration projectConfiguration;
+  private final Metric.Level previousStatus;
   private final Supplier<Optional<EvaluatedQualityGate>> qualityGateSupplier;
 
   public QGChangeEvent(ComponentDto project, BranchDto branch, SnapshotDto analysis, Configuration projectConfiguration,
-    Supplier<Optional<EvaluatedQualityGate>> qualityGateSupplier) {
+    @Nullable Metric.Level previousStatus, Supplier<Optional<EvaluatedQualityGate>> qualityGateSupplier) {
     this.project = requireNonNull(project, "project can't be null");
     this.branch = requireNonNull(branch, "branch can't be null");
     this.analysis = requireNonNull(analysis, "analysis can't be null");
     this.projectConfiguration = requireNonNull(projectConfiguration, "projectConfiguration can't be null");
+    this.previousStatus = previousStatus;
     this.qualityGateSupplier = requireNonNull(qualityGateSupplier, "qualityGateSupplier can't be null");
   }
 
@@ -63,6 +67,10 @@ public class QGChangeEvent {
     return projectConfiguration;
   }
 
+  public Optional<Metric.Level> getPreviousStatus() {
+    return Optional.ofNullable(previousStatus);
+  }
+
   public Supplier<Optional<EvaluatedQualityGate>> getQualityGateSupplier() {
     return qualityGateSupplier;
   }
@@ -74,6 +82,7 @@ public class QGChangeEvent {
       ", branch=" + toString(branch) +
       ", analysis=" + toString(analysis) +
       ", projectConfiguration=" + projectConfiguration +
+      ", previousStatus=" + previousStatus +
       ", qualityGateSupplier=" + qualityGateSupplier +
       '}';
   }
index be76582d739ffebecf763638d2b5e01658704657..c93037ddc445bdaa388e781c6b7abd7c9e9b10dd 100644 (file)
  */
 package org.sonar.server.measure.live;
 
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.Optional;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
-import org.mockito.Mockito;
+import org.junit.runner.RunWith;
 import org.sonar.api.config.PropertyDefinitions;
 import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Metric;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.core.config.CorePropertyDefinitions;
+import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchDto;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTesting;
 import org.sonar.db.measure.LiveMeasureDto;
 import org.sonar.db.metric.MetricDto;
+import org.sonar.db.organization.OrganizationDto;
 import org.sonar.server.computation.task.projectanalysis.qualitymodel.Rating;
 import org.sonar.server.es.ProjectIndexer;
 import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.qualitygate.EvaluatedQualityGate;
+import org.sonar.server.qualitygate.QualityGate;
 import org.sonar.server.qualitygate.changeevent.QGChangeEvent;
 import org.sonar.server.settings.ProjectConfigurationLoader;
 import org.sonar.server.settings.TestProjectConfigurationLoader;
@@ -49,9 +60,15 @@ import static java.util.Arrays.asList;
 import static java.util.Collections.emptyList;
 import static java.util.Collections.singleton;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.same;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 import static org.sonar.api.resources.Qualifiers.ORDERED_BOTTOM_UP;
 
+@RunWith(DataProviderRunner.class)
 public class LiveMeasureComputerImplTest {
 
   @Rule
@@ -62,23 +79,30 @@ public class LiveMeasureComputerImplTest {
   private TestProjectIndexers projectIndexer = new TestProjectIndexers();
   private MetricDto intMetric;
   private MetricDto ratingMetric;
+  private MetricDto alertStatusMetric;
+  private OrganizationDto organization;
   private ComponentDto project;
   private ComponentDto dir;
   private ComponentDto file1;
   private ComponentDto file2;
+  private LiveQualityGateComputer qGateComputer = mock(LiveQualityGateComputer.class);
+  private QualityGate qualityGate = mock(QualityGate.class);
+  private EvaluatedQualityGate newQualityGate = mock(EvaluatedQualityGate.class);
 
   @Before
   public void setUp() throws Exception {
     intMetric = db.measures().insertMetric(m -> m.setValueType(Metric.ValueType.INT.name()));
     ratingMetric = db.measures().insertMetric(m -> m.setValueType(Metric.ValueType.RATING.name()));
-    project = db.components().insertMainBranch();
+    alertStatusMetric = db.measures().insertMetric(m -> m.setKey(CoreMetrics.ALERT_STATUS_KEY));
+    organization = db.organizations().insert();
+    project = db.components().insertMainBranch(organization);
     dir = db.components().insertComponent(ComponentTesting.newDirectory(project, "src/main/java"));
     file1 = db.components().insertComponent(ComponentTesting.newFileDto(project, dir));
     file2 = db.components().insertComponent(ComponentTesting.newFileDto(project, dir));
   }
 
   @Test
-  public void compute_and_insert_measures_if_they_dont_exist_yet() {
+  public void compute_and_insert_measures_if_they_do_not_exist_yet() {
     markProjectAsAnalyzed(project);
 
     List<QGChangeEvent> result = run(asList(file1, file2), newQualifierBasedIntFormula(), newRatingConstantFormula(Rating.C));
@@ -171,11 +195,11 @@ public class LiveMeasureComputerImplTest {
   public void refresh_leak_measures() {
     markProjectAsAnalyzed(project);
     db.measures().insertLiveMeasure(project, intMetric, m -> m.setVariation(42.0).setValue(null));
-    db.measures().insertLiveMeasure(project, ratingMetric, m -> m.setVariation((double)Rating.E.getIndex()));
+    db.measures().insertLiveMeasure(project, ratingMetric, m -> m.setVariation((double) Rating.E.getIndex()));
     db.measures().insertLiveMeasure(dir, intMetric, m -> m.setVariation(42.0).setValue(null));
-    db.measures().insertLiveMeasure(dir, ratingMetric, m -> m.setVariation((double)Rating.D.getIndex()));
+    db.measures().insertLiveMeasure(dir, ratingMetric, m -> m.setVariation((double) Rating.D.getIndex()));
     db.measures().insertLiveMeasure(file1, intMetric, m -> m.setVariation(42.0).setValue(null));
-    db.measures().insertLiveMeasure(file1, ratingMetric, m -> m.setVariation((double)Rating.C.getIndex()));
+    db.measures().insertLiveMeasure(file1, ratingMetric, m -> m.setVariation((double) Rating.C.getIndex()));
 
     // generates values 1, 2, 3 on leak measures
     List<QGChangeEvent> result = run(file1, newQualifierBasedIntLeakFormula(), newRatingLeakFormula(Rating.B));
@@ -193,7 +217,7 @@ public class LiveMeasureComputerImplTest {
   }
 
   @Test
-  public void do_nothing_if_project_has_not_being_analyzed() {
+  public void do_nothing_if_project_has_not_been_analyzed() {
     // project has no snapshots
     List<QGChangeEvent> result = run(file1, newIncrementalFormula());
 
@@ -236,8 +260,90 @@ public class LiveMeasureComputerImplTest {
   }
 
   @Test
-  public void compute_quality_gate_status() {
-    // FIXME
+  public void event_contains_no_previousStatus_if_measure_does_not_exist() {
+    markProjectAsAnalyzed(project);
+
+    List<QGChangeEvent> result = run(file1);
+
+    assertThat(result)
+      .extracting(QGChangeEvent::getPreviousStatus)
+      .containsExactly(Optional.empty());
+  }
+
+  @Test
+  public void event_contains_no_previousStatus_if_measure_exists_and_has_no_value() {
+    markProjectAsAnalyzed(project);
+    db.measures().insertLiveMeasure(project, alertStatusMetric, m -> m.setData((String) null));
+
+    List<QGChangeEvent> result = run(file1);
+
+    assertThat(result)
+      .extracting(QGChangeEvent::getPreviousStatus)
+      .containsExactly(Optional.empty());
+  }
+
+  @Test
+  public void event_contains_no_previousStatus_if_measure_exists_and_is_empty() {
+    markProjectAsAnalyzed(project);
+    db.measures().insertLiveMeasure(project, alertStatusMetric, m -> m.setData(""));
+
+    List<QGChangeEvent> result = run(file1);
+
+    assertThat(result)
+      .extracting(QGChangeEvent::getPreviousStatus)
+      .containsExactly(Optional.empty());
+  }
+
+  @Test
+  public void event_contains_no_previousStatus_if_measure_exists_and_is_not_a_level() {
+    markProjectAsAnalyzed(project);
+    db.measures().insertLiveMeasure(project, alertStatusMetric, m -> m.setData("fooBar"));
+
+    List<QGChangeEvent> result = run(file1);
+
+    assertThat(result)
+      .extracting(QGChangeEvent::getPreviousStatus)
+      .containsExactly(Optional.empty());
+  }
+
+  @Test
+  @UseDataProvider("metricLevels")
+  public void event_contains_previousStatus_if_measure_exists(Metric.Level level) {
+    markProjectAsAnalyzed(project);
+    db.measures().insertLiveMeasure(project, alertStatusMetric, m -> m.setData(level.name()));
+    db.measures().insertLiveMeasure(project, intMetric, m -> m.setVariation(42.0).setValue(null));
+
+    List<QGChangeEvent> result = run(file1, newQualifierBasedIntLeakFormula());
+
+    assertThat(result)
+      .extracting(QGChangeEvent::getPreviousStatus)
+      .containsExactly(Optional.of(level));
+  }
+
+  @DataProvider
+  public static Object[][] metricLevels() {
+    return Arrays.stream(Metric.Level.values())
+      .map(l -> new Object[] {l})
+      .toArray(Object[][]::new);
+  }
+
+  @Test
+  public void event_contains_newQualityGate_computed_by_LiveQualityGateComputer() {
+    markProjectAsAnalyzed(project);
+    db.measures().insertLiveMeasure(project, alertStatusMetric, m -> m.setData(Metric.Level.WARN.name()));
+    db.measures().insertLiveMeasure(project, intMetric, m -> m.setVariation(42.0).setValue(null));
+    BranchDto branch = db.getDbClient().branchDao().selectByKey(db.getSession(), project.projectUuid(), "master")
+      .orElseThrow(() -> new IllegalStateException("Can't find master branch"));
+
+    List<QGChangeEvent> result = run(file1, newQualifierBasedIntLeakFormula());
+
+    assertThat(result)
+      .extracting(QGChangeEvent::getQualityGateSupplier)
+      .extracting(Supplier::get)
+      .containsExactly(Optional.of(newQualityGate));
+    verify(qGateComputer).loadQualityGate(any(DbSession.class), eq(organization), eq(project), eq(branch));
+    verify(qGateComputer).getMetricsRelatedTo(qualityGate);
+    verify(qGateComputer).refreshGateStatus(eq(project), same(qualityGate), any(MeasureMatrix.class));
   }
 
   @Test
@@ -260,7 +366,11 @@ public class LiveMeasureComputerImplTest {
   private List<QGChangeEvent> run(Collection<ComponentDto> components, IssueMetricFormula... formulas) {
     IssueMetricFormulaFactory formulaFactory = new TestIssueMetricFormulaFactory(asList(formulas));
 
-    LiveQualityGateComputer qGateComputer = mock(LiveQualityGateComputer.class, Mockito.RETURNS_DEEP_STUBS);
+    when(qGateComputer.loadQualityGate(any(DbSession.class), any(OrganizationDto.class), any(ComponentDto.class), any(BranchDto.class)))
+      .thenReturn(qualityGate);
+    when(qGateComputer.getMetricsRelatedTo(qualityGate)).thenReturn(singleton(CoreMetrics.ALERT_STATUS_KEY));
+    when(qGateComputer.refreshGateStatus(eq(project), same(qualityGate), any(MeasureMatrix.class)))
+      .thenReturn(newQualityGate);
     MapSettings settings = new MapSettings(new PropertyDefinitions(CorePropertyDefinitions.all()));
     ProjectConfigurationLoader configurationLoader = new TestProjectConfigurationLoader(settings.asConfig());
 
@@ -332,14 +442,6 @@ public class LiveMeasureComputerImplTest {
     });
   }
 
-  private IssueMetricFormula newIncrementalLeakFormula() {
-    Metric metric = new Metric.Builder(intMetric.getKey(), intMetric.getShortName(), Metric.ValueType.valueOf(intMetric.getValueType())).create();
-    AtomicInteger counter = new AtomicInteger();
-    return new IssueMetricFormula(metric, true, (ctx, issues) -> {
-      ctx.setLeakValue((double) counter.incrementAndGet());
-    });
-  }
-
   private IssueMetricFormula newRatingLeakFormula(Rating rating) {
     Metric metric = new Metric.Builder(ratingMetric.getKey(), ratingMetric.getShortName(), Metric.ValueType.valueOf(ratingMetric.getValueType())).create();
     return new IssueMetricFormula(metric, true, (ctx, issues) -> {
index 7a28aa4f93fb1cdfdcd35339f1a559b474103a79..71ec3e6cb7993341ee504259224bfd9310596a02 100644 (file)
 package org.sonar.server.qualitygate.changeevent;
 
 import java.util.Optional;
+import java.util.Random;
 import java.util.function.Supplier;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.mockito.Mockito;
 import org.sonar.api.config.Configuration;
+import org.sonar.api.measures.Metric;
 import org.sonar.db.component.BranchDto;
 import org.sonar.db.component.BranchType;
 import org.sonar.db.component.ComponentDto;
@@ -50,6 +52,7 @@ public class QGChangeEventTest {
     .setUuid("pto")
     .setCreatedAt(8_999_999_765L);
   private Configuration configuration = Mockito.mock(Configuration.class);
+  private Metric.Level previousStatus = Metric.Level.values()[new Random().nextInt(Metric.Level.values().length)];
   private Supplier<Optional<EvaluatedQualityGate>> supplier = Optional::empty;
 
   @Test
@@ -57,7 +60,7 @@ public class QGChangeEventTest {
     expectedException.expect(NullPointerException.class);
     expectedException.expectMessage("project can't be null");
 
-    new QGChangeEvent(null, branch, analysis, configuration, supplier);
+    new QGChangeEvent(null, branch, analysis, configuration, previousStatus, supplier);
   }
 
   @Test
@@ -65,7 +68,7 @@ public class QGChangeEventTest {
     expectedException.expect(NullPointerException.class);
     expectedException.expectMessage("branch can't be null");
 
-    new QGChangeEvent(project, null, analysis, configuration, supplier);
+    new QGChangeEvent(project, null, analysis, configuration, previousStatus, supplier);
   }
 
   @Test
@@ -73,7 +76,7 @@ public class QGChangeEventTest {
     expectedException.expect(NullPointerException.class);
     expectedException.expectMessage("analysis can't be null");
 
-    new QGChangeEvent(project, branch, null, configuration, supplier);
+    new QGChangeEvent(project, branch, null, configuration, previousStatus, supplier);
   }
 
   @Test
@@ -81,7 +84,12 @@ public class QGChangeEventTest {
     expectedException.expect(NullPointerException.class);
     expectedException.expectMessage("projectConfiguration can't be null");
 
-    new QGChangeEvent(project, branch, analysis, null, supplier);
+    new QGChangeEvent(project, branch, analysis, null, previousStatus, supplier);
+  }
+
+  @Test
+  public void constructor_does_not_fail_with_NPE_if_previousStatus_is_null() {
+    new QGChangeEvent(project, branch, analysis, configuration, null, supplier);
   }
 
   @Test
@@ -89,26 +97,36 @@ public class QGChangeEventTest {
     expectedException.expect(NullPointerException.class);
     expectedException.expectMessage("qualityGateSupplier can't be null");
 
-    new QGChangeEvent(project, branch, analysis, configuration, null);
+    new QGChangeEvent(project, branch, analysis, configuration, previousStatus, null);
   }
 
   @Test
   public void verify_getters() {
-    QGChangeEvent underTest = new QGChangeEvent(project, branch, analysis, configuration, supplier);
+    QGChangeEvent underTest = new QGChangeEvent(project, branch, analysis, configuration, previousStatus, supplier);
 
     assertThat(underTest.getProject()).isSameAs(project);
     assertThat(underTest.getBranch()).isSameAs(branch);
     assertThat(underTest.getAnalysis()).isSameAs(analysis);
     assertThat(underTest.getProjectConfiguration()).isSameAs(configuration);
+    assertThat(underTest.getPreviousStatus()).contains(previousStatus);
     assertThat(underTest.getQualityGateSupplier()).isSameAs(supplier);
   }
 
+  @Test
+  public void getPreviousStatus_returns_empty_when_previousStatus_is_null() {
+    QGChangeEvent underTest = new QGChangeEvent(project, branch, analysis, configuration, previousStatus, supplier);
+
+    assertThat(underTest.getPreviousStatus()).contains(previousStatus);
+  }
+
   @Test
   public void overrides_toString() {
-    QGChangeEvent underTest = new QGChangeEvent(project, branch, analysis, configuration, supplier);
+    QGChangeEvent underTest = new QGChangeEvent(project, branch, analysis, configuration, previousStatus, supplier);
 
     assertThat(underTest.toString())
-      .isEqualTo("QGChangeEvent{project=bar:foo, branch=SHORT:bar:doh:zop, analysis=pto:8999999765, projectConfiguration=" + configuration.toString() +
+      .isEqualTo("QGChangeEvent{project=bar:foo, branch=SHORT:bar:doh:zop, analysis=pto:8999999765" +
+        ", projectConfiguration=" + configuration.toString() +
+        ", previousStatus=" + previousStatus +
         ", qualityGateSupplier=" + supplier + "}");
 
   }
index 1f0bb7d2e7f5de2641465559f84a15d0870a9d19..747e5c802ffdf9e6f061a4ca025c8cae6dadfc28 100644 (file)
@@ -23,6 +23,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Random;
 import java.util.Set;
 import java.util.function.Supplier;
 import javax.annotation.Nullable;
@@ -55,7 +56,6 @@ 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.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
@@ -88,7 +88,8 @@ public class WebhookQGChangeEventListenerTest {
   public void onIssueChanges_has_no_effect_if_no_webhook_is_configured() {
     Configuration configuration1 = mock(Configuration.class);
     mockWebhookDisabled(configuration1);
-    QGChangeEvent qualityGateEvent = new QGChangeEvent(new ComponentDto(), new BranchDto(), new SnapshotDto(), configuration1, Optional::empty);
+    Metric.Level previousStatus = Metric.Level.values()[new Random().nextInt(Metric.Level.values().length)];
+    QGChangeEvent qualityGateEvent = new QGChangeEvent(new ComponentDto(), new BranchDto(), new SnapshotDto(), configuration1, previousStatus, Optional::empty);
 
     mockedUnderTest.onIssueChanges(qualityGateEvent, CHANGED_ISSUES_ARE_IGNORED);
 
@@ -214,28 +215,12 @@ public class WebhookQGChangeEventListenerTest {
       any(Supplier.class));
   }
 
-  private void verifyWebhookNotCalled(ComponentAndBranch componentAndBranch, SnapshotDto analysis, Configuration branchConfiguration) {
-    verify(webHooks).isEnabled(branchConfiguration);
-    verify(webHooks, times(0)).sendProjectAnalysisUpdate(
-      Matchers.same(branchConfiguration),
-      Matchers.eq(new WebHooks.Analysis(componentAndBranch.uuid(), analysis.getUuid(), null)),
-      any(Supplier.class));
-  }
-
   private List<ProjectAnalysis> extractPayloadFactoryArguments(int time) {
     ArgumentCaptor<ProjectAnalysis> projectAnalysisCaptor = ArgumentCaptor.forClass(ProjectAnalysis.class);
     verify(webhookPayloadFactory, Mockito.times(time)).create(projectAnalysisCaptor.capture());
     return projectAnalysisCaptor.getAllValues();
   }
 
-  private ComponentAndBranch insertPrivateBranch(OrganizationDto organization, BranchType branchType) {
-    ComponentDto project = dbTester.components().insertPrivateProject(organization);
-    BranchDto branchDto = newBranchDto(project.projectUuid(), branchType)
-      .setKey("foo");
-    ComponentDto newComponent = dbTester.components().insertProjectBranch(project, branchDto);
-    return new ComponentAndBranch(newComponent, branchDto);
-  }
-
   public ComponentAndBranch insertMainBranch(OrganizationDto organization) {
     ComponentDto project = newPrivateProjectDto(organization);
     BranchDto branch = newBranchDto(project, LONG).setKey("master");
@@ -276,7 +261,8 @@ public class WebhookQGChangeEventListenerTest {
   }
 
   private static QGChangeEvent newQGChangeEvent(ComponentAndBranch branch, SnapshotDto analysis, Configuration configuration, @Nullable EvaluatedQualityGate evaluatedQualityGate) {
-    return new QGChangeEvent(branch.component, branch.branch, analysis, configuration, () -> Optional.ofNullable(evaluatedQualityGate));
+    Metric.Level previousStatus = Metric.Level.values()[new Random().nextInt(Metric.Level.values().length)];
+    return new QGChangeEvent(branch.component, branch.branch, analysis, configuration, previousStatus, () -> Optional.ofNullable(evaluatedQualityGate));
   }
 
 }