packages feed

hats-0.1.0.0: test/MessageProps.hs

{-# LANGUAGE OverloadedStrings    #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module MessageProps
    ( encodeDecodeMessage
    ) where

import Data.Attoparsec.ByteString.Char8
import Data.ByteString.Char8 (ByteString)
import Test.QuickCheck

import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as LBS

import Network.Nats.Types (Sid)
import Network.Nats.Message.Message (Message (..), ProtocolError (..))
import Network.Nats.Message.Parser (parseMessage)
import Network.Nats.Message.Writer (writeMessage)

-- | Arbitrary instance for ProtocolError.
instance Arbitrary ProtocolError where
    arbitrary = elements [ minBound .. ]

-- | Arbitrary instance for Message.
instance Arbitrary Message where
    arbitrary = oneof [ arbitraryInfo
                      , arbitraryConnect
                      , arbitraryMsg
                      , arbitraryPub
                      , arbitrarySub
                      , arbitraryUnsub
                      , arbitraryPingPong
                      , arbitraryOk
                      , arbitraryErr
                      ]
       
-- | Arbitrary generation of Info messages.
arbitraryInfo :: Gen Message
arbitraryInfo =
    INFO <$> perhaps valueString
         <*> perhaps valueString
         <*> perhaps valueString
         <*> perhaps valueString
         <*> perhaps posInt
         <*> arbitrary
         <*> arbitrary
         <*> arbitrary
         <*> arbitrary
         <*> perhaps posInt

-- | Arbitrary generation of Connect messages.
arbitraryConnect :: Gen Message
arbitraryConnect =
    CONNECT <$> arbitrary
            <*> arbitrary
            <*> arbitrary
            <*> perhaps valueString
            <*> perhaps valueString
            <*> perhaps valueString
            <*> perhaps valueString
            <*> perhaps valueString
            <*> perhaps valueString

-- | Arbitrary generation of Msg messages.
arbitraryMsg :: Gen Message
arbitraryMsg = MSG <$> alnumString
                   <*> sid
                   <*> perhaps alnumString
                   <*> payloadString

-- | Arbitrary generation of Pub messages.
arbitraryPub :: Gen Message
arbitraryPub = PUB <$> alnumString
                   <*> perhaps alnumString
                   <*> payloadString

-- | Arbitrary generation of Sub messages.
arbitrarySub :: Gen Message
arbitrarySub = SUB <$> alnumString 
                   <*> perhaps alnumString 
                   <*> sid

-- | Arbitrary generation of Sub messages.
arbitraryUnsub :: Gen Message
arbitraryUnsub = UNSUB <$> sid
                       <*> perhaps posInt

-- | Arbitrary generation of Ping/Pong messages.
arbitraryPingPong :: Gen Message
arbitraryPingPong = oneof [ pure PING, pure PONG ]

-- | Arbitrary generation of Ok messages.
arbitraryOk :: Gen Message
arbitraryOk = pure OK

-- | Arbitrary generation of Err messages.
arbitraryErr :: Gen Message
arbitraryErr = ERR <$> arbitrary

-- | Test by write a Message to ByteString, and parse it back again.
encodeDecodeMessage :: Message -> Bool
encodeDecodeMessage msg =
    let asByteString = LBS.toStrict $ writeMessage msg
    in verify (parse parseMessage asByteString)
    where
      verify :: IResult ByteString Message -> Bool
      verify result =
        case result of
            (Done _ msg2) -> msg == msg2
            (Partial g)   -> verify (g "")
            _             -> False

-- | Generate Maybe's for the tailor made generators.
perhaps :: Gen a -> Gen (Maybe a)
perhaps gen = oneof [ return Nothing, Just <$> gen ]

-- | Generate a ByteString which not contains any quote characters.
valueString :: Gen ByteString
valueString =
    BS.pack <$> listOf (elements selection)
    where
      selection :: [Char]
      selection = ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "+-_!?*(){} "

payloadString :: Gen LBS.ByteString
payloadString = LBS.pack <$> listOf arbitrary

-- | Generate a non-empty ByteString which is plain alphanumeric.
alnumString :: Gen ByteString
alnumString =
    BS.pack <$> listOf1 (elements selection)
    where
      selection :: [Char]
      selection = ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] 

-- | Generate a positive integer. Can be max size of Int.
posInt :: Gen Int
posInt = choose (0, maxBound)

-- | Generate a Sid
sid :: Gen Sid
sid = choose (0, maxBound)