packages feed

katip-wai-0.1.0.0: test/DebugApplication.hs

{-# LANGUAGE OverloadedStrings #-}

module DebugApplication (
  DebugApplication,
  sendNoContentRequest,
  sendLogRequest,
  sendNotFoundRequest,
  withDebugApplication,
) where

import Control.Exception (bracket)
import Control.Monad (join)
import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Lazy as LazyByteString
import Data.Coerce (coerce)
import qualified Data.Maybe as Maybe
import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8With, encodeUtf8)
import Data.Text.Encoding.Error (lenientDecode)
import DebugScribe (withDebugScribe)
import Katip (KatipContextT, runKatipContextT)
import qualified Katip
import Katip.Wai (ApplicationT, runApplication)
import qualified Katip.Wai
import qualified Network.HTTP.Client as Http
import Network.HTTP.Types.Method (Method)
import qualified Network.HTTP.Types.Status as Status
import qualified Network.Wai as Wai
import qualified Network.Wai.Handler.Warp as Warp
import Test.Hspec (shouldBe)

newtype DebugApplication = DebugApplication Warp.Port

toUrl :: DebugApplication -> String
toUrl (DebugApplication port) = "http://localhost:" <> show port

sendLogRequest :: Http.Manager -> DebugApplication -> Method -> Text -> IO ()
sendLogRequest manager app method message = do
  baseRequest <- Http.parseRequest $ toUrl app <> "/log"
  let request =
        Http.setQueryString [("message", Just $ encodeUtf8 message)] $
          baseRequest{Http.method = method}

  response <- Http.httpLbs request manager

  let status = Http.responseStatus response
   in status `shouldBe` Status.status202

  let headers = Http.responseHeaders response
      contentType = lookup "Content-Type" headers
   in contentType `shouldBe` Just "text/plain; charset=UTF-8"

  let bytes = LazyByteString.toStrict $ Http.responseBody response
      body = decodeUtf8With lenientDecode bytes
   in body `shouldBe` message

sendNoContentRequest :: Http.Manager -> DebugApplication -> Method -> IO ()
sendNoContentRequest manager app method = do
  baseRequest <- Http.parseRequest $ toUrl app
  let request = baseRequest{Http.method = method}

  response <- Http.httpLbs request manager

  let body = Http.responseBody response
  body `shouldBe` mempty

  let status = Http.responseStatus response
  status `shouldBe` Status.status204

sendNotFoundRequest :: Http.Manager -> DebugApplication -> Method -> IO ()
sendNotFoundRequest manager app method = do
  baseRequest <- Http.parseRequest $ toUrl app <> "/not-found"
  let request = baseRequest{Http.method = method}

  response <- Http.httpLbs request manager

  let body = Http.responseBody response
  body `shouldBe` mempty

  let status = Http.responseStatus response
  status `shouldBe` Status.status404

mkApplication :: Katip.Severity -> ApplicationT (KatipContextT IO)
mkApplication severity =
  let base request send =
        case Wai.pathInfo request of
          ["log"] ->
            let queryString = Wai.queryString request
                message = Maybe.fromMaybe "Example log message" $ do
                  bytes <- join $ lookup "message" queryString
                  pure $ decodeUtf8With lenientDecode bytes
             in do
                  Katip.logFM Katip.InfoS (Katip.ls message)
                  send $
                    Wai.responseLBS
                      (toEnum 202)
                      [("Content-Type", "text/plain; charset=UTF-8")]
                      (LazyByteString.fromStrict $ encodeUtf8 message)
          [] ->
            send $ Wai.responseLBS (toEnum 204) [] mempty
          _ ->
            send $ Wai.responseLBS (toEnum 404) [] mempty
   in Katip.Wai.middleware severity base

withLogEnv :: (Katip.LogEnv -> IO a) -> IO [Aeson.Value]
withLogEnv useLogEnv = do
  withDebugScribe (Katip.permitItem minBound) Katip.V3 $ \scribe ->
    let makeLogEnv =
          Katip.initLogEnv "debug-app" "local-test"
            >>= Katip.registerScribe "debug" scribe Katip.defaultScribeSettings
     in bracket makeLogEnv Katip.closeScribes useLogEnv

withDebugApplication :: Katip.Severity -> (DebugApplication -> IO a) -> IO [Aeson.Value]
withDebugApplication severity useApp =
  withLogEnv $ \logEnv ->
    let toIO = runKatipContextT logEnv () "spec"
        app = runApplication toIO (mkApplication severity)
     in Warp.testWithApplication (pure app) (useApp . coerce)