Slightly nicer RequestPredicate.
Which still isn't beautiful.
This commit is contained in:
parent
c85d41ad79
commit
64c845cb45
@ -55,13 +55,19 @@ module Servant.QuickCheck
|
|||||||
-- | Helpers to setup and teardown @servant@ servers during tests.
|
-- | Helpers to setup and teardown @servant@ servers during tests.
|
||||||
, withServantServer
|
, withServantServer
|
||||||
, withServantServerAndContext
|
, withServantServerAndContext
|
||||||
|
, defaultArgs
|
||||||
|
|
||||||
-- ** Re-exports
|
-- ** Re-exports
|
||||||
, BaseUrl(..)
|
, BaseUrl(..)
|
||||||
, Scheme(..)
|
, Scheme(..)
|
||||||
|
, Args(..)
|
||||||
|
|
||||||
|
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Servant.QuickCheck.Internal
|
import Servant.QuickCheck.Internal
|
||||||
import Servant.Client (BaseUrl(..), Scheme(..))
|
import Servant.Client (BaseUrl(..), Scheme(..))
|
||||||
|
import Test.QuickCheck (Args(..), stdArgs)
|
||||||
|
|
||||||
|
defaultArgs :: Args
|
||||||
|
defaultArgs = stdArgs { maxSuccess = 1000 }
|
||||||
|
|||||||
@ -1,8 +1,4 @@
|
|||||||
-- | This module contains benchmark-related logic.
|
-- This is a WIP module that shouldn't be used.
|
||||||
--
|
|
||||||
-- Currently it generates 'wrk' scripts rather than benchmarking directly with
|
|
||||||
-- the @servant-client@ functions since the performance of 'wrk' is
|
|
||||||
-- significantly better.
|
|
||||||
module Servant.QuickCheck.Internal.Benchmarking where
|
module Servant.QuickCheck.Internal.Benchmarking where
|
||||||
|
|
||||||
import Data.ByteString (ByteString)
|
import Data.ByteString (ByteString)
|
||||||
|
|||||||
@ -55,19 +55,35 @@ onlyJsonObjects
|
|||||||
Nothing -> False
|
Nothing -> False
|
||||||
Just (_ :: Object) -> True)
|
Just (_ :: Object) -> True)
|
||||||
|
|
||||||
{-
|
-- | __Optional__
|
||||||
-- | When creating a new resource, it is good practice to provide a @Location@
|
--
|
||||||
|
-- When creating a new resource, it is good practice to provide a @Location@
|
||||||
-- header with a link to the created resource.
|
-- header with a link to the created resource.
|
||||||
--
|
--
|
||||||
-- This function checks that every @201 Created@ response contains a @Location@
|
-- This function checks that every @201 Created@ response contains a @Location@
|
||||||
-- header, and that the link in it responds with a 2XX response code to @GET@
|
-- header, and that the link in it responds with a 2XX response code to @GET@
|
||||||
-- requests.
|
-- requests.
|
||||||
--
|
--
|
||||||
-- References: <RFC 7231, Section 6.3.2 https://tools.ietf.org/html/rfc7231#section-6.3.2>
|
-- This is considered optional because other means of linking to the resource
|
||||||
createContainsValidLocation :: ResponsePredicate Text Bool
|
-- (e.g. via the response body) are also acceptable; linking to the resource in
|
||||||
createContainsValidLocation
|
-- some way is considered best practice.
|
||||||
= ResponsePredicate "createContainsValidLocation" (\resp ->
|
--
|
||||||
|
-- __References__:
|
||||||
|
--
|
||||||
|
-- * 201 Created: <https://tools.ietf.org/html/rfc7231#section-6.3.2 RFC 7231 Section 6.3.2>
|
||||||
|
-- * Location header: <https://tools.ietf.org/html/rfc7231#section-7.1.2 RFC 7231 Section 7.1.2>
|
||||||
|
{-createContainsValidLocation :: RequestPredicate Text Bool-}
|
||||||
|
{-createContainsValidLocation-}
|
||||||
|
{-= RequestPredicate-}
|
||||||
|
{-{ reqPredName = "createContainsValidLocation"-}
|
||||||
|
{-, reqResps = \req mg -> do-}
|
||||||
|
{-resp <- httpLbs mgr req-}
|
||||||
|
{-if responseStatus resp == status201-}
|
||||||
|
{-then case lookup "Location" $ responseHeaders resp of-}
|
||||||
|
{-Nothing -> return []-}
|
||||||
|
{-Just l -> if-}
|
||||||
|
|
||||||
|
{-
|
||||||
getsHaveLastModifiedHeader :: ResponsePredicate Text Bool
|
getsHaveLastModifiedHeader :: ResponsePredicate Text Bool
|
||||||
getsHaveLastModifiedHeader
|
getsHaveLastModifiedHeader
|
||||||
= ResponsePredicate "getsHaveLastModifiedHeader" (\resp ->
|
= ResponsePredicate "getsHaveLastModifiedHeader" (\resp ->
|
||||||
@ -91,19 +107,15 @@ getsHaveLastModifiedHeader
|
|||||||
notAllowedContainsAllowHeader :: RequestPredicate Text Bool
|
notAllowedContainsAllowHeader :: RequestPredicate Text Bool
|
||||||
notAllowedContainsAllowHeader
|
notAllowedContainsAllowHeader
|
||||||
= RequestPredicate
|
= RequestPredicate
|
||||||
{ reqPredName = name
|
{ reqPredName = "notAllowedContainsAllowHeader"
|
||||||
, reqResps = \req mgr -> mapM (flip httpLbs mgr)
|
, reqResps = \req mgr -> do
|
||||||
[ req { method = renderStdMethod m }
|
resp <- mapM (flip httpLbs mgr) $ [ req { method = renderStdMethod m }
|
||||||
| m <- [minBound .. maxBound ]
|
| m <- [minBound .. maxBound ]
|
||||||
, renderStdMethod m /= method req ]
|
, renderStdMethod m /= method req ]
|
||||||
, reqPred = pred'
|
return (all pred' resp, resp)
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
name = "notAllowedContainsAllowHeader"
|
pred' resp = responseStatus resp /= status405 || hasValidHeader "Allow" go resp
|
||||||
pred' = ResponsePredicate name (\resp ->
|
|
||||||
if responseStatus resp == status405
|
|
||||||
then hasValidHeader "Allow" go resp
|
|
||||||
else True)
|
|
||||||
where
|
where
|
||||||
go x = all (\y -> isRight $ parseMethod $ SBSC.pack y)
|
go x = all (\y -> isRight $ parseMethod $ SBSC.pack y)
|
||||||
$ wordsBy (`elem` (", " :: [Char])) (SBSC.unpack x)
|
$ wordsBy (`elem` (", " :: [Char])) (SBSC.unpack x)
|
||||||
@ -148,14 +160,14 @@ honoursAcceptHeader
|
|||||||
getsHaveCacheControlHeader :: RequestPredicate Text Bool
|
getsHaveCacheControlHeader :: RequestPredicate Text Bool
|
||||||
getsHaveCacheControlHeader
|
getsHaveCacheControlHeader
|
||||||
= RequestPredicate
|
= RequestPredicate
|
||||||
{ reqPredName = name
|
{ reqPredName = "getsHaveCacheControlHeader"
|
||||||
, reqResps = \req mgr -> if method req == methodGet
|
, reqResps = \req mgr -> if method req == methodGet
|
||||||
then return <$> httpLbs req mgr
|
then do
|
||||||
else return []
|
resp <- httpLbs req mgr
|
||||||
, reqPred = ResponsePredicate name $ \resp ->
|
let good = isJust $ lookup "Cache-Control" $ responseHeaders resp
|
||||||
isJust $ lookup "Cache-Control" $ responseHeaders resp
|
return (good, [resp])
|
||||||
|
else return (True, [])
|
||||||
}
|
}
|
||||||
where name = "getsHaveCacheControlHeader"
|
|
||||||
|
|
||||||
-- | [__Best Practice__]
|
-- | [__Best Practice__]
|
||||||
--
|
--
|
||||||
@ -163,15 +175,14 @@ getsHaveCacheControlHeader
|
|||||||
headsHaveCacheControlHeader :: RequestPredicate Text Bool
|
headsHaveCacheControlHeader :: RequestPredicate Text Bool
|
||||||
headsHaveCacheControlHeader
|
headsHaveCacheControlHeader
|
||||||
= RequestPredicate
|
= RequestPredicate
|
||||||
{ reqPredName = name
|
{ reqPredName = "headsHaveCacheControlHeader"
|
||||||
, reqResps = \req mgr -> if method req == methodHead
|
, reqResps = \req mgr -> if method req == methodHead
|
||||||
then return <$> httpLbs req mgr
|
then do
|
||||||
else return []
|
resp <- httpLbs req mgr
|
||||||
, reqPred = ResponsePredicate name $ \resp ->
|
let good = isJust $ lookup "Cache-Control" $ responseHeaders resp
|
||||||
isJust $ lookup "Cache-Control" $ responseHeaders resp
|
return (good, [resp])
|
||||||
|
else return (True, [])
|
||||||
}
|
}
|
||||||
where name = "headsHaveCacheControlHeader"
|
|
||||||
|
|
||||||
{-
|
{-
|
||||||
-- |
|
-- |
|
||||||
--
|
--
|
||||||
@ -264,21 +275,19 @@ instance (Monoid n, Monoid r) => Monoid (ResponsePredicate n r) where
|
|||||||
|
|
||||||
data RequestPredicate n r = RequestPredicate
|
data RequestPredicate n r = RequestPredicate
|
||||||
{ reqPredName :: n
|
{ reqPredName :: n
|
||||||
, reqResps :: Request -> Manager -> IO [Response LBS.ByteString]
|
, reqResps :: Request -> Manager -> IO (r, [Response LBS.ByteString])
|
||||||
, reqPred :: ResponsePredicate n r
|
|
||||||
} deriving (Generic, Functor)
|
} deriving (Generic, Functor)
|
||||||
|
|
||||||
instance Bifunctor RequestPredicate where
|
instance Bifunctor RequestPredicate where
|
||||||
first f (RequestPredicate a b c) = RequestPredicate (f a) b (first f c)
|
first f (RequestPredicate a b) = RequestPredicate (f a) b
|
||||||
second = fmap
|
second = fmap
|
||||||
|
|
||||||
-- TODO: This isn't actually a monoid
|
-- TODO: This isn't actually a monoid
|
||||||
instance (Monoid n, Monoid r) => Monoid (RequestPredicate n r) where
|
instance (Monoid n, Monoid r) => Monoid (RequestPredicate n r) where
|
||||||
mempty = RequestPredicate mempty (\r m -> return <$> httpLbs r m) mempty
|
mempty = RequestPredicate mempty (\r m -> httpLbs r m >>= \x -> return (mempty, [x]))
|
||||||
a `mappend` b = RequestPredicate
|
a `mappend` b = RequestPredicate
|
||||||
{ reqPredName = reqPredName a <> reqPredName b
|
{ reqPredName = reqPredName a <> reqPredName b
|
||||||
, reqResps = \x m -> liftM2 (<>) (reqResps a x m) (reqResps b x m)
|
, reqResps = \x m -> liftM2 (<>) (reqResps a x m) (reqResps b x m)
|
||||||
, reqPred = reqPred a <> reqPred b
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data Predicates n r = Predicates
|
data Predicates n r = Predicates
|
||||||
@ -316,9 +325,8 @@ infixr 6 <%>
|
|||||||
|
|
||||||
finishPredicates :: Predicates [Text] [Text] -> Request -> Manager -> IO [Text]
|
finishPredicates :: Predicates [Text] [Text] -> Request -> Manager -> IO [Text]
|
||||||
finishPredicates p req mgr = do
|
finishPredicates p req mgr = do
|
||||||
resps <- reqResps (reqPreds p) req mgr
|
(soFar, resps) <- reqResps (reqPreds p) req mgr
|
||||||
let preds = reqPred (reqPreds p) <> respPreds p
|
return $ soFar <> mconcat [respPred (respPreds p) r | r <- resps]
|
||||||
return $ mconcat [respPred preds r | r <- resps ]
|
|
||||||
|
|
||||||
-- * helpers
|
-- * helpers
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user