packages feed

wai-middleware-brotli-0.1.0.0: test/Spec.hs

{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Types
import Codec.Compression.Brotli (decompress, compress)
import Control.Monad.Trans
import Control.Concurrent.MVar
import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Builder as Builder
import Data.IORef
import Data.Monoid
import Network.Wai
import Network.Wai.Middleware.Brotli
import Network.Wai.Internal
import Network.Wai.Test
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.Hspec
import System.IO

main :: IO ()
main = do
  putStrLn ""

  bod <- L.readFile "/usr/share/dict/words" -- "Hello World 000000000000000000000000000000"
  let app req respond = respond $ responseLBS status200 [("Content-Type", "text/whatever")] bod

  specs <- testSpecs $ parallel $ do

    it "Encodes text" $ do
      -- app :: Application
      putStr "Encoding text: "
      var <- newEmptyMVar
      (brotli defaultSettings app)
        (defaultRequest {requestHeaders = [("Accept-Encoding", "br")]})
        (\resp -> do
          putMVar var resp
          return ResponseReceived)
      resp <- takeMVar var
      case resp of
        ResponseBuilder stats hs b -> do
          assert (decompress (Builder.toLazyByteString b) == bod)
        _ -> error "Not what we expected"

    it "Does not encode if brotli not in Accept-Encoding header" $ do
      var <- newEmptyMVar
      (brotli defaultSettings app)
        (defaultRequest {requestHeaders = [("Accept-Encoding", "gzip"), ("Content-Type", "text/whatever")]})
        (\resp -> do
          putMVar var resp
          return ResponseReceived)
      resp <- takeMVar var
      case resp of
        ResponseBuilder stats hs b -> do
          assert (Builder.toLazyByteString b == bod)
        _ -> error "Not what we expected"

    it "Decode request body if it's encoded" $ do
      var <- newEmptyMVar
      let reqWithBody =
            defaultRequest
            { requestMethod = "POST"
            , requestHeaders = [("Content-Encoding", "br")]
            , requestBody =
                return $ L.toStrict $ compress ("Hello World" :: L.ByteString)
            }
      (brotli defaultSettings $ \req resp -> do
        reqBod <- requestBody req
        resp $ responseLBS status200 [] (L.fromStrict reqBod))
        reqWithBody
        (\resp -> do
          putMVar var resp
          return ResponseReceived)
      resp <- takeMVar var
      case resp of
        ResponseBuilder stats hs b ->
          assert (Builder.toLazyByteString b == "Hello World")
        _ -> error "Not what we expected"

    it "Streaming response body encoding" $ do
      var <- newEmptyMVar
      (brotli defaultSettings $ \req respond -> do
          respond $ responseStream status200 [("Content-Type", "text/whatever")] $ \send flush -> do
            send "Hello world\n"
            flush
            send "Goodbye world"
        )
        (defaultRequest {requestHeaders = [("Accept-Encoding", "br")]})
        (\resp -> do
          putMVar var resp
          return ResponseReceived)
      resp <- takeMVar var
      r <- newIORef mempty
      case resp of
        ResponseStream stats hs f -> do
          f (\b -> modifyIORef r (<> b)) (return ())
          b <- readIORef r
          assert (decompress (Builder.toLazyByteString b) == "Hello world\nGoodbye world")
        _ -> error "Not what we expected"

    it "Don't encode non-textual content" $ do
      let app req respond = respond $ responseLBS status200 [("Content-Type", "application/binary")] bod
      var <- newEmptyMVar
      (brotli defaultSettings app)
        (defaultRequest {requestHeaders = [("Accept-Encoding", "br")]})
        (\resp -> do
          putMVar var resp
          return ResponseReceived)
      resp <- takeMVar var
      case resp of
        ResponseBuilder stats hs b -> do
          assert (Builder.toLazyByteString b == bod)
        _ -> error "Not what we expected"

    it "Don't encode if content length is too low" $ do
      let app req respond = respond $ responseLBS status200 [("Content-Type", "application/json"), ("Content-Length", "2")] "{}"
      var <- newEmptyMVar
      (brotli defaultSettings app)
        (defaultRequest {requestHeaders = [("Accept-Encoding", "br")]})
        (\resp -> do
          putMVar var resp
          return ResponseReceived)
      resp <- takeMVar var
      case resp of
        ResponseBuilder stats hs b -> do
          assert (Builder.toLazyByteString b == "{}")
        _ -> error "Not what we expected"

    it "Do encode if content length is long enough" $ do
      let app req respond = respond $ responseLBS status200 [("Content-Type", "text/whatever"), ("Content-Length", B.pack $ show $ L.length bod)] bod
      var <- newEmptyMVar
      (brotli defaultSettings app)
        (defaultRequest {requestHeaders = [("Accept-Encoding", "br")]})
        (\resp -> do
          putMVar var resp
          return ResponseReceived)
      resp <- takeMVar var
      case resp of
        ResponseBuilder stats hs b -> do
          assert (decompress (Builder.toLazyByteString b) == bod)
        _ -> error "Not what we expected"

    it "Performs on-the-fly compression of files if set to compress or requesting appropriate content range size" onTheFlyFileCompressTest
    it "Serves precomputed compressed files if appropriate & they exist" precompressedTest

  defaultMain $ testGroup "Tests" specs


  {-

  putStrLn ""

  putStrLn ""

  putStr ": "
  putStrLn ""

  putStrLn ""

  putStrLn "Compress fallback response for responseRaw if appropriate: Pending"
  putStrLn "Don't compress files if set to ignore: Pending"
  onTheFlyFileCompressTest
  precompressedTest
  putStrLn "Create cached file versions in given folder if appropriate: Pending"

  putStrLn "Support flushing compressed data: Pending"
  putStrLn "Return appropriate error statuses for malformed compressed input: Pending"
-}

onTheFlyFileCompressTest :: IO ()
onTheFlyFileCompressTest = do
  let settings = defaultSettings { brotliFilesBehavior = BrotliCompress, brotliMinimumSize = 5 }
  let app req respond =
        respond $
        responseFile
          status200
          [ ("Content-Type", "text/whatever")
          ]
          "test/sample.txt"
          Nothing
  runSession session $ brotli settings app
  where
    session = do
     resp <- request (defaultRequest {requestHeaders = [("Accept-Encoding", "br")]})
     liftIO $ assert (decompress (simpleBody resp) @=? "Hello world\n")

precompressedTest :: IO ()
precompressedTest = do
  let settings = defaultSettings { brotliFilesBehavior = BrotliPreCompressed BrotliIgnore }
  let app req respond =
        respond $
        responseFile
          status200
          [ ("Content-Type", "text/whatever")
          ]
          "test/words"
          Nothing
  var <- newEmptyMVar
  (brotli settings app)
    (defaultRequest {requestHeaders = [("Accept-Encoding", "br")]})
    (\resp -> do
       putMVar var resp
       return ResponseReceived)
  resp <- takeMVar var
  case resp of
    ResponseFile stats hs fp Nothing -> do
      assert (fp == "test/words.br")
    _ -> error "Not what we expected"