Use new client with per-client BaseUrl and Manager.

This may be a step back, and the instances aren't as nice.
This commit is contained in:
Julian K. Arni 2016-04-22 14:18:44 +02:00
parent 72abea9b0f
commit 0d455a9851
3 changed files with 49 additions and 28 deletions

View File

@ -49,9 +49,12 @@ withServantServer api server t
-- @serversEqual@ or @servantServersEqual@. -- @serversEqual@ or @servantServersEqual@.
serversEqualProperty :: (HasClient a, Testable (ShouldMatch (Client a))) serversEqualProperty :: (HasClient a, Testable (ShouldMatch (Client a)))
=> Proxy a -> Manager -> BaseUrl -> BaseUrl -> Property => Proxy a -> Manager -> BaseUrl -> BaseUrl -> Property
serversEqualProperty api mgr burl1 burl2 = property $ ShouldMatch c1 c2 serversEqualProperty api mgr burl1 burl2
where c1 = client api burl1 mgr = property $ ShouldMatch
c2 = client api burl2 mgr { smClient = client api
, smManager = mgr
, smBaseUrls = (burl1, burl2)
}
-- | Check that the two servers running under the provided @BaseUrl@s behave -- | Check that the two servers running under the provided @BaseUrl@s behave
-- identically by randomly generating arguments (captures, query params, request bodies, -- identically by randomly generating arguments (captures, query params, request bodies,
@ -78,7 +81,13 @@ serversEqual api burl1 burl2 tries = do
serverSatisfiesProperty :: (HasClient a, Testable (ShouldSatisfy filt exp (Client a))) serverSatisfiesProperty :: (HasClient a, Testable (ShouldSatisfy filt exp (Client a)))
=> Proxy a -> Manager -> BaseUrl -> Predicates filt -> Predicates exp -> Property => Proxy a -> Manager -> BaseUrl -> Predicates filt -> Predicates exp -> Property
serverSatisfiesProperty api mgr burl filters expect = do serverSatisfiesProperty api mgr burl filters expect = do
property $ ShouldSatisfy (client api burl mgr) filters expect property $ ShouldSatisfy
{ ssVal = client api
, ssFilter = filters
, ssExpect = expect
, ssManager = mgr
, ssBaseUrl = burl
}
-- | Check that a server's responses satisfies certain properties. -- | Check that a server's responses satisfies certain properties.
serverSatisfies :: (HasClient a, Testable (ShouldSatisfy filt exp (Client a))) serverSatisfies :: (HasClient a, Testable (ShouldSatisfy filt exp (Client a)))

View File

@ -2,12 +2,12 @@
module Servant.QuickCheck.Internal.Testable where module Servant.QuickCheck.Internal.Testable where
import Control.Concurrent.MVar (MVar, modifyMVar_, newMVar, readMVar) import Control.Concurrent.MVar (MVar, modifyMVar_, newMVar, readMVar)
import Control.Monad.Except (runExceptT) import Control.Monad.Except (runExceptT, ExceptT)
import GHC.Generics (Generic) import GHC.Generics (Generic)
import Network.HTTP.Client (Request, RequestBody (..), import Network.HTTP.Client (Request, RequestBody (..),
requestBody) requestBody, Manager)
import Servant.API ((:<|>)(..)) import Servant.API ((:<|>)(..))
import Servant.Client (ServantError (..), ClientM) import Servant.Client (ServantError (..), BaseUrl)
import System.IO.Unsafe (unsafePerformIO) import System.IO.Unsafe (unsafePerformIO)
import Test.QuickCheck (Arbitrary (..), discard) import Test.QuickCheck (Arbitrary (..), discard)
import Test.QuickCheck.Property (Testable (..), forAllShrink, import Test.QuickCheck.Property (Testable (..), forAllShrink,
@ -16,16 +16,22 @@ import Test.QuickCheck.Property (Testable (..), forAllShrink,
import Servant.QuickCheck.Internal.Predicates import Servant.QuickCheck.Internal.Predicates
type FinalClient a = Manager -> BaseUrl -> ExceptT ServantError IO a
-- * ShouldMatch -- * ShouldMatch
-- | Two corresponding client functions. Used for checking that APIs match. -- | Two corresponding client functions. Used for checking that APIs match.
data ShouldMatch a = ShouldMatch a a data ShouldMatch a = ShouldMatch
deriving (Eq, Show, Read, Generic) { smClient :: a
, smManager :: Manager
, smBaseUrls :: (BaseUrl, BaseUrl)
} deriving (Functor, Generic)
instance (Show a, Eq a) => Testable (ShouldMatch (ClientM a)) where instance (Show a, Eq a) => Testable (ShouldMatch (FinalClient a)) where
property (ShouldMatch e1 e2) = ioProperty $ do property sm = ioProperty $ do
e1' <- runExceptT e1 let (burl1, burl2) = smBaseUrls sm
e2' <- runExceptT e2 e1' <- runExceptT $ smClient sm (smManager sm) burl1
e2' <- runExceptT $ smClient sm (smManager sm) burl2
modifyMVar_ currentReq $ \x -> case x of modifyMVar_ currentReq $ \x -> case x of
Nothing -> error "impossible" Nothing -> error "impossible"
Just (x', _) -> return $ Just (x', "LHS:\n" ++ show e1' Just (x', _) -> return $ Just (x', "LHS:\n" ++ show e1'
@ -40,13 +46,12 @@ instance (Show a, Eq a) => Testable (ShouldMatch (ClientM a)) where
instance (Arbitrary a, Show a, Testable (ShouldMatch b)) instance (Arbitrary a, Show a, Testable (ShouldMatch b))
=> Testable (ShouldMatch (a -> b)) where => Testable (ShouldMatch (a -> b)) where
property (ShouldMatch f1 f2) = forAllShrink arbitrary shrink go property sm = forAllShrink arbitrary shrink go
where go x = ShouldMatch (f1 x) (f2 x) where go x = ($ x) <$> sm
instance (Testable (ShouldMatch a), Testable (ShouldMatch b)) instance (Testable (ShouldMatch a), Testable (ShouldMatch b))
=> Testable (ShouldMatch (a :<|> b)) where => Testable (ShouldMatch (a :<|> b)) where
property (ShouldMatch (a1 :<|> b1) (a2 :<|> b2)) property sm = property (fstAlt <$> sm) .&. property (sndAlt <$> sm)
= property (ShouldMatch a1 a2) .&. property (ShouldMatch b1 b2)
-- * ShouldSatisfy -- * ShouldSatisfy
@ -54,32 +59,39 @@ data ShouldSatisfy filter expect a = ShouldSatisfy
{ ssVal :: a { ssVal :: a
, ssFilter :: Predicates filter , ssFilter :: Predicates filter
, ssExpect :: Predicates expect , ssExpect :: Predicates expect
, ssManager :: Manager
, ssBaseUrl :: BaseUrl
} deriving (Functor) } deriving (Functor)
instance (Show a, Eq a, HasPredicate expect (Either ServantError a)) instance (Show a, Eq a, HasPredicate expect (Either ServantError a))
=> Testable (ShouldSatisfy filter expect (ClientM a)) where => Testable (ShouldSatisfy filter expect (FinalClient a)) where
property (ShouldSatisfy a _ e) = ioProperty $ do property ss = ioProperty $ do
a' <- runExceptT a a' <- runExceptT $ ssVal ss (ssManager ss) (ssBaseUrl ss)
modifyMVar_ currentReq $ \x -> case x of modifyMVar_ currentReq $ \x -> case x of
Nothing -> error "impossible" Nothing -> error "impossible"
Just (x', _) -> return $ Just (x', show a') Just (x', _) -> return $ Just (x', show a')
return $ getPredicate e a' return $ getPredicate (ssExpect ss) a'
instance ( Arbitrary a, Show a, Testable (ShouldSatisfy filter expect b) instance ( Arbitrary a, Show a, Testable (ShouldSatisfy filter expect b)
, HasPredicate filter a) , HasPredicate filter a)
=> Testable (ShouldSatisfy filter expect (a -> b)) where => Testable (ShouldSatisfy filter expect (a -> b)) where
property (ShouldSatisfy g f e) = forAllShrink arbitrary shrink go property ss = forAllShrink arbitrary shrink go
where go x | getPredicate f x = ShouldSatisfy (g x) f e where go x | getPredicate (ssFilter ss) x = ($ x) <$> ss
| otherwise = discard | otherwise = discard
instance ( Testable (ShouldSatisfy filter expect a) instance ( Testable (ShouldSatisfy filter expect a)
, Testable (ShouldSatisfy filter expect b)) , Testable (ShouldSatisfy filter expect b))
=> Testable (ShouldSatisfy filter expect (a :<|> b)) where => Testable (ShouldSatisfy filter expect (a :<|> b)) where
property (ShouldSatisfy (a :<|> b) f e) property ss = property (fstAlt <$> ss) .&. property (sndAlt <$> ss)
= property (ShouldSatisfy a f e) .&. property (ShouldSatisfy b f e)
-- * Utils -- * Utils
fstAlt :: (a :<|> b) -> a
fstAlt (a :<|> _) = a
sndAlt :: (a :<|> b) -> b
sndAlt (_ :<|> b) = b
-- Used to store the current request and response so that in case of failure we -- Used to store the current request and response so that in case of failure we
-- have the failing test in a user-friendly form. -- have the failing test in a user-friendly form.
currentReq :: MVar (Maybe (Request, String)) currentReq :: MVar (Maybe (Request, String))

View File

@ -1,6 +1,6 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
module Servant.CoMock.InternalSpec (spec) where module Servant.QuickCheck.InternalSpec (spec) where
import Control.Concurrent.MVar (newMVar, readMVar, swapMVar) import Control.Concurrent.MVar (newMVar, readMVar, swapMVar)
import Control.Monad.IO.Class (liftIO) import Control.Monad.IO.Class (liftIO)
@ -8,7 +8,7 @@ import Data.Proxy
import Servant import Servant
import Test.Hspec import Test.Hspec
import Servant.CoMock.Internal import Servant.QuickCheck.Internal
spec :: Spec spec :: Spec
spec = do spec = do