purescript-httpurple/test/HTTPure/SpecHelpers.purs

184 lines
6.2 KiB
Haskell
Raw Normal View History

2017-07-10 10:17:13 +00:00
module HTTPure.SpecHelpers where
import Prelude
2017-07-10 10:17:13 +00:00
import Control.Monad.Aff as Aff
import Control.Monad.Eff as Eff
2017-09-26 06:08:07 +00:00
import Control.Monad.Eff.Class as EffClass
2017-07-10 10:17:13 +00:00
import Control.Monad.Eff.Exception as Exception
import Control.Monad.ST as ST
import Data.Maybe as Maybe
2017-07-23 06:13:47 +00:00
import Data.Options ((:=))
import Data.String as StringUtil
import Data.StrMap as StrMap
2017-09-26 06:08:07 +00:00
import Data.Tuple as Tuple
2017-07-10 10:17:13 +00:00
import Node.Encoding as Encoding
2017-07-23 19:17:02 +00:00
import Node.FS as FS
2017-07-10 10:17:13 +00:00
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
2017-07-10 10:17:13 +00:00
import Unsafe.Coerce as Coerce
infix 1 Assertions.shouldEqual as ?=
2017-07-10 10:17:13 +00:00
-- | A type alias encapsulating all effect types used in making a mock request.
2017-07-18 05:25:14 +00:00
type HTTPRequestEffects e =
( st :: ST.ST String
2017-07-10 10:17:13 +00:00
, exception :: Exception.EXCEPTION
, http :: HTTP.HTTP
| e
)
2017-07-10 10:17:13 +00:00
-- | A type alias encapsulating all effect types used in tests.
2017-07-18 05:25:14 +00:00
type TestEffects =
Runner.RunnerEffects (
HTTPRequestEffects
( mockResponse :: MOCK_RESPONSE
, mockRequest :: MOCK_REQUEST
2017-07-23 19:17:02 +00:00
, fs :: FS.FS
2017-07-18 05:25:14 +00:00
)
)
2017-07-10 10:17:13 +00:00
-- | The type for integration tests.
2017-07-18 05:25:14 +00:00
type Test = Spec.Spec TestEffects Unit
2017-07-10 10:17:13 +00:00
-- | The type for the entire test suite.
2017-07-18 05:25:14 +00:00
type TestSuite = Eff.Eff TestEffects Unit
2017-07-10 10:17:13 +00:00
-- | Given a URL, a failure handler, and a success handler, create an HTTP
-- | client request.
2017-07-18 17:09:03 +00:00
request :: forall e.
2017-07-23 19:17:02 +00:00
Boolean ->
2017-07-18 17:09:03 +00:00
Int ->
String ->
StrMap.StrMap String ->
String ->
String ->
Aff.Aff (http :: HTTP.HTTP | e) HTTPClient.Response
2017-07-23 19:17:02 +00:00
request secure port method headers path body = Aff.makeAff \_ success -> void do
2017-07-18 17:09:03 +00:00
req <- HTTPClient.request options success
let stream = HTTPClient.requestAsStream req
2017-07-23 06:13:47 +00:00
_ <- Stream.writeString stream Encoding.UTF8 body $ pure unit
Stream.end stream $ pure unit
2017-07-18 17:09:03 +00:00
where
options =
2017-07-23 19:17:02 +00:00
HTTPClient.protocol := (if secure then "https:" else "http:") <>
2017-07-23 06:13:47 +00:00
HTTPClient.method := method <>
HTTPClient.hostname := "localhost" <>
HTTPClient.port := port <>
HTTPClient.path := path <>
2017-07-23 19:17:02 +00:00
HTTPClient.headers := HTTPClient.RequestHeaders headers <>
HTTPClient.rejectUnauthorized := false
2017-07-10 10:17:13 +00:00
-- | 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
2017-07-23 06:13:47 +00:00
concat buf new = void $ ST.modifySTRef buf ((<>) new)
2017-07-10 10:17:13 +00:00
-- | Convert a request to an Aff containing the string with the response body.
2017-07-18 05:25:14 +00:00
toString :: forall e.
HTTPClient.Response -> Aff.Aff (HTTPRequestEffects e) String
2017-07-10 10:17:13 +00:00
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.
2017-07-18 17:09:03 +00:00
get :: forall e.
Int ->
StrMap.StrMap String ->
String ->
Aff.Aff (HTTPRequestEffects e) String
2017-07-23 19:17:02 +00:00
get port headers path = request false port "GET" headers path "" >>= toString
-- | Run an HTTPS 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 true port "GET" headers path "" >>= toString
2017-07-18 17:09:03 +00:00
-- | 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
2017-07-23 19:17:02 +00:00
post port headers path = request false 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.
2017-07-18 05:25:14 +00:00
getHeader :: forall e.
2017-07-18 17:09:03 +00:00
Int ->
StrMap.StrMap String ->
String ->
String ->
2017-07-18 05:25:14 +00:00
Aff.Aff (HTTPRequestEffects e) String
2017-07-18 17:09:03 +00:00
getHeader port headers path header =
2017-07-23 19:17:02 +00:00
extractHeader header <$> request false port "GET" headers path ""
2017-07-10 10:17:13 +00:00
2017-07-18 05:25:14 +00:00
-- | An effect encapsulating creating a mock request object
foreign import data MOCK_REQUEST :: Eff.Effect
2017-07-10 10:17:13 +00:00
-- | Mock an HTTP Request object
2017-09-26 06:08:07 +00:00
foreign import mockRequestImpl ::
2017-07-18 05:25:14 +00:00
forall e.
String ->
String ->
String ->
StrMap.StrMap String ->
Eff.Eff (mockRequest :: MOCK_REQUEST | e) HTTP.Request
2017-07-10 10:17:13 +00:00
2017-09-26 06:08:07 +00:00
-- | Mock an HTTP Request object
mockRequest :: forall e.
String ->
String ->
String ->
Array (Tuple.Tuple String String) ->
Aff.Aff (mockRequest :: MOCK_REQUEST | e) HTTP.Request
mockRequest method url body =
EffClass.liftEff <<< mockRequestImpl method url body <<< StrMap.fromFoldable
-- | 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