packages feed

kademlia-1.1.0.0: test/Implementation.hs

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

Tests specific to Network.Kademlia.Implementation.
-}

module Implementation where

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

import qualified Network.Kademlia as K
import qualified Network.Kademlia.Tree as T
import Network.Kademlia.Types
import Network.Kademlia.Instance

import Control.Monad
import Control.Applicative
import Control.Concurrent.STM

import Data.Maybe (isJust, fromJust)
import qualified Data.ByteString.Char8 as C

constructNetwork :: IdBunch IdType -> PropertyM IO [KademliaInstance IdType String]
constructNetwork idBunch = run $ do
    let entryNode = Node (Peer "127.0.0.1" 1123) (head . getIds $ idBunch)
    instances <- zipWithM K.create [1123..] (getIds idBunch)
                        :: IO [KademliaInstance IdType String]

    forM_ (tail instances) (`K.joinNetwork` entryNode)
    return instances

joinCheck :: IdBunch IdType -> Property
joinCheck idBunch = monadicIO $ do
    instances <- constructNetwork idBunch
    present <- run $ do
        mapM_ K.close instances
        mapM filled instances
    assert . and $ present

    where filled inst = do
            tree <- atomically . readTVar . sTree . state $ inst
            return $ (length . T.toList $ tree) >= 7

-- | Make sure ID clashes are detected properly
idClashCheck :: IdType -> IdType -> Property
idClashCheck idA idB = monadicIO $ do
    let peers = map (Peer "127.0.0.1") [1123..]
        ids = [idA, idB, idA]
        entryNode = Node (Peer "127.0.0.1" 1124) idB

    joinResult <- run $ do
        insts@[kiA, _, kiB] <- zipWithM K.create [1123..] ids
                            :: IO [KademliaInstance IdType String]

        K.joinNetwork kiA $ entryNode
        joinResult <- K.joinNetwork kiB $ entryNode

        mapM_ K.close insts

        return joinResult

    assert $ joinResult == K.IDClash


-- | Make sure an offline peer is detected
nodeDownCheck :: Assertion
nodeDownCheck = do
    let entryNode = Node (Peer "127.0.0.1" 1124) idB
    inst <- K.create 1123 idA :: IO (KademliaInstance IdType String)
    joinResult <- K.joinNetwork inst entryNode
    K.close inst

    assertEqual "" joinResult K.NodeDown

    where idA = IT . C.pack $ "hello"
          idB = IT . C.pack $ "herro"

storeAndLookupCheck :: IdBunch IdType -> IdBunch IdType -> Property
storeAndLookupCheck ids keys = monadicIO $ do
    let keyVal = zip (getIds keys) vals
    instances <- constructNetwork ids

    success <- run $ do
        mapM_ (doStore instances) keyVal

        success <- forM instances $ \inst ->
            and <$> mapM (tryLookup inst) keyVal

        mapM_ K.close instances

        return success

    assert . and $ success

    where vals = take 20 . map (replicate 5) $ ['a'..]
          doStore instances (key, val) = K.store (head instances) key val
          tryLookup inst (key, val) = do
            result <- K.lookup inst key
            case result of
                Just (v, _) -> return $ v == val
                _ -> return False

lookupNodesCheck :: IdBunch IdType -> Property
lookupNodesCheck ids = monadicIO $ do
    instances <- constructNetwork ids

    success <- run $ do
        success <- forM instances $ \inst ->
            and <$> (mapM (tryLookup inst) . getIds $ ids)

        mapM_ K.close instances

        return success

    assert . and $ success

    where tryLookup inst id = check id <$> K.lookupNode inst id
          check id result = isJust result && id == (nodeId . fromJust $ result)