@@ -12,33 +12,23 @@ import Data.Maybe (fromJust)
12import Network.HTTP.Client (path, queryString)
12import Network.HTTP.Client (path, queryString)
13import Prelude.Compat
13import Prelude.Compat
14import Servant
14import Servant
15+import Servant.HTML.Blaze (HTML)
16+import qualified Text.Blaze.Html as Blaze
17+import qualified Text.Blaze.Html5 as Blaze5
15import Test.Hspec (Spec, context, describe, it, shouldBe,
18import Test.Hspec (Spec, context, describe, it, shouldBe,
16 shouldContain)
19 shouldContain)
17−import Test.Hspec.Core.Spec (Arg, Example, Result (..),
20+import Test.Hspec.Core.Spec (Arg, Example, Result (..), ResultStatus (..),
18− defaultParams)
21+ defaultParams, safeEvaluateExample)
19import Test.QuickCheck.Gen (generate, unGen)
22import Test.QuickCheck.Gen (generate, unGen)
20import Test.QuickCheck.Random (mkQCGen)
23import Test.QuickCheck.Random (mkQCGen)
23−#if MIN_VERSION_servant(0,8,0)
24−import Servant.API.Internal.Test.ComprehensiveAPI (comprehensiveAPIWithoutRaw)
26+import Servant.Test.ComprehensiveAPI (comprehensiveAPIWithoutStreamingOrRaw)
26−import Servant.API.Internal.Test.ComprehensiveAPI (ComprehensiveAPI,
30−#if MIN_VERSION_hspec(2,4,0)
31−import Test.Hspec.Core.Spec (safeEvaluateExample)
33−import Control.Exception (try)
34−import Test.Hspec.Core.Spec (evaluateExample)
37import Servant.QuickCheck
28import Servant.QuickCheck
38import Servant.QuickCheck.Internal (genRequest, runGenRequest,
29import Servant.QuickCheck.Internal (genRequest, runGenRequest,
39 serverDoesntSatisfy)
30 serverDoesntSatisfy)
42spec :: Spec
32spec :: Spec
44 serversEqualSpec
34 serversEqualSpec
@@ -49,6 +39,7 @@ spec = do
49 queryParamsSpec
39 queryParamsSpec
50 queryFlagsSpec
40 queryFlagsSpec
51 deepPathSpec
41 deepPathSpec
52 unbiasedGenerationSpec
43 unbiasedGenerationSpec
54serversEqualSpec :: Spec
45serversEqualSpec :: Spec
@@ -71,22 +62,28 @@ serversEqualSpec = describe "serversEqual" $ do
72 context "when JSON is equal but looks a bit different as a ByteString" $ do
63 context "when JSON is equal but looks a bit different as a ByteString" $ do
65+ it "sanity check: different whitespace same JSON objects bodyEquality fails" $ do
66+ FailedWith err <- withServantServer jsonApi jsonServer1 $ \burl1 ->
67+ withServantServer jsonApi jsonServer2 $ \burl2 -> do
68+ evalExample $ serversEqual jsonApi burl1 burl2 args bodyEquality
69+ show err `shouldContain` "Server equality failed"
74− it "considers equal JSON apis equal regardless of ordering or whitespace" $ do
71+ it "jsonEquality considers equal JSON apis equal regardless of key ordering or whitespace" $ do
75 withServantServerAndContext jsonApi ctx jsonServer1 $ \burl1 ->
72 withServantServerAndContext jsonApi ctx jsonServer1 $ \burl1 ->
76 withServantServerAndContext jsonApi ctx jsonServer2 $ \burl2 ->
73 withServantServerAndContext jsonApi ctx jsonServer2 $ \burl2 ->
77 serversEqual jsonApi burl1 burl2 args jsonEquality
74 serversEqual jsonApi burl1 burl2 args jsonEquality
79− it "sees when JSON apis are not equal regardless of ordering or whitespace" $ do
76+ it "sees when JSON apis are not equal because any value is different" $ do
80− Right (Failure _ err) <- withServantServer jsonApi jsonServer2 $ \burl1 ->
77+ FailedWith err <- withServantServer jsonApi jsonServer2 $ \burl1 ->
81 withServantServer jsonApi jsonServer3 $ \burl2 -> do
78 withServantServer jsonApi jsonServer3 $ \burl2 -> do
82− safeEvalExample $ serversEqual jsonApi burl1 burl2 args jsonEquality
79+ evalExample $ serversEqual jsonApi burl1 burl2 args jsonEquality
83 show err `shouldContain` "Server equality failed"
80 show err `shouldContain` "Server equality failed"
84 show err `shouldContain` "Path: /jsonComparison"
81 show err `shouldContain` "Path: /jsonComparison"
86 it "sees when JSON apis are not equal due to different keys but same values" $ do
83 it "sees when JSON apis are not equal due to different keys but same values" $ do
87− Right (Failure _ err) <- withServantServer jsonApi jsonServer2 $ \burl1 ->
84+ FailedWith err <- withServantServer jsonApi jsonServer2 $ \burl1 ->
88 withServantServer jsonApi jsonServer4 $ \burl2 -> do
85 withServantServer jsonApi jsonServer4 $ \burl2 -> do
89− safeEvalExample $ serversEqual jsonApi burl1 burl2 args jsonEquality
86+ evalExample $ serversEqual jsonApi burl1 burl2 args jsonEquality
90 show err `shouldContain` "Server equality failed"
87 show err `shouldContain` "Server equality failed"
91 show err `shouldContain` "Path: /jsonComparison"
88 show err `shouldContain` "Path: /jsonComparison"
@@ -131,6 +128,10 @@ onlyJsonObjectSpec = describe "onlyJsonObjects" $ do
131 withServantServerAndContext octetAPI ctx serverOctetAPI $ \burl ->
128 withServantServerAndContext octetAPI ctx serverOctetAPI $ \burl ->
132 serverSatisfies octetAPI burl args (onlyJsonObjects <%> mempty)
129 serverSatisfies octetAPI burl args (onlyJsonObjects <%> mempty)
131+ it "does not fail when there is no content-type" $ do
132+ withServantServerAndContext api2 ctx serverFailing $ \burl ->
133+ serverSatisfies api2 burl args (onlyJsonObjects <%> mempty)
134notLongerThanSpec :: Spec
135notLongerThanSpec :: Spec
135notLongerThanSpec = describe "notLongerThan" $ do
136notLongerThanSpec = describe "notLongerThan" $ do
@@ -148,7 +149,7 @@ isComprehensiveSpec :: Spec
148isComprehensiveSpec = describe "HasGenRequest" $ do
149isComprehensiveSpec = describe "HasGenRequest" $ do
150 it "has instances for all 'servant' combinators" $ do
151 it "has instances for all 'servant' combinators" $ do
151− let _g = genRequest comprehensiveAPIWithoutRaw
152+ let _g = genRequest comprehensiveAPIWithoutStreamingOrRaw
152 True `shouldBe` True -- This is a type-level check
153 True `shouldBe` True -- This is a type-level check
154deepPathSpec :: Spec
155deepPathSpec :: Spec
@@ -184,6 +185,25 @@ queryFlagsSpec = describe "QueryFlags" $ do
184 qs = C.unpack $ queryString req
185 qs = C.unpack $ queryString req
185 qs `shouldBe` "one&two"
186 qs `shouldBe` "one&two"
188+htmlDocTypesSpec :: Spec
189+htmlDocTypesSpec = describe "HtmlDocTypes" $ do
191+ it "fails HTML without doctype correctly" $ do
192+ err <- withServantServerAndContext docTypeApi ctx noDocTypeServer $ \burl -> do
193+ evalExample $ serverSatisfies docTypeApi burl args
194+ (htmlIncludesDoctype <%> mempty)
195+ show err `shouldContain` "htmlIncludesDoctype"
197+ it "passes HTML with a doctype at start" $ do
198+ withServantServerAndContext docTypeApi ctx docTypeServer $ \burl ->
199+ serverSatisfies docTypeApi burl args (htmlIncludesDoctype <%> mempty)
201+ it "accepts json endpoints and passes over them in silence" $ do
202+ withServantServerAndContext api ctx server $ \burl -> do
203+ serverSatisfies (Proxy :: Proxy (Get '[JSON] Int)) burl args
204+ (htmlIncludesDoctype <%> mempty)
187makeRandomRequest :: Proxy LargeAPI -> BaseUrl -> IO Integer
207makeRandomRequest :: Proxy LargeAPI -> BaseUrl -> IO Integer
188makeRandomRequest large burl = do
208makeRandomRequest large burl = do
189 req <- generate $ runGenRequest large
209 req <- generate $ runGenRequest large
@@ -253,7 +273,23 @@ server2 = return $ return 1
253server3 :: IO (Server API2)
273server3 :: IO (Server API2)
254server3 = return $ return 2
274server3 = return $ return 2
276+serverFailing :: IO (Server API2)
277+serverFailing = return . throwError $ err405
280+type HtmlDoctype = Get '[HTML] Blaze.Html
282+docTypeApi :: Proxy HtmlDoctype
285+docTypeServer :: IO (Server HtmlDoctype)
286+docTypeServer = pure $ pure $ Blaze5.docTypeHtml $ Blaze5.span "Hello Test!"
288+noDocTypeServer :: IO (Server HtmlDoctype)
289+noDocTypeServer = pure $ pure $ Blaze.text "Hello Test!"
292+-- Api for unbiased generation of requests tests
257largeApi :: Proxy LargeAPI
293largeApi :: Proxy LargeAPI
258largeApi = Proxy
294largeApi = Proxy
@@ -308,27 +344,14 @@ ctx = BasicAuthCheck (const . return $ NoSuchUser) :. EmptyContext
309------------------------------------------------------------------------------
345------------------------------------------------------------------------------
310evalExample :: (Example e, Arg e ~ ()) => e -> IO EvalResult
346evalExample :: (Example e, Arg e ~ ()) => e -> IO EvalResult
311−#if MIN_VERSION_hspec(2,4,0)
312evalExample e = do
347evalExample e = do
313 r <- safeEvaluateExample e defaultParams ($ ()) progCallback
348 r <- safeEvaluateExample e defaultParams ($ ()) progCallback
314− case r of
349+ case resultStatus r of
315− Left err -> return $ AnException err
316− Right Success -> return $ AllGood
350+ Success -> return $ AllGood
317− Right (Failure _ reason) -> return $ FailedWith $ show reason
351+ Failure _ reason -> return $ FailedWith $ show reason
318− Right (Pending _) -> error "should not happen"
352+ Pending {} -> error "should not happen"
320 progCallback _ = return ()
354 progCallback _ = return ()
323− r <- try $ evaluateExample e defaultParams ($ ()) progCallback
325− Left err -> return $ AnException err
326− Right Success -> return $ AllGood
327− Right (Fail _ reason) -> return $ FailedWith reason
328− Right (Pending _) -> error "should not happen"
330− progCallback _ = return ()
333data EvalResult
356data EvalResult
334 = AnException SomeException
357 = AnException SomeException
@@ -346,8 +369,3 @@ noOfTestCases = 20000
347noOfTestCases = 1000
370noOfTestCases = 1000
350−#if !MIN_VERSION_servant(0,8,0)
351−comprehensiveAPIWithoutRaw :: Proxy ComprehensiveAPI
352−comprehensiveAPIWithoutRaw = comprehensiveAPI