packages feed

stripe-hs-0.2.0.0: test/HelperSpec.hs

module HelperSpec (helperSpec) where

import Test.Hspec
import Data.IORef
import Servant.Client
import Servant.Client.Core.Request
import Network.HTTP.Types.Status
import Network.HTTP.Types.Version
import Network.HTTP.Types.Header
import qualified Data.Sequence as Seq
import qualified Data.ByteString as BS
import Data.Maybe
import Control.Exception.Base
import System.Exit

import Stripe.Client.Internal.Helpers

helperSpec :: Spec
helperSpec =
  do describe "retries" retryTests

makeFakeAction ::
  (Int -> Either ClientError a)
  -> IO (IO Int, IO (Either ClientError a))
makeFakeAction makeResult =
  do calls <- newIORef 0
     let action =
           do call <- atomicModifyIORef calls $ \i -> (i + 1, i)
              pure (makeResult call)
     pure (readIORef calls, action)

dummyReq :: RequestF () (BaseUrl, BS.ByteString)
dummyReq =
  defaultRequest
  { requestBody = Nothing
  , requestPath = (fromJust $ parseBaseUrl "api.example.com", "")
  }

retryAction ::
  Int
  -> Status
  -> Seq.Seq Header
  -> IO (ClientError, IO Int, IO (Either ClientError Bool))
retryAction n status headers =
  do let errResp =
           Response
           { responseStatusCode = status
           , responseHeaders = headers
           , responseHttpVersion = http11
           , responseBody = mempty
           }
         err = FailureResponse dummyReq errResp
     (getCalls, action) <-
       makeFakeAction $ \call ->
       if call < n
       then Left $ err
       else Right True
     pure (err, getCalls, action)

retryTests :: SpecWith ()
retryTests =
  do it "does not retry on success" $
       do (getCalls, action) <- makeFakeAction (const $ Right True)
          runRequest 10 0 action `shouldReturn` Right True
          getCalls `shouldReturn` 1
     it "retries on connection errors" $
       do (getCalls, action) <-
            makeFakeAction $ \call ->
            if call == 0
            then Left (ConnectionError $ toException ExitSuccess)
            else Right True
          runRequest 10 0 action `shouldReturn` Right True
          getCalls `shouldReturn` 2
     it "retries on stripe-should-retry=true headers" $
       do (_, getCalls, action) <-
            retryAction 1 status404 $
            Seq.fromList [("stripe-should-retry", "true")]
          runRequest 10 0 action `shouldReturn` Right True
          getCalls `shouldReturn` 2
     it "retries on 409 status code" $
       do (_, getCalls, action) <-
            retryAction 1 status409 mempty
          runRequest 10 0 action `shouldReturn` Right True
          getCalls `shouldReturn` 2
     it "retries on 500 status code" $
       do (_, getCalls, action) <-
            retryAction 1 status500 mempty
          runRequest 10 0 action `shouldReturn` Right True
          getCalls `shouldReturn` 2
     it "does not retry on status 500 with should retry false" $
       do (err, getCalls, action) <-
            retryAction 1 status500 $
            Seq.fromList [("stripe-should-retry", "false")]
          runRequest 10 0 action `shouldReturn` Left err
          getCalls `shouldReturn` 1
     it "does not retry on status 500 if limit exceeded" $
       do (err, getCalls, action) <-
            retryAction 11 status500 mempty
          runRequest 10 0 action `shouldReturn` Left err
          getCalls `shouldReturn` 10