context-http-client-0.2.0.1: test-suite/Test/Network/HTTP/Client/ContextSpec.hs
{-# OPTIONS_GHC -fno-warn-incomplete-uni-patterns #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module Test.Network.HTTP.Client.ContextSpec
( spec
) where
import Network.HTTP.Client (BodyReader, Request, Response)
import Prelude
import Test.Hspec (Spec, describe, it, shouldBe)
import qualified Context
import qualified Control.Concurrent.Async as Async
import qualified Control.Concurrent.STM as STM
import qualified Control.Concurrent.STM.TQueue as TQueue
import qualified Data.ByteString.Char8 as ByteString.Char8
import qualified Data.CaseInsensitive as CI
import qualified Data.Foldable as Foldable
import qualified Data.List as List
import qualified Network.HTTP.Client as HTTP.Client
import qualified Network.HTTP.Client.Context as HTTP.Client.Context
import qualified Network.HTTP.Types as HTTP.Types
import qualified Network.Wai as Wai
import qualified Network.Wai.Handler.Warp as Warp
spec :: Spec
spec = do
describe "modifyRequestsWithContext" $ do
it "concurrent test" $ do
numberQueue <- TQueue.newTQueueIO @Int
let app = \request sendResponse -> do
let headers = Wai.requestHeaders request
let [number] =
fmap (read . ByteString.Char8.unpack . snd)
$ flip filter headers $ \(headerName, _) ->
"number" == CI.foldedCase headerName
STM.atomically $ TQueue.writeTQueue numberQueue number
sendResponse
$ Wai.responseLBS
HTTP.Types.status200
[("Content-Type", "text/plain")]
"Test.Network.HTTP.Client.ContextSpec"
-- This function modifies the request using a value of our context type -
-- 'Int' - by adding a "number" header to the request containing this
-- value.
let modifyRequest :: Maybe Int -> Request -> IO Request
modifyRequest mContext request = do
case mContext of
Nothing -> do
error "mContext was Nothing!"
Just context -> do
pure request
{ HTTP.Client.requestHeaders =
("number", ByteString.Char8.pack $ show context)
: flip filter (HTTP.Client.requestHeaders request)
(\(headerName, _) ->
"number" /= CI.foldedCase headerName)
}
Context.withEmptyStore $ \contextStore -> do
manager <-
HTTP.Client.newManager
$ HTTP.Client.Context.modifyRequestsWithContext
contextStore
modifyRequest
HTTP.Client.defaultManagerSettings
-- Spin up a test server for the 'app' defined above.
Warp.testWithApplication (pure app) $ \port -> do
request <-
HTTP.Client.parseRequest
$ "http://localhost:" <> show port <> "/abc/def"
-- Spin up 10 threads that each make 3 http requests into the test
-- server.
Async.forConcurrently_ [0 :: Int .. 9] $ \i -> do
Foldable.for_ [1..3] $ \j -> do
-- By using a context here, behind the scenes the manager will also
-- have access to this context as it will send on this same thread.
Context.use contextStore (3 * i + j) $ do
response <- HTTP.Client.httpLbs request manager
HTTP.Types.statusCode (HTTP.Client.responseStatus response)
`shouldBe` 200
HTTP.Client.responseBody response
`shouldBe` "Test.Network.HTTP.Client.ContextSpec"
numbers <- STM.atomically $ TQueue.flushTQueue numberQueue
List.sort numbers `shouldBe` [1..30]
describe "modifyResponsesWithContext" $ do
it "concurrent test" $ do
let app = \_request sendResponse -> do
sendResponse
$ Wai.responseLBS
HTTP.Types.status200
[("Content-Type", "text/plain")]
"Test.Network.HTTP.Client.ContextSpec"
-- This function modifies the request using a value of our context type -
-- 'Int' - by adding a "number" header to the request containing this
-- value.
let modifyResponse
:: Maybe Int
-> Response BodyReader
-> IO (Response BodyReader)
modifyResponse mContext response = do
case mContext of
Nothing -> do
error "mContext was Nothing!"
Just context -> do
pure response
{ HTTP.Client.responseHeaders =
("number", ByteString.Char8.pack $ show context)
: HTTP.Client.responseHeaders response
}
Context.withEmptyStore $ \contextStore -> do
manager <-
HTTP.Client.newManager
$ HTTP.Client.Context.modifyResponsesWithContext
contextStore
modifyResponse
HTTP.Client.defaultManagerSettings
-- Spin up a test server for the 'app' defined above.
Warp.testWithApplication (pure app) $ \port -> do
request <-
HTTP.Client.parseRequest
$ "http://localhost:" <> show port <> "/abc/def"
-- Spin up 10 threads that each make 3 http requests into the test
-- server.
Async.forConcurrently_ [0 :: Int .. 9] $ \i -> do
Foldable.for_ [1..3] $ \j -> do
Context.use contextStore (3 * i + j) $ do
response <- HTTP.Client.httpLbs request manager
HTTP.Types.statusCode (HTTP.Client.responseStatus response)
`shouldBe` 200
HTTP.Client.responseBody response
`shouldBe` "Test.Network.HTTP.Client.ContextSpec"
let headers = HTTP.Client.responseHeaders response
let [number] =
fmap (read . ByteString.Char8.unpack . snd)
$ flip filter headers $ \(headerName, _) ->
"number" == CI.foldedCase headerName
number `shouldBe` 3 * i + j