Add notLongerThan predicate.

This commit is contained in:
Julian K. Arni 2016-10-04 19:09:23 +02:00
parent b4876468e6
commit feff40b2e4
5 changed files with 36 additions and 2 deletions

View File

@ -36,6 +36,7 @@ library
, aeson > 0.8 && < 2 , aeson > 0.8 && < 2
, bytestring == 0.10.* , bytestring == 0.10.*
, case-insensitive == 1.2.* , case-insensitive == 1.2.*
, clock >= 0.7 && < 0.8
, data-default-class >= 0.0 && < 0.2 , data-default-class >= 0.0 && < 0.2
, hspec == 2.2.* , hspec == 2.2.*
, http-client >= 0.4.30 && < 0.6 , http-client >= 0.4.30 && < 0.6

View File

@ -27,6 +27,7 @@ module Servant.QuickCheck
-- in RFCs. The __Best Practices__ includes, in addition to RFC -- in RFCs. The __Best Practices__ includes, in addition to RFC
-- recommendations, recommendations found elsewhere or generally accepted. -- recommendations, recommendations found elsewhere or generally accepted.
, not500 , not500
, notLongerThan
, onlyJsonObjects , onlyJsonObjects
, notAllowedContainsAllowHeader , notAllowedContainsAllowHeader
, unauthorizedContainsWWWAuthenticate , unauthorizedContainsWWWAuthenticate

View File

@ -5,7 +5,6 @@ module Servant.QuickCheck.Internal.HasGenRequest where
import Data.Monoid ((<>)) import Data.Monoid ((<>))
import Data.String (fromString) import Data.String (fromString)
import Data.String.Conversions (cs) import Data.String.Conversions (cs)
import qualified Data.ByteString as BS
import GHC.TypeLits (KnownSymbol, Nat, symbolVal) import GHC.TypeLits (KnownSymbol, Nat, symbolVal)
import Network.HTTP.Client (Request, RequestBody (..), host, method, path, import Network.HTTP.Client (Request, RequestBody (..), host, method, path,
port, queryString, requestBody, requestHeaders, port, queryString, requestBody, requestHeaders,
@ -16,6 +15,9 @@ import Servant
import Servant.API.ContentTypes (AllMimeRender (..)) import Servant.API.ContentTypes (AllMimeRender (..))
import Servant.Client (BaseUrl (..), Scheme (..)) import Servant.Client (BaseUrl (..), Scheme (..))
import Test.QuickCheck (Arbitrary (..), Gen, elements, oneof) import Test.QuickCheck (Arbitrary (..), Gen, elements, oneof)
#if MIN_VERSION_servant(0,8,0)
import qualified Data.ByteString as BS
#endif
class HasGenRequest a where class HasGenRequest a where

View File

@ -22,6 +22,7 @@ import Network.HTTP.Types (methodGet, methodHead, parseMethod,
renderStdMethod, status100, status200, renderStdMethod, status100, status200,
status201, status300, status401, status201, status300, status401,
status405, status500) status405, status500)
import System.Clock (toNanoSecs, Clock(Monotonic), getTime, diffTimeSpec)
import Prelude.Compat import Prelude.Compat
import Servant.QuickCheck.Internal.ErrorTypes import Servant.QuickCheck.Internal.ErrorTypes
@ -40,6 +41,22 @@ not500 :: ResponsePredicate
not500 = ResponsePredicate $ \resp -> not500 = ResponsePredicate $ \resp ->
when (responseStatus resp == status500) $ fail "not500" when (responseStatus resp == status500) $ fail "not500"
-- | [__Optional__]
--
-- This function checks that the response from the server does not take longer
-- than the specified number of nanoseconds.
--
-- #SINCE#
notLongerThan :: Integer -> RequestPredicate
notLongerThan maxAllowed
= RequestPredicate $ \req mgr -> do
start <- getTime Monotonic
resp <- httpLbs req mgr
end <- getTime Monotonic
when (toNanoSecs (end `diffTimeSpec` start) > maxAllowed) $
throw $ PredicateFailure "notLongerThan" (Just req) resp
return []
-- | [__Best Practice__] -- | [__Best Practice__]
-- --
-- Returning anything other than an object when returning JSON is considered -- Returning anything other than an object when returning JSON is considered
@ -126,7 +143,7 @@ createContainsValidLocation
-- * If-Unmodified-Since header: <https://tools.ietf.org/html/rfc7232#section-3.4 RFC 7232 Section 3.4> -- * If-Unmodified-Since header: <https://tools.ietf.org/html/rfc7232#section-3.4 RFC 7232 Section 3.4>
-- * Date format: <https://tools.ietf.org/html/rfc2616#section-3.3 RFC 2616 Section 3.3> -- * Date format: <https://tools.ietf.org/html/rfc2616#section-3.3 RFC 2616 Section 3.3>
-- --
-- #SINCECURRENT# -- #SINCE#
getsHaveLastModifiedHeader :: RequestPredicate getsHaveLastModifiedHeader :: RequestPredicate
getsHaveLastModifiedHeader getsHaveLastModifiedHeader
= RequestPredicate $ \req mgr -> = RequestPredicate $ \req mgr ->

View File

@ -25,6 +25,7 @@ spec = do
serverSatisfiesSpec serverSatisfiesSpec
isComprehensiveSpec isComprehensiveSpec
onlyJsonObjectSpec onlyJsonObjectSpec
notLongerThanSpec
serversEqualSpec :: Spec serversEqualSpec :: Spec
serversEqualSpec = describe "serversEqual" $ do serversEqualSpec = describe "serversEqual" $ do
@ -80,6 +81,18 @@ onlyJsonObjectSpec = describe "onlyJsonObjects" $ do
(onlyJsonObjects <%> mempty) (onlyJsonObjects <%> mempty)
err `shouldContain` "onlyJsonObjects" err `shouldContain` "onlyJsonObjects"
notLongerThanSpec :: Spec
notLongerThanSpec = describe "notLongerThan" $ do
it "fails correctly" $ do
Fail _ err <- withServantServerAndContext api ctx server $ \burl -> do
evalExample $ serverSatisfies (Proxy :: Proxy (Get '[JSON] Int)) burl args
(notLongerThan 1 <%> mempty)
err `shouldContain` "notLongerThan"
it "succeeds correctly" $ do
withServantServerAndContext api ctx server $ \burl ->
serverSatisfies api burl args (notLongerThan 1000000000000 <%> mempty)
isComprehensiveSpec :: Spec isComprehensiveSpec :: Spec
isComprehensiveSpec = describe "HasGenRequest" $ do isComprehensiveSpec = describe "HasGenRequest" $ do