content: {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE RecordWildCards #-}
  
  module Semantic.Stat.Spec (testTree) where
  
  import Control.Exception
  import Network.Socket (Family (..), Socket, SocketType (..), close, defaultProtocol, socketPair)
  import Network.Socket.ByteString
  import Semantic.Config
  import Semantic.Telemetry.Stat
  import System.Environment
  
  import Test.Tasty
  import Test.Tasty.HUnit
  
  withSocketPair :: ((Socket, Socket) -> IO c) -> IO c
  withSocketPair = bracket create release
    where create = socketPair AF_UNIX Datagram defaultProtocol
          release (client, server) = close client >> close server
  
  withEnvironment :: String -> String -> IO () -> IO ()
  withEnvironment key value = bracket_ (setEnv key value) (unsetEnv key)
  
  -- NOTE: These cannot easily run in parallel because we test things like
  -- setting/unsetting the environment.
  testTree :: TestTree
  testTree = testCaseSteps "Semantic.Stat.Spec" $ \step -> do
    step "Sets appropriate defaults"
    case_sets_appropriate_defaults
    step "Takes stats addr from environment"
    case_takes_stats_addr_from_environment
    step "Handles stats addr with just hostname"
    case_handles_stats_addr_with_just_hostname
    step "takes dogstats host from environment"
    case_takes_dogstats_host_from_environment
    step "rendering"
    case_render_counters *> case_render_tags
    step "stats deliver datagram"
    case_sendstat_delivers_datagram
  
  
  case_sets_appropriate_defaults :: Assertion
  case_sets_appropriate_defaults = do
    StatsClient{..} <- defaultStatsClient
    statsClientNamespace @?= "semantic"
    statsClientUDPHost   @?= "127.0.0.1"
    statsClientUDPPort   @?= "28125"
  
  case_takes_stats_addr_from_environment :: Assertion
  case_takes_stats_addr_from_environment =
    withEnvironment "STATS_ADDR" "localhost:8125" $ do
      StatsClient{..} <- defaultStatsClient
      statsClientUDPHost @?= "localhost"
      statsClientUDPPort @?= "8125"
  
  case_handles_stats_addr_with_just_hostname :: Assertion
  case_handles_stats_addr_with_just_hostname =
    withEnvironment "STATS_ADDR" "localhost" $ do
      StatsClient{..} <- defaultStatsClient
      statsClientUDPHost @?= "localhost"
      statsClientUDPPort @?= "28125"
  
  case_takes_dogstats_host_from_environment :: Assertion
  case_takes_dogstats_host_from_environment =
    withEnvironment "DOGSTATSD_HOST" "0.0.0.0" $ do
      StatsClient{..} <- defaultStatsClient
      statsClientUDPHost @?= "0.0.0.0"
      statsClientUDPPort @?= "28125"
  
  key :: String
  key = "app.metric"
  
  case_render_counters :: Assertion
  case_render_counters = do
    renderDatagram "" (increment key []) @?= "app.metric:1|c"
    renderDatagram "" (decrement key []) @?= "app.metric:-1|c"
    renderDatagram "" (count key 8 []) @?= "app.metric:8|c"
    renderDatagram "pre" (increment key []) @?= "pre.app.metric:1|c"
  
  case_render_tags :: Assertion
  case_render_tags = do
    let incTag = increment key [("key", "value")]
    renderDatagram "" incTag @?= "app.metric:1|c|#key:value"
  
    let tagWithoutValue = increment key [("a", "")]
    renderDatagram "" tagWithoutValue @?= "app.metric:1|c|#a"
  
    let tags = increment key [("key", "value"), ("a", "true")]
    renderDatagram "" tags @?= "app.metric:1|c|#key:value,a:true"
  
    let tagsWithoutValue = increment key [("key", "value"), ("a", "")]
    renderDatagram "" tagsWithoutValue @?= "app.metric:1|c|#key:value,a"
  
  case_sendstat_delivers_datagram :: Assertion
  case_sendstat_delivers_datagram = do
    client <- defaultStatsClient
    withSocketPair $ \(clientSoc, serverSoc) -> do
      sendStat client { statsClientUDPSocket = clientSoc } (increment "app.metric" [])
      info <- recv serverSoc 1024
      info @?= "semantic.app.metric:1|c"
  
  -- Defaults are all driven by defaultConfig.
  defaultStatsClient :: IO StatsClient
  defaultStatsClient = defaultConfig defaultOptions >>= \Config{..} -> statsClient configStatsHost configStatsPort configAppName
  