packages feed

katip-datadog-0.1.0.0: test/Katip/Tests/Scribes/Datadog/TCP.hs

{-# LANGUAGE OverloadedStrings #-}
module Katip.Tests.Scribes.Datadog.TCP
    ( tests
    ) where


-------------------------------------------------------------------------------
import           Control.Concurrent.Async
import qualified Control.Exception.Safe    as EX
import qualified Data.Aeson                as A
import qualified Data.Char                 as Char
import qualified Data.Conduit              as C
import qualified Data.Conduit.Binary       as CB
import qualified Data.Conduit.List         as CL
import qualified Data.Conduit.Network      as CN
import           Data.Foldable             (toList)
import qualified Data.HashMap.Strict       as HM
import           Data.IORef
import           Data.Sequence             ((|>))
import           Data.Text                 (Text)
import qualified Data.Text                 as T
import           Test.Tasty
import           Test.Tasty.HUnit
-------------------------------------------------------------------------------
import           Katip
import           Katip.Scribes.Datadog.TCP
-------------------------------------------------------------------------------



tests :: TestTree
tests = testGroup "Katip.Scribes.Datadog.TCP"
  [ testCase "logs well-formed messages in order" $ do
       logs <- collectLogs $ do
         logItem (sl "foo" ("bar" :: Text)) "mynamespace" noLoc InfoS "a message"
       length logs @?= 1
       let (CapturedLog ns dta sev msg thread) = head logs
       ns @?= Namespace ["katip-datadog-tests", "mynamespace"]
       dta @?= A.Object (HM.singleton "foo" (A.String "bar"))
       sev @?= InfoS
       msg @?= "a message"
       if T.all Char.isNumber thread && T.length thread > 0
         then pure ()
         else assertFailure ("Expected thread id to just be a number but it was " <> T.unpack thread)
  ]
  where
    noLoc = Nothing


-------------------------------------------------------------------------------
-- | A log type parsed out of the fake datadog agent's buffer
data CapturedLog = CapturedLog
  { capturedLog_namespace :: Namespace
  , capturedLog_data      :: A.Value
  , capturedLog_sev       :: Severity
  , capturedLog_message   :: Text
  , capturedLog_thread    :: Text
  } deriving (Show, Eq)


instance A.FromJSON CapturedLog where
  parseJSON = A.withObject "CapturedLog" $ \o -> do
    message <- o A..: "message"
    sev <- o A..: "severity"
    dta <- o A..: "data"
    namespace <- o A..: "ns"
    thread <- o A..: "thread"
    pure $ CapturedLog
      { capturedLog_namespace = namespace
      , capturedLog_data  = dta
      , capturedLog_sev = sev
      , capturedLog_message = message
      , capturedLog_thread = thread
      }


-------------------------------------------------------------------------------
collectLogs :: KatipT IO () -> IO [CapturedLog]
collectLogs f = do
  capturedRef <- newIORef mempty
  withAsync (startServer capturedRef) $ \_worker -> do
    EX.bracket mkLE closeLE $ \le -> do
      runKatipT le f
  toList <$> readIORef capturedRef
  where
    closeLE = closeScribes
    port = 1337
    mkLE = do
      datadogScribeSettings <- mkDatadogScribeSettings (localAgentConnectionParams (fromInteger port)) NoAuthLocal
      scribe <- mkDatadogScribe datadogScribeSettings (permitItem DebugS) V3
      registerScribe "datadog" scribe defaultScribeSettings =<< initLogEnv "katip-datadog-tests" "test"
    startServer capturedRef = CN.runTCPServer (CN.serverSettings (fromInteger port) "*") $ \appData -> C.runConduit $
       CN.appSource appData C..|
         CB.lines C..|
         CL.map A.eitherDecodeStrict C..|
         CL.mapM_ (\parseResult -> case parseResult of
           Left e -> EX.throwM (ParseError e)
           (Right capturedLog) -> modifyIORef' capturedRef (\captured -> captured |> capturedLog))


-------------------------------------------------------------------------------
newtype ParseError = ParseError String
  deriving (Show)

instance EX.Exception ParseError