1{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE TypeOperators #-}
4{-# LANGUAGE ViewPatterns #-}
6module Servant.ClientSpec where
8import Control.Concurrent
9import Control.Exception
10import Control.Monad.Trans.Either
16import Network.Wai.Handler.Warp
23import Servant.ServerSpec
27 :<|> "capture" :> Capture "name" String :> Get Person
28 :<|> "body" :> ReqBody Person :> Post Person
29 :<|> "param" :> QueryParam "name" String :> Get Person
36 :<|> (\ name -> return $ Person name 0)
38 :<|> (\ name -> case name of
39 Just "alice" -> return alice
40 Just name -> left (400, name ++ " not found")
41 Nothing -> left (400, "missing parameter"))
44withServer :: (URIAuth -> IO a) -> IO a
45withServer action = withWaiDaemon (return server) (action . mkHost "localhost")
47getGet :: URIAuth -> EitherT String IO Person
48getCapture :: String -> URIAuth -> EitherT String IO Person
49getBody :: Person -> URIAuth -> EitherT String IO Person
50getQueryParam :: Maybe String -> URIAuth -> EitherT String IO Person
51(getGet :<|> getCapture :<|> getBody :<|> getQueryParam) = client api
55 it "Servant.API.Get" $ withServer $ \ host -> do
56 runEitherT (getGet host) `shouldReturn` Right alice
58 it "Servant.API.Capture" $ withServer $ \ host -> do
59 runEitherT (getCapture "Paula" host) `shouldReturn` Right (Person "Paula" 0)
61 it "Servant.API.ReqBody" $ withServer $ \ host -> do
62 let p = Person "Clara" 42
63 runEitherT (getBody p host) `shouldReturn` Right p
65 it "Servant.API.QueryParam" $ withServer $ \ host -> do
66 runEitherT (getQueryParam (Just "alice") host) `shouldReturn` Right alice
67 Left result <- runEitherT (getQueryParam (Just "bob") host)
68 result `shouldContain` "bob not found"
70 context "client correctly handles error status codes" $ do
71 let test :: WrappedApi -> Spec
72 test (WrappedApi api) =
73 it (show (typeOf api)) $
74 withWaiDaemon (return (serve api (left (500, "error message")))) $
75 \ (mkHost "localhost" -> host) -> do
76 let getResponse :: URIAuth -> EitherT String IO ()
77 getResponse = client api
78 Left result <- runEitherT (getResponse host)
79 result `shouldContain` "error message"
81 (WrappedApi (Proxy :: Proxy Delete)) :
82 (WrappedApi (Proxy :: Proxy (Get ()))) :
83 (WrappedApi (Proxy :: Proxy (Post ()))) :
84 (WrappedApi (Proxy :: Proxy (Put ()))) :
88 WrappedApi :: (HasServer api, Server api ~ EitherT (Int, String) IO a,
89 HasClient api, Client api ~ (URIAuth -> EitherT String IO ()),
91 Proxy api -> WrappedApi
96withWaiDaemon :: IO Application -> (Port -> IO a) -> IO a
97withWaiDaemon mkApplication action = do
98 application <- mkApplication
99 bracket (acquire application) free (\ (_, _, port) -> action port)
101 acquire application = do
102 (notifyStart, waitForStart) <- lvar
103 (notifyKilled, waitForKilled) <- lvar
104 thread <- forkIO $ (do
105 (krakenPort, socket) <- openTestSocket
107 setPort krakenPort $ -- set here just for consistency, shouldn't be
108 -- used (it's set in the socket)
109 setBeforeMainLoop (notifyStart krakenPort)
111 runSettingsSocket settings socket application)
112 `finally` notifyKilled ()
113 krakenPort <- waitForStart
114 return (thread, waitForKilled, krakenPort)
115 free (thread, waitForKilled, _) = do
119 lvar :: IO (a -> IO (), IO a)
122 let put = putMVar mvar
126openTestSocket :: IO (Port, Socket)
128 s <- socket AF_INET Stream defaultProtocol
129 localhost <- inet_addr "127.0.0.1"
130 bind s (SockAddrInet aNY_PORT localhost)
133 return (fromIntegral port, s)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list