1 {-# LANGUAGE OverloadedStrings #-}
2 {-# LANGUAGE Rank2Types #-}
3 {-# LANGUAGE NoMonomorphismRestriction #-}
4 {-# LANGUAGE TypeApplications #-}
5 module Hspec.Client where
7 import Control.Arrow (left)
8 import Control.Concurrent (ThreadId, forkIO, killThread)
9 import Control.Monad (Monad(..), when)
11 import Data.Either (Either(..))
12 import Data.Eq (Eq(..))
13 import Data.Function (($), (.))
14 import Data.Functor ((<$>))
16 import Data.Map.Strict (Map)
17 import Data.Maybe (Maybe(..), fromJust)
18 import Data.Semigroup (Semigroup(..))
19 import Data.String (String)
20 import Data.Text (Text)
21 import Prelude (fromIntegral, (+))
24 import Test.Hspec.Wai (liftIO)
26 import Test.Tasty.Hspec
27 import Text.Read (readMaybe)
28 import Text.Show (Show(..))
29 import qualified Data.ByteString.Base64 as BS64
30 import qualified Data.ByteString as BS
31 import qualified Data.ByteString.Lazy as BSL
32 import qualified Data.Map.Strict as Map
33 import qualified Data.Text as Text
34 import qualified Data.Text.Encoding as Text
35 import qualified Data.Text.Lazy as TL
36 import qualified Data.Text.Lazy.Encoding as TL
37 import qualified Network.HTTP.Client as Client
38 import qualified Network.HTTP.Types as HTTP
39 import qualified Network.Socket as Net
40 import qualified Network.Wai as Wai
41 import qualified Network.Wai.Handler.Warp as Warp
42 import qualified Test.Hspec.Wai as Wai
46 api = "auth" </> basicAuth @User "realm"
47 <.> get @() @PlainText
49 srv = server api $ route_auth
51 route_auth User{} (ServerRespond respond) =
52 ServerResponse $ \_req res -> do
53 res $ respond status200 [] ()
56 alice = User "Alice" "pass" 19
57 carol = User "Carol" "pass" 31
66 instance ServerBasicAuth User where
67 serverBasicAuth user pass =
69 case Map.lookup user users of
70 Nothing -> BasicAuth_NoSuchUser
72 | user_pass == pass -> BasicAuth_Authorized u
73 | otherwise -> BasicAuth_BadPassword
75 users :: Map Text User
78 (\u -> (user_name u, u)) <$>
86 instance FromJSON User
87 instance FromForm User
89 instance Arbitrary User where
90 arbitrary = User <$> arbitrary <*> arbitrary
93 hspec :: IO [TestTree]
94 hspec = testSpecs $ describe "Client" $
95 beforeAll (runTestServer srv) $
96 afterAll killTestServer $ do
97 describe "BasicAuth" $ do
98 it "can authenticate" $ \TestServer{..} -> do
99 runClient (clientConnection $ client api (user_name alice) (user_pass alice))
100 `shouldReturn` Right ()
102 -- * Type 'TestServer'
103 data TestServer = TestServer
105 , socket :: Net.Socket
106 , runClient :: forall a. ClientConnection a -> IO (Either ClientError a)
109 runTestServer :: Wai.Application -> IO TestServer
110 runTestServer waiApp = do
111 let baseURI = fromJust $ parseURI "http://localhost:8080"
112 (port, socket) <- openTestSocket
114 Warp.runSettingsSocket
115 (Warp.setPort port $ Warp.defaultSettings)
117 manager <- Client.newManager Client.defaultManagerSettings
118 let runClient = runClientConnection $ clientEnv manager baseURI
119 return $ TestServer{..}
121 killTestServer :: TestServer -> IO ()
122 killTestServer TestServer{..} = do
126 openTestSocket :: IO (Warp.Port, Net.Socket)
128 let host = Net.tupleToHostAddress (127, 0, 0, 1)
130 sock <- Net.socket Net.AF_INET Net.Stream Net.defaultProtocol
131 Net.setSocketOption sock Net.ReuseAddr 1
132 Net.bind sock (Net.SockAddrInet port host)