packages feed

http-tower-hs-0.2.0.0: test/Network/HTTP/Tower/Middleware/TestDoubleSpec.hs

{-# LANGUAGE OverloadedStrings #-}

module Network.HTTP.Tower.Middleware.TestDoubleSpec (spec) where

import Data.IORef
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Map.Strict as Map
import qualified Network.HTTP.Client as HTTP
import qualified Network.HTTP.Client.Internal as HTTP
import qualified Network.HTTP.Types as HTTP
import Test.Hspec

import Network.HTTP.Tower.Client (HttpResponse)
import Tower.Service
import Tower.Error
import Network.HTTP.Tower.Middleware.TestDouble

spec :: Spec
spec = describe "TestDouble middleware" $ do
  describe "withMock" $ do
    it "replaces the inner service entirely" $ do
      innerCalled <- newIORef False
      let inner = Service $ \_ -> do
            writeIORef innerCalled True
            pure (Right fakeResponse)
          mocked = withMock (\_ -> pure (Right fakeResponse)) inner
      req <- HTTP.parseRequest "http://example.com"
      result <- runService mocked req
      case result of
        Right _ -> pure ()
        Left err -> expectationFailure $ show err
      readIORef innerCalled >>= (`shouldBe` False)

    it "can return errors" $ do
      let mocked = withMock (\_ -> pure (Left (CustomError "mock error"))) (Service $ \_ -> pure (Right fakeResponse))
      req <- HTTP.parseRequest "http://example.com"
      result <- runService mocked req
      case result of
        Left (CustomError "mock error") -> pure ()
        other -> expectationFailure $ "Expected mock error, got: " ++ show other

  describe "withMockMap" $ do
    it "routes to matching mock response" $ do
      let mocks = Map.fromList
            [ ("example.com/api/users", Right fakeResponse)
            ]
          mocked = withMockMap mocks (Service $ \_ -> pure (Left (CustomError "not mocked")))
      req <- HTTP.parseRequest "http://example.com/api/users"
      result <- runService mocked req
      case result of
        Right _ -> pure ()
        Left err -> expectationFailure $ "Expected mock match, got: " ++ show err

    it "falls through to inner service on no match" $ do
      let mocks = Map.fromList
            [ ("example.com/api/users", Right fakeResponse)
            ]
          mocked = withMockMap mocks (Service $ \_ -> pure (Right fakeResponse))
      req <- HTTP.parseRequest "http://example.com/api/other"
      result <- runService mocked req
      case result of
        Right _ -> pure ()
        Left err -> expectationFailure $ show err

  describe "withRecorder" $ do
    it "records all requests" $ do
      recorder <- newIORef []
      let svc = withRecorder recorder (Service $ \_ -> pure (Right fakeResponse))
      req1 <- HTTP.parseRequest "http://example.com/a"
      req2 <- HTTP.parseRequest "http://example.com/b"
      _ <- runService svc req1
      _ <- runService svc req2
      recorded <- readIORef recorder
      length recorded `shouldBe` 2

    it "still forwards to inner service" $ do
      recorder <- newIORef []
      innerCalled <- newIORef (0 :: Int)
      let inner = Service $ \_ -> do
            modifyIORef' innerCalled (+ 1)
            pure (Right fakeResponse)
          svc = withRecorder recorder inner
      req <- HTTP.parseRequest "http://example.com"
      _ <- runService svc req
      readIORef innerCalled >>= (`shouldBe` 1)

fakeResponse :: HttpResponse
fakeResponse = HTTP.Response
  { HTTP.responseStatus = HTTP.status200
  , HTTP.responseVersion = HTTP.http11
  , HTTP.responseHeaders = []
  , HTTP.responseBody = ""
  , HTTP.responseCookieJar = HTTP.createCookieJar []
  , HTTP.responseClose' = HTTP.ResponseClose (pure ())
  , HTTP.responseOriginalRequest = error "not used"
  , HTTP.responseEarlyHints = []
  }