1{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE DeriveGeneric #-}
3{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE QuasiQuotes #-}
5{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE TypeOperators #-}
8module Servant.ServerSpec where
11import Control.Monad.Trans.Either
16import Data.String.Conversions
18import Network.HTTP.Types
20import Network.Wai.Test
24import Servant.API.Capture
26import Servant.API.ReqBody
27import Servant.API.Post
28import Servant.API.QueryParam
31import Servant.API.Alternative
41 deriving (Eq, Show, Generic)
44instance FromJSON Person
47alice = Person "Alice" 42
51 numberOfLegs :: Integer
53 deriving (Eq, Show, Generic)
56instance FromJSON Animal
59jerry = Animal "Mouse" 4
62tweety = Animal "Bird" 2
77type CaptureApi = Capture "legs" Integer :> Get Animal
78captureApi :: Proxy CaptureApi
80captureServer :: Integer -> EitherT (Int, String) IO Animal
81captureServer legs = case legs of
84 _ -> left (404, "not found")
88 describe "Servant.API.Capture" $ do
89 with (return (serve captureApi captureServer)) $ do
90 it "can capture parts of the 'pathInfo'" $ do
93 decode' (simpleBody response) `shouldBe` Just tweety
96 (Proxy :: Proxy (Capture "captured" String :> Raw))
97 (\ "captured" request respond ->
98 respond $ responseLBS ok200 [] (cs $ show $ pathInfo request)))) $ do
99 it "strips the captured path snippet from pathInfo" $ do
100 get "/captured/foo" `shouldRespondWith` (fromString (show ["foo" :: String]))
103type GetApi = Get Person
104getApi :: Proxy GetApi
109 describe "Servant.API.Get" $ do
110 with (return (serve getApi (return alice))) $ do
111 it "allows to GET a Person" $ do
113 return response `shouldRespondWith` 200
115 decode' (simpleBody response) `shouldBe` Just alice
117 it "throws 405 (wrong method) on POSTs" $ do
118 post "/" "" `shouldRespondWith` 405
121type QueryParamApi = QueryParam "name" String :> Get Person
122 :<|> "a" :> QueryParams "names" String :> Get Person
123 :<|> "b" :> QueryFlag "capitalize" :> Get Person
125queryParamApi :: Proxy QueryParamApi
128qpServer :: Server QueryParamApi
129qpServer = queryParamServer :<|> qpNames :<|> qpCapitalize
131 where qpNames (_:name2:_) = return alice { name = name2 }
132 qpNames _ = return alice
134 qpCapitalize False = return alice
135 qpCapitalize True = return alice { name = map toUpper (name alice) }
137 queryParamServer (Just name) = return alice{name = name}
138 queryParamServer Nothing = return alice
140queryParamSpec :: Spec
142 describe "Servant.API.QueryParam" $ do
143 it "allows to retrieve simple GET parameters" $
144 (flip runSession) (serve queryParamApi qpServer) $ do
145 let params1 = "?name=bob"
146 response1 <- Network.Wai.Test.request defaultRequest{
147 rawQueryString = params1,
148 queryString = parseQuery params1
151 decode' (simpleBody response1) `shouldBe` Just alice{
155 it "allows to retrieve lists in GET parameters" $
156 (flip runSession) (serve queryParamApi qpServer) $ do
157 let params2 = "?names[]=bob&names[]=john"
158 response2 <- Network.Wai.Test.request defaultRequest{
159 rawQueryString = params2,
160 queryString = parseQuery params2,
164 decode' (simpleBody response2) `shouldBe` Just alice{
168 it "allows to retrieve value-less GET parameters" $
169 (flip runSession) (serve queryParamApi qpServer) $ do
170 let params3 = "?capitalize"
171 response3 <- Network.Wai.Test.request defaultRequest{
172 rawQueryString = params3,
173 queryString = parseQuery params3,
177 decode' (simpleBody response3) `shouldBe` Just alice{
181 let params3' = "?capitalize="
182 response3' <- Network.Wai.Test.request defaultRequest{
183 rawQueryString = params3',
184 queryString = parseQuery params3',
188 decode' (simpleBody response3') `shouldBe` Just alice{
192type PostApi = ReqBody Person :> Post Integer
193postApi :: Proxy PostApi
198 describe "Servant.API.Post and .ReqBody" $ do
199 with (return (serve postApi (return . age))) $ do
200 it "allows to POST a Person" $ do
201 post "/" (encode alice) `shouldRespondWith` "42"{
205 it "correctly rejects invalid request bodies with status 400" $ do
206 post "/" "some invalid body" `shouldRespondWith` 400
209type RawApi = "foo" :> Raw
210rawApi :: Proxy RawApi
212rawApplication :: Show a => (Request -> a) -> Application
213rawApplication f request respond = respond $ responseLBS ok200 [] (cs $ show $ f request)
217 describe "Servant.API.Raw" $ do
218 it "runs applications" $ do
219 (flip runSession) (serve rawApi (rawApplication (const (42 :: Integer)))) $ do
220 response <- Network.Wai.Test.request defaultRequest{
224 simpleBody response `shouldBe` "42"
226 it "gets the pathInfo modified" $ do
227 (flip runSession) (serve rawApi (rawApplication pathInfo)) $ do
228 response <- Network.Wai.Test.request defaultRequest{
229 pathInfo = ["foo", "bar"]
232 simpleBody response `shouldBe` cs (show ["bar" :: String])
237 :<|> "bar" :> Get Animal
238unionApi :: Proxy AlternativeApi
241unionServer :: Server AlternativeApi
248 describe "Servant.API.Alternative" $ do
249 with (return $ serve unionApi unionServer) $ do
250 it "unions endpoints" $ do
251 response <- get "/foo"
253 decode' (simpleBody response) `shouldBe`
255 response <- get "/bar"
257 decode' (simpleBody response) `shouldBe`
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