package org.robotninjas.barge.state; import com.google.common.base.Optional; import com.google.common.util.concurrent.ListenableFuture; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.robotninjas.barge.ClusterConfig; import org.robotninjas.barge.ClusterConfigStub; import org.robotninjas.barge.RaftException; import org.robotninjas.barge.Replica; import org.robotninjas.barge.api.AppendEntries; import org.robotninjas.barge.api.AppendEntriesResponse; import org.robotninjas.barge.api.RequestVote; import org.robotninjas.barge.api.RequestVoteResponse; import org.robotninjas.barge.log.RaftLog; import javax.annotation.Nonnull; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.when; public class BaseStateTest { private final ClusterConfig config = ClusterConfigStub.getStub(); private final Replica self = config.local(); private final Replica candidate = config.getReplica("candidate"); private @Mock RaftLog mockRaftLog; @Before public void initMocks() { MockitoAnnotations.initMocks(this); when(mockRaftLog.currentTerm()).thenReturn(2l); when(mockRaftLog.lastLogIndex()).thenReturn(2l); when(mockRaftLog.lastLogTerm()).thenReturn(2l); when(mockRaftLog.self()).thenReturn(self); when(mockRaftLog.config()).thenReturn(config); when(mockRaftLog.getReplica(anyString())).thenAnswer(new Answer<Replica>() { @Override public Replica answer(InvocationOnMock invocation) throws Throwable { String arg = (String) invocation.getArguments()[0]; return config.getReplica(arg); } }); } @Test public void testHaventVoted() { BaseState state = new EmptyState(mockRaftLog); RequestVote requestVote = RequestVote.newBuilder() .setCandidateId(candidate.toString()) .setLastLogIndex(2) .setLastLogTerm(2) .setTerm(2) .build(); when(mockRaftLog.votedFor()).thenReturn(Optional.<Replica>absent()); boolean shouldVote = state.shouldVoteFor(mockRaftLog, requestVote); assertTrue(shouldVote); } @Test public void testAlreadyVotedForCandidate() { BaseState state = new EmptyState(mockRaftLog); RequestVote requestVote = RequestVote.newBuilder() .setCandidateId(candidate.toString()) .setLastLogIndex(2) .setLastLogTerm(2) .setTerm(2) .build(); when(mockRaftLog.votedFor()).thenReturn(Optional.of(candidate)); boolean shouldVote = state.shouldVoteFor(mockRaftLog, requestVote); assertTrue(shouldVote); } @Test @Ignore public void testCandidateWithGreaterTerm() { BaseState state = new EmptyState(mockRaftLog); RequestVote requestVote = RequestVote.newBuilder() .setCandidateId(candidate.toString()) .setLastLogIndex(2) .setLastLogTerm(3) .setTerm(2) .build(); Replica otherCandidate = config.getReplica("other"); when(mockRaftLog.votedFor()).thenReturn(Optional.of(otherCandidate)); boolean shouldVote = state.shouldVoteFor(mockRaftLog, requestVote); assertTrue(shouldVote); } @Test public void testCandidateWithLesserTerm() { BaseState state = new EmptyState(mockRaftLog); RequestVote requestVote = RequestVote.newBuilder() .setCandidateId(candidate.toString()) .setLastLogIndex(2) .setLastLogTerm(1) .setTerm(2) .build(); Replica otherCandidate = config.getReplica("other"); when(mockRaftLog.votedFor()).thenReturn(Optional.of(otherCandidate)); boolean shouldVote = state.shouldVoteFor(mockRaftLog, requestVote); assertFalse(shouldVote); } @Test public void testCandidateWithLesserIndex() { BaseState state = new EmptyState(mockRaftLog); RequestVote requestVote = RequestVote.newBuilder() .setCandidateId(candidate.toString()) .setLastLogIndex(1) .setLastLogTerm(2) .setTerm(2) .build(); Replica otherCandidate = config.getReplica("other"); when(mockRaftLog.votedFor()).thenReturn(Optional.of(otherCandidate)); boolean shouldVote = state.shouldVoteFor(mockRaftLog, requestVote); assertFalse(shouldVote); } @Test @Ignore public void testCandidateWithGreaterIndex() { BaseState state = new EmptyState(mockRaftLog); RequestVote requestVote = RequestVote.newBuilder() .setCandidateId(candidate.toString()) .setLastLogIndex(3) .setLastLogTerm(2) .setTerm(2) .build(); Replica otherCandidate = config.getReplica("other"); when(mockRaftLog.votedFor()).thenReturn(Optional.of(otherCandidate)); boolean shouldVote = state.shouldVoteFor(mockRaftLog, requestVote); assertTrue(shouldVote); } static class EmptyState extends BaseState { protected EmptyState(RaftLog log) { super(null, log); } @Override public void init(@Nonnull RaftStateContext ctx) { } @Override public void destroy(@Nonnull RaftStateContext ctx) { } @Nonnull @Override public RequestVoteResponse requestVote(@Nonnull RaftStateContext ctx, @Nonnull RequestVote request) { return null; } @Nonnull @Override public AppendEntriesResponse appendEntries(@Nonnull RaftStateContext ctx, @Nonnull AppendEntries request) { return null; } @Nonnull @Override public ListenableFuture<Object> commitOperation(@Nonnull RaftStateContext ctx, @Nonnull byte[] operation) throws RaftException { return null; } } }