BaseState implements State { @VisibleForTesting boolean shouldVoteFor(@Nonnull RaftLog log, @Nonnull RequestVote request) { Optional<Replica> votedFor = log.votedFor(); Replica candidate = log.getReplica(request.getCandidateId()); if (votedFor.isPresent()) { if (!votedFor.get().equals(candidate)) { return false; } } assert !votedFor.isPresent() || votedFor.get().equals(candidate); boolean logIsComplete; if (request.getLastLogTerm() > log.lastLogTerm()) { logIsComplete = true; } else if (request.getLastLogTerm() == log.lastLogTerm()) { logIsComplete = request.getLastLogIndex() >= log.lastLogIndex(); } else { logIsComplete = false; } return logIsComplete; } protected  BaseState(@Nullable StateType type, @Nonnull RaftLog log); @Nullable @Override StateType type(); @Override void destroy(RaftStateContext ctx); @Nonnull @Override AppendEntriesResponse appendEntries(@Nonnull RaftStateContext ctx, @Nonnull AppendEntries request); @Nonnull @Override RequestVoteResponse requestVote(@Nonnull RaftStateContext ctx, @Nonnull RequestVote request); @Nonnull @Override CompletableFuture<Object> commitOperation(@Nonnull RaftStateContext ctx, @Nonnull byte[] operation); @Override void doStop(RaftStateContext ctx);  }
@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>empty()); 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); }