haskell-servant / servant-mock Goto Github PK
View Code? Open in Web Editor NEWDerive a mock server for free from your servant API types
License: BSD 3-Clause "New" or "Revised" License
Derive a mock server for free from your servant API types
License: BSD 3-Clause "New" or "Revised" License
From @RocketPuppy on September 6, 2016 0:53
While working on generating a mock server of my API (which is really cool now that I have it working), I ran into several issues regarding AuthProtect.
The first issue is simply that there isn't an instance for HasMock covering the AuthProtect combinator. Now that I've "implemented" one I think I'm starting to see why. The second issue is that authentication using AuthProtect is still done even in the mock servers. I have something working now but I'm not confident in it. I'll try and walk through my steps below.
Suppose an API like:
type API = AuthProtect "cookie-auth" AuthCookie :> Get '[JSON] SecretSauce
In order to mock I did the following:
api = Proxy :: Proxy API
type AppContext = '[ AuthHandler Request AuthCookie ]
context :: Context AppContext
context = (myAuthHandler :: AuthHandler Request AuthCookie) :. EmptyContext
mockServer = serveWithContext api context (mock api (Proxy :: AppContext))
context
is used in the canonical implementation of the API to provide the authentication handler. The first problem I ran into was the lack of a HasMock
instance. I came up with the following:
instance ( HasMock rest context
, HasServer rest context
, HasContextEntry context (AuthHandler Request (AuthServerData (AuthProtect sym)))
) =>
HasMock (AuthProtect (sym :: Symbol) :> rest) context where
mock _ context = \_ -> mock (Proxy :: Proxy rest) context
Which I'm not very happy with because it requires UndecidableInstances. The second problem I ran into, after I had that working, was that authentication still happens on the mock server. I'm on the fence as to whether this is actually a problem, but it was undesirable in my circumstances. I came up with the following solution:
mockAuthHandler :: (Arbitrary a) => AuthHandler Request a
mockAuthHandler = mkAuthHandler $ \ _ -> liftIO $ generate arbitrary
mockContext :: Context AppContext
mockContext = (mockAuthHandler :: AuthHandler Request AuthCookie) :. Context
-- and use mockContext in the mock server
mockServer = serveWithContext api mockContext (mock api (Proxy :: AppContext))
mockAuthHandler
is always successful at authenticating a user.
Altogether this solution works, in that I can run a mock server and get arbitrary values from the endpoints. But I wouldn't say that servant-mock plays well with AuthProtect.
I'd be interested in better solutions than the one I hacked up here.
Copied from original issue: haskell-servant/servant#596
As seen on the Stackage build server:
Preprocessing test suite 'spec' for servant-mock-0.8.1...
[1 of 2] Compiling Servant.MockSpec ( test/Servant/MockSpec.hs, dist/build/spec/spec-tmp/Servant/MockSpec.o )
test/Servant/MockSpec.hs:69:13: error:
• No instance for (ToHttpApiData TestHeader)
arising from a use of ‘toApp’
• In the first argument of ‘with’, namely ‘(toApp withHeader)’
In the expression: with (toApp withHeader)
In a stmt of a 'do' block:
with (toApp withHeader)
$ do { it "serves arbitrary response bodies"
$ do { get "/"
`shouldRespondWith`
200 {matchHeaders = return $ MatchHeader $ ...} } }
Hi @alpmestan
I'm trying to build this with a newer GHC ( 9.2.5 ) with stackage with resolver == lts-20.4 and it seems to fail.
The following errors show up
servant-mock/src/Servant/Mock.hs:121:10: error:
• Could not deduce (base-4.16.4.0:Data.Typeable.Internal.Typeable
a)
arising from the superclasses of an instance declaration
from the context: (KnownSymbol s, FromHttpApiData a,
HasMock rest context, SBoolI (FoldLenient mods))
bound by the instance declaration at src/Servant/Mock.hs:121:10-139
• In the instance declaration for
‘HasMock (Capture' mods s a :> rest) context’
|
121 | instance (KnownSymbol s, FromHttpApiData a, HasMock rest context, SBoolI (FoldLenient mods)) => HasMock (Capture' mods s a :> rest) context where
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
And
servant-mock/src/Servant/Mock.hs:124:10: error:
• Could not deduce (base-4.16.4.0:Data.Typeable.Internal.Typeable
a)
arising from the superclasses of an instance declaration
from the context: (KnownSymbol s, FromHttpApiData a,
HasMock rest context)
bound by the instance declaration at src/Servant/Mock.hs:124:10-109
• In the instance declaration for
‘HasMock (CaptureAll s a :> rest) context’
|
124 | instance (KnownSymbol s, FromHttpApiData a, HasMock rest context) => HasMock (CaptureAll s a :> rest) context where
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
https://travis-ci.org/haskell-servant/servant-mock/builds/166041304
Have to check, affects tests. https://hackage.haskell.org/package/hspec-wai-0.8.0 was released yesterday, probably related to it.
The Haddock documentation for Servant.Mock
gives example code similar to what's found in example/main.hs
, but with the most recent version of the package (0.8.7), the code in the Haddock documentation no longer compiles.
The module-level haddock documentation suggests the following should compile:
-- we need imports and User definition and instance -- we'll use the one from example/main.hs
import Data.Aeson
import GHC.Generics
import Network.Wai.Handler.Warp
import Servant
import Servant.Mock
import Test.QuickCheck.Arbitrary
newtype User = User { username :: String }
deriving (Eq, Show, Arbitrary, Generic)
instance ToJSON User
-- paste in the example from the Haddock documentation:
type API = "user" :> Get '[JSON] User
myAPI :: Proxy API
myAPI = Proxy
main :: IO ()
main = Network.Wai.Handler.Warp.run 8080 $
serve myAPI (mock myAPI Proxy)
However, in the arguments to serve
, it seems a reasonably recent GHC (8.6.5) can't deduce the type of the second Proxy
(whereas it could for servant-mock 0.8.5).
I used the Docker image for GHC 8.6.5 (running docker -D run --rm -it haskell:8.6.5 bash
), and pasted the above code into example/main.hs
, and within the container ran:
$ stack unpack servant-mock-0.8.7
$ cd servant-mock-0.8.7
$ cabal v2-update
$ cabal v2-build --dependencies-only all
$ cabal v2-build all
I get the following compile error:
[1 of 1] Compiling Main ( example/main.hs, /root/servant-mock-0.8.7/dist-newstyle/build/x86_64-linux/ghc-8.6.5/servant-mock-0.8.7/x/mock-app/build/mock-app/mock-app-tmp/Main.o )
example/main.hs:33:16: error:
* Ambiguous type variable `context0' arising from a use of `mock'
prevents the constraint `(HasContextEntry
(context0 .++ DefaultErrorFormatters)
ErrorFormatters)' from being solved.
Probable fix: use a type annotation to specify what `context0' should be.
These potential instances exist:
two instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
* In the second argument of `serve', namely `(mock myAPI Proxy)'
In the second argument of `($)', namely
`serve myAPI (mock myAPI Proxy)'
In the expression: run 8080 $ serve myAPI (mock myAPI Proxy)
|
33 | serve myAPI (mock myAPI Proxy)
I assume this is due to some change in servant-mock
and the other servant packages, since the example seems to build with no issues using servant-mock 0.8.5 and the same version of GHC.
The example/main.hs
code shows the fix - add a type declaration to the Proxy, thus: (Proxy :: Proxy '[]))
. But it seems worthwhile updating the Haddock documentation to reflect this.
(I'm not using contexts in my own project, so I don't know if that is an unduly restrictive signature and something looser could be used.)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.