@@ -38,8 +38,8 @@ import Network.Wai.Internal (Response (ResponseBuilder))
38import Network.Wai.Test (defaultRequest, request,
38import Network.Wai.Test (defaultRequest, request,
39 runSession, simpleBody,
39 runSession, simpleBody,
40 simpleHeaders, simpleStatus)
40 simpleHeaders, simpleStatus)
41−import Servant.API ((:<|>) (..), (:>), Capture, Delete,
41+import Servant.API ((:<|>) (..), (:>), BasicAuth, BasicAuthData(BasicAuthData),
42− Get, Header (..),
42+ Capture, Delete, Get, Header (..),
43 Headers, HttpVersion,
43 Headers, HttpVersion,
44 IsSecure (..), JSON,
44 IsSecure (..), JSON,
45 NoContent (..), Patch, PlainText,
45 NoContent (..), Patch, PlainText,
@@ -49,20 +49,23 @@ import Servant.API ((:<|>) (..), (:>), Capture, Delete,
49 StdMethod (..), Verb, addHeader)
49 StdMethod (..), Verb, addHeader)
50import Servant.API.Internal.Test.ComprehensiveAPI
50import Servant.API.Internal.Test.ComprehensiveAPI
51import Servant.Server (ServantErr (..), Server, err404,
51import Servant.Server (ServantErr (..), Server, err404,
52− serve, serveWithContext, Context(EmptyContext))
52+ serve, serveWithContext, Context((:.), EmptyContext))
53import Test.Hspec (Spec, context, describe, it,
53import Test.Hspec (Spec, context, describe, it,
54 shouldBe, shouldContain)
54 shouldBe, shouldContain)
55+import qualified Test.Hspec.Wai as THW
55import Test.Hspec.Wai (get, liftIO, matchHeaders,
56import Test.Hspec.Wai (get, liftIO, matchHeaders,
56− matchStatus, request,
57− shouldRespondWith, with, (<:>))
57+ matchStatus, shouldRespondWith,
60+import Servant.Server.Internal.BasicAuth (BasicAuthCheck(BasicAuthCheck),
61+ BasicAuthResult(Authorized,Unauthorized))
59import Servant.Server.Internal.RoutingApplication
62import Servant.Server.Internal.RoutingApplication
60 (toApplication, RouteResult(..))
63 (toApplication, RouteResult(..))
61import Servant.Server.Internal.Router
64import Servant.Server.Internal.Router
62 (tweakResponse, runRouter,
65 (tweakResponse, runRouter,
63 Router, Router'(LeafRouter))
66 Router, Router'(LeafRouter))
64import Servant.Server.Internal.Context
67import Servant.Server.Internal.Context
65− (Context(..), NamedContext(..))
68+ (NamedContext(..))
67-- * comprehensive api test
70-- * comprehensive api test
@@ -86,6 +89,7 @@ spec = do
86 responseHeadersSpec
89 responseHeadersSpec
87 routerSpec
90 routerSpec
88 miscCombinatorSpec
91 miscCombinatorSpec
90------------------------------------------------------------------------------
94------------------------------------------------------------------------------
91-- * verbSpec {{{
95-- * verbSpec {{{
@@ -117,49 +121,49 @@ verbSpec = describe "Servant.API.Verb" $ do
117 -- HEAD and 214/215 need not return bodies
121 -- HEAD and 214/215 need not return bodies
118 unless (status `elem` [214, 215] || method == methodHead) $
122 unless (status `elem` [214, 215] || method == methodHead) $
119 it "returns the person" $ do
123 it "returns the person" $ do
120− response <- Test.Hspec.Wai.request method "/" [] ""
124+ response <- THW.request method "/" [] ""
121 liftIO $ statusCode (simpleStatus response) `shouldBe` status
125 liftIO $ statusCode (simpleStatus response) `shouldBe` status
122 liftIO $ decode' (simpleBody response) `shouldBe` Just alice
126 liftIO $ decode' (simpleBody response) `shouldBe` Just alice
124 it "returns no content on NoContent" $ do
128 it "returns no content on NoContent" $ do
125− response <- Test.Hspec.Wai.request method "/noContent" [] ""
129+ response <- THW.request method "/noContent" [] ""
126 liftIO $ statusCode (simpleStatus response) `shouldBe` status
130 liftIO $ statusCode (simpleStatus response) `shouldBe` status
127 liftIO $ simpleBody response `shouldBe` ""
131 liftIO $ simpleBody response `shouldBe` ""
129 -- HEAD should not return body
133 -- HEAD should not return body
130 when (method == methodHead) $
134 when (method == methodHead) $
131 it "HEAD returns no content body" $ do
135 it "HEAD returns no content body" $ do
132− response <- Test.Hspec.Wai.request method "/" [] ""
136+ response <- THW.request method "/" [] ""
133 liftIO $ simpleBody response `shouldBe` ""
137 liftIO $ simpleBody response `shouldBe` ""
135 it "throws 405 on wrong method " $ do
139 it "throws 405 on wrong method " $ do
136− Test.Hspec.Wai.request (wrongMethod method) "/" [] ""
140+ THW.request (wrongMethod method) "/" [] ""
137 `shouldRespondWith` 405
141 `shouldRespondWith` 405
139 it "returns headers" $ do
143 it "returns headers" $ do
140− response1 <- Test.Hspec.Wai.request method "/header" [] ""
144+ response1 <- THW.request method "/header" [] ""
141 liftIO $ statusCode (simpleStatus response1) `shouldBe` status
145 liftIO $ statusCode (simpleStatus response1) `shouldBe` status
142 liftIO $ simpleHeaders response1 `shouldContain` [("H", "5")]
146 liftIO $ simpleHeaders response1 `shouldContain` [("H", "5")]
144− response2 <- Test.Hspec.Wai.request method "/header" [] ""
148+ response2 <- THW.request method "/header" [] ""
145 liftIO $ statusCode (simpleStatus response2) `shouldBe` status
149 liftIO $ statusCode (simpleStatus response2) `shouldBe` status
146 liftIO $ simpleHeaders response2 `shouldContain` [("H", "5")]
150 liftIO $ simpleHeaders response2 `shouldContain` [("H", "5")]
148 it "handles trailing '/' gracefully" $ do
152 it "handles trailing '/' gracefully" $ do
149− response <- Test.Hspec.Wai.request method "/headerNC/" [] ""
153+ response <- THW.request method "/headerNC/" [] ""
150 liftIO $ statusCode (simpleStatus response) `shouldBe` status
154 liftIO $ statusCode (simpleStatus response) `shouldBe` status
152 it "returns 406 if the Accept header is not supported" $ do
156 it "returns 406 if the Accept header is not supported" $ do
153− Test.Hspec.Wai.request method "" [(hAccept, "crazy/mime")] ""
157+ THW.request method "" [(hAccept, "crazy/mime")] ""
154 `shouldRespondWith` 406
158 `shouldRespondWith` 406
156 it "responds if the Accept header is supported" $ do
160 it "responds if the Accept header is supported" $ do
157− response <- Test.Hspec.Wai.request method ""
161+ response <- THW.request method ""
158 [(hAccept, "application/json")] ""
162 [(hAccept, "application/json")] ""
159 liftIO $ statusCode (simpleStatus response) `shouldBe` status
163 liftIO $ statusCode (simpleStatus response) `shouldBe` status
161 it "sets the Content-Type header" $ do
165 it "sets the Content-Type header" $ do
162− response <- Test.Hspec.Wai.request method "" [] ""
166+ response <- THW.request method "" [] ""
163 liftIO $ simpleHeaders response `shouldContain`
167 liftIO $ simpleHeaders response `shouldContain`
164 [("Content-Type", "application/json")]
168 [("Content-Type", "application/json")]
@@ -306,7 +310,7 @@ reqBodySpec = describe "Servant.API.ReqBody" $ do
307 let server :: Server ReqBodyApi
311 let server :: Server ReqBodyApi
308 server = return :<|> return . age
312 server = return :<|> return . age
309− mkReq method x = Test.Hspec.Wai.request method x
313+ mkReq method x = THW.request method x
310 [(hContentType, "application/json;charset=utf-8")]
314 [(hContentType, "application/json;charset=utf-8")]
312 with (return $ serve reqBodyApi server) $ do
316 with (return $ serve reqBodyApi server) $ do
@@ -319,7 +323,7 @@ reqBodySpec = describe "Servant.API.ReqBody" $ do
319 mkReq methodPut "/blah" "some invalid body" `shouldRespondWith` 400
323 mkReq methodPut "/blah" "some invalid body" `shouldRespondWith` 400
321 it "responds with 415 if the request body media type is unsupported" $ do
325 it "responds with 415 if the request body media type is unsupported" $ do
322− Test.Hspec.Wai.request methodPost "/"
326+ THW.request methodPost "/"
323 [(hContentType, "application/nonsense")] "" `shouldRespondWith` 415
327 [(hContentType, "application/nonsense")] "" `shouldRespondWith` 415
@@ -343,13 +347,13 @@ headerSpec = describe "Servant.API.Header" $ do
343 expectsString Nothing = error "Expected a string"
347 expectsString Nothing = error "Expected a string"
345 with (return (serve headerApi expectsInt)) $ do
349 with (return (serve headerApi expectsInt)) $ do
346− let delete' x = Test.Hspec.Wai.request methodDelete x [("MyHeader", "5")]
350+ let delete' x = THW.request methodDelete x [("MyHeader", "5")]
348 it "passes the header to the handler (Int)" $
352 it "passes the header to the handler (Int)" $
349 delete' "/" "" `shouldRespondWith` 200
353 delete' "/" "" `shouldRespondWith` 200
351 with (return (serve headerApi expectsString)) $ do
355 with (return (serve headerApi expectsString)) $ do
352− let delete' x = Test.Hspec.Wai.request methodDelete x [("MyHeader", "more from you")]
356+ let delete' x = THW.request methodDelete x [("MyHeader", "more from you")]
354 it "passes the header to the handler (String)" $
358 it "passes the header to the handler (String)" $
355 delete' "/" "" `shouldRespondWith` 200
359 delete' "/" "" `shouldRespondWith` 200
@@ -455,19 +459,19 @@ responseHeadersSpec = describe "ResponseHeaders" $ do
456 it "includes the headers in the response" $
460 it "includes the headers in the response" $
457 forM_ methods $ \method ->
461 forM_ methods $ \method ->
458− Test.Hspec.Wai.request method "/" [] ""
462+ THW.request method "/" [] ""
459 `shouldRespondWith` "\"hi\""{ matchHeaders = ["H1" <:> "5", "H2" <:> "kilroy"]
463 `shouldRespondWith` "\"hi\""{ matchHeaders = ["H1" <:> "5", "H2" <:> "kilroy"]
460 , matchStatus = 200
464 , matchStatus = 200
463 it "responds with not found for non-existent endpoints" $
467 it "responds with not found for non-existent endpoints" $
464 forM_ methods $ \method ->
468 forM_ methods $ \method ->
465− Test.Hspec.Wai.request method "blahblah" [] ""
469+ THW.request method "blahblah" [] ""
466 `shouldRespondWith` 404
470 `shouldRespondWith` 404
468 it "returns 406 if the Accept header is not supported" $
472 it "returns 406 if the Accept header is not supported" $
469 forM_ methods $ \method ->
473 forM_ methods $ \method ->
470− Test.Hspec.Wai.request method "" [(hAccept, "crazy/mime")] ""
474+ THW.request method "" [(hAccept, "crazy/mime")] ""
471 `shouldRespondWith` 406
475 `shouldRespondWith` 406
@@ -527,6 +531,39 @@ miscCombinatorSpec = with (return $ serve miscApi miscServ) $
527 go "/host" "\"0.0.0.0:0\""
531 go "/host" "\"0.0.0.0:0\""
529 where go path res = Test.Hspec.Wai.get path `shouldRespondWith` res
533 where go path res = Test.Hspec.Wai.get path `shouldRespondWith` res
536+------------------------------------------------------------------------------
537+-- * Authentication {{{
538+------------------------------------------------------------------------------
540+type BasicAuthAPI = BasicAuth "foo" () :> "basic" :> Get '[JSON] Animal
542+basicAuthApi :: Proxy BasicAuthAPI
544+basicAuthServer :: Server BasicAuthAPI
545+basicAuthServer = const (return jerry)
547+basicAuthContext :: Context '[ BasicAuthCheck () ]
549+ let basicHandler = BasicAuthCheck $ (\(BasicAuthData usr pass) ->
550+ if usr == "servant" && pass == "server"
551+ then return (Authorized ())
552+ else return Unauthorized
554+ in basicHandler :. EmptyContext
556+basicAuthSpec :: Spec
558+ describe "Servant.API.BasicAuth" $ do
559+ with (return (serveWithContext basicAuthApi basicAuthContext basicAuthServer)) $ do
561+ context "Basic Authentication" $ do
562+ it "returns with 401 with bad password" $ do
563+ get "/basic" `shouldRespondWith` 401
564+ it "returns 200 with the right password" $ do
565+ THW.request methodGet "/basic" [("Authorization","Basic c2VydmFudDpzZXJ2ZXI=")] "" `shouldRespondWith` 200
531------------------------------------------------------------------------------
568------------------------------------------------------------------------------
532-- * Test data types {{{
569-- * Test data types {{{