dormouse-client-0.2.0.0: test/Dormouse/ClientSpec.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ViewPatterns #-}
module Dormouse.ClientSpec
( spec
) where
import Control.Concurrent.MVar
import Control.Exception.Safe (MonadThrow)
import Control.Monad.IO.Class
import Control.Monad.Reader
import Data.Aeson (encode, Value)
import qualified Data.Map.Strict as Map
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as LB
import Dormouse.Client
import Dormouse.Client.Test.Class
import Dormouse.Url.QQ
import Dormouse.Client.Generators.Json
import Test.Hspec
import Test.Hspec.Hedgehog
import qualified Hedgehog.Range as Range
data TestEnv = TestEnv
{ sentJson :: MVar (Maybe (LB.ByteString))
, sentContentType :: MVar (Maybe B.ByteString)
, sentAcceptHeader :: MVar (Maybe B.ByteString)
, returnJson :: MVar (Maybe (LB.ByteString))
, triggered :: MVar Bool
}
newtype TestM a = TestM { unTestM :: ReaderT TestEnv IO a } deriving (Functor, Applicative, Monad, MonadReader TestEnv, MonadIO, MonadThrow)
runTestM :: TestEnv -> TestM a -> IO a
runTestM deps app = flip runReaderT deps $ unTestM app
instance MonadDormouseTestClient TestM where
expectLbs (req @ HttpRequest { requestUrl = u, requestMethod = method, requestBody = body, requestHeaders = headers }) = do
let reqUrl = asAnyUrl u
case (reqUrl, method) of
([url|https://starfleet.com/captains|], GET) -> do
testEnv <- ask
_ <- liftIO . swapMVar (triggered testEnv) $ True
pure HttpResponse
{ responseStatusCode = 200
, responseHeaders = Map.empty
, responseBody = encode ()
}
_ -> do
testEnv <- ask
_ <- liftIO . swapMVar (sentJson testEnv) . Just $ requestBody req
_ <- liftIO . swapMVar (sentContentType testEnv) . getHeaderValue "Content-Type" $ req
_ <- liftIO . swapMVar (sentAcceptHeader testEnv) . getHeaderValue "Accept" $ req
maybeResponseJson <- liftIO . readMVar $ returnJson testEnv
let respBs = maybe (encode ()) id $ maybeResponseJson
pure HttpResponse
{ responseStatusCode = 200
, responseHeaders = Map.empty
, responseBody = respBs
}
spec :: Spec
spec = before setup $ do
describe "expect" $ do
it "submits the correct json body, content-type and accept header for a json request" $ \(sentJson', sentContentType', sentAcceptHeader', _, testEnv, jsonGenRanges) -> do
hedgehog $ do
arbJson <- forAll . genJsonValue $ jsonGenRanges
let req = accept noPayload $ supplyBody json arbJson $ post [https|https://testing123.com|]
_ :: HttpResponse Empty <- liftIO $ runTestM testEnv $ expect req
lbs <- liftIO $ readMVar sentJson'
actualContentType <- liftIO $ readMVar sentContentType'
actualAcceptHeader <- liftIO $ readMVar sentAcceptHeader'
let expected = encode arbJson
lbs === (Just expected)
actualContentType === Just "application/json"
actualAcceptHeader === Nothing
it "submits the correct accept header and gets the correct json body for a json response" $ \(_, _, sentAcceptHeader', returnJson', testEnv, jsonGenRanges) -> do
hedgehog $ do
arbJson <- forAll . genJsonValue $ jsonGenRanges
_ <- liftIO $ swapMVar returnJson' $ Just (encode arbJson)
let req = accept json $ supplyBody json () $ post [https|https://testing123.com|]
r :: HttpResponse Value <- liftIO $ runTestM testEnv $ expect req
actualAcceptHeader <- liftIO $ readMVar sentAcceptHeader'
let actualJson = responseBody r
actualJson === arbJson
actualAcceptHeader === Just "application/json"
describe "expectAs" $ do
it "submits the correct json body, content-type and accept header for a json request" $ \(sentJson', sentContentType', sentAcceptHeader', _, testEnv, jsonGenRanges) -> do
hedgehog $ do
arbJson <- forAll . genJsonValue $ jsonGenRanges
let req = supplyBody json arbJson $ post [https|https://testing123.com|]
_ :: HttpResponse Empty <- liftIO $ runTestM testEnv $ expectAs noPayload req
lbs <- liftIO $ readMVar sentJson'
actualContentType <- liftIO $ readMVar sentContentType'
actualAcceptHeader <- liftIO $ readMVar sentAcceptHeader'
let expected = encode arbJson
lbs === (Just expected)
actualContentType === (Just "application/json")
actualAcceptHeader === Nothing
it "submits the correct accept header and gets the correct json body for a json response" $ \(_, _, sentAcceptHeader', returnJson', testEnv, jsonGenRanges) -> do
hedgehog $ do
arbJson <- forAll . genJsonValue $ jsonGenRanges
_ <- liftIO $ swapMVar returnJson' $ Just (encode arbJson)
let req = supplyBody json () $ post [https|https://testing123.com|]
r :: HttpResponse Value <- liftIO $ runTestM testEnv $ expectAs json req
actualAcceptHeader <- liftIO $ readMVar sentAcceptHeader'
let actualJson = responseBody r
actualJson === arbJson
actualAcceptHeader === Nothing
it "matches the expected url and method" $ \(_, _, _, _, testEnv, _) -> do
hedgehog $ do
let req = get [https|https://starfleet.com/captains|]
r :: HttpResponse Value <- liftIO $ runTestM testEnv $ expectAs json req
t <- liftIO . readMVar $ triggered testEnv
t === True
where
setup = do
sentJson' <- newMVar Nothing
sentContentType' <- newMVar Nothing
sentAcceptHeader' <- newMVar Nothing
returnJson' <- newMVar Nothing
triggered' <- newMVar False
let testEnv = TestEnv
{ sentJson = sentJson'
, sentContentType = sentContentType'
, sentAcceptHeader = sentAcceptHeader'
, returnJson = returnJson'
, triggered = triggered'
}
let jsonGenRanges = JsonGenRanges
{ stringRanges = Range.constant 0 15
, doubleRanges = Range.constant (-100000) (1000000)
, arrayLenRanges = Range.constant 0 7
}
return (sentJson', sentContentType', sentAcceptHeader', returnJson', testEnv, jsonGenRanges)