{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE OverloadedStrings #-} import Data.Bytes (Bytes) import Data.Bytes.Chunks (Chunks (ChunksCons, ChunksNil)) import Http.Request (Request (Request), RequestLine (RequestLine)) import Http.Types (Bodied (Bodied), Header (Header), Response) import OkChannel (M (M)) import Test.Tasty (TestTree, defaultMain, testGroup) import Test.Tasty.HUnit (testCase, (@=?)) import Data.Bytes qualified as Bytes import Data.Bytes.Chunks qualified as Chunks import Data.Bytes.Text.Ascii qualified as Ascii import GHC.Exts qualified as Exts import Http.Bodied qualified import Http.Header qualified import Http.Headers qualified as Headers import Http.Request qualified as Request import OkExchange qualified as E main :: IO () main = defaultMain tests tests :: TestTree tests = testGroup "tests" [ testCase "get-a" $ do (input, output, Bodied {body}) <- evaluateM (E.exchange () getReqA) (Chunks.fromBytes getRespA) body @=? ChunksNil input @=? mempty output @=? Chunks.concat (Request.bodiedToChunks getReqA) , testCase "get-chunked-a" $ do (input, output, Bodied {body}) <- evaluateM (E.exchange () getReqA) (Chunks.fromBytes getRespChunkedA) body @=? ChunksNil input @=? mempty output @=? Chunks.concat (Request.bodiedToChunks getReqA) , testCase "get-chunked-byte-by-byte-a" $ do (input, output, Bodied {body}) <- evaluateM (E.exchange () getReqA) (bytesToSingleByteChunks getRespChunkedA) body @=? ChunksNil input @=? mempty output @=? Chunks.concat (Request.bodiedToChunks getReqA) , testCase "get-body-a" $ do (input, output, Bodied {body}) <- evaluateM (E.exchange () getReqA) (Chunks.fromBytes getRespBodyA) input @=? mempty body @=? ChunksCons (Ascii.fromString "helloworld") ChunksNil output @=? Chunks.concat (Request.bodiedToChunks getReqA) , testCase "get-chunked-b" $ do (input, output, Bodied {body}) <- evaluateM (E.exchange () getReqA) (Chunks.fromBytes getRespChunkedB) Ascii.fromString "hello to my friends." @=? Chunks.concat body mempty @=? input Chunks.concat (Request.bodiedToChunks getReqA) @=? output , testCase "get-chunked-byte-by-byte-b" $ do (input, output, Bodied {body}) <- evaluateM (E.exchange () getReqA) (bytesToSingleByteChunks getRespChunkedB) Ascii.fromString "hello to my friends." @=? Chunks.concat body mempty @=? input Chunks.concat (Request.bodiedToChunks getReqA) @=? output , testCase "get-chunked-two-by-two-b" $ do (input, output, Bodied {body}) <- evaluateM (E.exchange () getReqA) (bytesToDoubletonByteChunks getRespChunkedB) Ascii.fromString "hello to my friends." @=? Chunks.concat body mempty @=? input Chunks.concat (Request.bodiedToChunks getReqA) @=? output ] bytesToSingleByteChunks :: Bytes -> Chunks bytesToSingleByteChunks = Bytes.foldr' ( \w acc -> ChunksCons (Bytes.singleton w) acc ) ChunksNil bytesToDoubletonByteChunks :: Bytes -> Chunks bytesToDoubletonByteChunks b0 = go (Exts.toList b0) where go (x : y : zs) = ChunksCons (Exts.fromList [x, y]) (go zs) go [x] = ChunksCons (Bytes.singleton x) ChunksNil go [] = ChunksNil evaluateM :: M (Either E.Exception (Bodied Response)) -> Chunks -> -- prebuilt response IO (Chunks, Bytes, Bodied Response) evaluateM (M f) resp = case f resp mempty of (input, output, r) -> case r of Left e -> case e of E.Http err -> fail ("exchange http protocol failure: " ++ show err) E.Receive err -> fail ("exchange http transport receive failure: " ++ show err) Right b -> pure (input, output, b) getReqA :: Bodied Request getReqA = Bodied { metadata = Request { requestLine = RequestLine { method = "GET" , path = "/health" } , headers = Headers.fromArray $ Exts.fromList [ Header {name = "Host", value = "example.com"} ] } , body = mempty } getRespA :: Bytes getRespA = Ascii.fromString "HTTP/1.1 200 OK\r\n\ \Server: testsuite/1.2.3\r\n\ \Content-Type: text/plain\r\n\r\n" getRespBodyA :: Bytes getRespBodyA = Ascii.fromString "HTTP/1.1 200 OK\r\n\ \Server: testsuite/1.2.3\r\n\ \Content-Type: text/plain\r\n\ \Content-Length: 10\r\n\r\n\ \helloworld" getRespChunkedA :: Bytes getRespChunkedA = Ascii.fromString "HTTP/1.1 200 OK\r\n\ \Server: testsuite/1.2.3\r\n\ \Transfer-Encoding: chunked\r\n\ \Content-Type: text/plain\r\n\r\n\ \0\r\n\r\n" getRespChunkedB :: Bytes getRespChunkedB = Ascii.fromString "HTTP/1.1 200 OK\r\n\ \Server: testsuite/1.2.3\r\n\ \Transfer-Encoding: chunked\r\n\ \Content-Type: text/plain\r\n\r\n\ \5\r\nhello\r\n\ \f\r\n to my friends.\r\n\ \0\r\n\r\n"