packages feed

katip-wai-0.2.0.0: test/Katip/WaiSpec.hs

module Katip.WaiSpec (spec) where

import Data.Foldable (for_)
import qualified Data.Map.Strict as Map
import qualified Data.Maybe as Maybe
import qualified Data.Set as Set
import qualified Data.Text as Text
import qualified Data.UUID as Uuid
import qualified Katip
import Katip.Wai.DebugApplication (withDebugApplication)
import qualified Katip.Wai.DebugApplication as DebugApplication
import Katip.Wai.LogEntry (LogEntry (LogEntry))
import qualified Katip.Wai.LogEntry as LogEntry
import qualified Network.HTTP.Client as Http
import Test.Hspec (Spec, describe, it, runIO, shouldBe, shouldSatisfy)


allSeverities :: [Katip.Severity]
allSeverities = [minBound ..]


spec :: Spec
spec = describe "middleware" $ do
  manager <- runIO $ Http.newManager Http.defaultManagerSettings

  for_ allSeverities $ \severity -> do
    let prettySeverity = Text.unpack . Text.toLower $ Katip.renderSeverity severity

    it ("logs as " <> prettySeverity <> " and adds the request information to the context") $ do
      let message = "Context should be added when severity is " <> Text.pack prettySeverity

      logs <- withDebugApplication severity $ \debugApp ->
        DebugApplication.sendLogRequest manager debugApp "GET" message

      [log1, log2, log3] <- traverse LogEntry.toLogEntry logs

      let expectedRequest =
            LogEntry.Request
              { id = Uuid.nil
              , httpVersion = "HTTP/1.1"
              , remoteHost = "127.0.0.1:1234"
              , isSecure = False
              , method = "GET"
              , path = "/log"
              , queryString = [("message", Just message)]
              , headers = Map.empty
              , receivedAt = "dont care"
              }

      let expectedLog1 =
            LogEntry
              { logMessage = "Request received."
              , logData =
                  LogEntry.LogData
                    { response = Nothing
                    , request = Just expectedRequest
                    }
              , logSeverity = severity
              }
          expectedLog2 =
            LogEntry
              { logMessage = message
              , logData =
                  LogEntry.LogData
                    { response = Nothing
                    , request = Just expectedRequest
                    }
              , logSeverity = severity
              }
          expectedLog3 =
            LogEntry
              { logMessage = "Response sent."
              , logData =
                  LogEntry.LogData
                    { response =
                        Just
                          LogEntry.Response
                            { status = LogEntry.Status 202 "Accepted"
                            , headers = Map.empty
                            , respondedAt = "dont care"
                            , responseTime =
                                LogEntry.ResponseTime
                                  { unit = "dont care"
                                  , time = 0
                                  }
                            }
                    , LogEntry.request = Just expectedRequest
                    }
              , logSeverity = severity
              }

      log1 `LogEntry.shouldBe` expectedLog1
      log2 `LogEntry.shouldBe` expectedLog2
      log3 `LogEntry.shouldBe` expectedLog3

    it ("logs as " <> prettySeverity <> " and uses a different requestId for every request") $ do
      logs <- withDebugApplication severity $ \debugApp ->
        for_ ["GET", "POST", "PATCH", "PUT", "DELETE"] $ \method -> do
          DebugApplication.sendNoContentRequest manager debugApp method
          DebugApplication.sendNotFoundRequest manager debugApp method
          DebugApplication.sendLogRequest manager debugApp method "Some example log message"

      parsedLogs <- traverse LogEntry.toLogEntry logs

      let requestIds =
            flip Maybe.mapMaybe parsedLogs $ \entry -> do
              _response <- LogEntry.response $ LogEntry.logData entry
              request <- LogEntry.request $ LogEntry.logData entry
              pure $ LogEntry.id request

          originalLength = length requestIds

          uniqueLength = Set.size $ Set.fromList requestIds

      originalLength `shouldBe` 15

      uniqueLength `shouldBe` originalLength

    it ("logs as " <> prettySeverity <> " and has non zero elapsed time") $ do
      logs <- withDebugApplication severity $ \debugApp ->
        for_ ["GET", "POST", "PATCH", "PUT", "DELETE"] $ \method -> do
          DebugApplication.sendNoContentRequest manager debugApp method
          DebugApplication.sendNotFoundRequest manager debugApp method
          DebugApplication.sendLogRequest manager debugApp method "Some example log message"

      parsedLogs <- traverse LogEntry.toLogEntry logs

      let elapsedTimes =
            flip Maybe.mapMaybe parsedLogs $ \entry -> do
              response <- LogEntry.response $ LogEntry.logData entry
              pure . LogEntry.time $ LogEntry.responseTime response

      length elapsedTimes `shouldBe` 15

      elapsedTimes `shouldSatisfy` all (> 0)