Merge branch 'stable-3.1'

* stable-3.1:
  Only keep single metric for replication status
  Tidy up constants ordering in ProjectVersionRefUpdate
  Add missing serialVersionIds to exceptions in ProjectVersionRefUpdate
  Remove redundant else{} clause in ProjectVersionRefUpdate
  Skip refs/starred-changes from version tracking
  Silence warnings when version is not found on global refdb
  Fix NPE when the replicated refs is not found locally

Change-Id: I337d44a63cd6658fb6de08da173f0fc9562c74ad
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetrics.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetrics.java
index f0c14fe..d325fd8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetrics.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetrics.java
@@ -14,8 +14,6 @@
 
 package com.googlesource.gerrit.plugins.multisite.consumer;
 
-import com.codahale.metrics.MetricFilter;
-import com.codahale.metrics.MetricRegistry;
 import com.gerritforge.gerrit.eventbroker.EventMessage;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.metrics.Counter1;
@@ -27,6 +25,8 @@
 import com.googlesource.gerrit.plugins.multisite.MultiSiteMetrics;
 import com.googlesource.gerrit.plugins.multisite.validation.ProjectVersionRefUpdate;
 import com.googlesource.gerrit.plugins.replication.RefReplicatedEvent;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
@@ -37,27 +37,21 @@
   private static final String SUBSCRIBER_SUCCESS_COUNTER = "subscriber_msg_consumer_counter";
   private static final String SUBSCRIBER_FAILURE_COUNTER =
       "subscriber_msg_consumer_failure_counter";
-  private static final String PROJECT_REPLICATION_LAG_MS_PREFIX =
-      "multi_site/subscriber/subscriber_replication_status/ms_behind_for_";
+  private static final String REPLICATION_LAG_MS =
+      "multi_site/subscriber/subscriber_replication_status/ms_behind";
 
   private final Counter1<String> subscriberSuccessCounter;
   private final Counter1<String> subscriberFailureCounter;
 
-  private MetricRegistry metricRegistry;
-  private MetricMaker metricMaker;
   public Map<String, Long> replicationStatusPerProject = new HashMap<>();
 
   private ProjectVersionRefUpdate projectVersionRefUpdate;
 
   @Inject
   public SubscriberMetrics(
-      MetricMaker metricMaker,
-      MetricRegistry metricRegistry,
-      ProjectVersionRefUpdate projectVersionRefUpdate) {
+      MetricMaker metricMaker, ProjectVersionRefUpdate projectVersionRefUpdate) {
 
     this.projectVersionRefUpdate = projectVersionRefUpdate;
-    this.metricMaker = metricMaker;
-    this.metricRegistry = metricRegistry;
     this.subscriberSuccessCounter =
         metricMaker.newCounter(
             "multi_site/subscriber/subscriber_message_consumer_counter",
@@ -72,6 +66,17 @@
                 .setRate()
                 .setUnit("errors"),
             stringField(SUBSCRIBER_FAILURE_COUNTER, "Subscriber failed to consume messages count"));
+    metricMaker.newCallbackMetric(
+        REPLICATION_LAG_MS,
+        Long.class,
+        new Description("Replication lag (ms)").setGauge().setUnit(Description.Units.MILLISECONDS),
+        () -> {
+          Collection<Long> lags = replicationStatusPerProject.values();
+          if (lags.isEmpty()) {
+            return 0L;
+          }
+          return Collections.max(lags);
+        });
   }
 
   public void incrementSubscriberConsumedMessage() {
@@ -91,12 +96,11 @@
       Optional<Long> remoteVersion = projectVersionRefUpdate.getProjectRemoteVersion(projectName);
       Optional<Long> localVersion = projectVersionRefUpdate.getProjectLocalVersion(projectName);
       if (remoteVersion.isPresent() && localVersion.isPresent()) {
-        Long lag = remoteVersion.get() - localVersion.get();
+        long lag = remoteVersion.get() - localVersion.get();
         logger.atFine().log("Calculated lag for project '%s' [%d]", projectName, lag);
         replicationStatusPerProject.put(projectName, lag);
-        upsertMetricsForProject(projectName);
       } else {
-        logger.atWarning().log(
+        logger.atFine().log(
             "Didn't update metric for %s. Local [%b] or remote [%b] version is not defined",
             projectName, localVersion.isPresent(), remoteVersion.isPresent());
       }
@@ -104,20 +108,4 @@
       logger.atFine().log("Not a ref-replicated-event event [%s], skipping", event.type);
     }
   }
