module Hydra.Network.HeartbeatSpec where

import Hydra.Prelude
import Test.Hydra.Prelude

import Control.Concurrent.Class.MonadSTM (MonadSTM (readTVarIO), modifyTVar', newTVarIO)
import Control.Monad.IOSim (runSimOrThrow)
import Hydra.Network (Network (..), NodeId (..))
import Hydra.Network.Heartbeat (Heartbeat (..), withHeartbeat)
import Hydra.Network.Message (Connectivity (Connected, Disconnected))

spec :: Spec
spec :: Spec
spec = Spec -> Spec
forall a. SpecWith a -> SpecWith a
parallel (Spec -> Spec) -> Spec -> Spec
forall a b. (a -> b) -> a -> b
$ do
  let captureOutgoing :: TVar m [a] -> p -> (Network m a -> b) -> b
captureOutgoing TVar m [a]
msgqueue p
_cb Network m a -> b
action =
        Network m a -> b
action (Network m a -> b) -> Network m a -> b
forall a b. (a -> b) -> a -> b
$ Network{$sel:broadcast:Network :: a -> m ()
broadcast = \a
msg -> STM m () -> m ()
forall a. HasCallStack => STM m a -> m a
forall (m :: * -> *) a.
(MonadSTM m, HasCallStack) =>
STM m a -> m a
atomically (STM m () -> m ()) -> STM m () -> m ()
forall a b. (a -> b) -> a -> b
$ TVar m [a] -> ([a] -> [a]) -> STM m ()
forall a. TVar m a -> (a -> a) -> STM m ()
forall (m :: * -> *) a.
MonadSTM m =>
TVar m a -> (a -> a) -> STM m ()
modifyTVar' TVar m [a]
msgqueue (a
msg :)}

      captureIncoming :: TVar m [a] -> a -> m ()
captureIncoming TVar m [a]
receivedMessages a
msg =
        STM m () -> m ()
forall a. HasCallStack => STM m a -> m a
forall (m :: * -> *) a.
(MonadSTM m, HasCallStack) =>
STM m a -> m a
atomically (STM m () -> m ()) -> STM m () -> m ()
forall a b. (a -> b) -> a -> b
$ TVar m [a] -> ([a] -> [a]) -> STM m ()
forall a. TVar m a -> (a -> a) -> STM m ()
forall (m :: * -> *) a.
MonadSTM m =>
TVar m a -> (a -> a) -> STM m ()
modifyTVar' TVar m [a]
receivedMessages (a
msg :)

      nodeId :: NodeId
nodeId = Text -> NodeId
NodeId Text
"node_id-1"

      otherNodeId :: NodeId
otherNodeId = Text -> NodeId
NodeId Text
"node_id-2"

  String -> Expectation -> SpecWith (Arg Expectation)
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"sends a heartbeat message with local host after 500 ms" (Expectation -> SpecWith (Arg Expectation))
-> Expectation -> SpecWith (Arg Expectation)
forall a b. (a -> b) -> a -> b
$ do
    let sentHeartbeats :: [Heartbeat ()]
sentHeartbeats = (forall s. IOSim s [Heartbeat ()]) -> [Heartbeat ()]
forall a. (forall s. IOSim s a) -> a
runSimOrThrow ((forall s. IOSim s [Heartbeat ()]) -> [Heartbeat ()])
-> (forall s. IOSim s [Heartbeat ()]) -> [Heartbeat ()]
forall a b. (a -> b) -> a -> b
$ do
          TVar s [Heartbeat ()]
sentMessages <- [Heartbeat ()] -> IOSim s (TVar (IOSim s) [Heartbeat ()])
forall a. a -> IOSim s (TVar (IOSim s) a)
forall (m :: * -> *) a. MonadSTM m => a -> m (TVar m a)
newTVarIO ([] :: [Heartbeat ()])

          NodeId
-> ConnectionMessages (IOSim s)
-> NetworkComponent (IOSim s) (Heartbeat Any) (Heartbeat ()) ()
-> NetworkComponent (IOSim s) Any () ()
forall (m :: * -> *) msg1 msg a.
(MonadAsync m, MonadDelay m) =>
NodeId
-> ConnectionMessages m
-> NetworkComponent m (Heartbeat msg1) (Heartbeat msg) a
-> NetworkComponent m msg1 msg a
withHeartbeat NodeId
nodeId ConnectionMessages (IOSim s)
forall (m :: * -> *) b. Monad m => b -> m ()
noop (TVar (IOSim s) [Heartbeat ()]
-> NetworkComponent (IOSim s) (Heartbeat Any) (Heartbeat ()) ()
forall {m :: * -> *} {a} {p} {b}.
MonadSTM m =>
TVar m [a] -> p -> (Network m a -> b) -> b
captureOutgoing TVar (IOSim s) [Heartbeat ()]
TVar s [Heartbeat ()]
sentMessages) Any -> IOSim s ()
forall (m :: * -> *) b. Monad m => b -> m ()
noop ((Network (IOSim s) () -> IOSim s ()) -> IOSim s ())
-> (Network (IOSim s) () -> IOSim s ()) -> IOSim s ()
forall a b. (a -> b) -> a -> b
$ \Network (IOSim s) ()
_ ->
            DiffTime -> IOSim s ()
forall (m :: * -> *). MonadDelay m => DiffTime -> m ()
threadDelay DiffTime
1.1

          TVar (IOSim s) [Heartbeat ()] -> IOSim s [Heartbeat ()]
forall a. TVar (IOSim s) a -> IOSim s a
forall (m :: * -> *) a. MonadSTM m => TVar m a -> m a
readTVarIO TVar (IOSim s) [Heartbeat ()]
TVar s [Heartbeat ()]
sentMessages

    [Heartbeat ()]
sentHeartbeats [Heartbeat ()] -> [Heartbeat ()] -> Expectation
forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation
`shouldBe` [NodeId -> Heartbeat ()
forall msg. NodeId -> Heartbeat msg
Ping NodeId
nodeId]

  String -> Expectation -> SpecWith (Arg Expectation)
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"sends Connected when Ping received from other peer" (Expectation -> SpecWith (Arg Expectation))
-> Expectation -> SpecWith (Arg Expectation)
forall a b. (a -> b) -> a -> b
$ do
    let receivedHeartbeats :: [Connectivity]
receivedHeartbeats = (forall s. IOSim s [Connectivity]) -> [Connectivity]
forall a. (forall s. IOSim s a) -> a
runSimOrThrow ((forall s. IOSim s [Connectivity]) -> [Connectivity])
-> (forall s. IOSim s [Connectivity]) -> [Connectivity]
forall a b. (a -> b) -> a -> b
$ do
          TVar s [Connectivity]
receivedMessages <- [Connectivity] -> IOSim s (TVar (IOSim s) [Connectivity])
forall a. a -> IOSim s (TVar (IOSim s) a)
forall (m :: * -> *) a. MonadSTM m => a -> m (TVar m a)
newTVarIO ([] :: [Connectivity])

          NodeId
-> ConnectionMessages (IOSim s)
-> NetworkComponent (IOSim s) (Heartbeat Any) (Heartbeat Any) ()
-> NetworkComponent (IOSim s) Any Any ()
forall (m :: * -> *) msg1 msg a.
(MonadAsync m, MonadDelay m) =>
NodeId
-> ConnectionMessages m
-> NetworkComponent m (Heartbeat msg1) (Heartbeat msg) a
-> NetworkComponent m msg1 msg a
withHeartbeat NodeId
nodeId (TVar (IOSim s) [Connectivity] -> ConnectionMessages (IOSim s)
forall {m :: * -> *} {a}. MonadSTM m => TVar m [a] -> a -> m ()
captureIncoming TVar (IOSim s) [Connectivity]
TVar s [Connectivity]
receivedMessages) (\NetworkCallback (Heartbeat Any) (IOSim s)
incoming Network (IOSim s) (Heartbeat Any) -> IOSim s ()
_ -> NetworkCallback (Heartbeat Any) (IOSim s)
incoming (NodeId -> Heartbeat Any
forall msg. NodeId -> Heartbeat msg
Ping NodeId
otherNodeId)) Any -> IOSim s ()
forall (m :: * -> *) b. Monad m => b -> m ()
noop ((Network (IOSim s) Any -> IOSim s ()) -> IOSim s ())
-> (Network (IOSim s) Any -> IOSim s ()) -> IOSim s ()
forall a b. (a -> b) -> a -> b
$ \Network (IOSim s) Any
_ ->
            DiffTime -> IOSim s ()
forall (m :: * -> *). MonadDelay m => DiffTime -> m ()
threadDelay DiffTime
1

          TVar (IOSim s) [Connectivity] -> IOSim s [Connectivity]
forall a. TVar (IOSim s) a -> IOSim s a
forall (m :: * -> *) a. MonadSTM m => TVar m a -> m a
readTVarIO TVar (IOSim s) [Connectivity]
TVar s [Connectivity]
receivedMessages

    [Connectivity]
receivedHeartbeats [Connectivity] -> [Connectivity] -> Expectation
forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation
`shouldBe` [NodeId -> Connectivity
Connected NodeId
otherNodeId]

  String -> Expectation -> SpecWith (Arg Expectation)
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"sends Connected when any message received from other party" (Expectation -> SpecWith (Arg Expectation))
-> Expectation -> SpecWith (Arg Expectation)
forall a b. (a -> b) -> a -> b
$ do
    let receivedHeartbeats :: [Connectivity]
receivedHeartbeats = (forall s. IOSim s [Connectivity]) -> [Connectivity]
forall a. (forall s. IOSim s a) -> a
runSimOrThrow ((forall s. IOSim s [Connectivity]) -> [Connectivity])
-> (forall s. IOSim s [Connectivity]) -> [Connectivity]
forall a b. (a -> b) -> a -> b
$ do
          TVar s [Connectivity]
receivedMessages <- [Connectivity] -> IOSim s (TVar (IOSim s) [Connectivity])
forall a. a -> IOSim s (TVar (IOSim s) a)
forall (m :: * -> *) a. MonadSTM m => a -> m (TVar m a)
newTVarIO ([] :: [Connectivity])

          NodeId
-> ConnectionMessages (IOSim s)
-> NetworkComponent (IOSim s) (Heartbeat ()) (Heartbeat Any) ()
-> NetworkComponent (IOSim s) () Any ()
forall (m :: * -> *) msg1 msg a.
(MonadAsync m, MonadDelay m) =>
NodeId
-> ConnectionMessages m
-> NetworkComponent m (Heartbeat msg1) (Heartbeat msg) a
-> NetworkComponent m msg1 msg a
withHeartbeat NodeId
nodeId (TVar (IOSim s) [Connectivity] -> ConnectionMessages (IOSim s)
forall {m :: * -> *} {a}. MonadSTM m => TVar m [a] -> a -> m ()
captureIncoming TVar (IOSim s) [Connectivity]
TVar s [Connectivity]
receivedMessages) (\NetworkCallback (Heartbeat ()) (IOSim s)
incoming Network (IOSim s) (Heartbeat Any) -> IOSim s ()
_ -> NetworkCallback (Heartbeat ()) (IOSim s)
incoming (NodeId -> () -> Heartbeat ()
forall msg. NodeId -> msg -> Heartbeat msg
Data NodeId
otherNodeId ())) () -> IOSim s ()
forall (m :: * -> *) b. Monad m => b -> m ()
noop ((Network (IOSim s) Any -> IOSim s ()) -> IOSim s ())
-> (Network (IOSim s) Any -> IOSim s ()) -> IOSim s ()
forall a b. (a -> b) -> a -> b
$ \Network (IOSim s) Any
_ ->
            DiffTime -> IOSim s ()
forall (m :: * -> *). MonadDelay m => DiffTime -> m ()
threadDelay DiffTime
1

          TVar (IOSim s) [Connectivity] -> IOSim s [Connectivity]
forall a. TVar (IOSim s) a -> IOSim s a
forall (m :: * -> *) a. MonadSTM m => TVar m a -> m a
readTVarIO TVar (IOSim s) [Connectivity]
TVar s [Connectivity]
receivedMessages

    [Connectivity]
receivedHeartbeats [Connectivity] -> [Connectivity] -> Expectation
forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation
`shouldBe` [NodeId -> Connectivity
Connected NodeId
otherNodeId]

  String -> Expectation -> SpecWith (Arg Expectation)
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"do not send Connected on subsequent messages from already Connected party" (Expectation -> SpecWith (Arg Expectation))
-> Expectation -> SpecWith (Arg Expectation)
forall a b. (a -> b) -> a -> b
$ do
    let receivedHeartbeats :: [Connectivity]
receivedHeartbeats = (forall s. IOSim s [Connectivity]) -> [Connectivity]
forall a. (forall s. IOSim s a) -> a
runSimOrThrow ((forall s. IOSim s [Connectivity]) -> [Connectivity])
-> (forall s. IOSim s [Connectivity]) -> [Connectivity]
forall a b. (a -> b) -> a -> b
$ do
          TVar s [Connectivity]
receivedMessages <- [Connectivity] -> IOSim s (TVar (IOSim s) [Connectivity])
forall a. a -> IOSim s (TVar (IOSim s) a)
forall (m :: * -> *) a. MonadSTM m => a -> m (TVar m a)
newTVarIO ([] :: [Connectivity])

          NodeId
-> ConnectionMessages (IOSim s)
-> NetworkComponent (IOSim s) (Heartbeat ()) (Heartbeat Any) ()
-> NetworkComponent (IOSim s) () Any ()
forall (m :: * -> *) msg1 msg a.
(MonadAsync m, MonadDelay m) =>
NodeId
-> ConnectionMessages m
-> NetworkComponent m (Heartbeat msg1) (Heartbeat msg) a
-> NetworkComponent m msg1 msg a
withHeartbeat NodeId
nodeId (TVar (IOSim s) [Connectivity] -> ConnectionMessages (IOSim s)
forall {m :: * -> *} {a}. MonadSTM m => TVar m [a] -> a -> m ()
captureIncoming TVar (IOSim s) [Connectivity]
TVar s [Connectivity]
receivedMessages) (\NetworkCallback (Heartbeat ()) (IOSim s)
incoming Network (IOSim s) (Heartbeat Any) -> IOSim s ()
_ -> NetworkCallback (Heartbeat ()) (IOSim s)
incoming (NodeId -> () -> Heartbeat ()
forall msg. NodeId -> msg -> Heartbeat msg
Data NodeId
otherNodeId ()) IOSim s () -> IOSim s () -> IOSim s ()
forall a b. IOSim s a -> IOSim s b -> IOSim s b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> NetworkCallback (Heartbeat ()) (IOSim s)
incoming (NodeId -> Heartbeat ()
forall msg. NodeId -> Heartbeat msg
Ping NodeId
otherNodeId)) () -> IOSim s ()
forall (m :: * -> *) b. Monad m => b -> m ()
noop ((Network (IOSim s) Any -> IOSim s ()) -> IOSim s ())
-> (Network (IOSim s) Any -> IOSim s ()) -> IOSim s ()
forall a b. (a -> b) -> a -> b
$ \Network (IOSim s) Any
_ ->
            DiffTime -> IOSim s ()
forall (m :: * -> *). MonadDelay m => DiffTime -> m ()
threadDelay DiffTime
1

          TVar (IOSim s) [Connectivity] -> IOSim s [Connectivity]
forall a. TVar (IOSim s) a -> IOSim s a
forall (m :: * -> *) a. MonadSTM m => TVar m a -> m a
readTVarIO TVar (IOSim s) [Connectivity]
TVar s [Connectivity]
receivedMessages

    [Connectivity]
receivedHeartbeats [Connectivity] -> [Connectivity] -> Expectation
forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation
`shouldBe` [NodeId -> Connectivity
Connected NodeId
otherNodeId]

  String -> Expectation -> SpecWith (Arg Expectation)
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"sends Disconnected given no messages has been received from known party within twice heartbeat delay" (Expectation -> SpecWith (Arg Expectation))
-> Expectation -> SpecWith (Arg Expectation)
forall a b. (a -> b) -> a -> b
$ do
    let receivedHeartbeats :: [Connectivity]
receivedHeartbeats = (forall s. IOSim s [Connectivity]) -> [Connectivity]
forall a. (forall s. IOSim s a) -> a
runSimOrThrow ((forall s. IOSim s [Connectivity]) -> [Connectivity])
-> (forall s. IOSim s [Connectivity]) -> [Connectivity]
forall a b. (a -> b) -> a -> b
$ do
          TVar s [Connectivity]
receivedMessages <- [Connectivity] -> IOSim s (TVar (IOSim s) [Connectivity])
forall a. a -> IOSim s (TVar (IOSim s) a)
forall (m :: * -> *) a. MonadSTM m => a -> m (TVar m a)
newTVarIO ([] :: [Connectivity])

          let component :: (Heartbeat msg -> m a) -> (Network m msg -> m a) -> m ()
component Heartbeat msg -> m a
incoming Network m msg -> m a
action =
                m a -> m () -> m ()
forall a b. m a -> m b -> m ()
forall (m :: * -> *) a b. MonadAsync m => m a -> m b -> m ()
race_
                  (Network m msg -> m a
action ((msg -> m ()) -> Network m msg
forall (m :: * -> *) msg. (msg -> m ()) -> Network m msg
Network msg -> m ()
forall (m :: * -> *) b. Monad m => b -> m ()
noop))
                  (Heartbeat msg -> m a
incoming (NodeId -> Heartbeat msg
forall msg. NodeId -> Heartbeat msg
Ping NodeId
otherNodeId) m a -> m () -> m ()
forall a b. m a -> m b -> m b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> DiffTime -> m ()
forall (m :: * -> *). MonadDelay m => DiffTime -> m ()
threadDelay DiffTime
4 m () -> m a -> m a
forall a b. m a -> m b -> m b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Heartbeat msg -> m a
incoming (NodeId -> Heartbeat msg
forall msg. NodeId -> Heartbeat msg
Ping NodeId
otherNodeId) m a -> m () -> m ()
forall a b. m a -> m b -> m b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> DiffTime -> m ()
forall (m :: * -> *). MonadDelay m => DiffTime -> m ()
threadDelay DiffTime
7)

          NodeId
-> ConnectionMessages (IOSim s)
-> NetworkComponent (IOSim s) (Heartbeat Any) (Heartbeat Any) ()
-> NetworkComponent (IOSim s) Any Any ()
forall (m :: * -> *) msg1 msg a.
(MonadAsync m, MonadDelay m) =>
NodeId
-> ConnectionMessages m
-> NetworkComponent m (Heartbeat msg1) (Heartbeat msg) a
-> NetworkComponent m msg1 msg a
withHeartbeat NodeId
nodeId (TVar (IOSim s) [Connectivity] -> ConnectionMessages (IOSim s)
forall {m :: * -> *} {a}. MonadSTM m => TVar m [a] -> a -> m ()
captureIncoming TVar (IOSim s) [Connectivity]
TVar s [Connectivity]
receivedMessages) NetworkComponent (IOSim s) (Heartbeat Any) (Heartbeat Any) ()
forall {m :: * -> *} {m :: * -> *} {msg} {a} {msg} {a}.
(MonadAsync m, MonadDelay m, Monad m) =>
(Heartbeat msg -> m a) -> (Network m msg -> m a) -> m ()
component Any -> IOSim s ()
forall (m :: * -> *) b. Monad m => b -> m ()
noop ((Network (IOSim s) Any -> IOSim s ()) -> IOSim s ())
-> (Network (IOSim s) Any -> IOSim s ()) -> IOSim s ()
forall a b. (a -> b) -> a -> b
$ \Network (IOSim s) Any
_ ->
            DiffTime -> IOSim s ()
forall (m :: * -> *). MonadDelay m => DiffTime -> m ()
threadDelay DiffTime
20

          TVar (IOSim s) [Connectivity] -> IOSim s [Connectivity]
forall a. TVar (IOSim s) a -> IOSim s a
forall (m :: * -> *) a. MonadSTM m => TVar m a -> m a
readTVarIO TVar (IOSim s) [Connectivity]
TVar s [Connectivity]
receivedMessages

    [Connectivity]
receivedHeartbeats [Connectivity] -> [Connectivity] -> Expectation
forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation
`shouldBe` [NodeId -> Connectivity
Disconnected NodeId
otherNodeId, NodeId -> Connectivity
Connected NodeId
otherNodeId]

  String -> Expectation -> SpecWith (Arg Expectation)
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"stop sending heartbeat message given action sends a message" (Expectation -> SpecWith (Arg Expectation))
-> Expectation -> SpecWith (Arg Expectation)
forall a b. (a -> b) -> a -> b
$ do
    let sentHeartbeats :: [Heartbeat ()]
sentHeartbeats = (forall s. IOSim s [Heartbeat ()]) -> [Heartbeat ()]
forall a. (forall s. IOSim s a) -> a
runSimOrThrow ((forall s. IOSim s [Heartbeat ()]) -> [Heartbeat ()])
-> (forall s. IOSim s [Heartbeat ()]) -> [Heartbeat ()]
forall a b. (a -> b) -> a -> b
$ do
          TVar s [Heartbeat ()]
sentMessages <- [Heartbeat ()] -> IOSim s (TVar (IOSim s) [Heartbeat ()])
forall a. a -> IOSim s (TVar (IOSim s) a)
forall (m :: * -> *) a. MonadSTM m => a -> m (TVar m a)
newTVarIO ([] :: [Heartbeat ()])

          NodeId
-> ConnectionMessages (IOSim s)
-> NetworkComponent (IOSim s) (Heartbeat Any) (Heartbeat ()) ()
-> NetworkComponent (IOSim s) Any () ()
forall (m :: * -> *) msg1 msg a.
(MonadAsync m, MonadDelay m) =>
NodeId
-> ConnectionMessages m
-> NetworkComponent m (Heartbeat msg1) (Heartbeat msg) a
-> NetworkComponent m msg1 msg a
withHeartbeat NodeId
nodeId ConnectionMessages (IOSim s)
forall (m :: * -> *) b. Monad m => b -> m ()
noop (TVar (IOSim s) [Heartbeat ()]
-> NetworkComponent (IOSim s) (Heartbeat Any) (Heartbeat ()) ()
forall {m :: * -> *} {a} {p} {b}.
MonadSTM m =>
TVar m [a] -> p -> (Network m a -> b) -> b
captureOutgoing TVar (IOSim s) [Heartbeat ()]
TVar s [Heartbeat ()]
sentMessages) Any -> IOSim s ()
forall (m :: * -> *) b. Monad m => b -> m ()
noop ((Network (IOSim s) () -> IOSim s ()) -> IOSim s ())
-> (Network (IOSim s) () -> IOSim s ()) -> IOSim s ()
forall a b. (a -> b) -> a -> b
$ \Network{() -> IOSim s ()
$sel:broadcast:Network :: forall (m :: * -> *) msg. Network m msg -> msg -> m ()
broadcast :: () -> IOSim s ()
broadcast} -> do
            DiffTime -> IOSim s ()
forall (m :: * -> *). MonadDelay m => DiffTime -> m ()
threadDelay DiffTime
0.6
            () -> IOSim s ()
broadcast ()
            DiffTime -> IOSim s ()
forall (m :: * -> *). MonadDelay m => DiffTime -> m ()
threadDelay DiffTime
1

          TVar (IOSim s) [Heartbeat ()] -> IOSim s [Heartbeat ()]
forall a. TVar (IOSim s) a -> IOSim s a
forall (m :: * -> *) a. MonadSTM m => TVar m a -> m a
readTVarIO TVar (IOSim s) [Heartbeat ()]
TVar s [Heartbeat ()]
sentMessages

    [Heartbeat ()]
sentHeartbeats [Heartbeat ()] -> [Heartbeat ()] -> Expectation
forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation
`shouldBe` [NodeId -> () -> Heartbeat ()
forall msg. NodeId -> msg -> Heartbeat msg
Data NodeId
nodeId (), NodeId -> Heartbeat ()
forall msg. NodeId -> Heartbeat msg
Ping NodeId
nodeId]

  String -> Expectation -> SpecWith (Arg Expectation)
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"restart sending heartbeat messages given last message sent is older than heartbeat delay" (Expectation -> SpecWith (Arg Expectation))
-> Expectation -> SpecWith (Arg Expectation)
forall a b. (a -> b) -> a -> b
$ do
    let sentHeartbeats :: [Heartbeat ()]
sentHeartbeats = (forall s. IOSim s [Heartbeat ()]) -> [Heartbeat ()]
forall a. (forall s. IOSim s a) -> a
runSimOrThrow ((forall s. IOSim s [Heartbeat ()]) -> [Heartbeat ()])
-> (forall s. IOSim s [Heartbeat ()]) -> [Heartbeat ()]
forall a b. (a -> b) -> a -> b
$ do
          TVar s [Heartbeat ()]
sentMessages <- [Heartbeat ()] -> IOSim s (TVar (IOSim s) [Heartbeat ()])
forall a. a -> IOSim s (TVar (IOSim s) a)
forall (m :: * -> *) a. MonadSTM m => a -> m (TVar m a)
newTVarIO ([] :: [Heartbeat ()])

          NodeId
-> ConnectionMessages (IOSim s)
-> NetworkComponent (IOSim s) (Heartbeat Any) (Heartbeat ()) ()
-> NetworkComponent (IOSim s) Any () ()
forall (m :: * -> *) msg1 msg a.
(MonadAsync m, MonadDelay m) =>
NodeId
-> ConnectionMessages m
-> NetworkComponent m (Heartbeat msg1) (Heartbeat msg) a
-> NetworkComponent m msg1 msg a
withHeartbeat NodeId
nodeId ConnectionMessages (IOSim s)
forall (m :: * -> *) b. Monad m => b -> m ()
noop (TVar (IOSim s) [Heartbeat ()]
-> NetworkComponent (IOSim s) (Heartbeat Any) (Heartbeat ()) ()
forall {m :: * -> *} {a} {p} {b}.
MonadSTM m =>
TVar m [a] -> p -> (Network m a -> b) -> b
captureOutgoing TVar (IOSim s) [Heartbeat ()]
TVar s [Heartbeat ()]
sentMessages) Any -> IOSim s ()
forall (m :: * -> *) b. Monad m => b -> m ()
noop ((Network (IOSim s) () -> IOSim s ()) -> IOSim s ())
-> (Network (IOSim s) () -> IOSim s ()) -> IOSim s ()
forall a b. (a -> b) -> a -> b
$ \Network{() -> IOSim s ()
$sel:broadcast:Network :: forall (m :: * -> *) msg. Network m msg -> msg -> m ()
broadcast :: () -> IOSim s ()
broadcast} -> do
            DiffTime -> IOSim s ()
forall (m :: * -> *). MonadDelay m => DiffTime -> m ()
threadDelay DiffTime
0.6
            () -> IOSim s ()
broadcast ()
            DiffTime -> IOSim s ()
forall (m :: * -> *). MonadDelay m => DiffTime -> m ()
threadDelay DiffTime
3.6

          TVar (IOSim s) [Heartbeat ()] -> IOSim s [Heartbeat ()]
forall a. TVar (IOSim s) a -> IOSim s a
forall (m :: * -> *) a. MonadSTM m => TVar m a -> m a
readTVarIO TVar (IOSim s) [Heartbeat ()]
TVar s [Heartbeat ()]
sentMessages

    [Heartbeat ()]
sentHeartbeats [Heartbeat ()] -> [Heartbeat ()] -> Expectation
forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation
`shouldBe` [NodeId -> Heartbeat ()
forall msg. NodeId -> Heartbeat msg
Ping NodeId
nodeId, NodeId -> () -> Heartbeat ()
forall msg. NodeId -> msg -> Heartbeat msg
Data NodeId
nodeId (), NodeId -> Heartbeat ()
forall msg. NodeId -> Heartbeat msg
Ping NodeId
nodeId]

noop :: Monad m => b -> m ()
noop :: forall (m :: * -> *) b. Monad m => b -> m ()
noop = m () -> b -> m ()
forall a b. a -> b -> a
const (m () -> b -> m ()) -> m () -> b -> m ()
forall a b. (a -> b) -> a -> b
$ () -> m ()
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()