packages feed

glue-ekg-0.7.0: test/Glue/EkgSpec.hs

{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Glue.EkgSpec (spec) where

import qualified Data.HashMap.Strict         as M
import           Data.Int                    (Int64)
import           Data.Text                   (Text, pack)
import           Glue.Ekg
import           Glue.Types
import           System.Metrics
import qualified System.Metrics.Distribution as MD
import           Test.Hspec
import           Test.QuickCheck
import           Test.QuickCheck.Instances   ()
import           UnliftIO.Exception          (Exception, throwIO)

data EkgTestException = EkgTestException
  deriving (Eq, Show)

instance Exception EkgTestException

data MetricsResult
  = CounterResult Int64
  | GaugeResult Int64
  | LabelResult Text
  | DistributionResult Int64
  deriving (Eq, Show)

checkResult :: Store -> Text -> (MetricsResult -> Expectation) -> Expectation
checkResult store name check = do
  allMetrics <- sampleAll store
  let possibleValue = M.lookup name allMetrics
  result <- case possibleValue of
    Just (Counter counterCount) -> pure $ CounterResult counterCount
    Just (Gauge value)          -> pure $ GaugeResult value
    Just (Label text)           -> pure $ LabelResult text
    Just (Distribution stats)   -> pure $ DistributionResult $ MD.count stats
    Nothing                     -> fail "No metric."
  check result

testStats
  :: String
  -> (Store -> Text -> BasicService IO Int Int -> IO (BasicService IO Int Int))
  -> (Int -> Int -> MetricsResult -> Expectation)
  -> (Int -> Int -> MetricsResult -> Expectation)
  -> Spec
testStats methodName method successCheck failureCheck =
  describe methodName $ do
    it "Successful call" $
      property $ \(request :: Int, result :: Int, name :: Text) -> do
        let service _ = pure result :: IO Int
        store <- newStore
        wrappedService <- method store name service :: IO (BasicService IO Int Int)
        wrappedService request `shouldReturn` result
        checkResult store name $ successCheck request result
    it "Failing call" $
      property $ \(request :: Int, result :: Int, name :: Text) -> do
        let service _ = throwIO EkgTestException :: IO Int
        store <- newStore
        wrappedService <- method store name service :: IO (BasicService IO Int Int)
        wrappedService request `shouldThrow` (== EkgTestException)
        checkResult store name $ failureCheck request result

spec :: Spec
spec = parallel $ do
  testStats "recordDistribution" recordDistribution (\_ _ m -> m `shouldBe` DistributionResult 1) (\_ _ m -> m `shouldBe` DistributionResult 1)
  testStats "recordAttempts" recordAttempts (\_ _ m -> m `shouldBe` CounterResult 1) (\_ _ m -> m `shouldBe` CounterResult 1)
  testStats "recordSuccesses" recordSuccesses (\_ _ m -> m `shouldBe` CounterResult 1) (\_ _ m -> m `shouldBe` CounterResult 0)
  testStats "recordFailures" recordFailures (\_ _ m -> m `shouldBe` CounterResult 0) (\_ _ m -> m `shouldBe` CounterResult 1)
  testStats "recordLastRequest" recordLastRequest (\r _ m -> m `shouldBe` LabelResult (pack $ show r)) (\r _ m -> m `shouldBe` LabelResult (pack $ show r))
  testStats "recordLastResult" recordLastResult (\_ r m -> m `shouldBe` LabelResult (pack $ show r)) (\_ _ m -> m `shouldBe` LabelResult "")