packages feed

dormouse-client-0.2.0.0: src/Dormouse/Client/Test/Class.hs

{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}

-- | 
-- This module is useful for testing by providing a concrete ByteString typed version of 'MonadDormouseClient' called `MonadDormouseTestClient`.  
--
-- The assumption is that, in most test cases, you probably want to verify the byte payload of the request (which you simply extract
-- from the request here as a @ByteString@) and provide a byte payload (also as a @ByteString@) in the response so that you can verify 
-- your repsonse payload can be decoded directly.
--
-- An implementation of `MonadDormouseTestClient` can be written in terms of either Strict or Lazy Bytestrings at your convenient and the other 
-- will be automatically provided for you.
--
-- The machinery in here uses orphan instances of 'MonadDormouseClient' so you should use this carefully and restrict this module to test 
-- cases only.
module Dormouse.Client.Test.Class
  ( MonadDormouseTestClient(..)
  ) where

import Control.Monad.IO.Class ( MonadIO(..) )
import qualified Data.ByteString  as SB
import qualified Data.ByteString.Lazy  as LB
import Data.Word ( Word8 )
import Dormouse.Client.Class ( MonadDormouseClient(..) )
import Dormouse.Client.Payload ( RawRequestPayload(..) )
import Dormouse.Client.Types ( HttpRequest(..), HttpResponse(..) )
import Dormouse.Url ( IsUrl )
import Streamly ( SerialT )
import qualified Streamly.Prelude as S
import qualified Streamly.External.ByteString as SEB
import qualified Streamly.External.ByteString.Lazy as SEBL

-- | MonadDormouseTestClient describes the capability to send and receive specifically ByteString typed HTTP Requests and Responses
class Monad m => MonadDormouseTestClient m where
  -- | Make the supplied HTTP request, expecting an HTTP response with a Lazy ByteString body to be delivered in some @MonadDormouseTest m@
  expectLbs :: IsUrl url => HttpRequest url method LB.ByteString contentTag acceptTag -> m (HttpResponse LB.ByteString)
  expectLbs req = do
    resp <- expectBs $ req {requestBody = LB.toStrict $ requestBody req}
    return $ resp {responseBody = LB.fromStrict $ responseBody resp}
  -- | Make the supplied HTTP request, expecting an HTTP response with a Strict ByteString body to be delivered in some @MonadDormouseTest m@
  expectBs :: IsUrl url => HttpRequest url method SB.ByteString contentTag acceptTag -> m (HttpResponse SB.ByteString)
  expectBs req = do
    resp <- expectLbs $ req {requestBody = LB.fromStrict $ requestBody req}
    return $ resp {responseBody = LB.toStrict $ responseBody resp}
  {-# MINIMAL expectLbs | expectBs #-}

instance (Monad m, MonadIO m, MonadDormouseTestClient m) => MonadDormouseClient m where
  send req deserialiseResp = do
    reqBody <- liftIO . S.fold SEB.write . extricateRequestStream . requestBody $ req
    let reqBs = req {requestBody = reqBody}
    respBs <- expectBs reqBs
    let respStream = S.unfold SEBL.read . LB.fromStrict $ responseBody respBs
    liftIO $ deserialiseResp $ respBs { responseBody = respStream }
    where 
      extricateRequestStream :: RawRequestPayload -> SerialT IO Word8
      extricateRequestStream (DefinedContentLength _ s) = s
      extricateRequestStream (ChunkedTransfer s) = s