@@ -7,7 +7,6 @@
7{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE ScopedTypeVariables #-}
8{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeFamilies #-}
9{-# LANGUAGE TypeOperators #-}
9{-# LANGUAGE TypeOperators #-}
10−{-# LANGUAGE TypeSynonymInstances #-}
11{-# OPTIONS_GHC -freduction-depth=100 #-}
10{-# OPTIONS_GHC -freduction-depth=100 #-}
13module Servant.ServerSpec where
12module Servant.ServerSpec where
@@ -49,14 +48,16 @@ import Network.Wai.Test
49import Servant.API
48import Servant.API
50 ((:<|>) (..), (:>), AuthProtect, BasicAuth,
49 ((:<|>) (..), (:>), AuthProtect, BasicAuth,
51 BasicAuthData (BasicAuthData), Capture, Capture', CaptureAll,
50 BasicAuthData (BasicAuthData), Capture, Capture', CaptureAll,
52− Delete, EmptyAPI, Get, Header, Headers, HttpVersion,
51+ Delete, EmptyAPI, Get, HasStatus(StatusOf), Header, Headers,
53− IsSecure (..), JSON, Lenient, NoContent (..), NoContentVerb,
52+ HttpVersion, IsSecure (..), JSON, Lenient, NoContent (..),
54− NoFraming, OctetStream, Patch, PlainText, Post, Put,
53+ NoContentVerb, NoFraming, OctetStream, Patch, PlainText, Post,
55− QueryFlag, QueryParam, QueryParams, Raw, RemoteHost, ReqBody,
54+ Put, QueryFlag, QueryParam, QueryParams, Raw, RemoteHost,
56− SourceIO, StdMethod (..), Stream, Strict, Verb, addHeader)
55+ ReqBody, SourceIO, StdMethod (..), Stream, Strict, Union,
56+ UVerb, Verb, addHeader)
57import Servant.Server
57import Servant.Server
58 (Context ((:.), EmptyContext), Handler, Server, Tagged (..),
58 (Context ((:.), EmptyContext), Handler, Server, Tagged (..),
59− emptyServer, err401, err403, err404, serve, serveWithContext)
59+ emptyServer, err401, err403, err404, respond, serve,
60import Servant.Test.ComprehensiveAPI
61import Servant.Test.ComprehensiveAPI
61import qualified Servant.Types.SourceT as S
62import qualified Servant.Types.SourceT as S
62import Test.Hspec
63import Test.Hspec
@@ -87,6 +88,7 @@ comprehensiveApiContext = NamedContext EmptyContext :. EmptyContext
87spec :: Spec
88spec :: Spec
90 captureSpec
92 captureSpec
91 captureAllSpec
93 captureAllSpec
92 queryParamSpec
94 queryParamSpec
@@ -253,8 +255,8 @@ captureSpec = do
254 with (return (serve
256 with (return (serve
255 (Proxy :: Proxy (Capture "captured" String :> Raw))
257 (Proxy :: Proxy (Capture "captured" String :> Raw))
256− (\ "captured" -> Tagged $ \request_ respond ->
258+ (\ "captured" -> Tagged $ \request_ sendResponse ->
257− respond $ responseLBS ok200 [] (cs $ show $ pathInfo request_)))) $ do
259+ sendResponse $ responseLBS ok200 [] (cs $ show $ pathInfo request_)))) $ do
258 it "strips the captured path snippet from pathInfo" $ do
260 it "strips the captured path snippet from pathInfo" $ do
259 get "/captured/foo" `shouldRespondWith` (fromString (show ["foo" :: String]))
261 get "/captured/foo" `shouldRespondWith` (fromString (show ["foo" :: String]))
@@ -305,8 +307,8 @@ captureAllSpec = do
306 with (return (serve
308 with (return (serve
307 (Proxy :: Proxy (CaptureAll "segments" String :> Raw))
309 (Proxy :: Proxy (CaptureAll "segments" String :> Raw))
308− (\ _captured -> Tagged $ \request_ respond ->
310+ (\ _captured -> Tagged $ \request_ sendResponse ->
309− respond $ responseLBS ok200 [] (cs $ show $ pathInfo request_)))) $ do
311+ sendResponse $ responseLBS ok200 [] (cs $ show $ pathInfo request_)))) $ do
310 it "consumes everything from pathInfo" $ do
312 it "consumes everything from pathInfo" $ do
311 get "/captured/foo/bar/baz" `shouldRespondWith` (fromString (show ([] :: [Int])))
313 get "/captured/foo/bar/baz" `shouldRespondWith` (fromString (show ([] :: [Int])))
@@ -544,8 +546,8 @@ rawApi :: Proxy RawApi
544rawApi = Proxy
546rawApi = Proxy
546rawApplication :: Show a => (Request -> a) -> Tagged m Application
548rawApplication :: Show a => (Request -> a) -> Tagged m Application
547−rawApplication f = Tagged $ \request_ respond ->
549+rawApplication f = Tagged $ \request_ sendResponse ->
548− respond $ responseLBS ok200 []
550+ sendResponse $ responseLBS ok200 []
549 (cs $ show $ f request_)
551 (cs $ show $ f request_)
551rawSpec :: Spec
553rawSpec :: Spec
@@ -706,7 +708,7 @@ basicAuthApi = Proxy
706basicAuthServer :: Server BasicAuthAPI
708basicAuthServer :: Server BasicAuthAPI
707basicAuthServer =
709basicAuthServer =
708 const (return jerry) :<|>
710 const (return jerry) :<|>
709− (Tagged $ \ _ respond -> respond $ responseLBS imATeapot418 [] "")
711+ (Tagged $ \ _ sendResponse -> sendResponse $ responseLBS imATeapot418 [] "")
711basicAuthContext :: Context '[ BasicAuthCheck () ]
713basicAuthContext :: Context '[ BasicAuthCheck () ]
712basicAuthContext =
714basicAuthContext =
@@ -751,7 +753,7 @@ genAuthApi = Proxy
752genAuthServer :: Server GenAuthAPI
754genAuthServer :: Server GenAuthAPI
753genAuthServer = const (return tweety)
755genAuthServer = const (return tweety)
754− :<|> (Tagged $ \ _ respond -> respond $ responseLBS imATeapot418 [] "")
756+ :<|> (Tagged $ \ _ sendResponse -> sendResponse $ responseLBS imATeapot418 [] "")
756type instance AuthServerData (AuthProtect "auth") = ()
758type instance AuthServerData (AuthProtect "auth") = ()
@@ -781,6 +783,73 @@ genAuthSpec = do
781 it "plays nice with subsequent Raw endpoints" $ do
783 it "plays nice with subsequent Raw endpoints" $ do
782 get "/foo" `shouldRespondWith` 418
784 get "/foo" `shouldRespondWith` 418
787+------------------------------------------------------------------------------
789+------------------------------------------------------------------------------
791+newtype PersonResponse = PersonResponse Person
793+instance ToJSON PersonResponse
794+instance HasStatus PersonResponse where
795+ type StatusOf PersonResponse = 200
797+newtype RedirectResponse = RedirectResponse String
799+instance ToJSON RedirectResponse
800+instance HasStatus RedirectResponse where
801+ type StatusOf RedirectResponse = 301
803+newtype AnimalResponse = AnimalResponse Animal
805+instance ToJSON AnimalResponse
806+instance HasStatus AnimalResponse where
807+ type StatusOf AnimalResponse = 203
811+ = "person" :> Capture "shouldRedirect" Bool :> UVerb 'GET '[JSON] '[PersonResponse, RedirectResponse]
812+ :<|> "animal" :> UVerb 'GET '[JSON] '[AnimalResponse]
815+uverbSpec = describe "Servant.API.UVerb " $ do
817+ joe = Person "joe" 42
818+ mouse = Animal "Mouse" 7
822+ -> Handler (Union '[PersonResponse
824+ personHandler True = respond $ RedirectResponse "over there!"
825+ personHandler False = respond $ PersonResponse joe
827+ animalHandler = respond $ AnimalResponse mouse
829+ server :: Server UVerbApi
830+ server = personHandler :<|> animalHandler
832+ with (pure $ serve (Proxy :: Proxy UVerbApi) server) $ do
833+ context "A route returning either 301/String or 200/Person" $ do
834+ context "when requesting the person" $ do
835+ let theRequest = THW.get "/person/false"
836+ it "returns status 200" $
837+ theRequest `shouldRespondWith` 200
838+ it "returns a person" $ do
839+ response <- theRequest
840+ liftIO $ decode' (simpleBody response) `shouldBe` Just joe
841+ context "requesting the redirect" $
842+ it "returns a message and status 301" $
843+ THW.get "/person/true"
844+ `shouldRespondWith` "\"over there!\"" {matchStatus = 301}
845+ context "a route with a single response type" $ do
846+ let theRequest = THW.get "/animal"
847+ it "should return the defined status code" $
848+ theRequest `shouldRespondWith` 203
849+ it "should return the expected response" $ do
850+ response <- theRequest
851+ liftIO $ decode' (simpleBody response) `shouldBe` Just mouse
785------------------------------------------------------------------------------
854------------------------------------------------------------------------------
786-- * Test data types {{{
855-- * Test data types {{{