public void testOrderedIncoming() throws Exception {
    TransactionBatchContext batch1 = transactionBatch(0, 1, 2);
    TransactionBatchContext batch2 = transactionBatch(1, 2, 3);

    sequencer.goToActiveMode();
    sequencer.addResentServerTransactionIDs(batch2.getTransactionIDs());
    sequencer.addResentServerTransactionIDs(batch1.getTransactionIDs());
    sequencer.transactionManagerStarted(clientIDs(0, 1));

    // Add a batch that isn't resent, should be processed last
    TransactionBatchContext batch3 = transactionBatch(0, 4, 5);
    TransactionBatchContext batch4 = transactionBatch(1, 4, 5);
    sequencer.addTransactions(batch3);
    sequencer.addTransactions(batch4);

    sequencer.addTransactions(batch2);
    sequencer.addTransactions(batch1);

    ArgumentCaptor<Map> c = ArgumentCaptor.<Map>forClass(Map.class);
    ArgumentCaptor<NodeID> n = ArgumentCaptor.<NodeID>forClass(NodeID.class);

    InOrder inOrder = inOrder(replicatedObjectManager, transactionManager);
    inOrder.verify(transactionManager).incomingTransactions(n.capture(), c.capture());
    inOrder.verify(replicatedObjectManager).relayTransactions(batch1);
    inOrder.verify(transactionManager).incomingTransactions(n.capture(), c.capture());
    inOrder.verify(replicatedObjectManager).relayTransactions(batch2);

    inOrder.verify(transactionManager).incomingTransactions(n.capture(), c.capture());
    inOrder.verify(replicatedObjectManager).relayTransactions(batch3);
    inOrder.verify(transactionManager).incomingTransactions(n.capture(), c.capture());
    inOrder.verify(replicatedObjectManager).relayTransactions(batch4);

    verifyBatches(true, c, batch1, batch2, batch3, batch4);
  }
 private void verifyBatches(
     boolean isResent, ArgumentCaptor<Map> captor, TransactionBatchContext... batch) {
   List<Map> actual = captor.getAllValues();
   Iterator<Map> itr = actual.iterator();
   for (TransactionBatchContext tb : batch) {
     Map am = itr.next();
     for (ServerTransactionID tid : tb.getTransactionIDs()) {
       ServerTransaction actualTxn = (ServerTransaction) am.get(tid);
       assertNotNull(tid.toString(), actualTxn);
       assertEquals(tid.toString(), isResent, actualTxn instanceof ResentServerTransaction);
     }
   }
 }
  public void testCallbackOnResentComplete() throws Exception {
    sequencer.callBackOnResentTxnsInSystemCompletion(callBack);
    sequencer.goToActiveMode();
    TransactionBatchContext context = transactionBatch(0, 1, 2, 3);
    sequencer.addResentServerTransactionIDs(context.getTransactionIDs());
    verify(transactionManager, never()).callBackOnTxnsInSystemCompletion(callBack);

    sequencer.transactionManagerStarted(clientIDs(0));

    verify(transactionManager, never()).callBackOnTxnsInSystemCompletion(callBack);

    sequencer.addTransactions(context);

    verify(transactionManager).callBackOnTxnsInSystemCompletion(callBack);
  }
  public void testResentTxnWrapping() throws Exception {
    TransactionBatchContext batch1 = transactionBatch(0, 1, 2);
    TransactionBatchContext batch2 = transactionBatch(1, 2, 3);

    sequencer.goToActiveMode();
    sequencer.addResentServerTransactionIDs(batch2.getTransactionIDs());
    sequencer.addResentServerTransactionIDs(batch1.getTransactionIDs());
    sequencer.transactionManagerStarted(clientIDs(0, 1));

    sequencer.addTransactions(batch2);
    sequencer.addTransactions(batch1);

    // Add non-resent batches (pass through mode)
    TransactionBatchContext batch3 = transactionBatch(0, 4, 5);
    TransactionBatchContext batch4 = transactionBatch(1, 4, 5);
    sequencer.addTransactions(batch3);
    sequencer.addTransactions(batch4);

    ArgumentCaptor<Map> c = ArgumentCaptor.<Map>forClass(Map.class);
    ArgumentCaptor<NodeID> n = ArgumentCaptor.<NodeID>forClass(NodeID.class);

    InOrder inOrder = inOrder(replicatedObjectManager, transactionManager);
    inOrder.verify(transactionManager).incomingTransactions(n.capture(), c.capture());
    inOrder.verify(replicatedObjectManager).relayTransactions(batch1);
    inOrder.verify(transactionManager).incomingTransactions(n.capture(), c.capture());
    inOrder.verify(replicatedObjectManager).relayTransactions(batch2);

    verifyBatches(true, c, batch1, batch2);

    // reset arg captor
    c = ArgumentCaptor.<Map>forClass(Map.class);

    inOrder.verify(transactionManager).incomingTransactions(n.capture(), c.capture());
    inOrder.verify(replicatedObjectManager).relayTransactions(batch3);
    inOrder.verify(transactionManager).incomingTransactions(n.capture(), c.capture());
    inOrder.verify(replicatedObjectManager).relayTransactions(batch4);

    verifyBatches(false, c, batch3, batch4);
  }
  public void testClientDisconnect() throws Exception {
    sequencer.callBackOnResentTxnsInSystemCompletion(callBack);
    sequencer.goToActiveMode();

    TransactionBatchContext batch1 = transactionBatch(0, 1);
    TransactionBatchContext batch2 = transactionBatch(1, 1);
    TransactionBatchContext batch3 = transactionBatch(2, 1);

    sequencer.addResentServerTransactionIDs(batch1.getTransactionIDs());
    sequencer.addResentServerTransactionIDs(batch2.getTransactionIDs());
    sequencer.addResentServerTransactionIDs(batch3.getTransactionIDs());
    sequencer.transactionManagerStarted(clientIDs(0, 1, 2));

    sequencer.addTransactions(batch2);
    sequencer.clearAllTransactionsFor(batch2.getSourceNodeID());

    sequencer.addTransactions(batch3);
    sequencer.addTransactions(batch1);

    InOrder inOrder = inOrder(transactionManager);
    inOrder
        .verify(transactionManager)
        .incomingTransactions(
            eq(batch1.getSourceNodeID()),
            argThat(hasServerTransactions(serverTransactionID(0, 1))));
    inOrder
        .verify(transactionManager)
        .incomingTransactions(
            eq(batch2.getSourceNodeID()),
            argThat(hasServerTransactions(serverTransactionID(1, 1))));
    inOrder
        .verify(transactionManager)
        .incomingTransactions(
            eq(batch3.getSourceNodeID()),
            argThat(hasServerTransactions(serverTransactionID(2, 1))));

    inOrder.verify(transactionManager).callBackOnTxnsInSystemCompletion(callBack);
  }
  public void testDiscontinuousGIDInOrder() throws Exception {
    // Cut batch1 into 2 sub-batches based on the GID split, batch2 is just 1 batch sitting between
    // batch1 part1 and batch1 part2
    when(gtxm.getGlobalTransactionID(serverTransactionID(0, 1)))
        .thenReturn(new GlobalTransactionID(0));
    when(gtxm.getGlobalTransactionID(serverTransactionID(1, 1)))
        .thenReturn(new GlobalTransactionID(1));
    when(gtxm.getGlobalTransactionID(serverTransactionID(0, 2)))
        .thenReturn(new GlobalTransactionID(2));

    TransactionBatchContext batch1 = transactionBatch(0, 1, 2);
    TransactionBatchContext batch2 = transactionBatch(1, 1);

    sequencer.goToActiveMode();
    sequencer.addResentServerTransactionIDs(batch1.getTransactionIDs());
    sequencer.addResentServerTransactionIDs(batch2.getTransactionIDs());
    sequencer.transactionManagerStarted(clientIDs(0, 1));

    sequencer.addTransactions(batch1);
    sequencer.addTransactions(batch2);

    InOrder inOrder = inOrder(transactionManager, replicatedObjectManager);
    // Make sure transactions are passed through and relayed in 3 chunks (1 per GID range) in the
    // correct order
    inOrder
        .verify(transactionManager)
        .incomingTransactions(
            eq(batch1.getSourceNodeID()),
            argThat(hasServerTransactions(serverTransactionID(0, 1))));
    inOrder
        .verify(replicatedObjectManager)
        .relayTransactions(argThat(hasClientIDAndTxns(0, serverTransactionID(0, 1))));
    inOrder
        .verify(transactionManager)
        .incomingTransactions(
            eq(batch2.getSourceNodeID()),
            argThat(hasServerTransactions(serverTransactionID(1, 1))));
    inOrder
        .verify(replicatedObjectManager)
        .relayTransactions(argThat(hasClientIDAndTxns(1, serverTransactionID(1, 1))));
    inOrder
        .verify(transactionManager)
        .incomingTransactions(
            eq(batch1.getSourceNodeID()),
            argThat(hasServerTransactions(serverTransactionID(0, 2))));
    inOrder
        .verify(replicatedObjectManager)
        .relayTransactions(argThat(hasClientIDAndTxns(0, serverTransactionID(0, 2))));
  }
 private TransactionBatchContext transactionBatch(long clientId, long... transactionIds) {
   TransactionBatchContext context = mock(TransactionBatchContext.class);
   List<ServerTransaction> transactions = new ArrayList<ServerTransaction>();
   List<ServerTransactionID> ids = transactionIDs(clientId, transactionIds);
   for (ServerTransactionID stxId : ids) {
     ServerTransaction transaction = mock(ServerTransaction.class);
     GlobalTransactionID globalTransactionID = getOrCreateGID(stxId);
     when(transaction.getGlobalTransactionID()).thenReturn(globalTransactionID);
     when(transaction.getServerTransactionID()).thenReturn(stxId);
     transactions.add(transaction);
   }
   when(context.getTransactionBatchReader()).thenReturn(batchReader);
   when(context.getNumTxns()).thenReturn(transactionIds.length);
   when(context.getTransactions()).thenReturn(transactions);
   when(context.getTransactionIDs()).thenReturn(Sets.newHashSet(ids));
   when(context.getSourceNodeID()).thenReturn(new ClientID(clientId));
   return context;
 }