package org.robotninjas.barge.state; import org.jetlang.fibers.Fiber; import org.jetlang.fibers.ThreadFiber; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.robotninjas.barge.api.AppendEntries; import org.robotninjas.barge.api.RequestVote; import java.util.Collections; import static org.mockito.Mockito.*; import static org.robotninjas.barge.state.Raft.StateType.*; public class RaftStateContextTest { private final StateFactory factory = mock(StateFactory.class); private final State start = mock(State.class); private final State follower = mock(State.class); private final State candidate = mock(State.class); private final Fiber executor = new ThreadFiber(); private final StateTransitionListener transitionListener = mock(StateTransitionListener.class); private final RaftProtocolListener protocolListener = mock(RaftProtocolListener.class); private final RaftStateContext context = new RaftStateContext("mockstatecontext", factory, executor, Collections.<StateTransitionListener>emptySet(), Collections.<RaftProtocolListener>emptySet()); @Before public void setup() { when(factory.makeState(START)).thenReturn(start); when(factory.makeState(FOLLOWER)).thenReturn(follower); executor.start(); } @Test public void notifiesSuccessfulStateTransitionFromNullToStartToRegisteredListener() throws Exception { context.addTransitionListener(transitionListener); context.setState(null, START); verify(transitionListener).changeState(context, null, START); } @Test public void notifiesInitToRegisteredProtocolListenerWhenInitialized() throws Exception { context.addRaftProtocolListener(protocolListener); context.init(); verify(protocolListener).init(context); } @Test public void notifiesAppendEntriesToRegisteredProtocolListenerWhenReceivingAppendEntries() throws Exception { AppendEntries entries = AppendEntries.getDefaultInstance(); context.addRaftProtocolListener(protocolListener); context.setState(null, FOLLOWER); context.appendEntries(entries); verify(protocolListener).appendEntries(context, entries); } @Test public void notifiesRequestVoteToRegisteredProtocolListenersWhenReceivingRequestVote() throws Exception { RequestVote vote = RequestVote.getDefaultInstance(); context.addRaftProtocolListener(protocolListener); context.setState(null, FOLLOWER); context.requestVote(vote); verify(protocolListener).requestVote(context, vote); } @Test public void notifiesCommitToRegisteredProtocolListenerWhenReceivingCommit() throws Exception { byte[] bytes = new byte[]{(byte) 0xca, (byte) 0xfe, (byte) 0xba, (byte) 0xbe}; context.addRaftProtocolListener(protocolListener); context.setState(null, FOLLOWER); context.commitOperation(bytes); verify(protocolListener).commit(context, bytes); } @Test public void throwsExceptionAndNotifiesInvalidTransitionToRegisteredListenerGivenUnexpectedPreviousState() throws Exception { when(candidate.type()).thenReturn(CANDIDATE); context.addTransitionListener(transitionListener); context.setState(null, START); try { context.setState(candidate, LEADER); } catch (IllegalStateException e) { verify(transitionListener).invalidTransition(context, START, CANDIDATE); } } @Test public void triggersInitOnNewStateWhenTransitioning() throws Exception { context.setState(null, START); verify(start).init(context); } @Test public void delegatesAppendEntriesRequestToCurrentState() throws Exception { AppendEntries appendEntries = AppendEntries.getDefaultInstance(); context.setState(null, FOLLOWER); context.appendEntries(appendEntries); verify(follower).appendEntries(context, appendEntries); } @Test public void delegateRequestVoteToCurrentState() throws Exception { RequestVote requestVote = RequestVote.getDefaultInstance(); context.setState(null, FOLLOWER); context.requestVote(requestVote); verify(follower).requestVote(context, requestVote); } @Test @Ignore public void delegateCommitOperationToCurrentState() throws Exception { byte[] bytes = new byte[]{1}; context.setState(null, FOLLOWER); context.commitOperation(bytes); verify(follower).commitOperation(context, bytes); } }