-
-  private void upsertMetricsForProject(String projectName) {
-    String metricName = PROJECT_REPLICATION_LAG_MS_PREFIX + projectName;
-    if (metricRegistry.getGauges(MetricFilter.contains(metricName)).isEmpty()) {
-      metricMaker.newCallbackMetric(
-          metricName,
-          Long.class,
-          new Description(String.format("%s replication lag (ms)", metricName))
-              .setGauge()
-              .setUnit(Description.Units.MILLISECONDS),
-          () -> replicationStatusPerProject.get(projectName));
-      logger.atFine().log("Added last replication timestamp callback metric for '%s'", projectName);
-    } else {
-      logger.atFine().log("Don't add metric since it already exists for project '%s'", projectName);
-    }
-  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdate.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdate.java
index c8ddc87..bcba594 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdate.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdate.java
@@ -23,6 +23,7 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.common.primitives.Ints;
 import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.server.events.Event;
 import com.google.gerrit.server.events.EventListener;
 import com.google.gerrit.server.events.RefUpdatedEvent;
@@ -32,7 +33,7 @@
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.multisite.SharedRefDatabaseWrapper;
 import com.googlesource.gerrit.plugins.multisite.forwarder.Context;
-import com.googlesource.gerrit.plugins.replication.RefReplicatedEvent;
+import com.googlesource.gerrit.plugins.replication.RefReplicationDoneEvent;
 import java.io.IOException;
 import java.util.Optional;
 import java.util.Set;
@@ -46,21 +47,21 @@
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.RemoteRefUpdate;
 
 @Singleton
 public class ProjectVersionRefUpdate implements EventListener {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-  public static final String MULTI_SITE_VERSIONING_REF = "refs/multi-site/version";
-  public static final String SEQUENCE_REF_PREFIX = "refs/sequences/";
-  private static final Ref NULL_PROJECT_VERSION_REF =
-      new ObjectIdRef.Unpeeled(Ref.Storage.NETWORK, MULTI_SITE_VERSIONING_REF, ObjectId.zeroId());
   private static final Set<RefUpdate.Result> SUCCESSFUL_RESULTS =
       ImmutableSet.of(RefUpdate.Result.NEW, RefUpdate.Result.FORCED, RefUpdate.Result.NO_CHANGE);
 
-  protected final SharedRefDatabaseWrapper sharedRefDb;
+  public static final String MULTI_SITE_VERSIONING_REF = "refs/multi-site/version";
+  public static final Ref NULL_PROJECT_VERSION_REF =
+      new ObjectIdRef.Unpeeled(Ref.Storage.NETWORK, MULTI_SITE_VERSIONING_REF, ObjectId.zeroId());
+
   private final GitRepositoryManager gitRepositoryManager;
 
+  protected final SharedRefDatabaseWrapper sharedRefDb;
+
   @Inject
   public ProjectVersionRefUpdate(
       GitRepositoryManager gitRepositoryManager, SharedRefDatabaseWrapper sharedRefDb) {
@@ -76,46 +77,57 @@
       updateProducerProjectVersionUpdate((RefUpdatedEvent) event);
     }
 
-    // Consumers of the Event use RefReplicatedEvent to trigger the version update
-    if (Context.isForwardedEvent() && event instanceof RefReplicatedEvent) {
-      updateConsumerProjectVersion((RefReplicatedEvent) event);
+    // Consumers of the Event use RefReplicationDoneEvent to trigger the version update
+    if (Context.isForwardedEvent() && event instanceof RefReplicationDoneEvent) {
+      updateConsumerProjectVersion((RefReplicationDoneEvent) event);
     }
   }
 
-  private void updateConsumerProjectVersion(RefReplicatedEvent refReplicatedEvent) {
-    Project.NameKey projectNameKey = refReplicatedEvent.getProjectNameKey();
-    if (!refReplicatedEvent.refStatus.equals(RemoteRefUpdate.Status.OK)) {
+  private void updateConsumerProjectVersion(RefReplicationDoneEvent refReplicationDoneEvent) {
+    Project.NameKey projectNameKey = refReplicationDoneEvent.getProjectNameKey();
+    String refName = refReplicationDoneEvent.getRefName();
+
+    if (isSpecialRefName(refName)) {
       logger.atFine().log(
-          String.format(
-              "Skipping version update for %s. RefReplicatedEvent failed with %s",
-              projectNameKey.get(), refReplicatedEvent.refStatus));
-      return;
-    }
-    if (refReplicatedEvent.getRefName().startsWith(SEQUENCE_REF_PREFIX)) {
-      logger.atFine().log("Found Sequence ref, skipping update for " + projectNameKey.get());
+          "Found a special ref name %s, skipping update for %s", refName, projectNameKey.get());
       return;
     }
     try {
-      updateLocalProjectVersion(projectNameKey, refReplicatedEvent.getRefName());
+      updateLocalProjectVersion(projectNameKey, refReplicationDoneEvent.getRefName());
     } catch (LocalProjectVersionUpdateException e) {
       logger.atSevere().withCause(e).log(
           "Issue encountered when updating version for project " + projectNameKey);
     }
   }
 
+  private boolean isSpecialRefName(String refName) {
+    return refName.startsWith(RefNames.REFS_SEQUENCES)
+        || refName.startsWith(RefNames.REFS_STARRED_CHANGES);
+  }
+
   private void updateProducerProjectVersionUpdate(RefUpdatedEvent refUpdatedEvent) {
-    if (refUpdatedEvent.getRefName().startsWith(SEQUENCE_REF_PREFIX)) {
+    String refName = refUpdatedEvent.getRefName();
+
+    if (isSpecialRefName(refName)) {
       logger.atFine().log(
-          "Found Sequence ref, skipping update for " + refUpdatedEvent.getProjectNameKey().get());
+          "Found a special ref name %s, skipping update for %s",
+          refName, refUpdatedEvent.getProjectNameKey().get());
       return;
     }
     try {
       Project.NameKey projectNameKey = refUpdatedEvent.getProjectNameKey();
       Ref currentProjectVersionRef = getLocalProjectVersionRef(refUpdatedEvent.getProjectNameKey());
-      ObjectId newProjectVersionObjectId =
+      Optional<ObjectId> newProjectVersionObjectId =
           updateLocalProjectVersion(projectNameKey, refUpdatedEvent.getRefName());
-      updateSharedProjectVersion(
-          projectNameKey, currentProjectVersionRef, newProjectVersionObjectId);
+
+      if (newProjectVersionObjectId.isPresent()) {
+        updateSharedProjectVersion(
+            projectNameKey, currentProjectVersionRef, newProjectVersionObjectId.get());
+      } else {
+        logger.atWarning().log(
+            "Ref %s not found on projet %s: skipping project version update",
+            refUpdatedEvent.getRefName(), projectNameKey);
+      }
     } catch (LocalProjectVersionUpdateException | SharedProjectVersionUpdateException e) {
       logger.atSevere().withCause(e).log(
           "Issue encountered when updating version for project "
@@ -151,16 +163,20 @@
     }
   }
 
-  private Long getLastRefUpdatedTimestamp(Project.NameKey projectNameKey, String refName)
+  private Optional<Long> getLastRefUpdatedTimestamp(Project.NameKey projectNameKey, String refName)
       throws LocalProjectVersionUpdateException {
     logger.atFine().log(
         String.format(
             "Getting last ref updated time for project %s, ref %s", projectNameKey.get(), refName));
     try (Repository repository = gitRepositoryManager.openRepository(projectNameKey)) {
       Ref ref = repository.findRef(refName);
+      if (ref == null) {
+        logger.atWarning().log("Unable to find ref " + refName + " in project " + projectNameKey);
+        return Optional.empty();
+      }
       try (RevWalk walk = new RevWalk(repository)) {
         RevCommit commit = walk.parseCommit(ref.getObjectId());
-        return Integer.toUnsignedLong(commit.getCommitTime());
+        return Optional.of(Integer.toUnsignedLong(commit.getCommitTime()));
       }
     } catch (IOException ioe) {
       String message =
@@ -208,7 +224,7 @@
       Optional<IntBlob> blob = IntBlob.parse(repository, MULTI_SITE_VERSIONING_REF);
       if (blob.isPresent()) {
         Long repoVersion = Integer.toUnsignedLong(blob.get().value());
-        logger.atInfo().log("Local project '%s' has version %d", projectName, repoVersion);
+        logger.atFine().log("Local project '%s' has version %d", projectName, repoVersion);
         return Optional.of(repoVersion);
       }
     } catch (IOException e) {
@@ -223,10 +239,9 @@
             Project.NameKey.parse(projectName), MULTI_SITE_VERSIONING_REF, ObjectId.class);
     if (remoteObjectId.isPresent()) {
       return getLongFromObjectId(projectName, remoteObjectId.get());
-    } else {
-      logger.atSevere().log("Didn't find remote version for %s", projectName);
-      return Optional.empty();
     }
+    logger.atFine().log("Didn't find remote version for %s", projectName);
+    return Optional.empty();
   }
 
   private Optional<Long> getLongFromObjectId(String projectName, ObjectId objectId) {
@@ -252,22 +267,26 @@
     }
   }
 
-  private ObjectId updateLocalProjectVersion(Project.NameKey projectNameKey, String refName)
-      throws LocalProjectVersionUpdateException {
-    Long lastRefUpdatedTimestamp = getLastRefUpdatedTimestamp(projectNameKey, refName);
+  private Optional<ObjectId> updateLocalProjectVersion(
+      Project.NameKey projectNameKey, String refName) throws LocalProjectVersionUpdateException {
+    Optional<Long> lastRefUpdatedTimestamp = getLastRefUpdatedTimestamp(projectNameKey, refName);
+    if (!lastRefUpdatedTimestamp.isPresent()) {
+      return Optional.empty();
+    }
+
     logger.atFine().log("Updating local version for project " + projectNameKey.get());
     try (Repository repository = gitRepositoryManager.openRepository(projectNameKey)) {
-      RefUpdate refUpdate = getProjectVersionRefUpdate(repository, lastRefUpdatedTimestamp);
+      RefUpdate refUpdate = getProjectVersionRefUpdate(repository, lastRefUpdatedTimestamp.get());
       RefUpdate.Result result = refUpdate.update();
       if (!isSuccessful(result)) {
         String message =
             String.format(
                 "RefUpdate failed with result %s for: project=%s, version=%d",
-                result.name(), projectNameKey.get(), lastRefUpdatedTimestamp);
+                result.name(), projectNameKey.get(), lastRefUpdatedTimestamp.get());
         logger.atSevere().log(message);
         throw new LocalProjectVersionUpdateException(message);
       }
-      return refUpdate.getNewObjectId();
+      return Optional.of(refUpdate.getNewObjectId());
     } catch (IOException e) {
       String message = "Cannot create versioning command for " + projectNameKey.get();
       logger.atSevere().withCause(e).log(message);
@@ -280,12 +299,16 @@
   }
 
   public static class LocalProjectVersionUpdateException extends Exception {
+    private static final long serialVersionUID = 7649956232401457023L;
+
     public LocalProjectVersionUpdateException(String projectName) {
       super("Cannot update local project version of " + projectName);
     }
   }
 
   public static class SharedProjectVersionUpdateException extends Exception {
+    private static final long serialVersionUID = -9153858177700286314L;
+
     public SharedProjectVersionUpdateException(String projectName) {
       super("Cannot update shared project version of " + projectName);
     }
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md
index c1135bb..5ac3dec 100644
--- a/src/main/resources/Documentation/about.md
+++ b/src/main/resources/Documentation/about.md
@@ -93,6 +93,6 @@
 
 `metric=plugins/multi-site/multi_site/subscriber/subscriber_message_consumer_failure_counter/subscriber_msg_consumer_poll_failure_counter, type=com.codahale.metrics.Meter`
 
-* Subscriber replication status per project (ms behind the producer)
+* Subscriber replication status (ms behind the producer)
 
-`metric=site/multi_site/subscriber/subscriber_replication_status/ms_behind_for_<projectName>, type=com.google.gerrit.metrics.dropwizard.CallbackMetricImpl`
\ No newline at end of file
+`metric=site/multi_site/subscriber/subscriber_replication_status/ms_behind, type=com.google.gerrit.metrics.dropwizard.CallbackMetricImpl`
\ No newline at end of file
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateTest.java
index 776fec8..5ea7b15 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateTest.java
@@ -17,9 +17,13 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.googlesource.gerrit.plugins.multisite.validation.ProjectVersionRefUpdate.MULTI_SITE_VERSIONING_REF;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.server.events.RefUpdatedEvent;
 import com.google.gerrit.server.project.ProjectConfig;
 import com.google.gerrit.testing.InMemoryRepositoryManager;
@@ -28,8 +32,7 @@
 import com.googlesource.gerrit.plugins.multisite.SharedRefDatabaseWrapper;
 import com.googlesource.gerrit.plugins.multisite.forwarder.Context;
 import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.RefFixture;
-import com.googlesource.gerrit.plugins.replication.RefReplicatedEvent;
-import com.googlesource.gerrit.plugins.replication.ReplicationState;
+import com.googlesource.gerrit.plugins.replication.RefReplicationDoneEvent;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.Optional;
@@ -40,7 +43,6 @@
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.transport.RemoteRefUpdate;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -101,11 +103,23 @@
   }
 
   @Test
-  public void producerShouldNotUpdateProjectVersionUponSequenceRefUpdatedEvent()
-      throws IOException {
+  public void producerShouldNotUpdateProjectVersionUponSequenceRefUpdatedEvent() throws Exception {
+    producerShouldNotUpdateProjectVersionUponMagicRefUpdatedEvent(RefNames.REFS_SEQUENCES);
+  }
+
+  @Test
+  public void producerShouldNotUpdateProjectVersionUponStarredChangesRefUpdatedEvent()
+      throws Exception {
+    producerShouldNotUpdateProjectVersionUponMagicRefUpdatedEvent(RefNames.REFS_STARRED_CHANGES);
+  }
+
+  private void producerShouldNotUpdateProjectVersionUponMagicRefUpdatedEvent(String magicRefPrefix)
+      throws Exception {
+    String magicRefName = magicRefPrefix + "/foo";
     Context.setForwardedEvent(false);
     when(refUpdatedEvent.getProjectNameKey()).thenReturn(A_TEST_PROJECT_NAME_KEY);
-    when(refUpdatedEvent.getRefName()).thenReturn("refs/sequences/changes");
+    when(refUpdatedEvent.getRefName()).thenReturn(magicRefName);
+    repo.branch(magicRefName).commit().create();
 
     new ProjectVersionRefUpdate(repoManager, sharedRefDb).onEvent(refUpdatedEvent);
 
@@ -126,15 +140,10 @@
   }
 
   @Test
-  public void consumerShouldUpdateProjectVersionUponRefReplicatedEvent() throws IOException {
+  public void consumerShouldUpdateProjectVersionUponRefReplicationDoneEvent() throws IOException {
     Context.setForwardedEvent(true);
-    RefReplicatedEvent refReplicatedEvent =
-        new RefReplicatedEvent(
-            A_TEST_PROJECT_NAME,
-            A_TEST_REF_NAME,
-            "targetNode",
-            ReplicationState.RefPushResult.SUCCEEDED,
-            RemoteRefUpdate.Status.OK);
+    RefReplicationDoneEvent refReplicatedEvent =
+        new RefReplicationDoneEvent(A_TEST_PROJECT_NAME, A_TEST_REF_NAME, 1);
 
     new ProjectVersionRefUpdate(repoManager, sharedRefDb).onEvent(refReplicatedEvent);
 
@@ -151,36 +160,27 @@
   }
 
   @Test
-  public void consumerShouldNotUpdateProjectVersionUponFailedRefReplicatedEvent()
-      throws IOException {
-    Context.setForwardedEvent(true);
-    RefReplicatedEvent refReplicatedEvent =
-        new RefReplicatedEvent(
-            A_TEST_PROJECT_NAME,
-            A_TEST_REF_NAME,
-            "targetNode",
-            ReplicationState.RefPushResult.SUCCEEDED,
-            RemoteRefUpdate.Status.REJECTED_OTHER_REASON);
-
-    new ProjectVersionRefUpdate(repoManager, sharedRefDb).onEvent(refReplicatedEvent);
-
-    Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF);
-    assertThat(ref).isNull();
+  public void consumerShouldNotUpdateProjectVersionUponSequenceRefReplicationDoneEvent()
+      throws Exception {
+    consumerShouldNotUpdateProjectVersionUponMagicRefReplicationDoneEvent(RefNames.REFS_SEQUENCES);
   }
 
   @Test
-  public void consumerShouldNotUpdateProjectVersionUponSequenceRefReplicatedEvent()
-      throws IOException {
-    Context.setForwardedEvent(true);
-    RefReplicatedEvent refReplicatedEvent =
-        new RefReplicatedEvent(
-            A_TEST_PROJECT_NAME,
-            "refs/sequences/groups",
-            "targetNode",
-            ReplicationState.RefPushResult.SUCCEEDED,
-            RemoteRefUpdate.Status.OK);
+  public void consumerShouldNotUpdateProjectVersionUponStarredChangesRefReplicationDoneEvent()
+      throws Exception {
+    consumerShouldNotUpdateProjectVersionUponMagicRefReplicationDoneEvent(
+        RefNames.REFS_STARRED_CHANGES);
+  }
 
-    new ProjectVersionRefUpdate(repoManager, sharedRefDb).onEvent(refReplicatedEvent);
+  private void consumerShouldNotUpdateProjectVersionUponMagicRefReplicationDoneEvent(
+      String magicRefPrefix) throws Exception {
+    String magicRef = magicRefPrefix + "/foo";
+    Context.setForwardedEvent(true);
+    RefReplicationDoneEvent refReplicationDoneEvent =
+        new RefReplicationDoneEvent(A_TEST_PROJECT_NAME, magicRef, 1);
+    repo.branch(magicRef).commit().create();
+
+    new ProjectVersionRefUpdate(repoManager, sharedRefDb).onEvent(refReplicationDoneEvent);
 
     Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF);
     assertThat(ref).isNull();
@@ -205,6 +205,7 @@
   public void getLocalProjectVersionShouldReturnCorrectValue() throws IOException {
     updateLocalVersion();
     Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF);
+    assertThat(ref).isNotNull();
 
     Optional<Long> version =
         new ProjectVersionRefUpdate(repoManager, sharedRefDb)
@@ -216,13 +217,8 @@
 
   private void updateLocalVersion() {
     Context.setForwardedEvent(true);
-    RefReplicatedEvent refReplicatedEvent =
-        new RefReplicatedEvent(
-            A_TEST_PROJECT_NAME,
-            A_TEST_REF_NAME,
-            "targetNode",
-            ReplicationState.RefPushResult.SUCCEEDED,
-            RemoteRefUpdate.Status.OK);
+    RefReplicationDoneEvent refReplicatedEvent =
+        new RefReplicationDoneEvent(A_TEST_PROJECT_NAME, A_TEST_REF_NAME, 1);
     new ProjectVersionRefUpdate(repoManager, sharedRefDb).onEvent(refReplicatedEvent);
   }
 }