@Test
public void shouldIgnoreDelayedAppendEntriesReplyWherePrevLogIndexGreaterThanNextIndexMinusOneAndAppliedIsFalse() throws StorageException, RPCException {

    insertIntoLog(
            NOOP(1, 1),
            NOOP(2, 1),
            NOOP(3, 2)
    );
    becomeLeaderInTerm(3, false);
    assertThatLogContains(
            SENTINEL(),
            NOOP(1, 1),
            NOOP(2, 1),
            NOOP(3, 2),
            NOOP(4, 3)
    );
    Collection<AppendEntries> appendEntriesRequests;
    appendEntriesRequests = getRPCs(4, AppendEntries.class);
    assertThatAppendEntriesHaveValues(appendEntriesRequests, 3, 0, 3, 2, NOOP(4, 3));
    assertThatNoMoreRPCsWereSent();
    // S_02 says it doesn't have entry at index 3
    algorithm.onAppendEntriesReply(S_02, 3, 3, 1, false);
    // move to the next heartbeat
    timer.fastForward();
    // send heartbeats again
    appendEntriesRequests = getRPCs(4, AppendEntries.class);
    for(AppendEntries appendEntries : appendEntriesRequests) {
        if (appendEntries.server.equals(S_02)) { // know for sure they're behind
            assertThatAppendEntriesHasValues(appendEntries,
                    3, 0, 2, 1,
                    NOOP(3, 2),
                    NOOP(4, 3));
        } else { // there's a chance the others are not
            assertThatAppendEntriesHasValues(appendEntries, 3, 0, 3, 2, NOOP(4, 3));
        }
    }
    assertThatNoMoreRPCsWereSent();
    // S_02 says it doesn't have entry at index 2 either
    algorithm.onAppendEntriesReply(S_02, 3, 2, 2, false);
    // move to the next heartbeat
    timer.fastForward();
    // send heartbeats again
    appendEntriesRequests = getRPCs(4, AppendEntries.class);
    for(AppendEntries appendEntries : appendEntriesRequests) {
        if (appendEntries.server.equals(S_02)) { // know for sure they're behind
            assertThatAppendEntriesHasValues(appendEntries,
                    3, 0, 1, 1,
                    NOOP(2, 1),
                    NOOP(3, 2),
                    NOOP(4, 3));
        } else {
            assertThatAppendEntriesHasValues(appendEntries, 3, 0, 3, 2, NOOP(4, 3));
        }
    }
    assertThatNoMoreRPCsWereSent();
    // S_02 _repeats_ that it doesn't have entry at index 2 either
    algorithm.onAppendEntriesReply(S_02, 3, 2, 2, false);
    // move to the next heartbeat
    timer.fastForward();
    // send heartbeats again
    appendEntriesRequests = getRPCs(4, AppendEntries.class);
    for(AppendEntries appendEntries : appendEntriesRequests) {
        if (appendEntries.server.equals(S_02)) { // we shouldn't have changed nextIndex despite the repeat
            assertThatAppendEntriesHasValues(appendEntries,
                    3, 0, 1, 1,
                    NOOP(2, 1),
                    NOOP(3, 2),
                    NOOP(4, 3));
        } else {
            assertThatAppendEntriesHasValues(appendEntries, 3, 0, 3, 2, NOOP(4, 3));
        }
    }
    assertThatNoMoreRPCsWereSent();
    assertThatLogContains(
            SENTINEL(),
            NOOP(1, 1),
            NOOP(2, 1),
            NOOP(3, 2),
            NOOP(4, 3)
    );
    assertThatTermAndCommitIndexHaveValues(3, 0);
}