Add ITs for replication events

Ahead of some refactoring, ensure there's at least some minimal test
coverage.

Change-Id: If7c6cd66b19d3b98ab0be2a5cbd6298b3d41a865
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationEventsIT.java b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationEventsIT.java
new file mode 100644
index 0000000..841bfd7
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationEventsIT.java
@@ -0,0 +1,119 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.replication;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.PushOneCommit.Result;
+import com.google.gerrit.acceptance.Sandboxed;
+import com.google.gerrit.acceptance.TestPlugin;
+import com.google.gerrit.acceptance.UseLocalDisk;
+import com.google.gerrit.acceptance.WaitUtil;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.extensions.api.projects.BranchInput;
+import com.google.gerrit.extensions.registration.DynamicItem;
+import com.google.gerrit.server.events.EventDispatcher;
+import com.google.gerrit.server.events.RefEvent;
+import com.google.inject.Inject;
+import java.time.Duration;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Supplier;
+import org.junit.Before;
+import org.junit.Test;
+
+@UseLocalDisk
+@Sandboxed
+@TestPlugin(
+    name = "replication",
+    sysModule = "com.googlesource.gerrit.plugins.replication.ReplicationModule")
+public class ReplicationEventsIT extends ReplicationDaemon {
+  private static final Duration TEST_POST_EVENT_TIMEOUT = Duration.ofSeconds(1);
+
+  @Inject private DynamicItem<EventDispatcher> eventDispatcher;
+  private TestDispatcher testDispatcher;
+
+  @Before
+  public void setup() throws Exception {
+    initConfig();
+    setReplicationDestination(
+        "remote1",
+        "suffix1",
+        Optional.of("not-used-project")); // Simulates a full replication.config initialization
+    setUpTestPlugin();
+    testDispatcher = new TestDispatcher();
+    eventDispatcher.set(testDispatcher, eventDispatcher.getPluginName());
+  }
+
+  @Test
+  public void replicateNewChangeSendsEvents() throws Exception {
+    Project.NameKey targetProject = createTestProject(project + "replica");
+
+    setReplicationDestination("foo", "replica", ALL_PROJECTS);
+    reloadConfig();
+
+    Result pushResult = createChange();
+    String sourceRef = pushResult.getPatchSet().refName();
+    String metaRef = pushResult.getChange().notes().getRefName();
+    BranchNameKey changeBranch = BranchNameKey.create(project, sourceRef);
+    BranchNameKey metaBranch = BranchNameKey.create(project, metaRef);
+
+    assertThat(testDispatcher.getEvents(changeBranch, ReplicationScheduledEvent.class)).hasSize(1);
+    assertThat(testDispatcher.getEvents(metaBranch, ReplicationScheduledEvent.class)).hasSize(1);
+
+    isPushCompleted(targetProject, sourceRef, TEST_PUSH_TIMEOUT);
+    isPushCompleted(targetProject, metaRef, TEST_PUSH_TIMEOUT);
+
+    waitForRefEvent(() -> testDispatcher.getEvents(RefReplicatedEvent.class), metaRef);
+    waitForRefEvent(() -> testDispatcher.getEvents(RefReplicatedEvent.class), sourceRef);
+    assertThat(testDispatcher.getEvents(RefReplicatedEvent.class).size()).isEqualTo(2);
+
+    waitForRefEvent(() -> testDispatcher.getEvents(RefReplicationDoneEvent.class), metaRef);
+    waitForRefEvent(() -> testDispatcher.getEvents(RefReplicationDoneEvent.class), sourceRef);
+    assertThat(testDispatcher.getEvents(RefReplicationDoneEvent.class).size()).isEqualTo(2);
+  }
+
+  @Test
+  public void replicateNewBranchSendsEvents() throws Exception {
+    setReplicationDestination("foo", "replica", ALL_PROJECTS);
+    reloadConfig();
+
+    Project.NameKey targetProject = createTestProject(project + "replica");
+    String newBranch = "refs/heads/mybranch";
+    BranchNameKey branchName = BranchNameKey.create(project, newBranch);
+    String master = "refs/heads/master";
+    BranchInput input = new BranchInput();
+    input.revision = master;
+    gApi.projects().name(project.get()).branch(newBranch).create(input);
+
+    assertThat(testDispatcher.getEvents(branchName, ReplicationScheduledEvent.class)).hasSize(1);
+
+    isPushCompleted(targetProject, newBranch, TEST_PUSH_TIMEOUT);
+
+    waitForRefEvent(() -> testDispatcher.getEvents(RefReplicatedEvent.class), newBranch);
+    assertThat(testDispatcher.getEvents(RefReplicatedEvent.class).size()).isEqualTo(1);
+
+    waitForRefEvent(() -> testDispatcher.getEvents(RefReplicationDoneEvent.class), newBranch);
+    assertThat(testDispatcher.getEvents(RefReplicationDoneEvent.class).size()).isEqualTo(1);
+  }
+
+  private <T extends RefEvent> void waitForRefEvent(Supplier<List<T>> events, String refName)
+      throws InterruptedException {
+    WaitUtil.waitUntil(
+        () -> events.get().stream().filter(e -> refName.equals(e.getRefName())).count() == 1,
+        TEST_POST_EVENT_TIMEOUT);
+  }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/TestDispatcher.java b/src/test/java/com/googlesource/gerrit/plugins/replication/TestDispatcher.java
new file mode 100644
index 0000000..bc5c35c
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/TestDispatcher.java
@@ -0,0 +1,63 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.replication;
+
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.server.events.ChangeEvent;
+import com.google.gerrit.server.events.Event;
+import com.google.gerrit.server.events.EventDispatcher;
+import com.google.gerrit.server.events.ProjectEvent;
+import com.google.gerrit.server.events.RefEvent;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class TestDispatcher implements EventDispatcher {
+  private final List<RefEvent> refEvents = new LinkedList<>();
+  private final List<Event> events = new LinkedList<>();
+
+  @Override
+  public void postEvent(Change change, ChangeEvent event) {} // Not used in replication
+
+  @Override
+  public void postEvent(BranchNameKey branchName, RefEvent event) {
+    refEvents.add(event);
+  }
+
+  @Override
+  public void postEvent(
+      Project.NameKey projectName, ProjectEvent event) {} // Not used in replication
+
+  @Override
+  public void postEvent(Event event) {
+    events.add(event);
+  }
+
+  public List<RefEvent> getEvents(BranchNameKey branch, Class<? extends RefEvent> clazz) {
+    return getEvents(branch).stream().filter(clazz::isInstance).collect(Collectors.toList());
+  }
+
+  public <T extends RefEvent> List<T> getEvents(Class<T> clazz) {
+    return events.stream().filter(clazz::isInstance).map(clazz::cast).collect(Collectors.toList());
+  }
+
+  private List<RefEvent> getEvents(BranchNameKey branch) {
+    return refEvents.stream()
+        .filter(e -> e.getBranchNameKey().equals(branch))
+        .collect(Collectors.toList());
+  }
+}