ReplicationIT: don't wait a full retry window to prove non-replication

ReplicationIT takes ~343s (5m40s) while the rest of the build finishes
in seconds. Five of its 26 cases each burn ~62s; the rest run in <=2s.

All five bottom out on the replication retry quantum: a failed push is
rescheduled in TimeUnit.MINUTES (Destination#reschedule), so the
smallest non-zero retry is one minute and the tests set
replicationRetry=1. The three negative tests --
shouldNotDrainTheQueueWhenReloading,
shouldNotReplicateToNonMatchingRemote and
shouldNotReplicateProjectListedInProjectsAndExcludeProjects -- assert a
ref is never replicated by waiting for it and expecting the wait to
expire. They reuse TEST_TIMEOUT, which is sized to span a retry cycle
((delay + retry*60)+1 = 62s), so each burns the full minute only to
prove a negative.

Give those waits their own TEST_NOT_REPLICATED_TIMEOUT (replication
delay + push time + a few seconds cushion, ~7s), applied via a private
waitUntil(Supplier, Duration) overload in ReplicationIT.

This does not weaken the tests: waitUntil still throws on timeout, so
assertThrows still verifies non-replication -- only the wait shortens.
Replication is structurally suppressed in these cases (destination down,
project excluded, remote non-matching), so the ref can never appear;
were a regression to leak a push it would replicate within the ~1s
delay, well inside the window, and the assertion would then fail. The
shared TEST_REPLICATION_RETRY_MINUTES is left untouched so tests that
rely on the real one-minute retry (e.g. ReplicationStorageIT) keep their
timing.

The two remaining ~60s cases (new-project replication) genuinely wait a
retry: the first push fails as REPOSITORY_MISSING while the replica repo
is still being created, and that reason is retried on the minutes-based
delay. That is a separate, arguably production-side latency issue and is
handled in a follow-up.

Net, the three negatives drop from ~62s to ~7s each while the two
new-project cases are untouched here, so ReplicationIT drops from ~343s
to ~173s (~2x60s new-project waits + ~24 fast tests), all 26 green.

Change-Id: I2d561950e51761a1d1b0fe09dc50763b3e5421af
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 d54cb7c..4a3bb9b 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationIT.java
@@ -59,10 +59,18 @@
     name = "replication",
     sysModule = "com.googlesource.gerrit.plugins.replication.TestReplicationModule")
 public class ReplicationIT extends ReplicationDaemon {
-  private static final int TEST_REPLICATION_DELAY = 1;
-  private static final int TEST_REPLICATION_RETRY = 1;
   private static final Duration TEST_TIMEOUT =
-      Duration.ofSeconds((TEST_REPLICATION_DELAY + TEST_REPLICATION_RETRY * 60) + 1);
+      Duration.ofSeconds(TEST_REPLICATION_DELAY_SECONDS + TEST_REPLICATION_RETRY_MINUTES * 60 + 1);
+
+  // Timeout for asserting that a ref is *not* replicated. Unlike TEST_TIMEOUT it
+  // deliberately excludes the retry cycle: a push that is going to happen at all
+  // completes within the replication delay plus the push time, so if the ref has
+  // not appeared within that window (plus a small cushion) it never will
+  // (destination shut down / project excluded / non-matching remote). Sized in
+  // seconds rather than the ~60s retry quantum so these negative tests do not each
+  // burn a full retry window while proving a negative.
+  private static final Duration TEST_NOT_REPLICATED_TIMEOUT =
+      Duration.ofSeconds(TEST_REPLICATION_DELAY_SECONDS + TEST_PUSH_TIME_SECONDS + 5);
 
   @Inject private DynamicSet<ProjectDeletedListener> deletedListeners;
 
@@ -457,7 +465,7 @@
         InterruptedException.class,
         () -> {
           try (Repository repo = repoManager.openRepository(targetProject)) {
-            waitUntil(() -> checkedGetRef(repo, sourceRef) != null);
+            waitUntil(() -> checkedGetRef(repo, sourceRef) != null, TEST_NOT_REPLICATED_TIMEOUT);
           }
         });
   }
@@ -741,7 +749,8 @@
     try (Repository repo = repoManager.openRepository(targetProject)) {
       assertThrows(
           InterruptedException.class,
-          () -> waitUntil(() -> checkedGetRef(repo, sourceRef) != null));
+          () ->
+              waitUntil(() -> checkedGetRef(repo, sourceRef) != null, TEST_NOT_REPLICATED_TIMEOUT));
     }
   }
 
@@ -763,12 +772,18 @@
 
     try (Repository repo = repoManager.openRepository(targetProject)) {
       assertThrows(
-          InterruptedException.class, () -> waitUntil(() -> checkedGetRef(repo, newRef) != null));
+          InterruptedException.class,
+          () -> waitUntil(() -> checkedGetRef(repo, newRef) != null, TEST_NOT_REPLICATED_TIMEOUT));
     }
   }
 
   private void waitUntil(Supplier<Boolean> waitCondition) throws InterruptedException {
-    WaitUtil.waitUntil(waitCondition, TEST_TIMEOUT);
+    waitUntil(waitCondition, TEST_TIMEOUT);
+  }
+
+  private void waitUntil(Supplier<Boolean> waitCondition, Duration timeout)
+      throws InterruptedException {
+    WaitUtil.waitUntil(waitCondition, timeout);
   }
 
   private void shutdownDestinations() {