Fix replication of project deletion

Fix a regression where the project deletion was not propagated
to the remote nodes because its associated project state was
missing from the project cache.

When replicating deletions, the project state is not present as
the replication plugin is receiving the deletions ex-post
and therefore do not have access to the project anymore.

The associated checks for project visibility and read only state
are not valid for project deletions.

Bug: Issue 12806
Change-Id: I7a9ac01b01d5dd40b8bf0c4d3347256f430329ac
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java
index e494c24..936d704 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java
@@ -255,7 +255,7 @@
 
     Set<URIish> uris = new HashSet<>();
     for (Destination config : this.config.getDestinations(filterType)) {
-      if (!config.wouldPushProject(projectName)) {
+      if (filterType != FilterType.PROJECT_DELETION && !config.wouldPushProject(projectName)) {
         continue;
       }
 
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationIT.java b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationIT.java
index 1203f8b..64430cf 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationIT.java
@@ -25,8 +25,11 @@
 import com.google.gerrit.acceptance.TestPlugin;
 import com.google.gerrit.acceptance.UseLocalDisk;
 import com.google.gerrit.extensions.annotations.PluginData;
+import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.projects.BranchInput;
 import com.google.gerrit.extensions.common.ProjectInfo;
+import com.google.gerrit.extensions.events.ProjectDeletedListener;
+import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.inject.Inject;
@@ -63,6 +66,7 @@
       Duration.ofSeconds((TEST_REPLICATION_DELAY + TEST_REPLICATION_RETRY * 60) + 1);
 
   @Inject private SitePaths sitePaths;
+  @Inject private DynamicSet<ProjectDeletedListener> deletedListeners;
   private Path pluginDataDir;
   private Path gitPath;
   private Path storagePath;
@@ -106,6 +110,34 @@
   }
 
   @Test
+  public void shouldReplicateProjectDeletion() throws Exception {
+    String projectNameDeleted = "project-deleted";
+    Project.NameKey replicaProject = createTestProject(projectNameDeleted + "replica");
+    setReplicationDestination("foo", "replica", ALL_PROJECTS);
+    setProjectDeletionReplication("foo", true);
+    reloadConfig();
+
+    ProjectDeletedListener.Event event =
+        new ProjectDeletedListener.Event() {
+          @Override
+          public String getProjectName() {
+            return name(projectNameDeleted);
+          }
+
+          @Override
+          public NotifyHandling getNotify() {
+            return NotifyHandling.NONE;
+          }
+        };
+
+    for (ProjectDeletedListener l : deletedListeners) {
+      l.onProjectDeleted(event);
+    }
+
+    waitUntil(() -> !nonEmptyProjectExists(replicaProject));
+  }
+
+  @Test
   public void shouldReplicateNewChangeRef() throws Exception {
     Project.NameKey targetProject = createTestProject("projectreplica");
 
@@ -367,6 +399,12 @@
     config.save();
   }
 
+  private void setProjectDeletionReplication(String remoteName, boolean replicateProjectDeletion)
+      throws IOException {
+    config.setBoolean("remote", remoteName, "replicateProjectDeletions", replicateProjectDeletion);
+    config.save();
+  }
+
   private void waitUntil(Supplier<Boolean> waitCondition) throws InterruptedException {
     WaitUtil.waitUntil(waitCondition, TEST_TIMEOUT);
   }