katip-wai-0.1.0.0: test/Katip/WaiSpec.hs
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StrictData #-}
module Katip.WaiSpec (spec) where
import Data.Foldable (for_)
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 DebugApplication (withDebugApplication)
import qualified DebugApplication
import qualified Katip
import LogEntry (LogEntry (LogEntry))
import qualified 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)]
, bodyLength = "KnownLength 0"
, headers =
LogEntry.Headers
{ host = Just "localhost:1234"
, referer = Nothing
, userAgent = Nothing
, range = Nothing
}
}
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
{ elapsedTimeInNanoSeconds = 123
, status = 202
}
, LogEntry.request = Just expectedRequest
}
, logSeverity = severity
}
log1 `shouldSatisfy` LogEntry.isMostlySameAs expectedLog1
log2 `shouldSatisfy` LogEntry.isMostlySameAs expectedLog2
log3 `shouldSatisfy` LogEntry.isMostlySameAs 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.elapsedTimeInNanoSeconds response
length elapsedTimes `shouldBe` 15
elapsedTimes `shouldSatisfy` all (> 0)