packages feed

kademlia-1.1.0.0: test/Instance.hs

{-|
Module      : Instance
Description : Tests for Network.Kademlia.Instance

Tests specific to Network.Kademlia.Instance.
-}

module Instance where

import Test.HUnit hiding (assert)
import Test.QuickCheck
import Test.QuickCheck.Monadic

import Network.Kademlia.Instance as I
import Network.Kademlia
import Network.Kademlia.Networking
import Network.Kademlia.Types
import Network.Kademlia.ReplyQueue
import qualified Data.ByteString.Char8 as C
import Control.Concurrent.Chan
import Control.Monad (liftM2)
import Data.Maybe (isJust, fromJust)

import TestTypes

-- | The default set of peers
peers :: (Peer, Peer)
peers = let pA = Peer "127.0.0.1" 1122
            pB = Peer "127.0.0.1" 1123
        in (pA, pB)

-- | A set of randomly generated Ids
ids :: (Monad m) => PropertyM m (IdType, IdType)
ids = liftM2 (,) (pick arbitrary) (pick arbitrary)

-- | Checks wether PINGs are handled appropriately
handlesPingCheck :: Assertion
handlesPingCheck = do
    let (pA, pB) = peers

    let (Right (idA, _)) = fromBS . C.replicate 32 $ 'a'
                           :: Either String (IdType, C.ByteString)
    let (Right (idB, _)) = fromBS . C.replicate 32 $ 'b'
                           :: Either String (IdType, C.ByteString)

    rq <- emptyReplyQueue

    khA <- openOn "1122" idA rq :: IO (KademliaHandle IdType String)
    kiB <- create 1123 idB   :: IO (KademliaInstance IdType String)

    startRecvProcess khA

    send khA pB PING
    (Answer sig) <- readChan . timeoutChan $ rq :: IO (Reply IdType String)

    closeK khA
    close kiB

    assertEqual "" (command sig) PONG
    assertEqual "" (peer . source $ sig) pB
    assertEqual "" (nodeId . source $ sig) idB

    return ()

-- | Make sure a stored value can be retrieved
storeAndFindValueCheck :: IdType -> String -> Property
storeAndFindValueCheck key value = monadicIO $ do
    let (pA, pB) = peers
    (idA, idB) <- ids

    receivedCmd <- run $ do
        rq <- emptyReplyQueue

        khA <- openOn "1122" idA rq
        kiB <- create 1123 idB :: IO (KademliaInstance IdType String)

        startRecvProcess khA

        send khA pB $ STORE key value
        send khA pB $ FIND_VALUE key

        -- There is a race condition, so the instance will sometimes try to store
        -- the value in the handle, before replying with a RETURN_VALUE
        (Answer sig) <- readChan . timeoutChan $ rq :: IO (Reply IdType String)
        sig <- case command sig of
                STORE _ _ -> do
                    (Answer sig) <- readChan . timeoutChan $ rq :: IO (Reply IdType String)
                    return sig
                _ -> return sig

        closeK khA
        close kiB

        return . command $ sig

    let cmd = RETURN_VALUE key value :: Command IdType String

    monitor . counterexample $ "Commands inequal: " ++ show cmd ++ " /= " ++ show receivedCmd
    assert $ cmd == receivedCmd

    return ()

-- | Assert that a peer is put into the NodeTree on first encounter
trackingKnownPeersCheck :: Property
trackingKnownPeersCheck = monadicIO $ do
    let (_, pB) = peers
    (idA, idB) <- ids

    (node, kiB) <- run $ do
        rq <- emptyReplyQueue :: IO (ReplyQueue IdType String)

        khA <- openOn "1122" idA rq
        kiB <- create 1123 idB :: IO (KademliaInstance IdType String)

        startRecvProcess khA

        send khA pB $ PING
        readChan . timeoutChan $ rq

        node <- I.lookupNode kiB idA

        closeK khA
        close kiB

        return (node, kiB)

    assert . isJust $ node

    nodes <- run . dumpPeers $ kiB
    assert $ nodes == [fromJust node]

    return ()