@@ -0,0 +1,81 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE TypeOperators #-}
3+
4+module Servant.ClientSpec where
5+
6+import Control.Concurrent
7+import Control.Exception
8+import Control.Monad.Trans.Either
9+import Data.Proxy
10+import Network.Socket
11+import Network.URI
12+import Network.Wai
13+import Network.Wai.Handler.Warp
14+import Test.Hspec
15+
16+import Servant.Client
17+import Servant.API.Sub
18+import Servant.API.Get
19+import Servant.Server
20+
21+import Servant.ServerSpec
22+
23+type Api =
24+ "a" :> Get Person
25+api :: Proxy Api
26+api = Proxy
27+
28+server :: Application
29+server = serve api (
30+ return alice
31+ )
32+
33+getA :: URIAuth -> EitherT String IO Person
34+getA = client api
35+
36+spec :: Spec
37+spec = do
38+ it "Servant.API.Get" $ withWaiDaemon (return server) $ \ port -> do
39+ runEitherT (getA (mkHost "localhost" port)) `shouldReturn` Right alice
40+
41+
42+-- * utils
43+
44+withWaiDaemon :: IO Application -> (Port -> IO a) -> IO a
45+withWaiDaemon mkApplication action = do
46+ application <- mkApplication
47+ bracket (acquire application) free (\ (_, _, port) -> action port)
48+ where
49+ acquire application = do
50+ (notifyStart, waitForStart) <- lvar
51+ (notifyKilled, waitForKilled) <- lvar
52+ thread <- forkIO $ (do
53+ (krakenPort, socket) <- openTestSocket
54+ let settings =
55+ setPort krakenPort $ -- set here just for consistency, shouldn't be
56+ -- used (it's set in the socket)
57+ setBeforeMainLoop (notifyStart krakenPort)
58+ defaultSettings
59+ runSettingsSocket settings socket application)
60+ `finally` notifyKilled ()
61+ krakenPort <- waitForStart
62+ return (thread, waitForKilled, krakenPort)
63+ free (thread, waitForKilled, _) = do
64+ killThread thread
65+ waitForKilled
66+
67+ lvar :: IO (a -> IO (), IO a)
68+ lvar = do
69+ mvar <- newEmptyMVar
70+ let put = putMVar mvar
71+ wait = readMVar mvar
72+ return (put, wait)
73+
74+openTestSocket :: IO (Port, Socket)
75+openTestSocket = do
76+ s <- socket AF_INET Stream defaultProtocol
77+ localhost <- inet_addr "127.0.0.1"
78+ bind s (SockAddrInet aNY_PORT localhost)
79+ listen s 1
80+ port <- socketPort s
81+ return (fromIntegral port, s)