Cover source code of high-availability plugin jgroups mode with tests

Feature: Issue 10020
Change-Id: Icc11d4287c64be10d3bcebed73c8bcb627886d85
diff --git a/BUILD b/BUILD
index 5975b31..9607415 100644
--- a/BUILD
+++ b/BUILD
@@ -47,5 +47,7 @@
         ":high-availability__plugin",
         "@global-refdb//jar",
         "@wiremock//jar",
+        "@jgroups//jar",
+        "@commons-net//jar"
     ],
 )
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProvider.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProvider.java
index ce47390..0e3fdc0 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProvider.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProvider.java
@@ -16,6 +16,7 @@
 
 import com.ericsson.gerrit.plugins.highavailability.Configuration;
 import com.ericsson.gerrit.plugins.highavailability.peers.PeerInfo;
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.extensions.events.LifecycleListener;
@@ -157,6 +158,16 @@
     }
   }
 
+  @VisibleForTesting
+  void setChannel(JChannel channel) {
+    this.channel = channel;
+  }
+
+  @VisibleForTesting
+  void setPeerInfo(Optional<PeerInfo> peerInfo) {
+    this.peerInfo = peerInfo;
+  }
+
   @Override
   public Set<PeerInfo> get() {
     return peerInfo.isPresent() ? ImmutableSet.of(peerInfo.get()) : ImmutableSet.of();
@@ -178,4 +189,14 @@
     peerInfo = Optional.empty();
     peerAddress = null;
   }
+
+  @VisibleForTesting
+  Address getPeerAddress() {
+    return peerAddress;
+  }
+
+  @VisibleForTesting
+  void setPeerAddress(Address peerAddress) {
+    this.peerAddress = peerAddress;
+  }
 }
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerImplTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerImplTest.java
new file mode 100644
index 0000000..38f71e5
--- /dev/null
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerImplTest.java
@@ -0,0 +1,84 @@
+// Copyright (C) 2022 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.ericsson.gerrit.plugins.highavailability.index;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
+import com.ericsson.gerrit.plugins.highavailability.forwarder.IndexEvent;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.server.CommentsUtil;
+import com.google.gerrit.server.change.ChangeFinder;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.util.OneOffRequestContext;
+import java.io.IOException;
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ChangeCheckerImplTest {
+
+  @Mock private GitRepositoryManager gitRepoMgr;
+  @Mock private CommentsUtil commentsUtil;
+  @Mock private ChangeFinder changeFinder;
+  @Mock private OneOffRequestContext oneOffReqCtx;
+  @Mock private ChangeNotes testChangeNotes;
+  @Mock private Change testChange;
+
+  private final Instant testLastUpdatedOn = Instant.now();
+  private final String changeId = "1";
+  Optional<IndexEvent> event = Optional.empty();
+  private Optional<Long> computedChangeTs = Optional.empty();
+  private ChangeCheckerImpl changeChecker;
+
+  @Before
+  public void setUp() {
+    changeChecker =
+        new ChangeCheckerImpl(gitRepoMgr, commentsUtil, changeFinder, oneOffReqCtx, changeId);
+  }
+
+  @Test
+  public void testGetChangeNotes() {
+    when(changeFinder.findOne(changeId)).thenReturn(Optional.of(testChangeNotes));
+    assertThat(changeChecker.getChangeNotes()).isEqualTo(Optional.of(testChangeNotes));
+  }
+
+  @Test
+  public void testGetComputedChangeTs() {
+    long testTime = Timestamp.from(testLastUpdatedOn).getTime();
+    computedChangeTs = Optional.of(testTime / 1000);
+    when(changeChecker.getChangeNotes()).thenReturn(Optional.of(testChangeNotes));
+    when(testChangeNotes.getChange()).thenReturn(testChange);
+    when(testChange.getLastUpdatedOn()).thenReturn(testLastUpdatedOn);
+    assertThat(changeChecker.getComputedChangeTs()).isEqualTo(computedChangeTs);
+  }
+
+  @Test
+  public void testNewIndexEventWhenChangeTimeStampIsEmpty() throws IOException {
+    assertThat(changeChecker.newIndexEvent().isPresent()).isFalse();
+  }
+
+  @Test
+  public void testIsChangeUpToDateWhenComputedChangeTsIsNotPresent() throws IOException {
+    assertThat(changeChecker.isChangeUpToDate(event)).isFalse();
+  }
+}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/InetAddressFinderTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/InetAddressFinderTest.java
index bfcffbb..c375345 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/InetAddressFinderTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/InetAddressFinderTest.java
@@ -16,10 +16,20 @@
 
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import com.ericsson.gerrit.plugins.highavailability.Configuration;
 import com.google.common.collect.ImmutableList;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Optional;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,11 +42,68 @@
   @Mock(answer = RETURNS_DEEP_STUBS)
   private Configuration configuration;
 
+  @Mock private NetworkInterface mockInterface;
+
   private InetAddressFinder finder;
