{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DerivingStrategies #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module Main
( main
) where
import Control.Monad (when)
import Data.Aeson (FromJSON)
import GHC.Generics (Generic)
import Http.Headers (Headers)
import Http.Types (Header, Request)
import System.FilePath (replaceExtension, takeBaseName)
import Test.Tasty (TestTree, defaultMain, testGroup)
import Test.Tasty.Golden (findByExtension, goldenVsString)
import Text.Show.Pretty (ppShow)
import Data.Aeson qualified as Aeson
import Data.ByteString.Lazy.Char8 qualified as LBC8
import Data.Bytes qualified as Bytes
import Data.Bytes.Chunks qualified as Chunks
import Data.List qualified as List
import Data.Primitive qualified as PM
import GHC.Exts qualified as Exts
import Http.Header qualified
import Http.Headers qualified as Headers
import Http.Request qualified as Request
import Http.Response qualified as Response
main :: IO ()
main = defaultMain =<< goldenTests
deriving stock instance Generic Header
deriving stock instance Generic Request.RequestLine
deriving stock instance Generic Request.Request
deriving anyclass instance FromJSON Header
deriving anyclass instance FromJSON Request.RequestLine
deriving anyclass instance FromJSON Request.Request
instance FromJSON Headers where
parseJSON = fmap Headers.fromArray . Aeson.parseJSON
goldenTests :: IO TestTree
goldenTests = do
responseFiles <- List.sort <$> findByExtension [".input"] "golden/response"
requestFiles <- List.sort <$> findByExtension [".json"] "golden/request"
return $
testGroup
"http"
[ testGroup
"response"
[ goldenVsString
(takeBaseName file) -- test name
outFile -- golden file path
( do
contents <- Bytes.readFile file
when (PM.sizeofPrimArray (Bytes.findIndices (Exts.fromList [0x0d, 0x0a]) contents) < 2) $ do
fail "Expected at least 2 CRLF sequences. Possible fix: unix2dos."
case Response.decode 128 contents of
Nothing -> fail "Could not decode HTTP response prelude"
Just resp -> pure (LBC8.pack (addTrailingNewline (ppShow resp)))
)
| file <- responseFiles
, let outFile = replaceExtension file ".output"
]
, testGroup
"request"
[ goldenVsString
(takeBaseName file) -- test name
outFile -- golden file path
( do
req :: Request <- either fail pure =<< Aeson.eitherDecodeFileStrict' file
pure $ LBC8.fromStrict $ Bytes.toByteString $ Chunks.concat $ Request.toChunks req
)
| file <- requestFiles
, let outFile = replaceExtension file ".output"
]
]
-- If the trailing newline is missing, add it. The pretty-show library
-- has a defect of omitting it, but if prett-show ever changes, this
-- function will prevent that change from breaking all the tests here.
addTrailingNewline :: String -> String
addTrailingNewline [] = "\n"
addTrailingNewline [x] = case x of
'\n' -> [x]
_ -> [x, '\n']
addTrailingNewline (x : xs) = x : addTrailingNewline xs