mercredi 31 août 2016

How do I modify my Yesod app during a test

I have a Yesod app with the type:

data App = App
    { appSettings    :: AppSettings
    , appStatic      :: Static
    , appConnPool    :: ConnectionPool
    , appHttpManager :: Manager
    , appLogger      :: Logger
    , appStripe      :: forall a. ((FromJSON (StripeReturn a)), Typeable (StripeReturn a))
                     => StripeConfig
                     -> StripeRequest a
                     -> IO (Either StripeError (StripeReturn a))
    }

and a helper function

stripe :: (FromJSON (StripeReturn a), Typeable (StripeReturn a))
       => StripeRequest a
       -> Handler (Either StripeError (StripeReturn a))
stripe req = do
  f <- appStripe <$> getYesod
  c <- appStripeConfig . appSettings <$> getYesod
  liftIO $ f c req

that is used in several handlers. (The appStripe field of the App is never referenced directly in any handlers.) In makeFoundation, everything is as scaffolded, except that the appStripe field is filled in with Web.Stripe.stripe from the stripe-haskell library.

In my tests, I would like to be able to mock the calls to Stripe, so I have the following function:

withStripeExpecting :: (FromJSON (StripeReturn a), Typeable (StripeReturn a))
                    => StripeRequest a
                    -> Either StripeError (StripeReturn a)
                    -> YesodExample App ()
                    -> YesodExample App ()
withStripeExpecting _expectedReq res = withStateT $ \yed -> yed {yedSite = f (yedSite yed)}
  where f app = app {appStripe = mock}
        mock :: Typeable (StripeReturn b)
             => StripeConfig
             -> StripeRequest b
             -> IO (Either StripeError (StripeReturn b))
        mock _ _actualReq = do
          -- assert actualReq matches expectedReq (in IO???)
          return $ case cast res of
                    Just a -> a
                    Nothing -> error "Stripe return types don’t match in mock."

which I use in test cases like:

spec :: Spec
spec = withApp $ do
  describe "create" $ do
    it "returns a 201" $ do
      -- a bunch of set-up elided
      withStripeExpecting stripeReq (Right stripeRes) $ do
        requestWithSubject "auth0|fake" $ do
          setMethod "POST"
          setUrl $ SubscriptionPlansR walletId
          setRequestBody encoded
          addRequestHeader (H.hContentType, "application/json")
        statusIs 201

which compiles and runs, but throws an error StripeError {errorType = InvalidRequest, errorMsg = "Invalid API Key provided: ", errorCode = Nothing, errorParam = Nothing, errorHTTP = Just UnAuthorized} suggesting that it is running the real stripe IO action instead of the mock.

How do I change a field of the App during a test so that it will be used by the handler under test?

Aucun commentaire:

Enregistrer un commentaire