From 91200c78691808c8e7ba517e34b9af876ce5fe45 Mon Sep 17 00:00:00 2001 From: Chris Richardson Date: Mon, 16 Oct 2023 09:24:08 -0700 Subject: [PATCH] Cleaned up Leadership code --- .../tests/AbstractLeadershipTest.java | 90 ++++++------------- .../tests/LeaderSelectorTestingWrap.java | 54 ----------- .../leadership/tests/SelectorUnderTest.java | 89 ++++++++++++++++++ .../build.gradle | 3 + .../leadership/zookeeper/LeadershipTest.java | 21 +++-- .../src/test/resources/application.properties | 1 + .../EventuateZookeeperContainer.java | 5 ++ 7 files changed, 142 insertions(+), 121 deletions(-) delete mode 100644 eventuate-common-coordination-leadership-tests/src/main/java/io/eventuate/coordination/leadership/tests/LeaderSelectorTestingWrap.java create mode 100644 eventuate-common-coordination-leadership-tests/src/main/java/io/eventuate/coordination/leadership/tests/SelectorUnderTest.java diff --git a/eventuate-common-coordination-leadership-tests/src/main/java/io/eventuate/coordination/leadership/tests/AbstractLeadershipTest.java b/eventuate-common-coordination-leadership-tests/src/main/java/io/eventuate/coordination/leadership/tests/AbstractLeadershipTest.java index 0d04646c..058c16d9 100644 --- a/eventuate-common-coordination-leadership-tests/src/main/java/io/eventuate/coordination/leadership/tests/AbstractLeadershipTest.java +++ b/eventuate-common-coordination-leadership-tests/src/main/java/io/eventuate/coordination/leadership/tests/AbstractLeadershipTest.java @@ -6,10 +6,6 @@ import io.eventuate.util.test.async.Eventually; import org.junit.Assert; import org.junit.Test; -import org.mockito.Mockito; - -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; public abstract class AbstractLeadershipTest { @@ -17,25 +13,25 @@ public abstract class AbstractLeadershipTest leaderSelectorTestingWrap = createAndStartLeaderSelector(); + SelectorUnderTest selector = createAndStartLeaderSelector(); - leaderSelectorTestingWrap.eventuallyAssertIsLeaderAndCallbackIsInvokedOnce(); - leaderSelectorTestingWrap.stop(); - leaderSelectorTestingWrap.eventuallyAssertIsNotLeaderAndCallbackIsInvokedOnce(); + selector.eventuallyAssertIsLeaderAndCallbackIsInvokedOnce(); + selector.stop(); + selector.eventuallyAssertIsNotLeaderAndCallbackIsInvokedOnce(); } @Test public void testThatLeaderIsChangedWhenStopped() { - LeaderSelectorTestingWrap leaderSelectorTestingWrap1 = createAndStartLeaderSelector(); - LeaderSelectorTestingWrap leaderSelectorTestingWrap2 = createAndStartLeaderSelector(); + SelectorUnderTest selector1 = createAndStartLeaderSelector(); + SelectorUnderTest selector2 = createAndStartLeaderSelector(); - eventuallyAssertLeadershipIsAssignedOnlyForOneSelector(leaderSelectorTestingWrap1, leaderSelectorTestingWrap2); + eventuallyAssertLeadershipIsAssignedOnlyForOneSelector(selector1, selector2); - LeaderSelectorTestingWrap instanceWhichBecameLeaderFirst = - leaderSelectorTestingWrap1.isLeader() ? leaderSelectorTestingWrap1 : leaderSelectorTestingWrap2; + SelectorUnderTest instanceWhichBecameLeaderFirst = + selector1.isLeader() ? selector1 : selector2; - LeaderSelectorTestingWrap instanceWhichBecameLeaderLast = - leaderSelectorTestingWrap2.isLeader() ? leaderSelectorTestingWrap1 : leaderSelectorTestingWrap2; + SelectorUnderTest instanceWhichBecameLeaderLast = + selector2.isLeader() ? selector1 : selector2; instanceWhichBecameLeaderFirst.stop(); @@ -46,70 +42,42 @@ public void testThatLeaderIsChangedWhenStopped() { @Test public void testThatOnlyOneLeaderIsWorkingInTheSameTime() throws Exception { - LeaderSelectorTestingWrap leaderSelectorTestingWrap1 = createAndStartLeaderSelector(); - LeaderSelectorTestingWrap leaderSelectorTestingWrap2 = createAndStartLeaderSelector(); + SelectorUnderTest selector1 = createAndStartLeaderSelector(); + SelectorUnderTest selector2 = createAndStartLeaderSelector(); - eventuallyAssertLeadershipIsAssignedOnlyForOneSelector(leaderSelectorTestingWrap1, leaderSelectorTestingWrap2); + eventuallyAssertLeadershipIsAssignedOnlyForOneSelector(selector1, selector2); Thread.sleep(3000); - eventuallyAssertLeadershipIsAssignedOnlyForOneSelector(leaderSelectorTestingWrap1, leaderSelectorTestingWrap2); + eventuallyAssertLeadershipIsAssignedOnlyForOneSelector(selector1, selector2); - leaderSelectorTestingWrap1.stop(); - leaderSelectorTestingWrap2.stop(); + selector1.stop(); + selector2.stop(); } @Test public void testRestart() { - LeaderSelectedCallback leaderSelectedCallback = Mockito.mock(LeaderSelectedCallback.class); - Runnable leaderRemovedCallback = Mockito.mock(Runnable.class); - - Mockito.doAnswer(invocation -> { - leadershipController = (LeadershipController)invocation.getArguments()[0]; - return null; - }).when(leaderSelectedCallback).run(Mockito.any()); - - SELECTOR leaderSelector = createLeaderSelector(leaderSelectedCallback, leaderRemovedCallback); - - leaderSelector.start(); - - Eventually.eventually(() -> Mockito.verify(leaderSelectedCallback).run(Mockito.any())); - - Mockito.verifyNoInteractions(leaderRemovedCallback); - leadershipController.stop(); + SelectorUnderTest selector = createAndStartLeaderSelector(); - Eventually.eventually(() -> { - Mockito.verify(leaderRemovedCallback).run(); - Mockito.verify(leaderSelectedCallback, Mockito.times(2)).run(Mockito.any()); - }); + selector.eventuallyAssertIsLeaderAndCallbackIsInvokedOnce(); + selector.relinquish(); + selector.eventuallyAssertIsLeaderAndCallbackIsInvokedTwice(); } - protected void eventuallyAssertLeadershipIsAssignedOnlyForOneSelector(LeaderSelectorTestingWrap selectorLeaderSelectorTestingWrap1, - LeaderSelectorTestingWrap selectorLeaderSelectorTestingWrap2) { + + protected void eventuallyAssertLeadershipIsAssignedOnlyForOneSelector(SelectorUnderTest selector1, + SelectorUnderTest selector2) { Eventually.eventually(() -> { - boolean leader1Condition = selectorLeaderSelectorTestingWrap1.isLeader() && !selectorLeaderSelectorTestingWrap2.isLeader(); - boolean leader2Condition = selectorLeaderSelectorTestingWrap2.isLeader() && !selectorLeaderSelectorTestingWrap1.isLeader(); + boolean leader1Condition = selector1.isLeader() && !selector2.isLeader(); + boolean leader2Condition = selector2.isLeader() && !selector1.isLeader(); Assert.assertTrue(leader1Condition || leader2Condition); }); } - protected LeaderSelectorTestingWrap createAndStartLeaderSelector() { - AtomicInteger invocationCounter = new AtomicInteger(0); - AtomicBoolean leaderFlag = new AtomicBoolean(false); - - SELECTOR selector = createLeaderSelector((leadershipController) -> { - leaderFlag.set(true); - invocationCounter.incrementAndGet(); - try { - Thread.sleep(Long.MAX_VALUE); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - }, () -> leaderFlag.set(false)); - + protected SelectorUnderTest createAndStartLeaderSelector() { + SelectorUnderTest selector = new SelectorUnderTest<>(this::createLeaderSelector); selector.start(); - - return new LeaderSelectorTestingWrap<>(selector, invocationCounter, leaderFlag); + return selector; } protected abstract SELECTOR createLeaderSelector(LeaderSelectedCallback leaderSelectedCallback, Runnable leaderRemovedCallback); diff --git a/eventuate-common-coordination-leadership-tests/src/main/java/io/eventuate/coordination/leadership/tests/LeaderSelectorTestingWrap.java b/eventuate-common-coordination-leadership-tests/src/main/java/io/eventuate/coordination/leadership/tests/LeaderSelectorTestingWrap.java deleted file mode 100644 index 4d8e9a98..00000000 --- a/eventuate-common-coordination-leadership-tests/src/main/java/io/eventuate/coordination/leadership/tests/LeaderSelectorTestingWrap.java +++ /dev/null @@ -1,54 +0,0 @@ -package io.eventuate.coordination.leadership.tests; - -import io.eventuate.coordination.leadership.EventuateLeaderSelector; -import io.eventuate.util.test.async.Eventually; -import org.junit.Assert; - -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -public class LeaderSelectorTestingWrap { - private SELECTOR selector; - private AtomicInteger invocationCounter; - private AtomicBoolean leaderFlag; - - public LeaderSelectorTestingWrap(SELECTOR selector, AtomicInteger invocationCounter, AtomicBoolean leaderFlag) { - this.selector = selector; - this.invocationCounter = invocationCounter; - this.leaderFlag = leaderFlag; - } - - public void eventuallyAssertIsLeaderAndCallbackIsInvokedOnce() { - Eventually.eventually(() -> { - Assert.assertTrue(isLeader()); - Assert.assertEquals(1, getInvocationCount()); - }); - } - - public void eventuallyAssertIsNotLeaderAndCallbackIsInvokedOnce() { - Eventually.eventually(() -> { - Assert.assertFalse(isLeader()); - Assert.assertEquals(1, getInvocationCount()); - }); - } - - public int getInvocationCount() { - return invocationCounter.get(); - } - - public boolean isLeader() { - return leaderFlag.get(); - } - - public SELECTOR getSelector() { - return selector; - } - - public void start() { - selector.start(); - } - - public void stop() { - selector.stop(); - } -} diff --git a/eventuate-common-coordination-leadership-tests/src/main/java/io/eventuate/coordination/leadership/tests/SelectorUnderTest.java b/eventuate-common-coordination-leadership-tests/src/main/java/io/eventuate/coordination/leadership/tests/SelectorUnderTest.java new file mode 100644 index 00000000..4971e531 --- /dev/null +++ b/eventuate-common-coordination-leadership-tests/src/main/java/io/eventuate/coordination/leadership/tests/SelectorUnderTest.java @@ -0,0 +1,89 @@ +package io.eventuate.coordination.leadership.tests; + +import io.eventuate.coordination.leadership.EventuateLeaderSelector; +import io.eventuate.coordination.leadership.LeaderSelectedCallback; +import io.eventuate.coordination.leadership.LeadershipController; +import io.eventuate.util.test.async.Eventually; +import org.junit.Assert; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiFunction; + +public class SelectorUnderTest { + private SELECTOR selector; + private final AtomicInteger invocationCounter = new AtomicInteger(0);; + private final AtomicBoolean leaderFlag = new AtomicBoolean(false); + private LeadershipController leadershipController; + + private CountDownLatch sleepLatch; + + public SelectorUnderTest(BiFunction factory) { + this.selector = factory.apply(this::leadershipSelectedCallback, this::leadershipRemovedCallback); + } + + private void leadershipRemovedCallback() { + leaderFlag.set(false); + } + + private void leadershipSelectedCallback(LeadershipController leadershipController) { + this.leadershipController = leadershipController; + leaderFlag.set(true); + invocationCounter.incrementAndGet(); + sleepLatch= new CountDownLatch(1); + try { + sleepLatch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + public void eventuallyAssertIsLeaderAndCallbackIsInvokedOnce() { + Eventually.eventually(() -> { + Assert.assertTrue("should be leader", isLeader()); + Assert.assertEquals(1, getInvocationCount()); + }); + } + + public void eventuallyAssertIsNotLeaderAndCallbackIsInvokedOnce() { + Eventually.eventually(() -> { + Assert.assertFalse("should not be leader", isLeader()); + Assert.assertEquals(1, getInvocationCount()); + }); + } + + public int getInvocationCount() { + return invocationCounter.get(); + } + + public boolean isLeader() { + return leaderFlag.get(); + } + + public SELECTOR getSelector() { + return selector; + } + + public void start() { + selector.start(); + } + + public void stop() { + selector.stop(); + } + + public void relinquish() { + Assert.assertTrue("should be leader", isLeader()); + sleepLatch.countDown(); + leadershipController.stop(); + } + + public void eventuallyAssertIsLeaderAndCallbackIsInvokedTwice() { + Eventually.eventually(() -> { + Assert.assertTrue("should be leader", isLeader()); + Assert.assertEquals(2, getInvocationCount()); + }); + + } +} diff --git a/eventuate-common-coordination-leadership-zookeeper/build.gradle b/eventuate-common-coordination-leadership-zookeeper/build.gradle index 266e4faf..bb4dd0cc 100644 --- a/eventuate-common-coordination-leadership-zookeeper/build.gradle +++ b/eventuate-common-coordination-leadership-zookeeper/build.gradle @@ -5,6 +5,9 @@ dependencies { compile "org.slf4j:slf4j-api:1.7.18" testCompile project(":eventuate-common-coordination-leadership-tests") + testCompile "org.testcontainers:testcontainers:$testContainersVersion" + + testCompile project(":eventuate-common-testcontainers") testCompile "io.eventuate.util:eventuate-util-test:$eventuateUtilVersion" testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" } diff --git a/eventuate-common-coordination-leadership-zookeeper/src/test/java/io/eventuate/coordination/leadership/zookeeper/LeadershipTest.java b/eventuate-common-coordination-leadership-zookeeper/src/test/java/io/eventuate/coordination/leadership/zookeeper/LeadershipTest.java index fa5a9aad..348b87fc 100644 --- a/eventuate-common-coordination-leadership-zookeeper/src/test/java/io/eventuate/coordination/leadership/zookeeper/LeadershipTest.java +++ b/eventuate-common-coordination-leadership-zookeeper/src/test/java/io/eventuate/coordination/leadership/zookeeper/LeadershipTest.java @@ -1,24 +1,23 @@ package io.eventuate.coordination.leadership.zookeeper; +import io.eventuate.common.testcontainers.EventuateZookeeperContainer; +import io.eventuate.common.testcontainers.PropertyProvidingContainer; import io.eventuate.coordination.leadership.LeaderSelectedCallback; -import io.eventuate.coordination.leadership.LeadershipController; import io.eventuate.coordination.leadership.tests.AbstractLeadershipTest; -import io.eventuate.util.test.async.Eventually; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; -import org.junit.Assert; import org.junit.Before; -import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; @SpringBootTest(classes = LeadershipTest.Config.class) @RunWith(SpringJUnit4ClassRunner.class) @@ -29,6 +28,16 @@ public class LeadershipTest extends AbstractLeadershipTest { public static class Config { } + public static EventuateZookeeperContainer zookeeper = new EventuateZookeeperContainer("eventuateio/eventuate-zookeeper:0.19.0.BUILD-SNAPSHOT") + .withReuse(true) + .withNetworkAliases("zookeeper"); + + @DynamicPropertySource + static void registerContainerProperties(DynamicPropertyRegistry registry) { + PropertyProvidingContainer.startAndProvideProperties(registry, zookeeper); + } + + @Value("${eventuatelocal.zookeeper.connection.string}") private String zkUrl; @@ -36,7 +45,7 @@ public static class Config { @Before public void init() { - lockId = String.format("/zk/lock/test/%s", UUID.randomUUID().toString()); + lockId = String.format("/zk/lock/test/%s", UUID.randomUUID()); } @Override diff --git a/eventuate-common-coordination-leadership-zookeeper/src/test/resources/application.properties b/eventuate-common-coordination-leadership-zookeeper/src/test/resources/application.properties index c6e64e82..9d7dbe6d 100644 --- a/eventuate-common-coordination-leadership-zookeeper/src/test/resources/application.properties +++ b/eventuate-common-coordination-leadership-zookeeper/src/test/resources/application.properties @@ -1 +1,2 @@ eventuatelocal.zookeeper.connection.string=localhost:2181 +logging.level.io.eventuate=INFO \ No newline at end of file diff --git a/eventuate-common-testcontainers/src/main/java/io/eventuate/common/testcontainers/EventuateZookeeperContainer.java b/eventuate-common-testcontainers/src/main/java/io/eventuate/common/testcontainers/EventuateZookeeperContainer.java index f6cb2aa7..b12137f2 100644 --- a/eventuate-common-testcontainers/src/main/java/io/eventuate/common/testcontainers/EventuateZookeeperContainer.java +++ b/eventuate-common-testcontainers/src/main/java/io/eventuate/common/testcontainers/EventuateZookeeperContainer.java @@ -16,6 +16,11 @@ public EventuateZookeeperContainer() { withConfiguration(); } + public EventuateZookeeperContainer(String image) { + super(image); + withConfiguration(); + } + public EventuateZookeeperContainer(Path path) { super(new ImageFromDockerfile().withDockerfile(path)); withConfiguration();