public void ShouldBecomeFollowerIfAppendEntriesReceivedFromNewLeader() { var electionTimeoutInMilliseconds = 1000; var initialTerm = 0; var outbox = new ConcurrentBag <object>(); var eventLog = new ConcurrentBag <object>(); var nodeId = Guid.NewGuid(); var numberOfOtherActors = 2; IEnumerable <Guid> getOtherActors() { return(Enumerable.Range(0, numberOfOtherActors).Select(_ => Guid.NewGuid())); } _raftNode = new RaftNode(nodeId, outbox.Add, eventLog.Add, getOtherActors, initialTerm, electionTimeoutInMilliseconds); void BlockUntilTrue(Func <bool> condition) { while (!condition()) { } } // Start the node and let it timeout to trigger an election _raftNode.Tell(new Initialize()); eventLog.BlockUntilAny(msg => msg is RoleStateChanged rsc && rsc.Term == initialTerm); // Look for the change in state to a candidate node Assert.True(eventLog.Count(msg => msg is RoleStateChanged rsc && rsc.NewState == RoleState.Candidate && rsc.Term == initialTerm) == 1); // Send a single heartbeat with a higher term so that the node reverts to // a follower state var leaderId = Guid.NewGuid(); var requesterId = Guid.NewGuid(); _raftNode.Tell(new Request <AppendEntries>(requesterId, new AppendEntries(42, leaderId, 0, 0, new object[0], 0))); // Wait for a response outbox.BlockUntilAny(msg => msg is Response <AppendEntries> response && response.ResponseMessage is AppendEntriesResult ae && ae.Term == 42); var result = outbox.CastAs <Response <AppendEntries> >().First(); Assert.IsType <AppendEntriesResult>(result.ResponseMessage); var responseMessage = (AppendEntriesResult)result.ResponseMessage; Assert.True(responseMessage.Success); // There should also be a state change that shows // that the candidate node reverted back to a follower state eventLog.ShouldHaveAtLeastOne(msg => msg is RoleStateChanged rsc && rsc.NewState == RoleState.Follower && rsc.Term > 42); }