packages feed

toxcore-0.2.11: test/Network/Tox/DHT/ClientListSpec.hs

{-# LANGUAGE Trustworthy #-}
module Network.Tox.DHT.ClientListSpec where

import           Test.Hspec
import           Test.QuickCheck

import           Control.Monad                 (unless, when)
import           Data.List                     (sort, sortOn)
import qualified Data.Map                      as Map
import           Data.Ord                      (comparing)
import           Data.Proxy                    (Proxy (..))
import           Network.Tox.Crypto.Key        (PublicKey)
import           Network.Tox.DHT.ClientList    (ClientList)
import qualified Network.Tox.DHT.ClientList    as ClientList
import qualified Network.Tox.DHT.Distance      as Distance
import           Network.Tox.EncodingSpec
import qualified Network.Tox.NodeInfo.NodeInfo as NodeInfo


spec :: Spec
spec = do
  readShowSpec (Proxy :: Proxy ClientList)

  it "has no more than maxSize elements" $
    property $ \clientList ->
      Map.size (ClientList.nodes clientList) `shouldSatisfy` (<= ClientList.maxSize clientList)

  it "removing a node twice has no effect" $
    property $ \baseKey time nodeInfo size ->
      let
        empty        = ClientList.empty baseKey
        afterAdd     = ClientList.addNode time nodeInfo $ empty size
        afterRemove0 = ClientList.removeNode (NodeInfo.publicKey nodeInfo) afterAdd
        afterRemove1 = ClientList.removeNode (NodeInfo.publicKey nodeInfo) afterRemove0
      in
      afterRemove0 `shouldBe` afterRemove1

  it "adding a node twice has no effect" $
    property $ \baseKey time nodeInfo size ->
      let
        empty        = ClientList.empty baseKey
        afterAdd0    = ClientList.addNode time nodeInfo $ empty size
        afterAdd1    = ClientList.addNode time nodeInfo afterAdd0
      in
      afterAdd0 `shouldBe` afterAdd1

  it "adding a non-viable node has no effect" $
    property $ \clientList time nodeInfo ->
      let
        viable   = ClientList.viable nodeInfo clientList
        afterAdd = ClientList.addNode time nodeInfo clientList
      in
      unless viable $ afterAdd `shouldBe` clientList

  describe "addNode" $
    it "keeps the k nodes closest to the base key" $
      property $ \clientList time nodeInfo ->
        let
          allNodes          = (nodeInfo:) $ ClientList.nodeInfos clientList
          keptNodes         = ClientList.nodeInfos $ ClientList.addNode time nodeInfo clientList
          nodeDistance node = Distance.xorDistance (ClientList.baseKey clientList) (NodeInfo.publicKey node)
          sortNodes         = sortOn nodeDistance
        in
        take (ClientList.maxSize clientList) (sortNodes allNodes) `shouldBe` sortNodes keptNodes

  describe "foldNodes" $
    it "iterates over nodes in order of distance from the base key" $
      property $ \clientList ->
        let
          nodes             = reverse $ ClientList.foldNodes (flip (:)) [] clientList
          nodeDistance node = Distance.xorDistance (ClientList.baseKey clientList) (NodeInfo.publicKey node)
          sortNodes         = sortOn nodeDistance
        in
        nodes `shouldBe` sortNodes nodes