namespace Raft.Test open System.Threading open Raft open NUnit.Framework open FsUnitTyped [] module TestServer = [] let ``Startup sequence, first fumbling steps`` () = let cluster, network = InMemoryCluster.make 5 let logger, logs = TestLogger.make () // Candidate 1 asks server 0 to vote for it. { CandidateTerm = 0 CandidateId = 1 ReplyChannel = fun message -> logger (sprintf "Received message for term %i" message.VoterTerm) CandidateLastLogEntry = 0, 0 } |> Instruction.RequestVote |> Message.Instruction |> cluster.SendMessageDirectly 0 logs () |> shouldEqual [ "Received message for term 0" ] // Candidate 1 asks to be elected again! This is fine, maybe the network is replaying requests // and the network swallowed our reply, so we should reply in the same way. { CandidateTerm = 0 CandidateId = 1 ReplyChannel = fun message -> logger (sprintf "Received message for term %i" message.VoterTerm) CandidateLastLogEntry = 0, 0 } |> Instruction.RequestVote |> Message.Instruction |> cluster.SendMessageDirectly 0 logs () |> shouldEqual [ "Received message for term 0" ; "Received message for term 0" ] // Candidate 2 asks to be elected. We won't vote for them, because we've already voted. // and the network swallowed our reply, so we should reply in the same way. let calls = ref 0 { CandidateTerm = 0 CandidateId = 2 ReplyChannel = fun _ -> Interlocked.Increment calls |> ignore CandidateLastLogEntry = 0, 0 } |> Instruction.RequestVote |> Message.Instruction |> cluster.SendMessageDirectly 0 calls.Value |> shouldEqual 0 [] let ``Startup sequence in prod`` () = let cluster, network = InMemoryCluster.make 5 cluster.Servers.[0].TriggerTimeout () cluster.Servers.[0].Sync () // We sent a message to every other server; process them. for i in 1..4 do network.InboundMessages.[i].Count |> shouldEqual 1 let message = network.InboundMessages.[i].[0] network.InboundMessages.[i].Clear () cluster.SendMessageDirectly (i * 1) message network.InboundMessages.[0].Count |> shouldEqual i for i in 1..4 do cluster.SendMessageDirectly 0 network.InboundMessages.[0].[i - 1] // (the messages we've already processed) network.InboundMessages.[0].Count |> shouldEqual 4 network.InboundMessages.[0].Clear () cluster.Servers.[0].State |> shouldEqual ServerStatus.Leader for i in 1..4 do cluster.Servers.[i].State |> shouldEqual ServerStatus.Follower