servant-mock 0.4.4.7 → 0.5
raw patch · 7 files changed
+181/−61 lines, 7 filesdep +bytestring-conversiondep +hspecdep +hspec-waidep ~QuickCheckdep ~basedep ~servantPVP ok
version bump matches the API change (PVP)
Dependencies added: bytestring-conversion, hspec, hspec-wai
Dependency ranges changed: QuickCheck, base, servant, servant-server, wai
API changes (from Hackage documentation)
- Servant.Mock: instance (GHC.TypeLits.KnownSymbol h, Servant.Common.Text.FromText a, Servant.Mock.HasMock rest) => Servant.Mock.HasMock (Servant.API.Header.Header h a Servant.API.Sub.:> rest)
- Servant.Mock: instance (GHC.TypeLits.KnownSymbol path, Servant.Mock.HasMock rest) => Servant.Mock.HasMock (path Servant.API.Sub.:> rest)
- Servant.Mock: instance (GHC.TypeLits.KnownSymbol s, Servant.Common.Text.FromText a, Servant.Mock.HasMock rest) => Servant.Mock.HasMock (Servant.API.Capture.Capture s a Servant.API.Sub.:> rest)
- Servant.Mock: instance (GHC.TypeLits.KnownSymbol s, Servant.Common.Text.FromText a, Servant.Mock.HasMock rest) => Servant.Mock.HasMock (Servant.API.MatrixParam.MatrixParam s a Servant.API.Sub.:> rest)
- Servant.Mock: instance (GHC.TypeLits.KnownSymbol s, Servant.Common.Text.FromText a, Servant.Mock.HasMock rest) => Servant.Mock.HasMock (Servant.API.MatrixParam.MatrixParams s a Servant.API.Sub.:> rest)
- Servant.Mock: instance (GHC.TypeLits.KnownSymbol s, Servant.Common.Text.FromText a, Servant.Mock.HasMock rest) => Servant.Mock.HasMock (Servant.API.QueryParam.QueryParam s a Servant.API.Sub.:> rest)
- Servant.Mock: instance (GHC.TypeLits.KnownSymbol s, Servant.Common.Text.FromText a, Servant.Mock.HasMock rest) => Servant.Mock.HasMock (Servant.API.QueryParam.QueryParams s a Servant.API.Sub.:> rest)
- Servant.Mock: instance (GHC.TypeLits.KnownSymbol s, Servant.Mock.HasMock rest) => Servant.Mock.HasMock (Servant.API.MatrixParam.MatrixFlag s Servant.API.Sub.:> rest)
- Servant.Mock: instance (GHC.TypeLits.KnownSymbol s, Servant.Mock.HasMock rest) => Servant.Mock.HasMock (Servant.API.QueryParam.QueryFlag s Servant.API.Sub.:> rest)
- Servant.Mock: instance (Servant.API.ContentTypes.AllCTUnrender ctypes a, Servant.Mock.HasMock rest) => Servant.Mock.HasMock (Servant.API.ReqBody.ReqBody ctypes a Servant.API.Sub.:> rest)
- Servant.Mock: instance (Servant.Mock.HasMock a, Servant.Mock.HasMock b) => Servant.Mock.HasMock (a Servant.API.Alternative.:<|> b)
- Servant.Mock: instance (Test.QuickCheck.Arbitrary.Arbitrary a, Servant.API.ContentTypes.AllCTRender ctypes a) => Servant.Mock.HasMock (Servant.API.Delete.Delete ctypes a)
- Servant.Mock: instance (Test.QuickCheck.Arbitrary.Arbitrary a, Servant.API.ContentTypes.AllCTRender ctypes a) => Servant.Mock.HasMock (Servant.API.Get.Get ctypes a)
- Servant.Mock: instance (Test.QuickCheck.Arbitrary.Arbitrary a, Servant.API.ContentTypes.AllCTRender ctypes a) => Servant.Mock.HasMock (Servant.API.Patch.Patch ctypes a)
- Servant.Mock: instance (Test.QuickCheck.Arbitrary.Arbitrary a, Servant.API.ContentTypes.AllCTRender ctypes a) => Servant.Mock.HasMock (Servant.API.Post.Post ctypes a)
- Servant.Mock: instance (Test.QuickCheck.Arbitrary.Arbitrary a, Servant.API.ContentTypes.AllCTRender ctypes a) => Servant.Mock.HasMock (Servant.API.Put.Put ctypes a)
- Servant.Mock: instance Servant.Mock.HasMock Servant.API.Raw.Raw
+ Servant.Mock: instance (Servant.Mock.HasMock a context, Servant.Mock.HasMock b context) => Servant.Mock.HasMock (a Servant.API.Alternative.:<|> b) context
+ Servant.Mock: instance (Servant.Server.Internal.Context.HasContextEntry context (Servant.Server.Internal.Context.NamedContext name subContext), Servant.Mock.HasMock rest subContext) => Servant.Mock.HasMock (Servant.API.WithNamedContext.WithNamedContext name subContext rest) context
+ Servant.Mock: instance Servant.Mock.HasMock Servant.API.Raw.Raw context
+ Servant.Mock: instance forall (k :: BOX) (ctypes :: [*]) a (rest :: k) (context :: [*]). (Servant.API.ContentTypes.AllCTUnrender ctypes a, Servant.Mock.HasMock rest context) => Servant.Mock.HasMock (Servant.API.ReqBody.ReqBody ctypes a Servant.API.Sub.:> rest) context
+ Servant.Mock: instance forall (k :: BOX) (h :: GHC.TypeLits.Symbol) a (rest :: k) (context :: [*]). (GHC.TypeLits.KnownSymbol h, Web.HttpApiData.Internal.FromHttpApiData a, Servant.Mock.HasMock rest context) => Servant.Mock.HasMock (Servant.API.Header.Header h a Servant.API.Sub.:> rest) context
+ Servant.Mock: instance forall (k :: BOX) (method :: k) (status :: GHC.TypeLits.Nat) (ctypes :: [*]) (headerTypes :: [*]) a (context :: [*]). (Servant.API.ResponseHeaders.GetHeaders (Servant.API.ResponseHeaders.Headers headerTypes a), Test.QuickCheck.Arbitrary.Arbitrary (Servant.API.ResponseHeaders.HList headerTypes), Test.QuickCheck.Arbitrary.Arbitrary a, GHC.TypeLits.KnownNat status, Servant.API.Verbs.ReflectMethod method, Servant.API.ContentTypes.AllCTRender ctypes a) => Servant.Mock.HasMock (Servant.API.Verbs.Verb method status ctypes (Servant.API.ResponseHeaders.Headers headerTypes a)) context
+ Servant.Mock: instance forall (k :: BOX) (method :: k) (status :: GHC.TypeLits.Nat) (ctypes :: [*]) a (context :: [*]). (Test.QuickCheck.Arbitrary.Arbitrary a, GHC.TypeLits.KnownNat status, Servant.API.Verbs.ReflectMethod method, Servant.API.ContentTypes.AllCTRender ctypes a) => Servant.Mock.HasMock (Servant.API.Verbs.Verb method status ctypes a) context
+ Servant.Mock: instance forall (k :: BOX) (path :: GHC.TypeLits.Symbol) (rest :: k) (context :: [*]). (GHC.TypeLits.KnownSymbol path, Servant.Mock.HasMock rest context) => Servant.Mock.HasMock (path Servant.API.Sub.:> rest) context
+ Servant.Mock: instance forall (k :: BOX) (rest :: k) (context :: [*]). Servant.Mock.HasMock rest context => Servant.Mock.HasMock (Data.Vault.Lazy.Vault Servant.API.Sub.:> rest) context
+ Servant.Mock: instance forall (k :: BOX) (rest :: k) (context :: [*]). Servant.Mock.HasMock rest context => Servant.Mock.HasMock (Network.HTTP.Types.Version.HttpVersion Servant.API.Sub.:> rest) context
+ Servant.Mock: instance forall (k :: BOX) (rest :: k) (context :: [*]). Servant.Mock.HasMock rest context => Servant.Mock.HasMock (Servant.API.IsSecure.IsSecure Servant.API.Sub.:> rest) context
+ Servant.Mock: instance forall (k :: BOX) (rest :: k) (context :: [*]). Servant.Mock.HasMock rest context => Servant.Mock.HasMock (Servant.API.RemoteHost.RemoteHost Servant.API.Sub.:> rest) context
+ Servant.Mock: instance forall (k :: BOX) (s :: GHC.TypeLits.Symbol) (rest :: k) (context :: [*]). (GHC.TypeLits.KnownSymbol s, Servant.Mock.HasMock rest context) => Servant.Mock.HasMock (Servant.API.QueryParam.QueryFlag s Servant.API.Sub.:> rest) context
+ Servant.Mock: instance forall (k :: BOX) (s :: GHC.TypeLits.Symbol) a (rest :: k) (context :: [*]). (GHC.TypeLits.KnownSymbol s, Web.HttpApiData.Internal.FromHttpApiData a, Servant.Mock.HasMock rest context) => Servant.Mock.HasMock (Servant.API.Capture.Capture s a Servant.API.Sub.:> rest) context
+ Servant.Mock: instance forall (k :: BOX) (s :: GHC.TypeLits.Symbol) a (rest :: k) (context :: [*]). (GHC.TypeLits.KnownSymbol s, Web.HttpApiData.Internal.FromHttpApiData a, Servant.Mock.HasMock rest context) => Servant.Mock.HasMock (Servant.API.QueryParam.QueryParam s a Servant.API.Sub.:> rest) context
+ Servant.Mock: instance forall (k :: BOX) (s :: GHC.TypeLits.Symbol) a (rest :: k) (context :: [*]). (GHC.TypeLits.KnownSymbol s, Web.HttpApiData.Internal.FromHttpApiData a, Servant.Mock.HasMock rest context) => Servant.Mock.HasMock (Servant.API.QueryParam.QueryParams s a Servant.API.Sub.:> rest) context
- Servant.Mock: class HasServer api => HasMock api
+ Servant.Mock: class HasServer api context => HasMock api context
- Servant.Mock: mock :: HasMock api => Proxy api -> Server api
+ Servant.Mock: mock :: HasMock api context => Proxy api -> Proxy context -> Server api
Files
- LICENSE +1/−1
- example/main.hs +1/−1
- include/overlapping-compat.h +8/−0
- servant-mock.cabal +29/−7
- src/Servant/Mock.hs +56/−52
- test/Servant/MockSpec.hs +85/−0
- test/Spec.hs +1/−0
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2015, Alp Mestanogullari+Copyright (c) 2015-2016, Servant Contributors All rights reserved.
example/main.hs view
@@ -20,4 +20,4 @@ api = Proxy main :: IO ()-main = run 8080 (serve api $ mock api)+main = run 8080 (serve api $ mock api Proxy)
+ include/overlapping-compat.h view
@@ -0,0 +1,8 @@+#if __GLASGOW_HASKELL__ >= 710+#define OVERLAPPABLE_ {-# OVERLAPPABLE #-}+#define OVERLAPPING_ {-# OVERLAPPING #-}+#else+{-# LANGUAGE OverlappingInstances #-}+#define OVERLAPPABLE_+#define OVERLAPPING_+#endif
servant-mock.cabal view
@@ -1,5 +1,5 @@ name: servant-mock-version: 0.4.4.7+version: 0.5 synopsis: Derive a mock server for free from your servant API types description: Derive a mock server for free from your servant API types@@ -8,17 +8,17 @@ homepage: http://github.com/haskell-servant/servant license: BSD3 license-file: LICENSE-author: Alp Mestanogullari-maintainer: alpmestan@gmail.com-copyright: 2015 Alp Mestanogullari+author: Servant Contributors+maintainer: haskell-servant-maintainers@googlegroups.com+copyright: 2015-2016 Servant Contributors category: Web build-type: Simple+extra-source-files: include/*.h cabal-version: >=1.10 flag example description: Build the example too- manual: True- default: False+ default: True library exposed-modules:@@ -30,10 +30,11 @@ servant >= 0.4, servant-server >= 0.4, transformers >= 0.3 && <0.5,- QuickCheck >= 2.8 && <2.9,+ QuickCheck >= 2.7 && <2.9, wai >= 3.0 && <3.3 hs-source-dirs: src default-language: Haskell2010+ include-dirs: include executable mock-app main-is: main.hs@@ -44,3 +45,24 @@ buildable: True else buildable: False++test-suite spec+ type: exitcode-stdio-1.0+ ghc-options:+ -Wall -fno-warn-name-shadowing+ default-language: Haskell2010+ hs-source-dirs: test+ main-is: Spec.hs+ other-modules:+ Servant.MockSpec+ build-depends:+ base,+ hspec,+ hspec-wai,+ QuickCheck,+ servant,+ servant-server,+ servant-mock,+ aeson,+ bytestring-conversion,+ wai
src/Servant/Mock.hs view
@@ -1,11 +1,16 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-} {-# OPTIONS_GHC -fno-warn-orphans #-}++#include "overlapping-compat.h"+ -- | -- Module : Servant.Mock -- Copyright : 2015 Alp Mestanogullari@@ -69,7 +74,7 @@ -- than turns them into random-response-generating -- request handlers, hence providing an instance for -- all the combinators of the core /servant/ library.-class HasServer api => HasMock api where+class HasServer api context => HasMock api context where -- | Calling this method creates request handlers of -- the right type to implement the API described by -- @api@ that just generate random response values of@@ -92,74 +97,75 @@ -- actually "means" 2 request handlers, of the following types: -- -- @- -- getUser :: EitherT ServantErr IO User- -- getBook :: EitherT ServantErr IO Book+ -- getUser :: ExceptT ServantErr IO User+ -- getBook :: ExceptT ServantErr IO Book -- @ -- -- So under the hood, 'mock' uses the 'IO' bit to generate -- random values of type 'User' and 'Book' every time these -- endpoints are requested.- mock :: Proxy api -> Server api+ mock :: Proxy api -> Proxy context -> Server api -instance (HasMock a, HasMock b) => HasMock (a :<|> b) where- mock _ = mock (Proxy :: Proxy a) :<|> mock (Proxy :: Proxy b)+instance (HasMock a context, HasMock b context) => HasMock (a :<|> b) context where+ mock _ context = mock (Proxy :: Proxy a) context :<|> mock (Proxy :: Proxy b) context -instance (KnownSymbol path, HasMock rest) => HasMock (path :> rest) where+instance (KnownSymbol path, HasMock rest context) => HasMock (path :> rest) context where mock _ = mock (Proxy :: Proxy rest) -instance (KnownSymbol s, FromText a, HasMock rest) => HasMock (Capture s a :> rest) where- mock _ = \_ -> mock (Proxy :: Proxy rest)--instance (AllCTUnrender ctypes a, HasMock rest) => HasMock (ReqBody ctypes a :> rest) where- mock _ = \_ -> mock (Proxy :: Proxy rest)--instance (KnownSymbol s, FromText a, HasMock rest)- => HasMock (QueryParam s a :> rest) where- mock _ = \_ -> mock (Proxy :: Proxy rest)+instance (KnownSymbol s, FromHttpApiData a, HasMock rest context) => HasMock (Capture s a :> rest) context where+ mock _ context = \_ -> mock (Proxy :: Proxy rest) context -instance (KnownSymbol s, FromText a, HasMock rest)- => HasMock (QueryParams s a :> rest) where- mock _ = \_ -> mock (Proxy :: Proxy rest)+instance (AllCTUnrender ctypes a, HasMock rest context) => HasMock (ReqBody ctypes a :> rest) context where+ mock _ context = \_ -> mock (Proxy :: Proxy rest) context -instance (KnownSymbol s, HasMock rest) => HasMock (QueryFlag s :> rest) where- mock _ = \_ -> mock (Proxy :: Proxy rest)+instance HasMock rest context => HasMock (RemoteHost :> rest) context where+ mock _ context = \_ -> mock (Proxy :: Proxy rest) context -instance (KnownSymbol s, FromText a, HasMock rest)- => HasMock (MatrixParam s a :> rest) where- mock _ = \_ -> mock (Proxy :: Proxy rest)+instance HasMock rest context => HasMock (IsSecure :> rest) context where+ mock _ context = \_ -> mock (Proxy :: Proxy rest) context -instance (KnownSymbol s, FromText a, HasMock rest)- => HasMock (MatrixParams s a :> rest) where- mock _ = \_ -> mock (Proxy :: Proxy rest)+instance HasMock rest context => HasMock (Vault :> rest) context where+ mock _ context = \_ -> mock (Proxy :: Proxy rest) context -instance (KnownSymbol s, HasMock rest) => HasMock (MatrixFlag s :> rest) where- mock _ = \_ -> mock (Proxy :: Proxy rest)+instance HasMock rest context => HasMock (HttpVersion :> rest) context where+ mock _ context = \_ -> mock (Proxy :: Proxy rest) context -instance (KnownSymbol h, FromText a, HasMock rest) => HasMock (Header h a :> rest) where- mock _ = \_ -> mock (Proxy :: Proxy rest)+instance (KnownSymbol s, FromHttpApiData a, HasMock rest context)+ => HasMock (QueryParam s a :> rest) context where+ mock _ context = \_ -> mock (Proxy :: Proxy rest) context -instance (Arbitrary a, AllCTRender ctypes a) => HasMock (Delete ctypes a) where- mock _ = mockArbitrary+instance (KnownSymbol s, FromHttpApiData a, HasMock rest context)+ => HasMock (QueryParams s a :> rest) context where+ mock _ context = \_ -> mock (Proxy :: Proxy rest) context -instance (Arbitrary a, AllCTRender ctypes a) => HasMock (Get ctypes a) where- mock _ = mockArbitrary+instance (KnownSymbol s, HasMock rest context) => HasMock (QueryFlag s :> rest) context where+ mock _ context = \_ -> mock (Proxy :: Proxy rest) context -instance (Arbitrary a, AllCTRender ctypes a) => HasMock (Patch ctypes a) where- mock _ = mockArbitrary+instance (KnownSymbol h, FromHttpApiData a, HasMock rest context) => HasMock (Header h a :> rest) context where+ mock _ context = \_ -> mock (Proxy :: Proxy rest) context -instance (Arbitrary a, AllCTRender ctypes a) => HasMock (Post ctypes a) where- mock _ = mockArbitrary+instance (Arbitrary a, KnownNat status, ReflectMethod method, AllCTRender ctypes a)+ => HasMock (Verb method status ctypes a) context where+ mock _ _ = mockArbitrary -instance (Arbitrary a, AllCTRender ctypes a) => HasMock (Put ctypes a) where- mock _ = mockArbitrary+instance OVERLAPPING_+ (GetHeaders (Headers headerTypes a), Arbitrary (HList headerTypes),+ Arbitrary a, KnownNat status, ReflectMethod method, AllCTRender ctypes a)+ => HasMock (Verb method status ctypes (Headers headerTypes a)) context where+ mock _ _ = mockArbitrary -instance HasMock Raw where- mock _ = \_ respond -> do+instance HasMock Raw context where+ mock _ _ = \_req respond -> do bdy <- genBody respond $ responseLBS status200 [] bdy where genBody = pack <$> generate (vector 100 :: Gen [Char]) +instance (HasContextEntry context (NamedContext name subContext), HasMock rest subContext) =>+ HasMock (WithNamedContext name subContext rest) context where++ mock _ _ = mock (Proxy :: Proxy rest) (Proxy :: Proxy subContext)+ mockArbitrary :: (MonadIO m, Arbitrary a) => m a mockArbitrary = liftIO (generate arbitrary) @@ -174,5 +180,3 @@ instance (Arbitrary a, Arbitrary (HList hs)) => Arbitrary (HList (Header h a ': hs)) where arbitrary = HCons <$> fmap Header arbitrary <*> arbitrary--
+ test/Servant/MockSpec.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeOperators #-}++module Servant.MockSpec where++import Data.Aeson as Aeson+import Data.ByteString.Conversion.To+import Data.Proxy+import Data.String+import GHC.Generics+import Network.Wai+import Servant.API+import Test.Hspec hiding (pending)+import Test.Hspec.Wai+import Test.QuickCheck++import Servant+import Servant.API.Internal.Test.ComprehensiveAPI+import Servant.Mock++-- This declaration simply checks that all instances are in place.+_ = mock comprehensiveAPI (Proxy :: Proxy '[NamedContext "foo" '[]])++data Body+ = Body+ | ArbitraryBody+ deriving (Generic)++instance ToJSON Body++instance Arbitrary Body where+ arbitrary = return ArbitraryBody++data TestHeader+ = TestHeader+ | ArbitraryHeader+ deriving (Show)++instance ToByteString TestHeader where+ builder = fromString . show++instance Arbitrary TestHeader where+ arbitrary = return ArbitraryHeader++spec :: Spec+spec = do+ describe "mock" $ do+ context "Get" $ do+ let api :: Proxy (Get '[JSON] Body)+ api = Proxy+ app = serve api (mock api Proxy)+ with (return app) $ do+ it "serves arbitrary response bodies" $ do+ get "/" `shouldRespondWith` 200{+ matchBody = Just $ Aeson.encode ArbitraryBody+ }++ context "response headers" $ do+ let withHeader :: Proxy (Get '[JSON] (Headers '[Header "foo" TestHeader] Body))+ withHeader = Proxy+ withoutHeader :: Proxy (Get '[JSON] (Headers '[] Body))+ withoutHeader = Proxy+ toApp :: (HasMock api '[]) => Proxy api -> IO Application+ toApp api = return $ serve api (mock api (Proxy :: Proxy '[]))+ with (toApp withHeader) $ do+ it "serves arbitrary response bodies" $ do+ get "/" `shouldRespondWith` 200{+ matchHeaders = return $ MatchHeader $ \ h ->+ if h == [("Content-Type", "application/json"), ("foo", "ArbitraryHeader")]+ then Nothing+ else Just ("headers not correct\n")+ }++ with (toApp withoutHeader) $ do+ it "works for no additional headers" $ do+ get "/" `shouldRespondWith` 200{+ matchHeaders = return $ MatchHeader $ \ h ->+ if h == [("Content-Type", "application/json")]+ then Nothing+ else Just ("headers not correct\n")+ }
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}