servant/servant-server/test/Servant/Server/CombinatorUtilsSpec.hs

176 lines
5.5 KiB
Haskell
Raw Normal View History

2016-10-23 19:36:45 +02:00
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
2016-10-23 19:36:45 +02:00
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Servant.Server.CombinatorUtilsSpec where
import Control.Concurrent
2016-10-23 20:00:52 +02:00
import Data.ByteString.Builder
2016-10-23 19:36:45 +02:00
import Data.ByteString.Lazy
import Data.Monoid
import Data.Proxy
import Data.String.Conversions
2016-10-23 20:00:52 +02:00
import Data.Text
2016-10-23 19:36:45 +02:00
import Network.HTTP.Types
import Network.Wai
import Network.Wai.Internal
import Test.Hspec
import Servant.API
import Servant.Server
import Servant.Server.CombinatorUtils
runApp :: Application -> Request -> IO Response
runApp app req = do
mvar <- newMVar Nothing
2016-10-23 20:26:14 +02:00
ResponseReceived <- app req $ \ response -> do
2016-10-23 19:36:45 +02:00
modifyMVar mvar $ \ Nothing ->
return $ (Just response, ResponseReceived)
modifyMVar mvar $ \mResponse -> do
case mResponse of
Nothing -> error "shouldn't happen"
Just response -> return (Just response, response)
responseBodyLbs :: Response -> IO ByteString
responseBodyLbs response = do
let (_, _, action) = responseToStream response
action $ \ streamingBody -> do
mvar <- newMVar ""
streamingBody
(\ builder -> modifyMVar_ mvar $ \ acc ->
return $ acc <> toLazyByteString builder)
(return ())
readMVar mvar
spec :: Spec
spec = do
it "allows to write capture combinators" $ do
2016-10-23 19:36:45 +02:00
let server = return
app = serve (Proxy :: Proxy (StringCapture :> Get' String)) server
request = defaultRequest{
rawPathInfo = "/foo",
pathInfo = ["foo"]
}
response <- runApp app request
responseBodyLbs response `shouldReturn` "\"foo\""
it "allows to write request check combinators" $ do
let server = return ()
app = serve (Proxy :: Proxy (CheckFooHeader :> Get' ())) server
2016-10-23 19:36:45 +02:00
request = defaultRequest{
requestHeaders =
("Foo", "foo") :
2016-10-23 19:36:45 +02:00
requestHeaders defaultRequest
}
response <- runApp app request
responseBodyLbs response `shouldReturn` "[]"
2016-10-23 19:36:45 +02:00
it "allows to write a combinator that errors out" $ do
2016-10-23 19:36:45 +02:00
let server = return
app = serve (Proxy :: Proxy (StringCapture :> Get' String)) server
request = defaultRequest {
rawPathInfo = "/error",
pathInfo = ["error"]
}
2016-10-23 19:36:45 +02:00
response <- runApp app request
responseStatus response `shouldBe` status418
2016-10-23 19:36:45 +02:00
it "allows to write a combinator using IO" $ do
pending
it "allows to write a combinator by providing a function (Request -> a)" $ do
2016-10-23 20:00:52 +02:00
let server = return
app = serve (Proxy :: Proxy (FooHeader :> Get' String)) server
2016-10-23 20:00:52 +02:00
request = defaultRequest{
requestHeaders =
("Foo", "foo") :
requestHeaders defaultRequest
2016-10-23 20:00:52 +02:00
}
response <- runApp app request
responseBodyLbs response `shouldReturn` "\"foo\""
it "allows to write an auth combinator" $ do
let server (User name) = return name
app = serve (Proxy :: Proxy (AuthCombinator :> Get' String)) server
request = defaultRequest{
requestHeaders =
("Auth", "secret") :
requestHeaders defaultRequest
}
response <- runApp app request
responseStatus response `shouldBe` ok200
responseBodyLbs response `shouldReturn` "\"Alice\""
2016-10-23 20:00:52 +02:00
it "allows to pick the request check phase" $ do
pending
it "allows to write streaming combinators for request bodies" $ do
pending
it "disallows to access the request body unless in the checkBody phase" $ do
pending
it "allows to access the context" $ do
pending
it "allows to write combinators without args" $ do
pending
it "allows to implement combinators based in terms of existing combinators" $ do
2016-10-23 19:36:45 +02:00
pending
type Get' = Get '[JSON]
data StringCapture
instance HasServer api context => HasServer (StringCapture :> api) context where
type ServerT (StringCapture :> api) m = String -> ServerT api m
route = runCI $ implementCaptureCombinator getCapture
getCapture :: Text -> RouteResult String
getCapture = \case
"error" -> FailFatal $ ServantErr 418 "I'm a teapot" "" []
text -> Route $ cs text
data CheckFooHeader
instance HasServer api context => HasServer (CheckFooHeader :> api) context where
type ServerT (CheckFooHeader :> api) m = ServerT api m
route = runCI $ implementRequestCheck checkFooHeader
checkFooHeader :: Request -> RouteResult ()
checkFooHeader request = case lookup "Foo" (requestHeaders request) of
Just _ -> Route ()
Nothing -> FailFatal err400
data AuthCombinator
data User = User String
deriving (Eq, Show)
instance HasServer api context => HasServer (AuthCombinator :> api) context where
type ServerT (AuthCombinator :> api) m = User -> ServerT api m
route = runCI $ implementAuthCombinator checkAuth
checkAuth :: Request -> RouteResult User
checkAuth request = case lookup "Auth" (requestHeaders request) of
Just "secret" -> Route $ User "Alice"
Just _ -> FailFatal err401
Nothing -> FailFatal err400
2016-10-23 20:00:52 +02:00
data FooHeader
2016-10-23 19:36:45 +02:00
2016-10-23 20:00:52 +02:00
instance HasServer api context => HasServer (FooHeader :> api) context where
type ServerT (FooHeader :> api) m = String -> ServerT api m
2016-10-23 20:26:14 +02:00
route = runCI $ argumentCombinator getCustom
2016-10-23 19:36:45 +02:00
getCustom :: Request -> RouteResult String
getCustom request = case lookup "Foo" (requestHeaders request) of
2016-10-23 19:36:45 +02:00
Nothing -> FailFatal err400
Just l -> Route $ cs l