2022-05-22 11:47:58 +00:00
|
|
|
module Test.HTTPurple.TestHelpers where
|
2017-07-10 10:17:13 +00:00
|
|
|
|
2017-07-18 05:31:46 +00:00
|
|
|
import Prelude
|
2022-05-04 21:02:29 +00:00
|
|
|
|
2021-11-19 06:16:35 +00:00
|
|
|
import Data.Either (Either(Right))
|
|
|
|
import Data.Maybe (fromMaybe)
|
|
|
|
import Data.String (toLower)
|
|
|
|
import Data.Tuple (Tuple)
|
2022-05-04 21:02:29 +00:00
|
|
|
import Effect (Effect)
|
|
|
|
import Effect.Aff (Aff, makeAff, nonCanceler)
|
|
|
|
import Effect.Class (liftEffect)
|
2023-08-07 14:37:29 +00:00
|
|
|
import Foreign (Foreign)
|
2021-11-19 06:16:35 +00:00
|
|
|
import Foreign.Object (Object, lookup)
|
2022-05-04 21:02:29 +00:00
|
|
|
import Foreign.Object (fromFoldable) as Object
|
2023-08-07 14:37:29 +00:00
|
|
|
import Node.Buffer (Buffer, create, fromString)
|
|
|
|
import Node.Buffer (concat, toString) as Buffer
|
2021-11-19 06:16:35 +00:00
|
|
|
import Node.Encoding (Encoding(UTF8))
|
2023-08-07 14:37:29 +00:00
|
|
|
import Node.EventEmitter (once_)
|
|
|
|
import Node.HTTP as HTTP
|
|
|
|
import Node.HTTP.ClientRequest as HTTPClient
|
|
|
|
import Node.HTTP.IncomingMessage as IM
|
|
|
|
import Node.HTTP.OutgoingMessage as OM
|
|
|
|
import Node.HTTP.Types (IMClientRequest, IncomingMessage, ServerResponse)
|
|
|
|
import Node.HTTPS as HTTPS
|
|
|
|
import Node.Stream (Readable)
|
|
|
|
import Node.Stream as Stream
|
|
|
|
import Node.Stream.Aff (readableToBuffers)
|
2021-11-19 06:16:35 +00:00
|
|
|
import Test.Spec (Spec)
|
|
|
|
import Test.Spec.Assertions (shouldEqual)
|
|
|
|
import Unsafe.Coerce (unsafeCoerce)
|
|
|
|
|
|
|
|
infix 1 shouldEqual as ?=
|
2017-07-19 05:21:36 +00:00
|
|
|
|
2017-07-10 10:17:13 +00:00
|
|
|
-- | The type for integration tests.
|
2021-11-19 06:16:35 +00:00
|
|
|
type Test = Spec Unit
|
2017-07-10 10:17:13 +00:00
|
|
|
|
|
|
|
-- | The type for the entire test suite.
|
2021-11-19 06:16:35 +00:00
|
|
|
type TestSuite = Effect Unit
|
2017-07-10 10:17:13 +00:00
|
|
|
|
|
|
|
-- | Given a URL, a failure handler, and a success handler, create an HTTP
|
|
|
|
-- | client request.
|
2021-03-22 19:02:36 +00:00
|
|
|
request ::
|
|
|
|
Boolean ->
|
|
|
|
Int ->
|
|
|
|
String ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Object String ->
|
2021-03-22 19:02:36 +00:00
|
|
|
String ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Buffer ->
|
2023-08-07 14:37:29 +00:00
|
|
|
Aff (IncomingMessage IMClientRequest)
|
2021-11-19 06:16:35 +00:00
|
|
|
request secure port' method' headers' path' body =
|
|
|
|
makeAff \done -> do
|
2023-08-07 14:37:29 +00:00
|
|
|
req <- case secure of
|
|
|
|
true -> HTTPS.requestOpts
|
|
|
|
{ method: method'
|
|
|
|
, host: "localhost"
|
|
|
|
, port: port'
|
|
|
|
, path: path'
|
|
|
|
, headers: unsafeCoerce headers' :: Object Foreign
|
|
|
|
, rejectUnauthorized: false
|
|
|
|
}
|
|
|
|
false -> HTTP.requestOpts
|
|
|
|
{ method: method'
|
|
|
|
, host: "localhost"
|
|
|
|
, port: port'
|
|
|
|
, path: path'
|
|
|
|
, headers: unsafeCoerce headers' :: Object Foreign
|
|
|
|
}
|
|
|
|
req # once_ HTTPClient.responseH (Right >>> done)
|
|
|
|
let stream = OM.toWriteable $ HTTPClient.toOutgoingMessage req
|
2021-06-25 17:58:16 +00:00
|
|
|
void
|
2023-08-07 14:37:29 +00:00
|
|
|
$ Stream.write' stream body
|
2022-05-04 21:02:29 +00:00
|
|
|
$ const
|
2023-08-07 14:37:29 +00:00
|
|
|
$ Stream.end stream
|
2021-11-19 06:16:35 +00:00
|
|
|
pure nonCanceler
|
2017-07-10 10:17:13 +00:00
|
|
|
|
2021-11-16 04:02:36 +00:00
|
|
|
-- | Same as `request` but without.
|
|
|
|
request' ::
|
|
|
|
Boolean ->
|
|
|
|
Int ->
|
|
|
|
String ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Object String ->
|
2021-11-16 04:02:36 +00:00
|
|
|
String ->
|
2023-08-07 14:37:29 +00:00
|
|
|
Aff (IncomingMessage IMClientRequest)
|
2021-11-16 04:02:36 +00:00
|
|
|
request' secure port method headers path =
|
2021-11-19 06:16:35 +00:00
|
|
|
liftEffect (create 0)
|
2021-11-16 04:02:36 +00:00
|
|
|
>>= request secure port method headers path
|
|
|
|
|
|
|
|
-- | Same as `request` but with a `String` body.
|
|
|
|
requestString ::
|
|
|
|
Boolean ->
|
|
|
|
Int ->
|
|
|
|
String ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Object String ->
|
2021-11-16 04:02:36 +00:00
|
|
|
String ->
|
|
|
|
String ->
|
2023-08-07 14:37:29 +00:00
|
|
|
Aff (IncomingMessage IMClientRequest)
|
2021-11-16 04:02:36 +00:00
|
|
|
requestString secure port method headers path body = do
|
2021-11-19 06:16:35 +00:00
|
|
|
liftEffect (fromString body UTF8)
|
2021-11-16 04:02:36 +00:00
|
|
|
>>= request secure port method headers path
|
|
|
|
|
2018-08-20 02:50:07 +00:00
|
|
|
-- | Convert a request to an Aff containing the `Buffer with the response body.
|
2023-08-07 14:37:29 +00:00
|
|
|
toBuffer :: IncomingMessage IMClientRequest -> Aff Buffer
|
|
|
|
toBuffer response = do
|
|
|
|
bufs <- readableToBuffers $ IM.toReadable response
|
|
|
|
liftEffect $ Buffer.concat bufs
|
2017-07-10 10:17:13 +00:00
|
|
|
|
|
|
|
-- | Convert a request to an Aff containing the string with the response body.
|
2023-08-07 14:37:29 +00:00
|
|
|
toString :: IncomingMessage IMClientRequest -> Aff String
|
2018-08-20 02:50:07 +00:00
|
|
|
toString resp = do
|
|
|
|
buf <- toBuffer resp
|
2021-11-19 06:16:35 +00:00
|
|
|
liftEffect $ Buffer.toString UTF8 buf
|
2017-07-10 10:17:13 +00:00
|
|
|
|
|
|
|
-- | Run an HTTP GET with the given url and return an Aff that contains the
|
|
|
|
-- | string with the response body.
|
2021-03-22 19:02:36 +00:00
|
|
|
get ::
|
|
|
|
Int ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Object String ->
|
2021-03-22 19:02:36 +00:00
|
|
|
String ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Aff String
|
2021-11-16 04:02:36 +00:00
|
|
|
get port headers path = request' false port "GET" headers path >>= toString
|
2017-07-23 19:17:02 +00:00
|
|
|
|
2018-08-20 02:50:07 +00:00
|
|
|
-- | Like `get` but return a response body in a `Buffer`
|
2021-03-22 19:02:36 +00:00
|
|
|
getBinary ::
|
|
|
|
Int ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Object String ->
|
2021-03-22 19:02:36 +00:00
|
|
|
String ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Aff Buffer
|
2021-11-16 04:02:36 +00:00
|
|
|
getBinary port headers path = request' false port "GET" headers path >>= toBuffer
|
2018-08-20 02:50:07 +00:00
|
|
|
|
2017-07-23 19:17:02 +00:00
|
|
|
-- | Run an HTTPS GET with the given url and return an Aff that contains the
|
|
|
|
-- | string with the response body.
|
2021-03-22 19:02:36 +00:00
|
|
|
get' ::
|
|
|
|
Int ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Object String ->
|
2021-03-22 19:02:36 +00:00
|
|
|
String ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Aff String
|
2021-11-16 04:02:36 +00:00
|
|
|
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.
|
2021-03-22 19:02:36 +00:00
|
|
|
post ::
|
|
|
|
Int ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Object String ->
|
2021-03-22 19:02:36 +00:00
|
|
|
String ->
|
|
|
|
String ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Aff String
|
2021-11-16 04:02:36 +00:00
|
|
|
post port headers path = requestString false port "POST" headers path >=> toString
|
|
|
|
|
|
|
|
-- | Run an HTTP POST with the given url and binary buffer body and return an
|
|
|
|
-- | Aff that contains the string with the response body.
|
|
|
|
postBinary ::
|
|
|
|
Int ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Object String ->
|
2021-11-16 04:02:36 +00:00
|
|
|
String ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Buffer ->
|
|
|
|
Aff String
|
2021-11-16 04:02:36 +00:00
|
|
|
postBinary port headers path = request false port "POST" headers path >=> toString
|
2017-07-17 23:42:13 +00:00
|
|
|
|
|
|
|
-- | Convert a request to an Aff containing the string with the given header
|
|
|
|
-- | value.
|
2023-08-07 14:37:29 +00:00
|
|
|
extractHeader :: String -> IncomingMessage IMClientRequest -> String
|
|
|
|
extractHeader header = unmaybe <<< lookup' <<< IM.headers
|
2017-07-17 23:42:13 +00:00
|
|
|
where
|
2021-11-19 06:16:35 +00:00
|
|
|
unmaybe = fromMaybe ""
|
2021-03-22 19:02:36 +00:00
|
|
|
|
2021-11-19 06:16:35 +00:00
|
|
|
lookup' = lookup $ toLower header
|
2017-07-17 23:42:13 +00:00
|
|
|
|
|
|
|
-- | Run an HTTP GET with the given url and return an Aff that contains the
|
|
|
|
-- | string with the header value for the given header.
|
2021-03-22 19:02:36 +00:00
|
|
|
getHeader ::
|
|
|
|
Int ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Object String ->
|
2021-03-22 19:02:36 +00:00
|
|
|
String ->
|
|
|
|
String ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Aff String
|
2021-11-16 04:02:36 +00:00
|
|
|
getHeader port headers path header = extractHeader header <$> request' false port "GET" headers path
|
2021-03-22 19:02:36 +00:00
|
|
|
|
|
|
|
getStatus ::
|
|
|
|
Int ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Object String ->
|
2021-03-22 19:02:36 +00:00
|
|
|
String ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Aff Int
|
2023-08-07 14:37:29 +00:00
|
|
|
getStatus port headers path = IM.statusCode <$> request' false port "GET" headers path
|
2018-10-09 17:37:23 +00:00
|
|
|
|
2017-07-10 10:17:13 +00:00
|
|
|
-- | Mock an HTTP Request object
|
2017-09-26 06:08:07 +00:00
|
|
|
foreign import mockRequestImpl ::
|
2023-08-07 14:37:29 +00:00
|
|
|
forall a.
|
2017-07-18 05:25:14 +00:00
|
|
|
String ->
|
|
|
|
String ->
|
|
|
|
String ->
|
2019-04-25 17:13:04 +00:00
|
|
|
String ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Object String ->
|
2023-08-07 14:37:29 +00:00
|
|
|
Effect (IncomingMessage a)
|
2017-07-10 10:17:13 +00:00
|
|
|
|
2017-09-26 06:08:07 +00:00
|
|
|
-- | Mock an HTTP Request object
|
2021-03-22 19:02:36 +00:00
|
|
|
mockRequest ::
|
2023-08-07 14:37:29 +00:00
|
|
|
forall a.
|
2021-03-22 19:02:36 +00:00
|
|
|
String ->
|
|
|
|
String ->
|
|
|
|
String ->
|
|
|
|
String ->
|
2021-11-19 06:16:35 +00:00
|
|
|
Array (Tuple String String) ->
|
2023-08-07 14:37:29 +00:00
|
|
|
Aff (IncomingMessage a)
|
2021-11-19 06:16:35 +00:00
|
|
|
mockRequest httpVersion method url body = liftEffect <<< mockRequestImpl httpVersion method url body <<< Object.fromFoldable
|
2017-07-17 23:42:13 +00:00
|
|
|
|
|
|
|
-- | Mock an HTTP Response object
|
2023-08-07 14:37:29 +00:00
|
|
|
foreign import mockResponse :: Effect ServerResponse
|
2017-07-17 23:42:13 +00:00
|
|
|
|
|
|
|
-- | Get the current body from an HTTP Response object (note this will only work
|
|
|
|
-- | with an object returned from mockResponse).
|
2023-08-07 14:37:29 +00:00
|
|
|
getResponseBody :: ServerResponse -> String
|
2021-11-19 06:16:35 +00:00
|
|
|
getResponseBody = _.body <<< unsafeCoerce
|
2017-07-17 23:42:13 +00:00
|
|
|
|
|
|
|
-- | Get the currently set status from an HTTP Response object.
|
2023-08-07 14:37:29 +00:00
|
|
|
getResponseStatus :: ServerResponse -> Int
|
2021-11-19 06:16:35 +00:00
|
|
|
getResponseStatus = _.statusCode <<< unsafeCoerce
|
2017-07-17 23:42:13 +00:00
|
|
|
|
|
|
|
-- | Get all current headers on the HTTP Response object.
|
2023-08-07 14:37:29 +00:00
|
|
|
getResponseHeaders :: ServerResponse -> Object (Array String)
|
2021-11-19 06:16:35 +00:00
|
|
|
getResponseHeaders = unsafeCoerce <<< _.headers <<< unsafeCoerce
|
2017-07-17 23:42:13 +00:00
|
|
|
|
|
|
|
-- | Get the current value for the header on the HTTP Response object.
|
2023-08-07 14:37:29 +00:00
|
|
|
getResponseHeader :: String -> ServerResponse -> Array String
|
2022-06-16 18:58:33 +00:00
|
|
|
getResponseHeader header = fromMaybe [ "" ] <<< lookup header <<< getResponseHeaders
|
2018-08-30 22:01:49 +00:00
|
|
|
|
|
|
|
-- | Create a stream out of a string.
|
2021-11-19 06:16:35 +00:00
|
|
|
foreign import stringToStream :: String -> Readable ()
|