json-rpc-0.5.0.0: test/Network/JsonRpc/Tests.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE Rank2Types #-}
module Network.JsonRpc.Tests (tests) where
import Control.Applicative
import Control.Concurrent.Async.Lifted
import Control.Concurrent.STM
import qualified Data.ByteString.Lazy as L
import Data.Conduit
import qualified Data.Conduit.List as CL
import Data.Conduit.TMChan
import Control.Monad
import Control.Monad.Logger
import Control.Monad.Trans
import Data.Aeson
import Data.Aeson.Types
import Data.Either
import Data.Maybe
import Network.JsonRpc
import Network.JsonRpc.Arbitrary()
import Test.QuickCheck
import Test.QuickCheck.Monadic
import Test.Framework
import Test.Framework.Providers.QuickCheck2
tests :: [Test]
tests =
[ testGroup "JSON-RPC Requests"
[ testProperty "Check fields"
(reqFields :: Request -> Bool)
, testProperty "Encode/decode"
(testEncodeDecode :: Request -> Bool)
]
, testGroup "JSON-RPC Notifications"
[ testProperty "Check fields"
(notifFields :: Notif -> Bool)
, testProperty "Encode/decode"
(testEncodeDecode :: Notif -> Bool)
]
, testGroup "JSON-RPC Responses"
[ testProperty "Check fields"
(resFields :: Response -> Bool)
, testProperty "Encode/decode"
(testEncodeDecode :: Response -> Bool)
]
, testGroup "JSON-RPC Errors"
[ testProperty "Check fields"
(errFields :: Err -> Bool)
, testProperty "Encode/decode"
(testEncodeDecode :: Err -> Bool)
]
, testGroup "Network"
[ testProperty "Test server" serverTest
, testProperty "Test client" clientTest
]
]
checkVerId :: Ver -> Id -> Object -> Parser Bool
checkVerId ver i o = do
j <- o .:? "jsonrpc"
guard $ if ver == V2 then j == Just (String "2.0") else isNothing j
o .:? "id" .!= IdNull >>= guard . (==i)
return True
checkFieldsReqNotif :: Ver -> Method -> Value -> Id -> Object -> Parser Bool
checkFieldsReqNotif ver m v i o = do
checkVerId ver i o >>= guard
o .: "method" >>= guard . (==m)
o .: "params" >>= guard . (==v)
return True
checkFieldsReq :: Request -> Object -> Parser Bool
checkFieldsReq (Request ver m v i) = checkFieldsReqNotif ver m v i
checkFieldsNotif :: Notif -> Object -> Parser Bool
checkFieldsNotif (Notif ver m v) = checkFieldsReqNotif ver m v IdNull
checkFieldsRes :: Response -> Object -> Parser Bool
checkFieldsRes (Response ver v i) o = do
checkVerId ver i o >>= guard
o .: "result" >>= guard . (==v)
return True
checkFieldsErr :: Err -> Object -> Parser Bool
checkFieldsErr (Err ver e i) o = do
checkVerId ver i o >>= guard
o .: "error" >>= guard . (==e)
return True
testFields :: ToJSON r => (Object -> Parser Bool) -> r -> Bool
testFields ck r = fromMaybe False . parseMaybe f $ toJSON r where
f = withObject "json" ck
testEncodeDecode :: (Eq r, ToJSON r, FromJSON r) => r -> Bool
testEncodeDecode r = maybe False (==r) $ parseMaybe parseJSON (toJSON r)
reqFields :: Request -> Bool
reqFields rq = testFields (checkFieldsReq rq) rq
notifFields :: Notif -> Bool
notifFields nt = testFields (checkFieldsNotif nt) nt
resFields :: Response -> Bool
resFields rs = testFields (checkFieldsRes rs) rs
errFields :: Err -> Bool
errFields er = testFields (checkFieldsErr er) er
serverTest :: ([Request], Ver) -> Property
serverTest (reqs, ver) = monadicIO $ do
rt <- run $ runNoLoggingT $ do
(bso, bsi) <- liftIO . atomically $ (,) <$> newTBMChan 16
<*> newTBMChan 16
let snk = sinkTBMChan bso False
src = sourceTBMChan bsi
withAsync (srv snk src) $ const $
withAsync (sender bsi) $ const $
receiver bso []
assert $ length rt == length reqs
assert $ null rt || all isJust rt
assert $ params == reverse (results rt)
where
r q = return $ Right (q :: Value)
srv snk src = runJsonRpcT ver r
(encodeConduit =$ snk) (src =$ decodeConduit ver) dummySrv
sender bsi = forM_ reqs $ liftIO . atomically .
writeTBMChan bsi . L.toStrict . encode . MsgRequest
receiver bso xs = if length xs == length reqs
then return xs
else liftIO (atomically $ readTBMChan bso) >>= \b -> case b of
Just x -> do
let res = decodeStrict' x :: Maybe Response
receiver bso (res:xs)
Nothing -> undefined
params = map getReqParams reqs
results = map $ getResult . fromJust
clientTest :: ([Value], Ver) -> Property
clientTest (qs, ver) = monadicIO $ do
rt <- run $ runNoLoggingT $ do
(bso, bsi) <- liftIO . atomically $ (,) <$> newTBMChan 16
<*> newTBMChan 16
let snk = sinkTBMChan bso False
src = sourceTBMChan bsi
csnk = sinkTBMChan bsi False
csrc = sourceTBMChan bso
withAsync (srv snk src) $ const $ cli
(CL.map Right =$ csnk)
(csrc $= CL.map Right)
assert $ length rt == length qs
assert $ null rt || all correct rt
assert $ qs == results rt
where
r q = return $ Right (q :: Value)
srv snk src = runJsonRpcT ver r snk src dummySrv
cli snk src = runJsonRpcT ver r snk src . forM qs $ sendRequest
results = map fromJust . rights
correct (Right (Just _)) = True
correct _ = False