package lsr.paxos.replica; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import java.io.IOException; import java.util.concurrent.ConcurrentHashMap; import lsr.common.ClientCommand; import lsr.common.ClientCommand.CommandType; import lsr.common.ClientReply; import lsr.common.ClientReply.Result; import lsr.common.PrimitivesByteArray; import lsr.common.Reply; import lsr.common.Request; import lsr.common.RequestId; import lsr.paxos.NotLeaderException; import lsr.paxos.Paxos; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; public class ReplicaCommandCallbackTest { private Paxos paxos; private ConcurrentHashMap<Long, Reply> lastReplies; private ClientProxy client; @Before public void setUp() { paxos = mock(Paxos.class); lastReplies = new ConcurrentHashMap<Long, Reply>(); client = mock(ClientProxy.class); } @Test public void shouldRedirectToLeaderWhenNotALeader() throws IOException, NotLeaderException { when(paxos.isLeader()).thenReturn(false); when(paxos.getLeaderId()).thenReturn(1); ReplicaCommandCallback callback = new ReplicaCommandCallback(paxos, lastReplies); Request request = new Request(new RequestId(0, 0), new byte[] {1, 2, 3}); ClientCommand command = new ClientCommand(CommandType.REQUEST, request); callback.execute(command, client); ArgumentCaptor<ClientReply> clientReplyCaptor = ArgumentCaptor.forClass(ClientReply.class); verify(client).send(clientReplyCaptor.capture()); ClientReply clientReply = clientReplyCaptor.getValue(); assertEquals(Result.REDIRECT, clientReply.getResult()); assertArrayEquals(PrimitivesByteArray.fromInt(1), clientReply.getValue()); verify(paxos, never()).enqueueRequest(any(Request.class)); } @Test public void shouldProposeNewRequest() throws IOException, NotLeaderException { when(paxos.isLeader()).thenReturn(true); ReplicaCommandCallback callback = new ReplicaCommandCallback(paxos, lastReplies); Request request = new Request(new RequestId(0, 0), new byte[] {1, 2, 3}); ClientCommand command = new ClientCommand(CommandType.REQUEST, request); callback.execute(command, client); verify(paxos).enqueueRequest(request); } @Test public void shouldRedirectToLeaderWhenLeaderIsChanging() throws NotLeaderException, IOException { when(paxos.isLeader()).thenReturn(true); when(paxos.getLeaderId()).thenReturn(1); doThrow(new NotLeaderException("Not a leader")).when(paxos).enqueueRequest(any(Request.class)); ReplicaCommandCallback callback = new ReplicaCommandCallback(paxos, lastReplies); Request request = new Request(new RequestId(0, 0), new byte[] {1, 2, 3}); ClientCommand command = new ClientCommand(CommandType.REQUEST, request); callback.execute(command, client); ArgumentCaptor<ClientReply> clientReplyCaptor = ArgumentCaptor.forClass(ClientReply.class); verify(client).send(clientReplyCaptor.capture()); ClientReply clientReply = clientReplyCaptor.getValue(); assertEquals(Result.REDIRECT, clientReply.getResult()); assertArrayEquals(PrimitivesByteArray.fromInt(1), clientReply.getValue()); } @Test public void shouldDiscardAllCommandsDifferentThanRequest() throws IOException { ReplicaCommandCallback callback = new ReplicaCommandCallback(paxos, lastReplies); Request request = new Request(new RequestId(0, 0), new byte[] {1, 2, 3}); ClientCommand command = new ClientCommand(CommandType.ALIVE, request); callback.execute(command, client); ArgumentCaptor<ClientReply> clientReplyCaptor = ArgumentCaptor.forClass(ClientReply.class); verify(client).send(clientReplyCaptor.capture()); ClientReply clientReply = clientReplyCaptor.getValue(); assertEquals(Result.NACK, clientReply.getResult()); } @Test public void shouldHandleCommandRetransmission() throws IOException { Reply reply = new Reply(new RequestId(0, 0), new byte[] {1, 2, 3}); lastReplies.put((long) 0, reply); ReplicaCommandCallback callback = new ReplicaCommandCallback(paxos, lastReplies); Request request = new Request(new RequestId(0, 0), new byte[] {1, 2, 3}); ClientCommand command = new ClientCommand(CommandType.REQUEST, request); callback.execute(command, client); ArgumentCaptor<ClientReply> clientReplyCaptor = ArgumentCaptor.forClass(ClientReply.class); verify(client).send(clientReplyCaptor.capture()); ClientReply clientReply = clientReplyCaptor.getValue(); assertEquals(Result.OK, clientReply.getResult()); assertArrayEquals(reply.toByteArray(), clientReply.getValue()); } @Test public void shouldHandleOldCommand() throws IOException { Reply reply = new Reply(new RequestId(0, 1), new byte[] {1, 2, 3}); lastReplies.put((long) 0, reply); ReplicaCommandCallback callback = new ReplicaCommandCallback(paxos, lastReplies); Request request = new Request(new RequestId(0, 0), new byte[] {1, 2, 3}); ClientCommand command = new ClientCommand(CommandType.REQUEST, request); callback.execute(command, client); ArgumentCaptor<ClientReply> clientReplyCaptor = ArgumentCaptor.forClass(ClientReply.class); verify(client).send(clientReplyCaptor.capture()); ClientReply clientReply = clientReplyCaptor.getValue(); assertEquals(Result.NACK, clientReply.getResult()); } @Test public void shouldHandleReplyAsNotLeader() { ReplicaCommandCallback callback = new ReplicaCommandCallback(paxos, lastReplies); Request request = new Request(new RequestId(0, 0), new byte[] {1, 2, 3}); Reply reply = new Reply(new RequestId(0, 0), new byte[] {1}); callback.handleReply(request, reply); verifyNoMoreInteractions(client); } @Test public void shouldHandleReplyAsLeader() throws IOException { when(paxos.isLeader()).thenReturn(true); ReplicaCommandCallback callback = new ReplicaCommandCallback(paxos, lastReplies); Request request = new Request(new RequestId(0, 0), new byte[] {1, 2, 3}); Reply reply = new Reply(new RequestId(0, 0), new byte[] {1}); ClientCommand command = new ClientCommand(CommandType.REQUEST, request); callback.execute(command, client); callback.handleReply(request, reply); ArgumentCaptor<ClientReply> clientReplyCaptor = ArgumentCaptor.forClass(ClientReply.class); verify(client).send(clientReplyCaptor.capture()); ClientReply clientReply = clientReplyCaptor.getValue(); assertEquals(Result.OK, clientReply.getResult()); assertArrayEquals(reply.toByteArray(), clientReply.getValue()); } }