2014-10-27 08:10:48 +01:00
|
|
|
{-# LANGUAGE DataKinds #-}
|
2014-10-27 07:24:23 +01:00
|
|
|
{-# LANGUAGE DeriveGeneric #-}
|
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
{-# LANGUAGE QuasiQuotes #-}
|
|
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
2014-10-27 08:10:48 +01:00
|
|
|
{-# LANGUAGE TypeOperators #-}
|
2014-10-27 07:24:23 +01:00
|
|
|
|
|
|
|
module Servant.ServerSpec where
|
|
|
|
|
|
|
|
|
|
|
|
import Data.Aeson
|
|
|
|
import Data.Proxy
|
2014-10-27 11:24:20 +01:00
|
|
|
import Data.String.Conversions
|
2014-10-27 07:24:23 +01:00
|
|
|
import GHC.Generics
|
2014-10-27 09:24:56 +01:00
|
|
|
import Network.HTTP.Types
|
|
|
|
import Network.Wai
|
2014-10-27 07:24:23 +01:00
|
|
|
import Network.Wai.Test
|
|
|
|
import Test.Hspec
|
2014-10-27 09:24:56 +01:00
|
|
|
import Test.Hspec.Wai
|
2014-10-27 07:24:23 +01:00
|
|
|
|
|
|
|
import Servant.API.Get
|
2014-10-27 09:24:56 +01:00
|
|
|
import Servant.API.GetParam
|
2014-10-27 08:10:48 +01:00
|
|
|
import Servant.API.Post
|
2014-10-27 11:24:20 +01:00
|
|
|
import Servant.API.Raw
|
2014-10-27 08:10:48 +01:00
|
|
|
import Servant.API.RQBody
|
|
|
|
import Servant.API.Sub
|
2014-10-27 08:52:18 +01:00
|
|
|
import Servant.API.Union
|
2014-10-27 07:24:23 +01:00
|
|
|
import Servant.Server
|
|
|
|
|
|
|
|
|
2014-10-27 08:52:18 +01:00
|
|
|
-- * test data types
|
|
|
|
|
2014-10-27 07:24:23 +01:00
|
|
|
data Person = Person {
|
|
|
|
name :: String,
|
|
|
|
age :: Integer
|
|
|
|
}
|
|
|
|
deriving (Eq, Show, Generic)
|
|
|
|
|
|
|
|
instance ToJSON Person
|
|
|
|
instance FromJSON Person
|
|
|
|
|
|
|
|
alice :: Person
|
2014-10-27 08:10:48 +01:00
|
|
|
alice = Person "Alice" 42
|
2014-10-27 07:24:23 +01:00
|
|
|
|
2014-10-27 08:52:18 +01:00
|
|
|
data Animal = Animal {
|
|
|
|
species :: String,
|
|
|
|
numberOfLegs :: Integer
|
|
|
|
}
|
|
|
|
deriving (Eq, Show, Generic)
|
|
|
|
|
|
|
|
instance ToJSON Animal
|
|
|
|
instance FromJSON Animal
|
|
|
|
|
|
|
|
jerry :: Animal
|
|
|
|
jerry = Animal "Mouse" 4
|
|
|
|
|
|
|
|
|
|
|
|
-- * specs
|
2014-10-27 07:24:23 +01:00
|
|
|
|
|
|
|
spec :: Spec
|
|
|
|
spec = do
|
|
|
|
getSpec
|
2014-10-27 09:24:56 +01:00
|
|
|
getParamSpec
|
2014-10-27 08:10:48 +01:00
|
|
|
postSpec
|
2014-10-27 11:24:20 +01:00
|
|
|
rawSpec
|
2014-10-27 08:52:18 +01:00
|
|
|
unionSpec
|
2014-10-27 07:24:23 +01:00
|
|
|
|
|
|
|
|
|
|
|
type GetApi = Get Person
|
|
|
|
getApi :: Proxy GetApi
|
|
|
|
getApi = Proxy
|
|
|
|
|
2014-10-27 11:24:20 +01:00
|
|
|
getSpec :: Spec
|
|
|
|
getSpec = do
|
2014-10-27 07:24:23 +01:00
|
|
|
describe "Servant.API.Get" $ do
|
|
|
|
with (return (serve getApi (return alice))) $ do
|
2014-10-27 08:10:48 +01:00
|
|
|
it "allows to GET a Person" $ do
|
2014-10-27 07:24:23 +01:00
|
|
|
response <- get "/"
|
|
|
|
return response `shouldRespondWith` 200
|
|
|
|
liftIO $ do
|
|
|
|
decode' (simpleBody response) `shouldBe` Just alice
|
2014-10-27 08:10:48 +01:00
|
|
|
|
|
|
|
it "throws 404 on POSTs" $ do
|
|
|
|
post "/" "" `shouldRespondWith` 404
|
|
|
|
|
|
|
|
|
2014-10-27 09:24:56 +01:00
|
|
|
type GetParamApi = GetParam "name" String :> Get Person
|
|
|
|
getParamApi :: Proxy GetParamApi
|
|
|
|
getParamApi = Proxy
|
|
|
|
|
|
|
|
getParamServer :: Server GetParamApi
|
|
|
|
getParamServer (Just name) = return alice{name = name}
|
|
|
|
getParamServer Nothing = return alice
|
|
|
|
|
|
|
|
getParamSpec :: Spec
|
|
|
|
getParamSpec = do
|
|
|
|
describe "Servant.API.GetParam" $ do
|
|
|
|
it "allows to retrieve GET parameters" $ do
|
|
|
|
(flip runSession) (serve getParamApi getParamServer) $ do
|
|
|
|
let params = "?name=bob"
|
|
|
|
response <- Network.Wai.Test.request defaultRequest{
|
|
|
|
rawQueryString = params,
|
|
|
|
queryString = parseQuery params
|
|
|
|
}
|
|
|
|
liftIO $ do
|
|
|
|
decode' (simpleBody response) `shouldBe` Just alice{
|
|
|
|
name = "bob"
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-10-27 08:10:48 +01:00
|
|
|
type PostApi = RQBody Person :> (Post Integer)
|
|
|
|
postApi :: Proxy PostApi
|
|
|
|
postApi = Proxy
|
|
|
|
|
|
|
|
postSpec :: Spec
|
|
|
|
postSpec = do
|
|
|
|
describe "Servant.API.Post and .RQBody" $ do
|
|
|
|
with (return (serve postApi (return . age))) $ do
|
|
|
|
it "allows to POST a Person" $ do
|
|
|
|
post "/" (encode alice) `shouldRespondWith` "42"{
|
|
|
|
matchStatus = 201
|
|
|
|
}
|
2014-10-27 08:52:18 +01:00
|
|
|
|
|
|
|
|
2014-10-27 11:24:20 +01:00
|
|
|
type RawApi = "foo" :> Raw
|
|
|
|
rawApi :: Proxy RawApi
|
|
|
|
rawApi = Proxy
|
|
|
|
rawApplication :: Show a => (Request -> a) -> Application
|
|
|
|
rawApplication f request respond = respond $ responseLBS ok200 [] (cs $ show $ f request)
|
|
|
|
|
|
|
|
rawSpec :: Spec
|
|
|
|
rawSpec = do
|
|
|
|
describe "Servant.API.Raw" $ do
|
|
|
|
it "runs applications" $ do
|
|
|
|
(flip runSession) (serve rawApi (rawApplication (const (42 :: Integer)))) $ do
|
|
|
|
response <- Network.Wai.Test.request defaultRequest{
|
|
|
|
pathInfo = ["foo"]
|
|
|
|
}
|
|
|
|
liftIO $ do
|
|
|
|
simpleBody response `shouldBe` "42"
|
|
|
|
|
|
|
|
it "gets the pathInfo unmodified" $ do
|
|
|
|
(flip runSession) (serve rawApi (rawApplication pathInfo)) $ do
|
|
|
|
response <- Network.Wai.Test.request defaultRequest{
|
|
|
|
pathInfo = ["foo"]
|
|
|
|
}
|
|
|
|
liftIO $ do
|
|
|
|
simpleBody response `shouldBe` cs (show ["foo" :: String])
|
|
|
|
|
|
|
|
|
2014-10-27 08:52:18 +01:00
|
|
|
type UnionApi =
|
|
|
|
"foo" :> Get Person
|
|
|
|
:<|> "bar" :> Get Animal
|
|
|
|
unionApi :: Proxy UnionApi
|
|
|
|
unionApi = Proxy
|
|
|
|
|
|
|
|
unionServer :: Server UnionApi
|
|
|
|
unionServer =
|
|
|
|
return alice
|
|
|
|
:<|> return jerry
|
|
|
|
|
|
|
|
unionSpec :: Spec
|
|
|
|
unionSpec = do
|
|
|
|
describe "Servant.API.Union" $ do
|
|
|
|
with (return $ serve unionApi unionServer) $ do
|
|
|
|
it "unions endpoints" $ do
|
|
|
|
response <- get "/foo"
|
|
|
|
liftIO $ do
|
|
|
|
decode' (simpleBody response) `shouldBe`
|
|
|
|
Just alice
|
|
|
|
response <- get "/bar"
|
|
|
|
liftIO $ do
|
|
|
|
decode' (simpleBody response) `shouldBe`
|
|
|
|
Just jerry
|