+  private List<NetworkInterface> testNetworkInterfaces;
 
   @Before
   public void setUp() {
     finder = new InetAddressFinder(configuration);
+    testNetworkInterfaces = new ArrayList<>();
+  }
+
+  @Test
+  public void testNoSuitableInterfaceWhenFindFirstAppropriateAddress() throws SocketException {
+    when(mockInterface.isLoopback()).thenReturn(true);
+    when(configuration.jgroups().skipInterface()).thenReturn(ImmutableList.of("mockInterface1"));
+    testNetworkInterfaces.add(mockInterface);
+    assertThat(finder.findFirstAppropriateAddress(testNetworkInterfaces).isPresent()).isFalse();
+  }
+
+  @Test
+  public void testOptionalEmptyIsReturnedWhenFindFirstAppropriateAddress() throws SocketException {
+    setUpCustomMockInterfaceMocks();
+    when(configuration.jgroups().skipInterface()).thenReturn(ImmutableList.of());
+    testNetworkInterfaces.add(mockInterface);
+    Enumeration mockInetAddresses = mock(Enumeration.class);
+
+    when(mockInterface.getInetAddresses()).thenReturn(mockInetAddresses);
+    assertThat(finder.findFirstAppropriateAddress(testNetworkInterfaces))
+        .isEqualTo(Optional.empty());
+  }
+
+  @Test
+  public void testInet6AddressIsReturnedWhenFindFirstAppropriateAddress() throws SocketException {
+    setUpCustomMockInterfaceMocks();
+    when(configuration.jgroups().skipInterface()).thenReturn(ImmutableList.of());
+    testNetworkInterfaces.add(mockInterface);
+    Inet6Address mockInet6Address = mock(Inet6Address.class);
+    List<Inet6Address> mocklist = new ArrayList<>();
+    mocklist.add(mockInet6Address);
+    Enumeration mockInetAddresses = Collections.enumeration(mocklist);
+
+    when(mockInterface.getInetAddresses()).thenReturn(mockInetAddresses);
+    assertThat(finder.findFirstAppropriateAddress(testNetworkInterfaces))
+        .isEqualTo(Optional.of(mockInet6Address));
+  }
+
+  @Test
+  public void testInet4AddressIsReturnedWhenFindFirstAppropriateAddress() throws SocketException {
+    setUpCustomMockInterfaceMocks();
+    when(configuration.jgroups().skipInterface()).thenReturn(ImmutableList.of());
+    System.setProperty("java.net.preferIPv4Stack", "true");
+    testNetworkInterfaces.add(mockInterface);
+    Inet4Address mockInet4Address = mock(Inet4Address.class);
+    List<Inet4Address> mocklist = new ArrayList<>();
+    mocklist.add(mockInet4Address);
+
+    Enumeration mockInetAddresses = Collections.enumeration(mocklist);
+    when(mockInterface.getInetAddresses()).thenReturn(mockInetAddresses);
+
+    finder = new InetAddressFinder(configuration);
+    assertThat(finder.findFirstAppropriateAddress(testNetworkInterfaces))
+        .isEqualTo(Optional.of(mockInet4Address));
   }
 
   @Test
@@ -64,4 +131,10 @@
     assertThat(finder.shouldSkip("foo1")).isTrue();
     assertThat(finder.shouldSkip("bar")).isFalse();
   }
