Cover the replication failure scenario and fix the metrics

The failed replication fetch was silently dropping the event
without any possibility to detect the failure. Cover the missing
use-case and generate the task/failed metric whenever the
replication does not succeed.

Also consider a success when the replication event does not
result in any ref-specs, which isn't a failure but simply a NOOP.

Change-Id: I9bc6127d4f9e237772a43d787f8b862319e8d84c
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/FetchOne.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/FetchOne.java
index 5421b21..52f5422 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/FetchOne.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/FetchOne.java
@@ -573,6 +573,6 @@
 
   @Override
   public boolean hasSucceeded() {
-    return succeeded;
+    return succeeded || getFetchRefSpecs().isEmpty();
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueueMetrics.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueueMetrics.java
index 741927f..b0fa7e9 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueueMetrics.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationQueueMetrics.java
@@ -66,8 +66,11 @@
       incrementTaskStarted(source);
       runnable.run();
       if (runnable instanceof Completable) {
-        if (((Completable) runnable).hasSucceeded()) {
+        Completable completedRunnable = (Completable) runnable;
+        if (completedRunnable.hasSucceeded()) {
           incrementTaskCompleted(source);
+        } else {
+          incrementTaskFailed(source);
         }
       }
     }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationITAbstract.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationITAbstract.java
index 8e04d5b..7d8a164 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationITAbstract.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/PullReplicationITAbstract.java
@@ -157,6 +157,32 @@
   }
 
   @Test
+  @GerritConfig(name = "gerrit.instanceId", value = TEST_REPLICATION_REMOTE)
+  public void shouldFailReplicatingInexistentRepository() throws Exception {
+    String newBranch = "refs/heads/mybranch";
+    String branchRevision = "7bb81c29e14a4169e5ca4f43992094c209aae26c";
+
+    ReplicationQueue pullReplicationQueue =
+        plugin.getSysInjector().getInstance(ReplicationQueue.class);
+    FakeGitReferenceUpdatedEvent event =
+        new FakeGitReferenceUpdatedEvent(
+            project,
+            newBranch,
+            ObjectId.zeroId().getName(),
+            branchRevision,
+            TEST_REPLICATION_REMOTE);
+    pullReplicationQueue.onEvent(event);
+    waitUntilReplicationFailed(1);
+
+    try (Repository repo = repoManager.openRepository(project);
+        Repository sourceRepo = repoManager.openRepository(project)) {
+
+      Ref targetBranchRef = getRef(repo, newBranch);
+      assertThat(targetBranchRef).isNull();
+    }
+  }
+
+  @Test
   @UseLocalDisk
   @GerritConfig(name = "gerrit.instanceId", value = TEST_REPLICATION_REMOTE)
   public void shouldReplicateForceUpdatedBranch() throws Exception {
@@ -414,10 +440,18 @@
   }
 
   private void waitUntilReplicationCompleted(int expected) throws Exception {
+    waitUntilReplicationTask("completed", expected);
+  }
+
+  private void waitUntilReplicationFailed(int expected) throws Exception {
+    waitUntilReplicationTask("failed", expected);
+  }
+
+  private void waitUntilReplicationTask(String status, int expected) throws Exception {
     waitUntil(
         () ->
             inMemoryMetrics()
-                .counterValue("tasks/completed", TEST_REPLICATION_REMOTE)
+                .counterValue("tasks/" + status, TEST_REPLICATION_REMOTE)
                 .filter(counter -> counter == expected)
                 .isPresent());
   }