158 lines
5.3 KiB
Haskell
158 lines
5.3 KiB
Haskell
module HTTPure.SpecHelpers where
|
|
|
|
import Prelude
|
|
|
|
import Control.Monad.Aff as Aff
|
|
import Control.Monad.Eff as Eff
|
|
import Control.Monad.Eff.Exception as Exception
|
|
import Control.Monad.ST as ST
|
|
import Data.Maybe as Maybe
|
|
import Data.Options ((:=))
|
|
import Data.String as StringUtil
|
|
import Data.StrMap as StrMap
|
|
import Node.Encoding as Encoding
|
|
import Node.HTTP as HTTP
|
|
import Node.HTTP.Client as HTTPClient
|
|
import Node.Stream as Stream
|
|
import Test.Spec as Spec
|
|
import Test.Spec.Runner as Runner
|
|
import Test.Spec.Assertions as Assertions
|
|
import Unsafe.Coerce as Coerce
|
|
|
|
infix 1 Assertions.shouldEqual as ?=
|
|
|
|
-- | A type alias encapsulating all effect types used in making a mock request.
|
|
type HTTPRequestEffects e =
|
|
( st :: ST.ST String
|
|
, exception :: Exception.EXCEPTION
|
|
, http :: HTTP.HTTP
|
|
| e
|
|
)
|
|
|
|
-- | A type alias encapsulating all effect types used in tests.
|
|
type TestEffects =
|
|
Runner.RunnerEffects (
|
|
HTTPRequestEffects
|
|
( mockResponse :: MOCK_RESPONSE
|
|
, mockRequest :: MOCK_REQUEST
|
|
)
|
|
)
|
|
|
|
-- | The type for integration tests.
|
|
type Test = Spec.Spec TestEffects Unit
|
|
|
|
-- | The type for the entire test suite.
|
|
type TestSuite = Eff.Eff TestEffects Unit
|
|
|
|
-- | Given a URL, a failure handler, and a success handler, create an HTTP
|
|
-- | client request.
|
|
request :: forall e.
|
|
Int ->
|
|
String ->
|
|
StrMap.StrMap String ->
|
|
String ->
|
|
String ->
|
|
Aff.Aff (http :: HTTP.HTTP | e) HTTPClient.Response
|
|
request port method headers path body = Aff.makeAff \_ success -> void do
|
|
req <- HTTPClient.request options success
|
|
let stream = HTTPClient.requestAsStream req
|
|
_ <- Stream.writeString stream Encoding.UTF8 body $ pure unit
|
|
Stream.end stream $ pure unit
|
|
where
|
|
options =
|
|
HTTPClient.method := method <>
|
|
HTTPClient.hostname := "localhost" <>
|
|
HTTPClient.port := port <>
|
|
HTTPClient.path := path <>
|
|
HTTPClient.headers := HTTPClient.RequestHeaders headers
|
|
|
|
-- | Given an ST String buffer and a new string, concatenate that new string
|
|
-- | onto the ST buffer.
|
|
concat :: forall e s.
|
|
ST.STRef s String -> String -> Eff.Eff (st :: ST.ST s | e) Unit
|
|
concat buf new = void $ ST.modifySTRef buf ((<>) new)
|
|
|
|
-- | Convert a request to an Aff containing the string with the response body.
|
|
toString :: forall e.
|
|
HTTPClient.Response -> Aff.Aff (HTTPRequestEffects e) String
|
|
toString response = Aff.makeAff \_ success -> do
|
|
let stream = HTTPClient.responseAsStream response
|
|
buf <- ST.newSTRef ""
|
|
Stream.onDataString stream Encoding.UTF8 $ concat buf
|
|
Stream.onEnd stream $ ST.readSTRef buf >>= success
|
|
|
|
-- | Run an HTTP GET with the given url and return an Aff that contains the
|
|
-- | string with the response body.
|
|
get :: forall e.
|
|
Int ->
|
|
StrMap.StrMap String ->
|
|
String ->
|
|
Aff.Aff (HTTPRequestEffects e) String
|
|
get port headers path = request port "GET" headers path "" >>= toString
|
|
|
|
-- | Run an HTTP POST with the given url and body and return an Aff that
|
|
-- | contains the string with the response body.
|
|
post :: forall e.
|
|
Int ->
|
|
StrMap.StrMap String ->
|
|
String ->
|
|
String ->
|
|
Aff.Aff (HTTPRequestEffects e) String
|
|
post port headers path = request port "POST" headers path >=> toString
|
|
|
|
-- | Convert a request to an Aff containing the string with the given header
|
|
-- | value.
|
|
extractHeader :: String -> HTTPClient.Response -> String
|
|
extractHeader header = unmaybe <<< lookup <<< HTTPClient.responseHeaders
|
|
where
|
|
unmaybe = Maybe.fromMaybe ""
|
|
lookup = StrMap.lookup $ StringUtil.toLower header
|
|
|
|
-- | Run an HTTP GET with the given url and return an Aff that contains the
|
|
-- | string with the header value for the given header.
|
|
getHeader :: forall e.
|
|
Int ->
|
|
StrMap.StrMap String ->
|
|
String ->
|
|
String ->
|
|
Aff.Aff (HTTPRequestEffects e) String
|
|
getHeader port headers path header =
|
|
extractHeader header <$> request port "GET" headers path ""
|
|
|
|
-- | An effect encapsulating creating a mock request object
|
|
foreign import data MOCK_REQUEST :: Eff.Effect
|
|
|
|
-- | Mock an HTTP Request object
|
|
foreign import mockRequest ::
|
|
forall e.
|
|
String ->
|
|
String ->
|
|
String ->
|
|
StrMap.StrMap String ->
|
|
Eff.Eff (mockRequest :: MOCK_REQUEST | e) HTTP.Request
|
|
|
|
-- | An effect encapsulating creating a mock response object
|
|
foreign import data MOCK_RESPONSE :: Eff.Effect
|
|
|
|
-- | Mock an HTTP Response object
|
|
foreign import mockResponse ::
|
|
forall e. Eff.Eff (mockResponse :: MOCK_RESPONSE | e) HTTP.Response
|
|
|
|
-- | Get the current body from an HTTP Response object (note this will only work
|
|
-- | with an object returned from mockResponse).
|
|
getResponseBody :: HTTP.Response -> String
|
|
getResponseBody = _.body <<< Coerce.unsafeCoerce
|
|
|
|
-- | Get the currently set status from an HTTP Response object.
|
|
getResponseStatus :: HTTP.Response -> Int
|
|
getResponseStatus = _.statusCode <<< Coerce.unsafeCoerce
|
|
|
|
-- | Get all current headers on the HTTP Response object.
|
|
getResponseHeaders :: HTTP.Response -> StrMap.StrMap String
|
|
getResponseHeaders = Coerce.unsafeCoerce <<< _.headers <<< Coerce.unsafeCoerce
|
|
|
|
-- | Get the current value for the header on the HTTP Response object.
|
|
getResponseHeader :: String -> HTTP.Response -> String
|
|
getResponseHeader header =
|
|
Maybe.fromMaybe "" <<< StrMap.lookup header <<< getResponseHeaders
|