@Test
public void testFlipMemberVotingStates() throws Exception {

    String name = "testFlipMemberVotingStates";
    ServerConfigurationPayload persistedServerConfig = new ServerConfigurationPayload(Arrays.asList(
            new ServerInfo("member-1", true), new ServerInfo("member-2", true),
            new ServerInfo("member-3", false)));
    setupPersistedServerConfigPayload(persistedServerConfig, "member-1", name, "cars", "people");
    setupPersistedServerConfigPayload(persistedServerConfig, "member-2", name, "cars", "people");
    setupPersistedServerConfigPayload(persistedServerConfig, "member-3", name, "cars", "people");
    String moduleShardsConfig = "module-shards-member1-and-2-and-3.conf";
    final MemberNode leaderNode1 = MemberNode.builder(memberNodes).akkaConfig("Member1").testName(name)
            .moduleShardsConfig(moduleShardsConfig).datastoreContextBuilder(DatastoreContext.newBuilder()
                    .shardHeartbeatIntervalInMillis(100).shardElectionTimeoutFactor(10))
            .build();
    final MemberNode replicaNode2 = MemberNode.builder(memberNodes).akkaConfig("Member2").testName(name)
            .moduleShardsConfig(moduleShardsConfig).build();
    final MemberNode replicaNode3 = MemberNode.builder(memberNodes).akkaConfig("Member3").testName(name)
            .moduleShardsConfig(moduleShardsConfig).build();
    leaderNode1.configDataStore().waitTillReady();
    leaderNode1.operDataStore().waitTillReady();
    replicaNode3.configDataStore().waitTillReady();
    replicaNode3.operDataStore().waitTillReady();
    verifyVotingStates(leaderNode1.configDataStore(), "cars", new SimpleEntry<>("member-1", TRUE),
            new SimpleEntry<>("member-2", TRUE), new SimpleEntry<>("member-3", FALSE));
    ClusterAdminRpcService service3 = new ClusterAdminRpcService(replicaNode3.configDataStore(),
            replicaNode3.operDataStore(), null);
    RpcResult<FlipMemberVotingStatesForAllShardsOutput> rpcResult = service3.flipMemberVotingStatesForAllShards(
        new FlipMemberVotingStatesForAllShardsInputBuilder().build()).get(10, TimeUnit.SECONDS);
    FlipMemberVotingStatesForAllShardsOutput result = verifySuccessfulRpcResult(rpcResult);
    verifyShardResults(result.getShardResult(), successShardResult("cars", DataStoreType.Config),
            successShardResult("people", DataStoreType.Config),
            successShardResult("cars", DataStoreType.Operational),
            successShardResult("people", DataStoreType.Operational));
    verifyVotingStates(new AbstractDataStore[]{leaderNode1.configDataStore(), leaderNode1.operDataStore(),
            replicaNode2.configDataStore(), replicaNode2.operDataStore(),
            replicaNode3.configDataStore(), replicaNode3.operDataStore()},
            new String[]{"cars", "people"},
            new SimpleEntry<>("member-1", FALSE), new SimpleEntry<>("member-2", FALSE),
            new SimpleEntry<>("member-3", TRUE));
    // Leadership should have transferred to member 3 since it is the only remaining voting member.
    verifyRaftState(leaderNode1.configDataStore(), "cars", raftState -> {
        assertNotNull("Expected non-null leader Id", raftState.getLeader());
        assertTrue("Expected leader member-3. Actual: " + raftState.getLeader(),
                raftState.getLeader().contains("member-3"));
    });
    verifyRaftState(leaderNode1.operDataStore(), "cars", raftState -> {
        assertNotNull("Expected non-null leader Id", raftState.getLeader());
        assertTrue("Expected leader member-3. Actual: " + raftState.getLeader(),
                raftState.getLeader().contains("member-3"));
    });
    // Flip the voting states back to the original states.
    rpcResult = service3.flipMemberVotingStatesForAllShards(
        new FlipMemberVotingStatesForAllShardsInputBuilder().build()).get(10, TimeUnit.SECONDS);
    result = verifySuccessfulRpcResult(rpcResult);
    verifyShardResults(result.getShardResult(), successShardResult("cars", DataStoreType.Config),
            successShardResult("people", DataStoreType.Config),
            successShardResult("cars", DataStoreType.Operational),
            successShardResult("people", DataStoreType.Operational));
    verifyVotingStates(new AbstractDataStore[]{leaderNode1.configDataStore(), leaderNode1.operDataStore(),
            replicaNode2.configDataStore(), replicaNode2.operDataStore(),
            replicaNode3.configDataStore(), replicaNode3.operDataStore()},
            new String[]{"cars", "people"},
            new SimpleEntry<>("member-1", TRUE), new SimpleEntry<>("member-2", TRUE),
            new SimpleEntry<>("member-3", FALSE));
    // Leadership should have transferred to member 1 or 2.
    verifyRaftState(leaderNode1.configDataStore(), "cars", raftState -> {
        assertNotNull("Expected non-null leader Id", raftState.getLeader());
        assertTrue("Expected leader member-1 or member-2. Actual: " + raftState.getLeader(),
                raftState.getLeader().contains("member-1") || raftState.getLeader().contains("member-2"));
    });
}