runtime: count only trigger event frames and harden metrics tests

This commit is contained in:
2026-09-15 17:25:31 +03:00
parent 2a477b4977
commit 7d08acec9d
4 changed files with 36 additions and 23 deletions
+6 -2
View File
@@ -6,6 +6,7 @@ module HomeAssistant.Runtime.Connection
, writerAction , writerAction
, encodeService , encodeService
, dedupeBatch , dedupeBatch
, isTriggerEvent
) where ) where
import Control.Concurrent.STM import Control.Concurrent.STM
@@ -23,7 +24,7 @@ import Control.Concurrent (threadDelay)
import Control.Exception (onException) import Control.Exception (onException)
import Control.Exception.Annotated (throw) import Control.Exception.Annotated (throw)
import Control.Lens ((^?)) import Control.Lens ((^?))
import Control.Monad (forever, forM_) import Control.Monad (forever, forM_, when)
import Data.Aeson (Value, eitherDecode, encode, object, (.=)) import Data.Aeson (Value, eitherDecode, encode, object, (.=))
import Data.Aeson.Lens (key, _String) import Data.Aeson.Lens (key, _String)
import Data.List (sort) import Data.List (sort)
@@ -69,6 +70,9 @@ expectType expected msg =
Just t | t == expected -> pure () Just t | t == expected -> pure ()
_ -> throw (Fatal $ "expected " <> expected <> ", got: " <> T.pack (show msg)) _ -> throw (Fatal $ "expected " <> expected <> ", got: " <> T.pack (show msg))
isTriggerEvent :: Value -> Bool
isTriggerEvent v = v ^? key "type" . _String == Just "event"
subscribe :: Bus -> WS.Connection -> S.Set T.Text -> IO () subscribe :: Bus -> WS.Connection -> S.Set T.Text -> IO ()
subscribe bus conn ents = subscribe bus conn ents =
forM_ (S.toList ents) $ \entityId -> do forM_ (S.toList ents) $ \entityId -> do
@@ -97,7 +101,7 @@ receiveLoop bus conn = forever $ do
Right msg -> case eitherDecode msg of Right msg -> case eitherDecode msg of
Left err -> putStrLn $ "[reader] skipping undecodable message: " <> err Left err -> putStrLn $ "[reader] skipping undecodable message: " <> err
Right v -> do Right v -> do
recordInbound bus when (isTriggerEvent v) (recordInbound bus)
atomically $ writeTChan (busInbound bus) (Event v) atomically $ writeTChan (busInbound bus) (Event v)
receiveJSON :: WS.Connection -> IO Value receiveJSON :: WS.Connection -> IO Value
+9 -1
View File
@@ -9,7 +9,7 @@ import Data.Text (Text)
import Data.Time (UTCTime (..), utc) import Data.Time (UTCTime (..), utc)
import Data.UUID (fromString) import Data.UUID (fromString)
import HomeAssistant.Controller (Service (..), Target(..)) import HomeAssistant.Controller (Service (..), Target(..))
import HomeAssistant.Runtime.Connection (encodeService, dedupeBatch) import HomeAssistant.Runtime.Connection (encodeService, dedupeBatch, isTriggerEvent)
import Test.Hspec import Test.Hspec
spec :: Spec spec :: Spec
@@ -36,6 +36,14 @@ spec = do
, "service_data" .= object ["brightness" .= (200 :: Int)] , "service_data" .= object ["brightness" .= (200 :: Int)]
] ]
describe "isTriggerEvent" $ do
it "is true for event frames" $
isTriggerEvent (object ["type" .= ("event" :: Text)]) `shouldBe` True
it "is false for result frames" $
isTriggerEvent (object ["type" .= ("result" :: Text)]) `shouldBe` False
it "is false when there is no type" $
isTriggerEvent (object ["id" .= (1 :: Int)]) `shouldBe` False
describe "dedupeBatch" $ do describe "dedupeBatch" $ do
it "collapses identical calls to one" $ it "collapses identical calls to one" $
let batch = [ (req 1, lightOn [AreaId "x"]) let batch = [ (req 1, lightOn [AreaId "x"])
+10 -9
View File
@@ -4,7 +4,6 @@ module MetricsSpec (spec) where
import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import Data.Int (Int64)
import Data.List (isPrefixOf) import Data.List (isPrefixOf)
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text as T import qualified Data.Text as T
@@ -18,7 +17,6 @@ import HomeAssistant.Runtime.Metrics
, buildUpdateArgs , buildUpdateArgs
, ensureRrd , ensureRrd
, sampleAndUpdate , sampleAndUpdate
, metricsAction
, parseInfoDs , parseInfoDs
, schemaMatches , schemaMatches
, AppMetrics (..) , AppMetrics (..)
@@ -26,7 +24,7 @@ import HomeAssistant.Runtime.Metrics
) )
import qualified System.Metrics as M (Value (..)) import qualified System.Metrics as M (Value (..))
import Test.Hspec import Test.Hspec
import Control.Exception (try, SomeException) import Control.Exception (try, SomeException, finally)
import Control.Monad (forM_) import Control.Monad (forM_)
import System.Directory (findExecutable, getTemporaryDirectory, listDirectory, removeFile) import System.Directory (findExecutable, getTemporaryDirectory, listDirectory, removeFile)
import System.Exit (ExitCode (ExitSuccess)) import System.Exit (ExitCode (ExitSuccess))
@@ -171,9 +169,10 @@ spec = do
store <- Metrics.newStore store <- Metrics.newStore
m <- registerAppMetrics store m <- registerAppMetrics store
Counter.inc (amTriggersIn m) Counter.inc (amTriggersIn m)
Counter.inc (amServicesOut m)
sample <- Metrics.sampleAll store sample <- Metrics.sampleAll store
HM.lookup "hass.trigger.in" sample `shouldBe` Just (M.Counter 1) HM.lookup "hass.trigger.in" sample `shouldBe` Just (M.Counter 1)
HM.lookup "hass.service.out" sample `shouldBe` Just (M.Counter 0) HM.lookup "hass.service.out" sample `shouldBe` Just (M.Counter 1)
describe "end-to-end (rrdtool-gated)" $ do describe "end-to-end (rrdtool-gated)" $ do
it "creates an rrd, samples, and updates it" $ do it "creates an rrd, samples, and updates it" $ do
@@ -208,7 +207,12 @@ spec = do
bakPrefix = "hass-controller-schema-change-test.rrd.bak-" bakPrefix = "hass-controller-schema-change-test.rrd.bak-"
base = [ DsSpec "a" "a" Derive, DsSpec "b" "b" Gauge ] base = [ DsSpec "a" "a" Derive, DsSpec "b" "b" Gauge ]
expanded = base ++ [ DsSpec "c" "c" Derive ] expanded = base ++ [ DsSpec "c" "c" Derive ]
_ <- try (removeFile rrdPath) :: IO (Either SomeException ()) clean = do
stale <- filter (isPrefixOf bakPrefix) <$> listDirectory tmp
forM_ (rrdPath : map (\f -> tmp ++ "/" ++ f) stale) $ \f ->
try (removeFile f) :: IO (Either SomeException ())
clean
( do
ensureRrd rrdtool rrdPath base ensureRrd rrdtool rrdPath base
ensureRrd rrdtool rrdPath base ensureRrd rrdtool rrdPath base
noBackups <- filter (isPrefixOf bakPrefix) <$> listDirectory tmp noBackups <- filter (isPrefixOf bakPrefix) <$> listDirectory tmp
@@ -219,7 +223,4 @@ spec = do
(rc, out, _) <- readProcessWithExitCode rrdtool ["info", rrdPath] "" (rc, out, _) <- readProcessWithExitCode rrdtool ["info", rrdPath] ""
rc `shouldBe` ExitSuccess rc `shouldBe` ExitSuccess
out `shouldContain` "ds[c].type" out `shouldContain` "ds[c].type"
_ <- try (removeFile rrdPath) :: IO (Either SomeException ()) ) `finally` clean
forM_ backups $ \f -> do
_ <- try (removeFile (tmp ++ "/" ++ f)) :: IO (Either SomeException ())
pure ()
+1 -1
View File
@@ -11,7 +11,7 @@ spec = pure ()
-- --
-- spec :: Spec -- spec :: Spec
-- spec = describe "runController" $ do -- spec = describe "runController" $ do
-- it "feeds inbound events through the machine and forwards service calls" $ withBus InfoS $ \bus -> do -- it "feeds inbound events through the machine and forwards service calls" $ withBus severity appMetrics $ \bus -> do
-- _ <- async (runController bus (Controller "test" lightController True)) -- _ <- async (runController bus (Controller "test" lightController True))
-- putStrLn "Before the delay" -- putStrLn "Before the delay"
-- threadDelay 100000 -- let the controller dup its inbound channel -- threadDelay 100000 -- let the controller dup its inbound channel