servant-matrix-param-0.3.3: test/Servant/MatrixParam/ClientSpec.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
module Servant.MatrixParam.ClientSpec (spec) where
import Control.Exception
import Control.Monad
import Data.ByteString (ByteString)
import Data.IORef
import Data.Proxy
import Network.HTTP.Client (Manager, Request, defaultManagerSettings,
managerModifyRequest, managerResponseTimeout,
newManager, path)
import Servant
import Servant.Client
import System.IO.Unsafe
import Test.Hspec
#if MIN_VERSION_http_client(0,5,0)
import Network.HTTP.Client (responseTimeoutMicro)
#endif
#if MIN_VERSION_servant_client(0,9,0)
import Network.Wai.Handler.Warp (testWithApplication)
import Data.Maybe (fromMaybe)
#else
import Control.Monad.Trans.Except (runExceptT)
#endif
import Servant.MatrixParam
import Servant.MatrixParam.Client ()
import Servant.MatrixParam.Server ()
spec :: Spec
spec = do
describe "Servant.API.MatrixParam" $ do
it "generates correct paths when arguments are not Nothing" $ do
cliA (Just "alice") `hasRequestPath` "/a;name=alice"
cliB (Just 3) (Just 2) `hasRequestPath` "/b;foo=3;bar=2"
it "generates correct paths when arguments are Nothing" $ do
cliA Nothing `hasRequestPath` "/a"
cliB Nothing Nothing `hasRequestPath` "/b"
it "can be combined with captures" $ do
cliC 5 (Just 3) False `hasRequestPath` "/5;foo=3"
it "supports matrix flags" $ do
cliC 5 (Just 3) True `hasRequestPath` "/5;foo=3;bool"
#if MIN_VERSION_servant_client(0,9,0)
it "generates paths that servant-server understands" $ do
testWithApplication (return $ serve api server) $ \port -> do
let res = (,,) <$> cliA (Just "There is a there there")
<*> cliB (Just 1) (Just 2)
<*> cliC 5 (Just 1) True
url = BaseUrl Http "localhost" port ""
mgr' <- newManager defaultManagerSettings
runClientM res (ClientEnv mgr' url)
`shouldReturn` Right ("There is a there there", "3", "6")
#endif
------------------------------------------------------------------------------
-- API
------------------------------------------------------------------------------
type Api =
WithMatrixParams "a" '[MatrixParam "name" String] :> Get '[JSON] String
:<|> WithMatrixParams "b" '[MatrixParam "foo" Int, MatrixParam "bar" Int]
:> Get '[JSON] String
:<|> CaptureWithMatrixParams "c" Int '[MatrixParam "foo" Int, MatrixFlag "bool"]
:> Get '[JSON] String
api :: Proxy Api
api = Proxy
#if MIN_VERSION_servant_client(0,9,0)
server :: Server Api
server = e1 :<|> e2 :<|> e3
where
e1 name = return $ fromMaybe "" name
e2 foo bar = return . show $ fromMaybe 0 foo + fromMaybe 0 bar
e3 int foo bool
| bool = return . show $ int + fromMaybe 0 foo
| otherwise = return ""
#endif
------------------------------------------------------------------------------
-- Utils
------------------------------------------------------------------------------
#if MIN_VERSION_servant_client(0,9,0)
hasRequestPath :: ClientM any -> ByteString -> IO ()
#else
hasRequestPath :: (Manager -> BaseUrl -> ClientM any) -> ByteString -> IO ()
#endif
hasRequestPath c expectedPath = do
let anyBurl = BaseUrl Http "localhost" 6660 ""
#if MIN_VERSION_servant_client(0,9,0)
_ <- void (runClientM c $ ClientEnv mgr anyBurl)
`catch` \(_ :: SomeException) -> return ()
#else
_ <- void (runExceptT $ c mgr anyBurl)
`catch` \(_ :: SomeException) -> return ()
#endif
Just r <- readIORef lastRequest
path r `shouldBe` expectedPath
lastRequest :: IORef (Maybe Network.HTTP.Client.Request)
lastRequest = unsafePerformIO $ newIORef Nothing
{-# NOINLINE lastRequest #-}
mgr :: Manager
mgr = unsafePerformIO . newManager $ defaultManagerSettings
{ managerModifyRequest = \req -> writeIORef lastRequest (Just req) >> return req
#if MIN_VERSION_http_client(0,5,0)
, managerResponseTimeout = responseTimeoutMicro 1
#else
, managerResponseTimeout = Just 1
#endif
}
{-# NOINLINE mgr #-}
#if MIN_VERSION_servant_client(0,9,0)
cliA :: Maybe String -> ClientM String
cliB :: Maybe Int -> Maybe Int -> ClientM String
cliC :: Int -> Maybe Int -> Bool -> ClientM String
#else
cliA :: Maybe String -> Manager -> BaseUrl -> ClientM String
cliB :: Maybe Int -> Maybe Int -> Manager -> BaseUrl -> ClientM String
cliC :: Int -> Maybe Int -> Bool -> Manager -> BaseUrl -> ClientM String
#endif
cliA :<|> cliB :<|> cliC = client api