From 7c4e1ab8e3d2c58db97b0030d1e91f8d5a35ccd3 Mon Sep 17 00:00:00 2001 From: Jiri Luzny Date: Sun, 12 Nov 2023 21:23:00 +0100 Subject: [PATCH 1/6] Initial version of the sample live tests --- example/config.yaml | 7 +++ example/example_live_test.go | 90 ++++++++++++++++++++++++++++++++++++ example/go.mod | 23 +++++++++ 3 files changed, 120 insertions(+) create mode 100644 example/config.yaml create mode 100644 example/example_live_test.go create mode 100644 example/go.mod diff --git a/example/config.yaml b/example/config.yaml new file mode 100644 index 0000000..9e42dee --- /dev/null +++ b/example/config.yaml @@ -0,0 +1,7 @@ +app: + ip_address: 192.168.0.204 + port: 8123 + home_zone_entity_id: zone.home + +entities: + light_entity_id: light.office_bed_lamp diff --git a/example/example_live_test.go b/example/example_live_test.go new file mode 100644 index 0000000..d2bbaac --- /dev/null +++ b/example/example_live_test.go @@ -0,0 +1,90 @@ +package example + +import ( + "log/slog" + "os" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + "gopkg.in/yaml.v3" + ga "saml.dev/gome-assistant" +) + +type ( + MySuite struct { + suite.Suite + app *ga.App + config *Config + } + + Config struct { + App struct { + HAAuthToken string `yaml:"ha_auth_token"` + IpAddress string `yaml:"ip_address"` + Port string `yaml:"port"` + HomeZoneEntityId string `yaml:"home_zone_entity_id"` + } + Entities struct { + LightEntityId string `yaml:"light_entity_id"` + } + } +) + +func (s *MySuite) SetupTest() { + configFile, err := os.ReadFile("./config.yaml") + if err != nil { + slog.Error("Error reading config file", err) + } + s.config = &Config{} + // either env var or config file can be used to set HA auth. token + s.config.App.HAAuthToken = os.Getenv("HA_AUTH_TOKEN") + if err := yaml.Unmarshal(configFile, s.config); err != nil { + slog.Error("Error unmarshalling config file:", err) + } + + s.app, err = ga.NewApp(ga.NewAppRequest{ + HAAuthToken: s.config.App.HAAuthToken, + IpAddress: s.config.App.IpAddress, + HomeZoneEntityId: s.config.App.HomeZoneEntityId, + }) + if err != nil { + slog.Error("Failed to createw new app:", err) + s.T().FailNow() + } +} + +func (s *MySuite) TearDownSuite() { + if s.app != nil { + s.app.Cleanup() + } +} + +// Basic test of ga app creation and light toggle service +func (s *MySuite) TestLightService() { + entityId := s.config.Entities.LightEntityId + + initialState, err := s.app.GetState().Get(entityId) + if err != nil { + slog.Error("Error getting entity state:", err) + } + slog.Info("Initial state of entity:", "state", initialState.State) + + s.app.GetService().Light.Toggle(entityId) + + time.Sleep(1 * time.Second) // wait for state to update + // <-time.After(1 * time.Second) + + newState, err := s.app.GetState().Get(entityId) + if err != nil { + slog.Error("Error getting entity state:", err) + } + slog.Info("New state of entity:", "state", newState.State) + assert.Equal(s.T(), initialState.State, newState.State) +} + +// Run the test suite +func TestMySuite(t *testing.T) { + suite.Run(t, new(MySuite)) +} diff --git a/example/go.mod b/example/go.mod new file mode 100644 index 0000000..95813bf --- /dev/null +++ b/example/go.mod @@ -0,0 +1,23 @@ +module example + +go 1.21.0 + +require ( + github.com/stretchr/testify v1.8.4 + gopkg.in/yaml.v3 v3.0.1 + saml.dev/gome-assistant v0.2.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gobuffalo/envy v1.10.2 // indirect + github.com/gobuffalo/packd v1.0.2 // indirect + github.com/gobuffalo/packr v1.30.1 // indirect + github.com/golang-module/carbon v1.7.3 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/nathan-osman/go-sunrise v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + golang.org/x/mod v0.9.0 // indirect +) From 60b851ddfa43b0f55935005acc4835c378a6bbd7 Mon Sep 17 00:00:00 2001 From: Jiri Luzny Date: Sun, 12 Nov 2023 21:37:13 +0100 Subject: [PATCH 2/6] aligned module name --- example/example_live_test.go | 3 +-- example/go.mod | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/example/example_live_test.go b/example/example_live_test.go index d2bbaac..99d0f0e 100644 --- a/example/example_live_test.go +++ b/example/example_live_test.go @@ -1,4 +1,4 @@ -package example +package main import ( "log/slog" @@ -74,7 +74,6 @@ func (s *MySuite) TestLightService() { s.app.GetService().Light.Toggle(entityId) time.Sleep(1 * time.Second) // wait for state to update - // <-time.After(1 * time.Second) newState, err := s.app.GetState().Get(entityId) if err != nil { diff --git a/example/go.mod b/example/go.mod index 95813bf..3121021 100644 --- a/example/go.mod +++ b/example/go.mod @@ -1,4 +1,4 @@ -module example +module main go 1.21.0 From 7aa407a515bef474b2190baf3b7d298890e6326c Mon Sep 17 00:00:00 2001 From: Jiri Luzny Date: Sun, 12 Nov 2023 21:50:50 +0100 Subject: [PATCH 3/6] Fix of assert condition --- example/example_live_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/example_live_test.go b/example/example_live_test.go index 99d0f0e..0cf8478 100644 --- a/example/example_live_test.go +++ b/example/example_live_test.go @@ -80,7 +80,7 @@ func (s *MySuite) TestLightService() { slog.Error("Error getting entity state:", err) } slog.Info("New state of entity:", "state", newState.State) - assert.Equal(s.T(), initialState.State, newState.State) + assert.NotEqual(s.T(), initialState.State, newState.State) } // Run the test suite From 5b1ee13fa3fca3131a1ec57514cfff6bcaf8d3bc Mon Sep 17 00:00:00 2001 From: Jiri Luzny Date: Wed, 15 Nov 2023 03:35:36 +0100 Subject: [PATCH 4/6] renamed to example package to allow correct build --- example/example.go | 4 +--- example/example_live_test.go | 2 +- example/go.mod | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/example/example.go b/example/example.go index 8d3dc05..fa5dc74 100644 --- a/example/example.go +++ b/example/example.go @@ -1,4 +1,4 @@ -package main +package example import ( "encoding/json" @@ -15,7 +15,6 @@ func main() { HAAuthToken: os.Getenv("HA_AUTH_TOKEN"), HomeZoneEntityId: "zone.home", }) - if err != nil { log.Fatalln("Error connecting to HASS:", err) } @@ -51,7 +50,6 @@ func main() { app.RegisterEventListeners(zwaveEventListener) app.Start() - } func pantryLights(service *ga.Service, state ga.State, sensor ga.EntityData) { diff --git a/example/example_live_test.go b/example/example_live_test.go index 0cf8478..92d6e0e 100644 --- a/example/example_live_test.go +++ b/example/example_live_test.go @@ -1,4 +1,4 @@ -package main +package example import ( "log/slog" diff --git a/example/go.mod b/example/go.mod index 3121021..95813bf 100644 --- a/example/go.mod +++ b/example/go.mod @@ -1,4 +1,4 @@ -module main +module example go 1.21.0 From 596024ce2573759f45f619a66ac43c5c9a6719c6 Mon Sep 17 00:00:00 2001 From: Jiri Luzny Date: Mon, 4 Dec 2023 21:56:27 +0100 Subject: [PATCH 5/6] Added check of entity state change callback. --- example/config.yaml | 8 ++-- example/example_live_test.go | 74 ++++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 29 deletions(-) diff --git a/example/config.yaml b/example/config.yaml index 9e42dee..5c72f02 100644 --- a/example/config.yaml +++ b/example/config.yaml @@ -1,7 +1,7 @@ -app: - ip_address: 192.168.0.204 +hass: + address: 192.168.0.204 port: 8123 - home_zone_entity_id: zone.home + zone: zone.home entities: - light_entity_id: light.office_bed_lamp + light_entity_id: light.office_desk_lamp diff --git a/example/example_live_test.go b/example/example_live_test.go index 92d6e0e..85fe40d 100644 --- a/example/example_live_test.go +++ b/example/example_live_test.go @@ -15,16 +15,17 @@ import ( type ( MySuite struct { suite.Suite - app *ga.App - config *Config + app *ga.App + config *Config + suiteCtx map[string]any } Config struct { - App struct { - HAAuthToken string `yaml:"ha_auth_token"` - IpAddress string `yaml:"ip_address"` + Hass struct { + HAAuthToken string `yaml:"token"` + IpAddress string `yaml:"address"` Port string `yaml:"port"` - HomeZoneEntityId string `yaml:"home_zone_entity_id"` + HomeZoneEntityId string `yaml:"zone"` } Entities struct { LightEntityId string `yaml:"light_entity_id"` @@ -32,55 +33,78 @@ type ( } ) -func (s *MySuite) SetupTest() { +func (s *MySuite) SetupSuite() { + s.suiteCtx = make(map[string]any) + configFile, err := os.ReadFile("./config.yaml") if err != nil { slog.Error("Error reading config file", err) } s.config = &Config{} // either env var or config file can be used to set HA auth. token - s.config.App.HAAuthToken = os.Getenv("HA_AUTH_TOKEN") + s.config.Hass.HAAuthToken = os.Getenv("HA_AUTH_TOKEN") if err := yaml.Unmarshal(configFile, s.config); err != nil { slog.Error("Error unmarshalling config file:", err) } s.app, err = ga.NewApp(ga.NewAppRequest{ - HAAuthToken: s.config.App.HAAuthToken, - IpAddress: s.config.App.IpAddress, - HomeZoneEntityId: s.config.App.HomeZoneEntityId, + HAAuthToken: s.config.Hass.HAAuthToken, + IpAddress: s.config.Hass.IpAddress, + HomeZoneEntityId: s.config.Hass.HomeZoneEntityId, }) if err != nil { slog.Error("Failed to createw new app:", err) s.T().FailNow() } + + entityId := s.config.Entities.LightEntityId + if entityId != "" { + s.suiteCtx["entityCallbackInvoked"] = false + etl := ga.NewEntityListener().EntityIds(entityId).Call(s.entityCallback).Build() + s.app.RegisterEntityListeners(etl) + go s.app.Start() + } } func (s *MySuite) TearDownSuite() { if s.app != nil { s.app.Cleanup() + s.app = nil } } -// Basic test of ga app creation and light toggle service +// Basic test of light toggle service and entity listener func (s *MySuite) TestLightService() { entityId := s.config.Entities.LightEntityId - initialState, err := s.app.GetState().Get(entityId) + if entityId != "" { + initState := getEntityState(s, entityId) + s.app.GetService().Light.Toggle(entityId) + + assert.EventuallyWithT(s.T(), func(c *assert.CollectT) { + newState := getEntityState(s, entityId) + assert.NotEqual(c, initState, newState) + assert.True(c, s.suiteCtx["entityCallbackInvoked"].(bool)) + }, 10*time.Second, 1*time.Second, "State of light entity did not change or callback was invoked") + } else { + s.T().Skip("No light entity id provided") + } +} + +// Test if event has been captured after light entity state changed +func (s *MySuite) entityCallback(se *ga.Service, st ga.State, e ga.EntityData) { + slog.Info("Entity callback called.", "entity id:", e.TriggerEntityId, "from state:", e.FromState, "to state:", e.ToState) + s.suiteCtx["entityCallbackInvoked"] = true +} + +func getEntityState(s *MySuite, entityId string) string { + state, err := s.app.GetState().Get(entityId) if err != nil { slog.Error("Error getting entity state:", err) + s.T().FailNow() } - slog.Info("Initial state of entity:", "state", initialState.State) - - s.app.GetService().Light.Toggle(entityId) - - time.Sleep(1 * time.Second) // wait for state to update - - newState, err := s.app.GetState().Get(entityId) - if err != nil { - slog.Error("Error getting entity state:", err) - } - slog.Info("New state of entity:", "state", newState.State) - assert.NotEqual(s.T(), initialState.State, newState.State) + slog.Info("State of entity:", "state", state.State) + return state.State } // Run the test suite From 983ac712d8e87cf5018d643e077d341c4900cc24 Mon Sep 17 00:00:00 2001 From: Jiri Luzny Date: Tue, 5 Dec 2023 19:04:01 +0100 Subject: [PATCH 6/6] Fixed typo in an assert message. --- example/example_live_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/example_live_test.go b/example/example_live_test.go index 85fe40d..e88611b 100644 --- a/example/example_live_test.go +++ b/example/example_live_test.go @@ -85,7 +85,7 @@ func (s *MySuite) TestLightService() { newState := getEntityState(s, entityId) assert.NotEqual(c, initState, newState) assert.True(c, s.suiteCtx["entityCallbackInvoked"].(bool)) - }, 10*time.Second, 1*time.Second, "State of light entity did not change or callback was invoked") + }, 10*time.Second, 1*time.Second, "State of light entity did not change or callback was not invoked") } else { s.T().Skip("No light entity id provided") }