Slightly nicer RequestPredicate.

Which still isn't beautiful.
This commit is contained in:
Julian K. Arni 2016-04-30 16:25:54 +02:00
parent c85d41ad79
commit 64c845cb45
3 changed files with 53 additions and 43 deletions

View File

@ -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 }

View File

@ -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)

View File

@ -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