+
+  private void setUpCustomMockInterfaceMocks() throws SocketException {
+    when(mockInterface.isLoopback()).thenReturn(false);
+    when(mockInterface.isUp()).thenReturn(true);
+    when(mockInterface.supportsMulticast()).thenReturn(true);
+  }
 }
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProviderTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProviderTest.java
new file mode 100644
index 0000000..1110fc3
--- /dev/null
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProviderTest.java
@@ -0,0 +1,158 @@
+// Copyright (C) 2022 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.ericsson.gerrit.plugins.highavailability.peers.jgroups;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.when;
+
+import com.ericsson.gerrit.plugins.highavailability.Configuration;
+import com.ericsson.gerrit.plugins.highavailability.peers.PeerInfo;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import org.jgroups.Address;
+import org.jgroups.JChannel;
+import org.jgroups.Message;
+import org.jgroups.View;
+import org.jgroups.stack.IpAddress;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.Test.None;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class JGroupsPeerInfoProviderTest {
+
+  @Mock(answer = RETURNS_DEEP_STUBS)
+  private Configuration pluginConfigurationMock;
+
+  private InetAddressFinder finder;
+  private JGroupsPeerInfoProvider jGroupsPeerInfoProvider;
+  private Optional<PeerInfo> peerInfo;
+  @Mock private JChannel channel;
+  @Mock private MyUrlProvider myUrlProviderTest;
+  @Mock private Message message;
+  @Mock private Address peerAddress;
+  @Mock private View view;
+  @Mock private List<Address> members;
+
+  @Before
+  public void setUp() throws Exception {
+    finder = new InetAddressFinder(pluginConfigurationMock);
+    jGroupsPeerInfoProvider =
+        new JGroupsPeerInfoProvider(pluginConfigurationMock, finder, myUrlProviderTest);
+    peerInfo = Optional.of(new PeerInfo("test message"));
+    channel.setName("testChannel");
+  }
+
+  @Test
+  public void testRecieveWhenPeerAddressIsNull() {
+    when(message.getSrc()).thenReturn(peerAddress);
+    when(message.getObject()).thenReturn("test message");
+
+    jGroupsPeerInfoProvider.receive(message);
+
+    assertThat(jGroupsPeerInfoProvider.getPeerAddress()).isEqualTo(peerAddress);
+    Set<PeerInfo> testPeerInfoSet = jGroupsPeerInfoProvider.get();
+    for (PeerInfo testPeerInfo : testPeerInfoSet) {
+      assertThat(testPeerInfo.getDirectUrl()).contains("test message");
+    }
+    assertThat(testPeerInfoSet.size()).isEqualTo(1);
+  }
+
+  @Test
+  public void testReceiveWhenPeerAddressIsNotNull() throws Exception {
+    jGroupsPeerInfoProvider.setPeerAddress(new IpAddress("checkAddress.com"));
+
+    jGroupsPeerInfoProvider.receive(message);
+
+    Set<PeerInfo> testPeerInfoSet = jGroupsPeerInfoProvider.get();
+    assertThat(testPeerInfoSet.isEmpty()).isTrue();
+    assertThat(testPeerInfoSet.size()).isEqualTo(0);
+  }
+
+  @Test(expected = None.class)
+  public void testViewAcceptedWithNoExceptionThrown() throws Exception {
+    when(view.getMembers()).thenReturn(members);
+    when(view.size()).thenReturn(3);
+    when(members.size()).thenReturn(3);
+    jGroupsPeerInfoProvider.setChannel(channel);
+    jGroupsPeerInfoProvider.viewAccepted(view);
+  }
+
+  @Test(expected = NullPointerException.class)
+  public void testViewAcceptedWithExceptionThrown() throws Exception {
+    when(view.getMembers()).thenReturn(members);
+    when(view.size()).thenReturn(2);
+    when(members.size()).thenReturn(2);
+    jGroupsPeerInfoProvider.viewAccepted(view);
+  }
+
+  @Test
+  public void testViewAcceptedWhenPeerAddressIsNotNullAndIsNotMemberOfView() {
+    when(view.getMembers()).thenReturn(members);
+    when(view.size()).thenReturn(2);
+    when(members.size()).thenReturn(2);
+    when(members.contains(peerAddress)).thenReturn(false);
+    jGroupsPeerInfoProvider.setPeerAddress(peerAddress);
+    jGroupsPeerInfoProvider.setPeerInfo(peerInfo);
+    jGroupsPeerInfoProvider.setChannel(channel);
+    jGroupsPeerInfoProvider.viewAccepted(view);
+
+    assertThat(jGroupsPeerInfoProvider.getPeerAddress()).isEqualTo(null);
+    Set<PeerInfo> testPeerInfoSet = jGroupsPeerInfoProvider.get();
+    assertThat(testPeerInfoSet.isEmpty()).isTrue();
+    assertThat(testPeerInfoSet.size()).isEqualTo(0);
+  }
+
+  @Test
+  public void testConnect() throws NoSuchFieldException, IllegalAccessException {
+    jGroupsPeerInfoProvider.connect();
+    Set<PeerInfo> testPeerInfoSet = jGroupsPeerInfoProvider.get();
+    assertThat(testPeerInfoSet.isEmpty()).isTrue();
+    assertThat(testPeerInfoSet.size()).isEqualTo(0);
+  }
+
+  @Test
+  public void testGetWhenPeerInfoIsOptionalEmpty() {
+    Set<PeerInfo> testPeerInfoSet = jGroupsPeerInfoProvider.get();
+    assertThat(testPeerInfoSet.isEmpty()).isTrue();
+    assertThat(testPeerInfoSet.size()).isEqualTo(0);
+  }
+
+  @Test
+  public void testGetWhenPeerInfoIsPresent() {
+    jGroupsPeerInfoProvider.setPeerInfo(peerInfo);
+    Set<PeerInfo> testPeerInfoSet = jGroupsPeerInfoProvider.get();
+    for (PeerInfo testPeerInfo : testPeerInfoSet) {
+      assertThat(testPeerInfo.getDirectUrl()).contains("test message");
+    }
+    assertThat(testPeerInfoSet.size()).isEqualTo(1);
+  }
+
+  @Test
+  public void testStop() throws Exception {
+    jGroupsPeerInfoProvider.setPeerAddress(peerAddress);
+    jGroupsPeerInfoProvider.setPeerInfo(peerInfo);
+    jGroupsPeerInfoProvider.stop();
+    assertThat(jGroupsPeerInfoProvider.getPeerAddress()).isEqualTo(null);
+    Set<PeerInfo> testPeerInfoSet = jGroupsPeerInfoProvider.get();
+    assertThat(testPeerInfoSet.isEmpty()).isTrue();
+    assertThat(testPeerInfoSet.size()).isEqualTo(0);
+  }
+}
diff --git a/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..1f0955d
--- /dev/null
+++ b/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline