From 887f4d2c4cd70eff1e0ce0e35fabe45f4a3f761a Mon Sep 17 00:00:00 2001 From: Daniel Simmons Date: Tue, 22 Jul 2025 13:41:13 +0200 Subject: [PATCH 1/4] Some lint fixes --- config_test.go | 137 ++--------------------------------- config_test_internal.go | 133 ++++++++++++++++++++++++++++++++++ entity_binary_sensor_test.go | 2 +- entity_input_boolean_test.go | 2 +- entity_light_test.go | 6 +- store/sqlite_test.go | 12 +-- sun_test.go | 6 +- util_test.go | 4 +- 8 files changed, 156 insertions(+), 146 deletions(-) create mode 100644 config_test_internal.go diff --git a/config_test.go b/config_test.go index 4ab1200..39fd1a3 100644 --- a/config_test.go +++ b/config_test.go @@ -1,10 +1,11 @@ -package hal +package hal_test import ( "os" "path/filepath" "testing" + "github.com/dansimau/hal" "gotest.tools/v3/assert" ) @@ -24,14 +25,14 @@ location: lng: -122.4194` configPath := filepath.Join(tmpDir, "hal.yaml") - err := os.WriteFile(configPath, []byte(configContent), 0644) + err := os.WriteFile(configPath, []byte(configContent), 0o644) assert.NilError(t, err) // Change to temp directory err = os.Chdir(tmpDir) assert.NilError(t, err) - config, err := LoadConfig() + config, err := hal.LoadConfig() assert.NilError(t, err) assert.Equal(t, config.HomeAssistant.Host, "localhost:8123") assert.Equal(t, config.HomeAssistant.Token, "test-token") @@ -48,7 +49,7 @@ location: err := os.Chdir(tmpDir) assert.NilError(t, err) - _, err = LoadConfig() + _, err = hal.LoadConfig() assert.ErrorContains(t, err, "no such file or directory") }) @@ -59,137 +60,13 @@ location: invalidContent := `invalid: yaml: content:` configPath := filepath.Join(tmpDir, "hal.yaml") - err := os.WriteFile(configPath, []byte(invalidContent), 0644) + err := os.WriteFile(configPath, []byte(invalidContent), 0o644) assert.NilError(t, err) err = os.Chdir(tmpDir) assert.NilError(t, err) - _, err = LoadConfig() + _, err = hal.LoadConfig() assert.ErrorContains(t, err, "yaml") }) } - -func TestSearchParentsForFile(t *testing.T) { - t.Run("finds file in current directory", func(t *testing.T) { - tmpDir := t.TempDir() - testFile := "test.txt" - testPath := filepath.Join(tmpDir, testFile) - - err := os.WriteFile(testPath, []byte("test"), 0644) - assert.NilError(t, err) - - foundPath, err := searchParentsForFile(testFile, tmpDir) - assert.NilError(t, err) - assert.Equal(t, foundPath, testPath) - }) - - t.Run("finds file in parent directory", func(t *testing.T) { - tmpDir := t.TempDir() - subDir := filepath.Join(tmpDir, "subdir") - err := os.Mkdir(subDir, 0755) - assert.NilError(t, err) - - testFile := "test.txt" - testPath := filepath.Join(tmpDir, testFile) - - err = os.WriteFile(testPath, []byte("test"), 0644) - assert.NilError(t, err) - - foundPath, err := searchParentsForFile(testFile, subDir) - assert.NilError(t, err) - assert.Equal(t, foundPath, testPath) - }) - - t.Run("returns empty string when file not found", func(t *testing.T) { - tmpDir := t.TempDir() - foundPath, err := searchParentsForFile("nonexistent.txt", tmpDir) - assert.NilError(t, err) - assert.Equal(t, foundPath, "") - }) -} - -func TestSearchParentsForFileFromCwd(t *testing.T) { - t.Run("searches from current working directory", func(t *testing.T) { - tmpDir := t.TempDir() - originalDir, _ := os.Getwd() - defer os.Chdir(originalDir) - - testFile := "test.txt" - testPath := filepath.Join(tmpDir, testFile) - - err := os.WriteFile(testPath, []byte("test"), 0644) - assert.NilError(t, err) - - err = os.Chdir(tmpDir) - assert.NilError(t, err) - - foundPath, err := searchParentsForFileFromCwd(testFile) - assert.NilError(t, err) - - // Resolve symlinks to handle macOS /private/var vs /var differences - resolvedFound, err := filepath.EvalSymlinks(foundPath) - assert.NilError(t, err) - resolvedExpected, err := filepath.EvalSymlinks(testPath) - assert.NilError(t, err) - - assert.Equal(t, resolvedFound, resolvedExpected) - }) -} - -func TestGetParents(t *testing.T) { - t.Run("returns correct parent paths", func(t *testing.T) { - if filepath.Separator == '\\' { - // Windows paths - paths := getParents("C:\\Users\\test\\project") - expected := []string{ - "C:\\Users\\test\\project", - "C:\\Users\\test", - "C:\\Users", - "C:\\", - "/", - } - assert.DeepEqual(t, paths, expected) - } else { - // Unix paths - paths := getParents("/home/user/project") - expected := []string{ - "/home/user/project", - "/home/user", - "/home", - "/", - } - assert.DeepEqual(t, paths, expected) - } - }) - - t.Run("handles root directory", func(t *testing.T) { - paths := getParents("/") - expected := []string{"/"} - assert.DeepEqual(t, paths, expected) - }) -} - -func TestFileExists(t *testing.T) { - t.Run("returns true for existing file", func(t *testing.T) { - tmpDir := t.TempDir() - testPath := filepath.Join(tmpDir, "test.txt") - - err := os.WriteFile(testPath, []byte("test"), 0644) - assert.NilError(t, err) - - exists := fileExists(testPath) - assert.Equal(t, exists, true) - }) - - t.Run("returns false for non-existent file", func(t *testing.T) { - exists := fileExists("/nonexistent/path/file.txt") - assert.Equal(t, exists, false) - }) - - t.Run("returns true for existing directory", func(t *testing.T) { - tmpDir := t.TempDir() - exists := fileExists(tmpDir) - assert.Equal(t, exists, true) - }) -} \ No newline at end of file diff --git a/config_test_internal.go b/config_test_internal.go new file mode 100644 index 0000000..f677805 --- /dev/null +++ b/config_test_internal.go @@ -0,0 +1,133 @@ +package hal + +import ( + "os" + "path/filepath" + "testing" + + "gotest.tools/v3/assert" +) + +func TestSearchParentsForFile(t *testing.T) { + t.Run("finds file in current directory", func(t *testing.T) { + tmpDir := t.TempDir() + testFile := "test.txt" + testPath := filepath.Join(tmpDir, testFile) + + err := os.WriteFile(testPath, []byte("test"), 0o644) + assert.NilError(t, err) + + foundPath, err := searchParentsForFile(testFile, tmpDir) + assert.NilError(t, err) + assert.Equal(t, foundPath, testPath) + }) + + t.Run("finds file in parent directory", func(t *testing.T) { + tmpDir := t.TempDir() + subDir := filepath.Join(tmpDir, "subdir") + err := os.Mkdir(subDir, 0o755) + assert.NilError(t, err) + + testFile := "test.txt" + testPath := filepath.Join(tmpDir, testFile) + + err = os.WriteFile(testPath, []byte("test"), 0o644) + assert.NilError(t, err) + + foundPath, err := searchParentsForFile(testFile, subDir) + assert.NilError(t, err) + assert.Equal(t, foundPath, testPath) + }) + + t.Run("returns empty string when file not found", func(t *testing.T) { + tmpDir := t.TempDir() + foundPath, err := searchParentsForFile("nonexistent.txt", tmpDir) + assert.NilError(t, err) + assert.Equal(t, foundPath, "") + }) +} + +func TestSearchParentsForFileFromCwd(t *testing.T) { + t.Run("searches from current working directory", func(t *testing.T) { + tmpDir := t.TempDir() + originalDir, _ := os.Getwd() + defer os.Chdir(originalDir) + + testFile := "test.txt" + testPath := filepath.Join(tmpDir, testFile) + + err := os.WriteFile(testPath, []byte("test"), 0o644) + assert.NilError(t, err) + + err = os.Chdir(tmpDir) + assert.NilError(t, err) + + foundPath, err := searchParentsForFileFromCwd(testFile) + assert.NilError(t, err) + + // Resolve symlinks to handle macOS /private/var vs /var differences + resolvedFound, err := filepath.EvalSymlinks(foundPath) + assert.NilError(t, err) + resolvedExpected, err := filepath.EvalSymlinks(testPath) + assert.NilError(t, err) + + assert.Equal(t, resolvedFound, resolvedExpected) + }) +} + +func TestGetParents(t *testing.T) { + t.Run("returns correct parent paths", func(t *testing.T) { + if filepath.Separator == '\\' { + // Windows paths + paths := getParents("C:\\Users\\test\\project") + expected := []string{ + "C:\\Users\\test\\project", + "C:\\Users\\test", + "C:\\Users", + "C:\\", + "/", + } + assert.DeepEqual(t, paths, expected) + } else { + // Unix paths + paths := getParents("/home/user/project") + expected := []string{ + "/home/user/project", + "/home/user", + "/home", + "/", + } + assert.DeepEqual(t, paths, expected) + } + }) + + t.Run("handles root directory", func(t *testing.T) { + paths := getParents("/") + expected := []string{"/"} + assert.DeepEqual(t, paths, expected) + }) +} + +func TestFileExists(t *testing.T) { + t.Run("returns true for existing file", func(t *testing.T) { + tmpDir := t.TempDir() + testPath := filepath.Join(tmpDir, "test.txt") + + err := os.WriteFile(testPath, []byte("test"), 0o644) + assert.NilError(t, err) + + exists := fileExists(testPath) + assert.Equal(t, exists, true) + }) + + t.Run("returns false for non-existent file", func(t *testing.T) { + exists := fileExists("/nonexistent/path/file.txt") + assert.Equal(t, exists, false) + }) + + t.Run("returns true for existing directory", func(t *testing.T) { + tmpDir := t.TempDir() + exists := fileExists(tmpDir) + assert.Equal(t, exists, true) + }) +} diff --git a/entity_binary_sensor_test.go b/entity_binary_sensor_test.go index 8ae4b65..c77025e 100644 --- a/entity_binary_sensor_test.go +++ b/entity_binary_sensor_test.go @@ -62,4 +62,4 @@ func TestBinarySensor_IsOn(t *testing.T) { assert.Equal(t, sensor.IsOn(), false) }) -} \ No newline at end of file +} diff --git a/entity_input_boolean_test.go b/entity_input_boolean_test.go index d3f0b2d..7be62fd 100644 --- a/entity_input_boolean_test.go +++ b/entity_input_boolean_test.go @@ -84,4 +84,4 @@ func TestInputBoolean_TurnOff(t *testing.T) { err := inputBoolean.TurnOff() assert.Equal(t, err, ErrEntityNotRegistered) }) -} \ No newline at end of file +} diff --git a/entity_light_test.go b/entity_light_test.go index ce174b2..540b1fe 100644 --- a/entity_light_test.go +++ b/entity_light_test.go @@ -88,7 +88,7 @@ func TestLight_TurnOn(t *testing.T) { // This would require mocking the connection and CallService method // For now, we'll test the error case above light := NewLight("light.test") - + // Test with attributes err := light.TurnOn(map[string]any{"brightness": 128}) assert.Equal(t, err, ErrEntityNotRegistered) @@ -130,7 +130,7 @@ func TestLightGroup_GetBrightness(t *testing.T) { state1 := homeassistant.State{Attributes: map[string]any{"brightness": float64(100)}} state2 := homeassistant.State{Attributes: map[string]any{"brightness": float64(200)}} - + light1.SetState(state1) light2.SetState(state2) @@ -252,4 +252,4 @@ func TestLightGroup_BindConnection(t *testing.T) { // For now, just verify the method doesn't panic assert.Equal(t, len(lg), 2) }) -} \ No newline at end of file +} diff --git a/store/sqlite_test.go b/store/sqlite_test.go index bcd7cfb..4fea89e 100644 --- a/store/sqlite_test.go +++ b/store/sqlite_test.go @@ -9,32 +9,32 @@ func TestSQLitePragmaConfiguration(t *testing.T) { // Create a temporary database file tmpFile := "test_sqlite.db" defer os.Remove(tmpFile) - + // Test opening database with PRAGMA settings db, err := Open(tmpFile) if err != nil { t.Fatalf("Failed to open database: %v", err) } - + // Verify WAL mode is set var journalMode string err = db.Raw("PRAGMA journal_mode").Scan(&journalMode).Error if err != nil { t.Fatalf("Failed to query journal_mode: %v", err) } - + if journalMode != "wal" { t.Errorf("Expected journal_mode to be 'wal', got: %s", journalMode) } - + // Verify synchronous mode is set var syncMode string err = db.Raw("PRAGMA synchronous").Scan(&syncMode).Error if err != nil { t.Fatalf("Failed to query synchronous: %v", err) } - + if syncMode != "1" { // NORMAL = 1 t.Errorf("Expected synchronous to be '1' (NORMAL), got: %s", syncMode) } -} \ No newline at end of file +} diff --git a/sun_test.go b/sun_test.go index e1f0bbd..7b8167a 100644 --- a/sun_test.go +++ b/sun_test.go @@ -29,7 +29,7 @@ func TestSunTimes_Sunrise(t *testing.T) { sunrise := sunTimes.Sunrise() // Sunrise should be a valid time and in the past or future of today assert.Assert(t, !sunrise.IsZero()) - + // Sunrise should be before sunset sunset := sunTimes.Sunset() assert.Assert(t, sunrise.Before(sunset)) @@ -61,7 +61,7 @@ func TestSunTimes_Sunset(t *testing.T) { sunset := sunTimes.Sunset() // Sunset should be a valid time assert.Assert(t, !sunset.IsZero()) - + // Sunset should be after sunrise sunrise := sunTimes.Sunrise() assert.Assert(t, sunset.After(sunrise)) @@ -154,4 +154,4 @@ func TestSunTimes_EdgeCases(t *testing.T) { assert.Assert(t, !sunset.IsZero()) assert.Assert(t, sunrise.Before(sunset)) }) -} \ No newline at end of file +} diff --git a/util_test.go b/util_test.go index 1177b9b..4833bcc 100644 --- a/util_test.go +++ b/util_test.go @@ -10,7 +10,7 @@ func TestGetShortFunctionName(t *testing.T) { t.Run("extracts function name from function reference", func(t *testing.T) { testFunc := func() {} name := getShortFunctionName(testFunc) - + // The name should contain "TestGetShortFunctionName.func1" or similar // depending on Go version and compilation assert.Assert(t, len(name) > 0) @@ -43,4 +43,4 @@ func TestGetStringOrStringSliceAdditional(t *testing.T) { result := getStringOrStringSlice(testStruct{field: "test"}) assert.DeepEqual(t, result, []string{}) }) -} \ No newline at end of file +} From 826eefe9317da20cc8a200b8fcd48eb2ad45cd1a Mon Sep 17 00:00:00 2001 From: Daniel Simmons Date: Tue, 22 Jul 2025 13:51:27 +0200 Subject: [PATCH 2/4] fix: Update test packages to comply with testpackage linter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change package declarations from `hal` to `hal_test` for external API tests - Change package declarations from `store` to `store_test` for store tests - Move tests accessing internal functions/fields to `*_internal_test.go` files: - Move TestNewSunTimes (accesses sunTimes.location) to sun_internal_test.go - Move getShortFunctionName and getStringOrStringSlice tests to util_internal_test.go - Update imports and function calls to use proper package prefixes - Remove util_test.go (tests moved to util_internal_test.go) - Add hal import to test files that now use external package 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- ...est_internal.go => config_internal_test.go | 0 entity_binary_sensor_test.go | 17 ++-- entity_input_boolean_test.go | 29 +++--- entity_light_test.go | 97 ++++++++++--------- store/sqlite_test.go | 6 +- sun_internal_test.go | 19 ++++ sun_test.go | 50 ++++------ util_internal_test.go | 41 ++++++++ util_test.go | 46 --------- 9 files changed, 157 insertions(+), 148 deletions(-) rename config_test_internal.go => config_internal_test.go (100%) create mode 100644 sun_internal_test.go delete mode 100644 util_test.go diff --git a/config_test_internal.go b/config_internal_test.go similarity index 100% rename from config_test_internal.go rename to config_internal_test.go diff --git a/entity_binary_sensor_test.go b/entity_binary_sensor_test.go index c77025e..7a79bc7 100644 --- a/entity_binary_sensor_test.go +++ b/entity_binary_sensor_test.go @@ -1,20 +1,21 @@ -package hal +package hal_test import ( "testing" + "github.com/dansimau/hal" "github.com/dansimau/hal/homeassistant" "gotest.tools/v3/assert" ) func TestNewBinarySensor(t *testing.T) { - sensor := NewBinarySensor("binary_sensor.test") + sensor := hal.NewBinarySensor("binary_sensor.test") assert.Equal(t, sensor.GetID(), "binary_sensor.test") } func TestBinarySensor_IsOff(t *testing.T) { t.Run("returns true when state is off", func(t *testing.T) { - sensor := NewBinarySensor("binary_sensor.test") + sensor := hal.NewBinarySensor("binary_sensor.test") state := homeassistant.State{State: "off"} sensor.SetState(state) @@ -22,7 +23,7 @@ func TestBinarySensor_IsOff(t *testing.T) { }) t.Run("returns false when state is on", func(t *testing.T) { - sensor := NewBinarySensor("binary_sensor.test") + sensor := hal.NewBinarySensor("binary_sensor.test") state := homeassistant.State{State: "on"} sensor.SetState(state) @@ -30,7 +31,7 @@ func TestBinarySensor_IsOff(t *testing.T) { }) t.Run("returns false when state is other value", func(t *testing.T) { - sensor := NewBinarySensor("binary_sensor.test") + sensor := hal.NewBinarySensor("binary_sensor.test") state := homeassistant.State{State: "unavailable"} sensor.SetState(state) @@ -40,7 +41,7 @@ func TestBinarySensor_IsOff(t *testing.T) { func TestBinarySensor_IsOn(t *testing.T) { t.Run("returns true when state is on", func(t *testing.T) { - sensor := NewBinarySensor("binary_sensor.test") + sensor := hal.NewBinarySensor("binary_sensor.test") state := homeassistant.State{State: "on"} sensor.SetState(state) @@ -48,7 +49,7 @@ func TestBinarySensor_IsOn(t *testing.T) { }) t.Run("returns false when state is off", func(t *testing.T) { - sensor := NewBinarySensor("binary_sensor.test") + sensor := hal.NewBinarySensor("binary_sensor.test") state := homeassistant.State{State: "off"} sensor.SetState(state) @@ -56,7 +57,7 @@ func TestBinarySensor_IsOn(t *testing.T) { }) t.Run("returns false when state is other value", func(t *testing.T) { - sensor := NewBinarySensor("binary_sensor.test") + sensor := hal.NewBinarySensor("binary_sensor.test") state := homeassistant.State{State: "unknown"} sensor.SetState(state) diff --git a/entity_input_boolean_test.go b/entity_input_boolean_test.go index 7be62fd..4bb31a0 100644 --- a/entity_input_boolean_test.go +++ b/entity_input_boolean_test.go @@ -1,20 +1,21 @@ -package hal +package hal_test import ( "testing" + "github.com/dansimau/hal" "github.com/dansimau/hal/homeassistant" "gotest.tools/v3/assert" ) func TestNewInputBoolean(t *testing.T) { - inputBoolean := NewInputBoolean("input_boolean.test") + inputBoolean := hal.NewInputBoolean("input_boolean.test") assert.Equal(t, inputBoolean.GetID(), "input_boolean.test") } func TestInputBoolean_IsOff(t *testing.T) { t.Run("returns true when state is off", func(t *testing.T) { - inputBoolean := NewInputBoolean("input_boolean.test") + inputBoolean := hal.NewInputBoolean("input_boolean.test") state := homeassistant.State{State: "off"} inputBoolean.SetState(state) @@ -22,7 +23,7 @@ func TestInputBoolean_IsOff(t *testing.T) { }) t.Run("returns false when state is on", func(t *testing.T) { - inputBoolean := NewInputBoolean("input_boolean.test") + inputBoolean := hal.NewInputBoolean("input_boolean.test") state := homeassistant.State{State: "on"} inputBoolean.SetState(state) @@ -30,7 +31,7 @@ func TestInputBoolean_IsOff(t *testing.T) { }) t.Run("returns false when state is other value", func(t *testing.T) { - inputBoolean := NewInputBoolean("input_boolean.test") + inputBoolean := hal.NewInputBoolean("input_boolean.test") state := homeassistant.State{State: "unavailable"} inputBoolean.SetState(state) @@ -40,7 +41,7 @@ func TestInputBoolean_IsOff(t *testing.T) { func TestInputBoolean_IsOn(t *testing.T) { t.Run("returns true when state is on", func(t *testing.T) { - inputBoolean := NewInputBoolean("input_boolean.test") + inputBoolean := hal.NewInputBoolean("input_boolean.test") state := homeassistant.State{State: "on"} inputBoolean.SetState(state) @@ -48,7 +49,7 @@ func TestInputBoolean_IsOn(t *testing.T) { }) t.Run("returns false when state is off", func(t *testing.T) { - inputBoolean := NewInputBoolean("input_boolean.test") + inputBoolean := hal.NewInputBoolean("input_boolean.test") state := homeassistant.State{State: "off"} inputBoolean.SetState(state) @@ -56,7 +57,7 @@ func TestInputBoolean_IsOn(t *testing.T) { }) t.Run("returns false when state is other value", func(t *testing.T) { - inputBoolean := NewInputBoolean("input_boolean.test") + inputBoolean := hal.NewInputBoolean("input_boolean.test") state := homeassistant.State{State: "unknown"} inputBoolean.SetState(state) @@ -66,22 +67,22 @@ func TestInputBoolean_IsOn(t *testing.T) { func TestInputBoolean_TurnOn(t *testing.T) { t.Run("returns error when not registered", func(t *testing.T) { - inputBoolean := NewInputBoolean("input_boolean.test") + inputBoolean := hal.NewInputBoolean("input_boolean.test") err := inputBoolean.TurnOn() - assert.Equal(t, err, ErrEntityNotRegistered) + assert.Equal(t, err, hal.ErrEntityNotRegistered) }) t.Run("accepts attributes", func(t *testing.T) { - inputBoolean := NewInputBoolean("input_boolean.test") + inputBoolean := hal.NewInputBoolean("input_boolean.test") err := inputBoolean.TurnOn(map[string]any{"custom": "value"}) - assert.Equal(t, err, ErrEntityNotRegistered) + assert.Equal(t, err, hal.ErrEntityNotRegistered) }) } func TestInputBoolean_TurnOff(t *testing.T) { t.Run("returns error when not registered", func(t *testing.T) { - inputBoolean := NewInputBoolean("input_boolean.test") + inputBoolean := hal.NewInputBoolean("input_boolean.test") err := inputBoolean.TurnOff() - assert.Equal(t, err, ErrEntityNotRegistered) + assert.Equal(t, err, hal.ErrEntityNotRegistered) }) } diff --git a/entity_light_test.go b/entity_light_test.go index 540b1fe..68df28a 100644 --- a/entity_light_test.go +++ b/entity_light_test.go @@ -1,20 +1,21 @@ -package hal +package hal_test import ( "testing" + "github.com/dansimau/hal" "github.com/dansimau/hal/homeassistant" "gotest.tools/v3/assert" ) func TestNewLight(t *testing.T) { - light := NewLight("light.test") + light := hal.NewLight("light.test") assert.Equal(t, light.GetID(), "light.test") } func TestLight_GetBrightness(t *testing.T) { t.Run("returns brightness from attributes", func(t *testing.T) { - light := NewLight("light.test") + light := hal.NewLight("light.test") state := homeassistant.State{ Attributes: map[string]any{ "brightness": float64(255), @@ -27,7 +28,7 @@ func TestLight_GetBrightness(t *testing.T) { }) t.Run("returns 0 when brightness not in attributes", func(t *testing.T) { - light := NewLight("light.test") + light := hal.NewLight("light.test") state := homeassistant.State{ Attributes: map[string]any{}, } @@ -38,7 +39,7 @@ func TestLight_GetBrightness(t *testing.T) { }) t.Run("returns 0 when brightness is wrong type", func(t *testing.T) { - light := NewLight("light.test") + light := hal.NewLight("light.test") state := homeassistant.State{ Attributes: map[string]any{ "brightness": "invalid", @@ -53,7 +54,7 @@ func TestLight_GetBrightness(t *testing.T) { func TestLight_IsOn(t *testing.T) { t.Run("returns true when state is on", func(t *testing.T) { - light := NewLight("light.test") + light := hal.NewLight("light.test") state := homeassistant.State{State: "on"} light.SetState(state) @@ -61,7 +62,7 @@ func TestLight_IsOn(t *testing.T) { }) t.Run("returns false when state is off", func(t *testing.T) { - light := NewLight("light.test") + light := hal.NewLight("light.test") state := homeassistant.State{State: "off"} light.SetState(state) @@ -69,7 +70,7 @@ func TestLight_IsOn(t *testing.T) { }) t.Run("returns false when state is other value", func(t *testing.T) { - light := NewLight("light.test") + light := hal.NewLight("light.test") state := homeassistant.State{State: "unavailable"} light.SetState(state) @@ -79,40 +80,40 @@ func TestLight_IsOn(t *testing.T) { func TestLight_TurnOn(t *testing.T) { t.Run("returns error when not registered", func(t *testing.T) { - light := NewLight("light.test") + light := hal.NewLight("light.test") err := light.TurnOn() - assert.Equal(t, err, ErrEntityNotRegistered) + assert.Equal(t, err, hal.ErrEntityNotRegistered) }) t.Run("calls service with attributes", func(t *testing.T) { // This would require mocking the connection and CallService method // For now, we'll test the error case above - light := NewLight("light.test") + light := hal.NewLight("light.test") // Test with attributes err := light.TurnOn(map[string]any{"brightness": 128}) - assert.Equal(t, err, ErrEntityNotRegistered) + assert.Equal(t, err, hal.ErrEntityNotRegistered) }) } func TestLight_TurnOff(t *testing.T) { t.Run("returns error when not registered", func(t *testing.T) { - light := NewLight("light.test") + light := hal.NewLight("light.test") err := light.TurnOff() - assert.Equal(t, err, ErrEntityNotRegistered) + assert.Equal(t, err, hal.ErrEntityNotRegistered) }) } func TestLightGroup_GetID(t *testing.T) { t.Run("returns empty group message for empty group", func(t *testing.T) { - lg := LightGroup{} + lg := hal.LightGroup{} assert.Equal(t, lg.GetID(), "(empty light group)") }) t.Run("returns joined IDs for multiple lights", func(t *testing.T) { - light1 := NewLight("light.1") - light2 := NewLight("light.2") - lg := LightGroup{light1, light2} + light1 := hal.NewLight("light.1") + light2 := hal.NewLight("light.2") + lg := hal.LightGroup{light1, light2} assert.Equal(t, lg.GetID(), "light.1, light.2") }) @@ -120,13 +121,13 @@ func TestLightGroup_GetID(t *testing.T) { func TestLightGroup_GetBrightness(t *testing.T) { t.Run("returns 0 for empty group", func(t *testing.T) { - lg := LightGroup{} + lg := hal.LightGroup{} assert.Equal(t, lg.GetBrightness(), float64(0)) }) t.Run("returns brightness of first light", func(t *testing.T) { - light1 := NewLight("light.1") - light2 := NewLight("light.2") + light1 := hal.NewLight("light.1") + light2 := hal.NewLight("light.2") state1 := homeassistant.State{Attributes: map[string]any{"brightness": float64(100)}} state2 := homeassistant.State{Attributes: map[string]any{"brightness": float64(200)}} @@ -134,24 +135,24 @@ func TestLightGroup_GetBrightness(t *testing.T) { light1.SetState(state1) light2.SetState(state2) - lg := LightGroup{light1, light2} + lg := hal.LightGroup{light1, light2} assert.Equal(t, lg.GetBrightness(), float64(100)) }) } func TestLightGroup_GetState(t *testing.T) { t.Run("returns empty state for empty group", func(t *testing.T) { - lg := LightGroup{} + lg := hal.LightGroup{} state := lg.GetState() assert.DeepEqual(t, state, homeassistant.State{}) }) t.Run("returns state of first light", func(t *testing.T) { - light1 := NewLight("light.1") + light1 := hal.NewLight("light.1") expectedState := homeassistant.State{State: "on", Attributes: map[string]any{"brightness": float64(255)}} light1.SetState(expectedState) - lg := LightGroup{light1} + lg := hal.LightGroup{light1} state := lg.GetState() assert.DeepEqual(t, state, expectedState) }) @@ -159,9 +160,9 @@ func TestLightGroup_GetState(t *testing.T) { func TestLightGroup_SetState(t *testing.T) { t.Run("sets state on all lights in group", func(t *testing.T) { - light1 := NewLight("light.1") - light2 := NewLight("light.2") - lg := LightGroup{light1, light2} + light1 := hal.NewLight("light.1") + light2 := hal.NewLight("light.2") + lg := hal.LightGroup{light1, light2} newState := homeassistant.State{State: "on", Attributes: map[string]any{"brightness": float64(128)}} lg.SetState(newState) @@ -173,29 +174,29 @@ func TestLightGroup_SetState(t *testing.T) { func TestLightGroup_IsOn(t *testing.T) { t.Run("returns true when all lights are on", func(t *testing.T) { - light1 := NewLight("light.1") - light2 := NewLight("light.2") + light1 := hal.NewLight("light.1") + light2 := hal.NewLight("light.2") light1.SetState(homeassistant.State{State: "on"}) light2.SetState(homeassistant.State{State: "on"}) - lg := LightGroup{light1, light2} + lg := hal.LightGroup{light1, light2} assert.Equal(t, lg.IsOn(), true) }) t.Run("returns false when any light is off", func(t *testing.T) { - light1 := NewLight("light.1") - light2 := NewLight("light.2") + light1 := hal.NewLight("light.1") + light2 := hal.NewLight("light.2") light1.SetState(homeassistant.State{State: "on"}) light2.SetState(homeassistant.State{State: "off"}) - lg := LightGroup{light1, light2} + lg := hal.LightGroup{light1, light2} assert.Equal(t, lg.IsOn(), false) }) t.Run("returns true for empty group", func(t *testing.T) { - lg := LightGroup{} + lg := hal.LightGroup{} assert.Equal(t, lg.IsOn(), true) }) } @@ -203,17 +204,17 @@ func TestLightGroup_IsOn(t *testing.T) { func TestLightGroup_TurnOn(t *testing.T) { t.Run("returns nil when no errors", func(t *testing.T) { // Empty group should not error - lg := LightGroup{} + lg := hal.LightGroup{} err := lg.TurnOn() assert.NilError(t, err) }) t.Run("collects errors from individual lights", func(t *testing.T) { - light1 := NewLight("light.1") - light2 := NewLight("light.2") - lg := LightGroup{light1, light2} + light1 := hal.NewLight("light.1") + light2 := hal.NewLight("light.2") + lg := hal.LightGroup{light1, light2} - // Both lights should return ErrEntityNotRegistered + // Both lights should return hal.ErrEntityNotRegistered err := lg.TurnOn() // With 2 errors, should return a joined error assert.ErrorContains(t, err, "entity not registered") @@ -222,15 +223,15 @@ func TestLightGroup_TurnOn(t *testing.T) { func TestLightGroup_TurnOff(t *testing.T) { t.Run("returns nil when no errors", func(t *testing.T) { - lg := LightGroup{} + lg := hal.LightGroup{} err := lg.TurnOff() assert.NilError(t, err) }) t.Run("collects errors from individual lights", func(t *testing.T) { - light1 := NewLight("light.1") - light2 := NewLight("light.2") - lg := LightGroup{light1, light2} + light1 := hal.NewLight("light.1") + light2 := hal.NewLight("light.2") + lg := hal.LightGroup{light1, light2} err := lg.TurnOff() // With 2 errors, should return joined error @@ -240,12 +241,12 @@ func TestLightGroup_TurnOff(t *testing.T) { func TestLightGroup_BindConnection(t *testing.T) { t.Run("binds connection to all lights", func(t *testing.T) { - light1 := NewLight("light.1") - light2 := NewLight("light.2") - lg := LightGroup{light1, light2} + light1 := hal.NewLight("light.1") + light2 := hal.NewLight("light.2") + lg := hal.LightGroup{light1, light2} // Create a mock connection (would need proper setup in real scenario) - conn := &Connection{} + conn := &hal.Connection{} lg.BindConnection(conn) // Verify connection was bound (in real test, would check that connection property was set) diff --git a/store/sqlite_test.go b/store/sqlite_test.go index 4fea89e..0fe1a25 100644 --- a/store/sqlite_test.go +++ b/store/sqlite_test.go @@ -1,8 +1,10 @@ -package store +package store_test import ( "os" "testing" + + "github.com/dansimau/hal/store" ) func TestSQLitePragmaConfiguration(t *testing.T) { @@ -11,7 +13,7 @@ func TestSQLitePragmaConfiguration(t *testing.T) { defer os.Remove(tmpFile) // Test opening database with PRAGMA settings - db, err := Open(tmpFile) + db, err := store.Open(tmpFile) if err != nil { t.Fatalf("Failed to open database: %v", err) } diff --git a/sun_internal_test.go b/sun_internal_test.go new file mode 100644 index 0000000..c362726 --- /dev/null +++ b/sun_internal_test.go @@ -0,0 +1,19 @@ +package hal + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestNewSunTimes(t *testing.T) { + config := LocationConfig{ + Latitude: 37.7749, + Longitude: -122.4194, + } + + sunTimes := NewSunTimes(config) + assert.Assert(t, sunTimes != nil) + assert.Equal(t, sunTimes.location.Latitude, 37.7749) + assert.Equal(t, sunTimes.location.Longitude, -122.4194) +} \ No newline at end of file diff --git a/sun_test.go b/sun_test.go index 7b8167a..8499edf 100644 --- a/sun_test.go +++ b/sun_test.go @@ -1,30 +1,20 @@ -package hal +package hal_test import ( "testing" + "github.com/dansimau/hal" "gotest.tools/v3/assert" ) -func TestNewSunTimes(t *testing.T) { - config := LocationConfig{ - Latitude: 37.7749, - Longitude: -122.4194, - } - - sunTimes := NewSunTimes(config) - assert.Assert(t, sunTimes != nil) - assert.Equal(t, sunTimes.location.Latitude, 37.7749) - assert.Equal(t, sunTimes.location.Longitude, -122.4194) -} func TestSunTimes_Sunrise(t *testing.T) { t.Run("returns sunrise time for San Francisco", func(t *testing.T) { - config := LocationConfig{ + config := hal.LocationConfig{ Latitude: 37.7749, Longitude: -122.4194, } - sunTimes := NewSunTimes(config) + sunTimes := hal.NewSunTimes(config) sunrise := sunTimes.Sunrise() // Sunrise should be a valid time and in the past or future of today @@ -36,11 +26,11 @@ func TestSunTimes_Sunrise(t *testing.T) { }) t.Run("returns different times for different locations", func(t *testing.T) { - sfConfig := LocationConfig{Latitude: 37.7749, Longitude: -122.4194} // San Francisco - nyConfig := LocationConfig{Latitude: 40.7128, Longitude: -74.0060} // New York + sfConfig := hal.LocationConfig{Latitude: 37.7749, Longitude: -122.4194} // San Francisco + nyConfig := hal.LocationConfig{Latitude: 40.7128, Longitude: -74.0060} // New York - sfSunTimes := NewSunTimes(sfConfig) - nySunTimes := NewSunTimes(nyConfig) + sfSunTimes := hal.NewSunTimes(sfConfig) + nySunTimes := hal.NewSunTimes(nyConfig) sfSunrise := sfSunTimes.Sunrise() nySunrise := nySunTimes.Sunrise() @@ -52,11 +42,11 @@ func TestSunTimes_Sunrise(t *testing.T) { func TestSunTimes_Sunset(t *testing.T) { t.Run("returns sunset time for San Francisco", func(t *testing.T) { - config := LocationConfig{ + config := hal.LocationConfig{ Latitude: 37.7749, Longitude: -122.4194, } - sunTimes := NewSunTimes(config) + sunTimes := hal.NewSunTimes(config) sunset := sunTimes.Sunset() // Sunset should be a valid time @@ -70,11 +60,11 @@ func TestSunTimes_Sunset(t *testing.T) { func TestSunTimes_IsDayTime(t *testing.T) { t.Run("correctly determines day/night for known time", func(t *testing.T) { - config := LocationConfig{ + config := hal.LocationConfig{ Latitude: 37.7749, Longitude: -122.4194, } - sunTimes := NewSunTimes(config) + sunTimes := hal.NewSunTimes(config) // This is a basic test - in real usage, the result depends on current time // We just verify the method returns a boolean and doesn't panic @@ -83,11 +73,11 @@ func TestSunTimes_IsDayTime(t *testing.T) { }) t.Run("day/night are opposite", func(t *testing.T) { - config := LocationConfig{ + config := hal.LocationConfig{ Latitude: 37.7749, Longitude: -122.4194, } - sunTimes := NewSunTimes(config) + sunTimes := hal.NewSunTimes(config) isDayTime := sunTimes.IsDayTime() isNightTime := sunTimes.IsNightTime() @@ -99,11 +89,11 @@ func TestSunTimes_IsDayTime(t *testing.T) { func TestSunTimes_IsNightTime(t *testing.T) { t.Run("is opposite of IsDayTime", func(t *testing.T) { - config := LocationConfig{ + config := hal.LocationConfig{ Latitude: 37.7749, Longitude: -122.4194, } - sunTimes := NewSunTimes(config) + sunTimes := hal.NewSunTimes(config) isDayTime := sunTimes.IsDayTime() isNightTime := sunTimes.IsNightTime() @@ -116,11 +106,11 @@ func TestSunTimes_IsNightTime(t *testing.T) { func TestSunTimes_EdgeCases(t *testing.T) { t.Run("handles extreme northern latitude", func(t *testing.T) { // Test with extreme latitude (northern Norway) - config := LocationConfig{ + config := hal.LocationConfig{ Latitude: 78.2156, Longitude: 15.5503, } - sunTimes := NewSunTimes(config) + sunTimes := hal.NewSunTimes(config) // Should not panic, even in polar regions sunrise := sunTimes.Sunrise() @@ -140,11 +130,11 @@ func TestSunTimes_EdgeCases(t *testing.T) { }) t.Run("handles zero coordinates", func(t *testing.T) { - config := LocationConfig{ + config := hal.LocationConfig{ Latitude: 0, Longitude: 0, } - sunTimes := NewSunTimes(config) + sunTimes := hal.NewSunTimes(config) // Should work at equator/prime meridian sunrise := sunTimes.Sunrise() diff --git a/util_internal_test.go b/util_internal_test.go index 58eb47b..09575da 100644 --- a/util_internal_test.go +++ b/util_internal_test.go @@ -2,6 +2,8 @@ package hal import ( "testing" + + "gotest.tools/v3/assert" ) func TestGetStringOrStringSlice(t *testing.T) { @@ -71,3 +73,42 @@ func TestGetStringOrStringSlice(t *testing.T) { }) } } + +func TestGetShortFunctionName(t *testing.T) { + t.Run("extracts function name from function reference", func(t *testing.T) { + testFunc := func() {} + name := getShortFunctionName(testFunc) + + // The name should contain "TestGetShortFunctionName.func1" or similar + // depending on Go version and compilation + assert.Assert(t, len(name) > 0) + assert.Assert(t, name != "") + }) + + t.Run("handles named function", func(t *testing.T) { + name := getShortFunctionName(getShortFunctionName) + assert.Equal(t, name, "getShortFunctionName") + }) + + t.Run("handles method", func(t *testing.T) { + light := NewLight("test") + name := getShortFunctionName(light.TurnOn) + // Method names may have "-fm" suffix in Go runtime + assert.Assert(t, name == "TurnOn" || name == "TurnOn-fm") + }) +} + +func TestGetStringOrStringSliceAdditional(t *testing.T) { + t.Run("handles boolean input", func(t *testing.T) { + result := getStringOrStringSlice(true) + assert.DeepEqual(t, result, []string{}) + }) + + t.Run("handles struct input", func(t *testing.T) { + type testStruct struct { + field string + } + result := getStringOrStringSlice(testStruct{field: "test"}) + assert.DeepEqual(t, result, []string{}) + }) +} diff --git a/util_test.go b/util_test.go deleted file mode 100644 index 4833bcc..0000000 --- a/util_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package hal - -import ( - "testing" - - "gotest.tools/v3/assert" -) - -func TestGetShortFunctionName(t *testing.T) { - t.Run("extracts function name from function reference", func(t *testing.T) { - testFunc := func() {} - name := getShortFunctionName(testFunc) - - // The name should contain "TestGetShortFunctionName.func1" or similar - // depending on Go version and compilation - assert.Assert(t, len(name) > 0) - assert.Assert(t, name != "") - }) - - t.Run("handles named function", func(t *testing.T) { - name := getShortFunctionName(getShortFunctionName) - assert.Equal(t, name, "getShortFunctionName") - }) - - t.Run("handles method", func(t *testing.T) { - light := NewLight("test") - name := getShortFunctionName(light.TurnOn) - // Method names may have "-fm" suffix in Go runtime - assert.Assert(t, name == "TurnOn" || name == "TurnOn-fm") - }) -} - -func TestGetStringOrStringSliceAdditional(t *testing.T) { - t.Run("handles boolean input", func(t *testing.T) { - result := getStringOrStringSlice(true) - assert.DeepEqual(t, result, []string{}) - }) - - t.Run("handles struct input", func(t *testing.T) { - type testStruct struct { - field string - } - result := getStringOrStringSlice(testStruct{field: "test"}) - assert.DeepEqual(t, result, []string{}) - }) -} From d1d96b0a97298cfe79de59101655eb68815943e5 Mon Sep 17 00:00:00 2001 From: Daniel Simmons Date: Tue, 22 Jul 2025 14:16:45 +0200 Subject: [PATCH 3/4] fix: paralleltest linter --- .claude/settings.local.json | 5 ++++- Makefile | 2 +- config_internal_test.go | 15 +++++++++++++++ config_test.go | 1 + entity_binary_sensor_test.go | 12 ++++++++++++ entity_input_boolean_test.go | 14 ++++++++++++++ entity_light_test.go | 37 ++++++++++++++++++++++++++++++++++++ store/sqlite_test.go | 2 ++ sun_internal_test.go | 2 ++ sun_test.go | 13 +++++++++++++ util_internal_test.go | 9 +++++++++ 11 files changed, 110 insertions(+), 2 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 159584d..54154ca 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -6,7 +6,10 @@ "Bash(go test:*)", "Bash(git add:*)", "Bash(make lint:*)", - "Bash(golangci-lint run:*)" + "Bash(golangci-lint run:*)", + "Bash(rm:*)", + "Bash(git rm:*)", + "Bash(grep:*)" ], "deny": [] } diff --git a/Makefile b/Makefile index cecf187..61d9cf8 100644 --- a/Makefile +++ b/Makefile @@ -7,5 +7,5 @@ lint: .PHONY: test test: which go-test-coverage || go install github.com/vladopajic/go-test-coverage/v2@latest - go test -v ./... -coverprofile=./cover.out -covermode=atomic -coverpkg=./... + go test -v ./... -coverprofile=./cover.out -covermode=atomic -coverpkg=./... -json | python3 testutil/colourise-go-test-output.py go-test-coverage --config=./.testcoverage.yaml diff --git a/config_internal_test.go b/config_internal_test.go index f677805..108e845 100644 --- a/config_internal_test.go +++ b/config_internal_test.go @@ -9,7 +9,10 @@ import ( ) func TestSearchParentsForFile(t *testing.T) { + t.Parallel() + t.Run("finds file in current directory", func(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() testFile := "test.txt" testPath := filepath.Join(tmpDir, testFile) @@ -23,6 +26,7 @@ func TestSearchParentsForFile(t *testing.T) { }) t.Run("finds file in parent directory", func(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() subDir := filepath.Join(tmpDir, "subdir") err := os.Mkdir(subDir, 0o755) @@ -40,6 +44,7 @@ func TestSearchParentsForFile(t *testing.T) { }) t.Run("returns empty string when file not found", func(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() foundPath, err := searchParentsForFile("nonexistent.txt", tmpDir) assert.NilError(t, err) @@ -47,6 +52,7 @@ func TestSearchParentsForFile(t *testing.T) { }) } +//nolint:paralleltest // This test changes working directory and cannot run in parallel func TestSearchParentsForFileFromCwd(t *testing.T) { t.Run("searches from current working directory", func(t *testing.T) { tmpDir := t.TempDir() @@ -76,7 +82,10 @@ func TestSearchParentsForFileFromCwd(t *testing.T) { } func TestGetParents(t *testing.T) { + t.Parallel() + t.Run("returns correct parent paths", func(t *testing.T) { + t.Parallel() if filepath.Separator == '\\' { // Windows paths paths := getParents("C:\\Users\\test\\project") @@ -102,6 +111,7 @@ func TestGetParents(t *testing.T) { }) t.Run("handles root directory", func(t *testing.T) { + t.Parallel() paths := getParents("/") expected := []string{"/"} assert.DeepEqual(t, paths, expected) @@ -109,7 +119,10 @@ func TestGetParents(t *testing.T) { } func TestFileExists(t *testing.T) { + t.Parallel() + t.Run("returns true for existing file", func(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() testPath := filepath.Join(tmpDir, "test.txt") @@ -121,11 +134,13 @@ func TestFileExists(t *testing.T) { }) t.Run("returns false for non-existent file", func(t *testing.T) { + t.Parallel() exists := fileExists("/nonexistent/path/file.txt") assert.Equal(t, exists, false) }) t.Run("returns true for existing directory", func(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() exists := fileExists(tmpDir) assert.Equal(t, exists, true) diff --git a/config_test.go b/config_test.go index 39fd1a3..bfeb9a4 100644 --- a/config_test.go +++ b/config_test.go @@ -9,6 +9,7 @@ import ( "gotest.tools/v3/assert" ) +//nolint:paralleltest // This test changes working directory and cannot run in parallel func TestLoadConfig(t *testing.T) { t.Run("loads valid config", func(t *testing.T) { // Create a temporary directory and config file diff --git a/entity_binary_sensor_test.go b/entity_binary_sensor_test.go index 7a79bc7..10c2091 100644 --- a/entity_binary_sensor_test.go +++ b/entity_binary_sensor_test.go @@ -9,12 +9,17 @@ import ( ) func TestNewBinarySensor(t *testing.T) { + t.Parallel() + sensor := hal.NewBinarySensor("binary_sensor.test") assert.Equal(t, sensor.GetID(), "binary_sensor.test") } func TestBinarySensor_IsOff(t *testing.T) { + t.Parallel() + t.Run("returns true when state is off", func(t *testing.T) { + t.Parallel() sensor := hal.NewBinarySensor("binary_sensor.test") state := homeassistant.State{State: "off"} sensor.SetState(state) @@ -23,6 +28,7 @@ func TestBinarySensor_IsOff(t *testing.T) { }) t.Run("returns false when state is on", func(t *testing.T) { + t.Parallel() sensor := hal.NewBinarySensor("binary_sensor.test") state := homeassistant.State{State: "on"} sensor.SetState(state) @@ -31,6 +37,7 @@ func TestBinarySensor_IsOff(t *testing.T) { }) t.Run("returns false when state is other value", func(t *testing.T) { + t.Parallel() sensor := hal.NewBinarySensor("binary_sensor.test") state := homeassistant.State{State: "unavailable"} sensor.SetState(state) @@ -40,7 +47,10 @@ func TestBinarySensor_IsOff(t *testing.T) { } func TestBinarySensor_IsOn(t *testing.T) { + t.Parallel() + t.Run("returns true when state is on", func(t *testing.T) { + t.Parallel() sensor := hal.NewBinarySensor("binary_sensor.test") state := homeassistant.State{State: "on"} sensor.SetState(state) @@ -49,6 +59,7 @@ func TestBinarySensor_IsOn(t *testing.T) { }) t.Run("returns false when state is off", func(t *testing.T) { + t.Parallel() sensor := hal.NewBinarySensor("binary_sensor.test") state := homeassistant.State{State: "off"} sensor.SetState(state) @@ -57,6 +68,7 @@ func TestBinarySensor_IsOn(t *testing.T) { }) t.Run("returns false when state is other value", func(t *testing.T) { + t.Parallel() sensor := hal.NewBinarySensor("binary_sensor.test") state := homeassistant.State{State: "unknown"} sensor.SetState(state) diff --git a/entity_input_boolean_test.go b/entity_input_boolean_test.go index 4bb31a0..f58d29b 100644 --- a/entity_input_boolean_test.go +++ b/entity_input_boolean_test.go @@ -9,12 +9,15 @@ import ( ) func TestNewInputBoolean(t *testing.T) { + t.Parallel() inputBoolean := hal.NewInputBoolean("input_boolean.test") assert.Equal(t, inputBoolean.GetID(), "input_boolean.test") } func TestInputBoolean_IsOff(t *testing.T) { + t.Parallel() t.Run("returns true when state is off", func(t *testing.T) { + t.Parallel() inputBoolean := hal.NewInputBoolean("input_boolean.test") state := homeassistant.State{State: "off"} inputBoolean.SetState(state) @@ -23,6 +26,7 @@ func TestInputBoolean_IsOff(t *testing.T) { }) t.Run("returns false when state is on", func(t *testing.T) { + t.Parallel() inputBoolean := hal.NewInputBoolean("input_boolean.test") state := homeassistant.State{State: "on"} inputBoolean.SetState(state) @@ -31,6 +35,7 @@ func TestInputBoolean_IsOff(t *testing.T) { }) t.Run("returns false when state is other value", func(t *testing.T) { + t.Parallel() inputBoolean := hal.NewInputBoolean("input_boolean.test") state := homeassistant.State{State: "unavailable"} inputBoolean.SetState(state) @@ -40,7 +45,9 @@ func TestInputBoolean_IsOff(t *testing.T) { } func TestInputBoolean_IsOn(t *testing.T) { + t.Parallel() t.Run("returns true when state is on", func(t *testing.T) { + t.Parallel() inputBoolean := hal.NewInputBoolean("input_boolean.test") state := homeassistant.State{State: "on"} inputBoolean.SetState(state) @@ -49,6 +56,7 @@ func TestInputBoolean_IsOn(t *testing.T) { }) t.Run("returns false when state is off", func(t *testing.T) { + t.Parallel() inputBoolean := hal.NewInputBoolean("input_boolean.test") state := homeassistant.State{State: "off"} inputBoolean.SetState(state) @@ -57,6 +65,7 @@ func TestInputBoolean_IsOn(t *testing.T) { }) t.Run("returns false when state is other value", func(t *testing.T) { + t.Parallel() inputBoolean := hal.NewInputBoolean("input_boolean.test") state := homeassistant.State{State: "unknown"} inputBoolean.SetState(state) @@ -66,13 +75,16 @@ func TestInputBoolean_IsOn(t *testing.T) { } func TestInputBoolean_TurnOn(t *testing.T) { + t.Parallel() t.Run("returns error when not registered", func(t *testing.T) { + t.Parallel() inputBoolean := hal.NewInputBoolean("input_boolean.test") err := inputBoolean.TurnOn() assert.Equal(t, err, hal.ErrEntityNotRegistered) }) t.Run("accepts attributes", func(t *testing.T) { + t.Parallel() inputBoolean := hal.NewInputBoolean("input_boolean.test") err := inputBoolean.TurnOn(map[string]any{"custom": "value"}) assert.Equal(t, err, hal.ErrEntityNotRegistered) @@ -80,7 +92,9 @@ func TestInputBoolean_TurnOn(t *testing.T) { } func TestInputBoolean_TurnOff(t *testing.T) { + t.Parallel() t.Run("returns error when not registered", func(t *testing.T) { + t.Parallel() inputBoolean := hal.NewInputBoolean("input_boolean.test") err := inputBoolean.TurnOff() assert.Equal(t, err, hal.ErrEntityNotRegistered) diff --git a/entity_light_test.go b/entity_light_test.go index 68df28a..098fa82 100644 --- a/entity_light_test.go +++ b/entity_light_test.go @@ -9,12 +9,15 @@ import ( ) func TestNewLight(t *testing.T) { + t.Parallel() light := hal.NewLight("light.test") assert.Equal(t, light.GetID(), "light.test") } func TestLight_GetBrightness(t *testing.T) { + t.Parallel() t.Run("returns brightness from attributes", func(t *testing.T) { + t.Parallel() light := hal.NewLight("light.test") state := homeassistant.State{ Attributes: map[string]any{ @@ -28,6 +31,7 @@ func TestLight_GetBrightness(t *testing.T) { }) t.Run("returns 0 when brightness not in attributes", func(t *testing.T) { + t.Parallel() light := hal.NewLight("light.test") state := homeassistant.State{ Attributes: map[string]any{}, @@ -39,6 +43,7 @@ func TestLight_GetBrightness(t *testing.T) { }) t.Run("returns 0 when brightness is wrong type", func(t *testing.T) { + t.Parallel() light := hal.NewLight("light.test") state := homeassistant.State{ Attributes: map[string]any{ @@ -53,7 +58,9 @@ func TestLight_GetBrightness(t *testing.T) { } func TestLight_IsOn(t *testing.T) { + t.Parallel() t.Run("returns true when state is on", func(t *testing.T) { + t.Parallel() light := hal.NewLight("light.test") state := homeassistant.State{State: "on"} light.SetState(state) @@ -62,6 +69,7 @@ func TestLight_IsOn(t *testing.T) { }) t.Run("returns false when state is off", func(t *testing.T) { + t.Parallel() light := hal.NewLight("light.test") state := homeassistant.State{State: "off"} light.SetState(state) @@ -70,6 +78,7 @@ func TestLight_IsOn(t *testing.T) { }) t.Run("returns false when state is other value", func(t *testing.T) { + t.Parallel() light := hal.NewLight("light.test") state := homeassistant.State{State: "unavailable"} light.SetState(state) @@ -79,13 +88,16 @@ func TestLight_IsOn(t *testing.T) { } func TestLight_TurnOn(t *testing.T) { + t.Parallel() t.Run("returns error when not registered", func(t *testing.T) { + t.Parallel() light := hal.NewLight("light.test") err := light.TurnOn() assert.Equal(t, err, hal.ErrEntityNotRegistered) }) t.Run("calls service with attributes", func(t *testing.T) { + t.Parallel() // This would require mocking the connection and CallService method // For now, we'll test the error case above light := hal.NewLight("light.test") @@ -97,7 +109,9 @@ func TestLight_TurnOn(t *testing.T) { } func TestLight_TurnOff(t *testing.T) { + t.Parallel() t.Run("returns error when not registered", func(t *testing.T) { + t.Parallel() light := hal.NewLight("light.test") err := light.TurnOff() assert.Equal(t, err, hal.ErrEntityNotRegistered) @@ -105,12 +119,15 @@ func TestLight_TurnOff(t *testing.T) { } func TestLightGroup_GetID(t *testing.T) { + t.Parallel() t.Run("returns empty group message for empty group", func(t *testing.T) { + t.Parallel() lg := hal.LightGroup{} assert.Equal(t, lg.GetID(), "(empty light group)") }) t.Run("returns joined IDs for multiple lights", func(t *testing.T) { + t.Parallel() light1 := hal.NewLight("light.1") light2 := hal.NewLight("light.2") lg := hal.LightGroup{light1, light2} @@ -120,12 +137,15 @@ func TestLightGroup_GetID(t *testing.T) { } func TestLightGroup_GetBrightness(t *testing.T) { + t.Parallel() t.Run("returns 0 for empty group", func(t *testing.T) { + t.Parallel() lg := hal.LightGroup{} assert.Equal(t, lg.GetBrightness(), float64(0)) }) t.Run("returns brightness of first light", func(t *testing.T) { + t.Parallel() light1 := hal.NewLight("light.1") light2 := hal.NewLight("light.2") @@ -141,13 +161,16 @@ func TestLightGroup_GetBrightness(t *testing.T) { } func TestLightGroup_GetState(t *testing.T) { + t.Parallel() t.Run("returns empty state for empty group", func(t *testing.T) { + t.Parallel() lg := hal.LightGroup{} state := lg.GetState() assert.DeepEqual(t, state, homeassistant.State{}) }) t.Run("returns state of first light", func(t *testing.T) { + t.Parallel() light1 := hal.NewLight("light.1") expectedState := homeassistant.State{State: "on", Attributes: map[string]any{"brightness": float64(255)}} light1.SetState(expectedState) @@ -159,7 +182,9 @@ func TestLightGroup_GetState(t *testing.T) { } func TestLightGroup_SetState(t *testing.T) { + t.Parallel() t.Run("sets state on all lights in group", func(t *testing.T) { + t.Parallel() light1 := hal.NewLight("light.1") light2 := hal.NewLight("light.2") lg := hal.LightGroup{light1, light2} @@ -173,7 +198,9 @@ func TestLightGroup_SetState(t *testing.T) { } func TestLightGroup_IsOn(t *testing.T) { + t.Parallel() t.Run("returns true when all lights are on", func(t *testing.T) { + t.Parallel() light1 := hal.NewLight("light.1") light2 := hal.NewLight("light.2") @@ -185,6 +212,7 @@ func TestLightGroup_IsOn(t *testing.T) { }) t.Run("returns false when any light is off", func(t *testing.T) { + t.Parallel() light1 := hal.NewLight("light.1") light2 := hal.NewLight("light.2") @@ -196,13 +224,16 @@ func TestLightGroup_IsOn(t *testing.T) { }) t.Run("returns true for empty group", func(t *testing.T) { + t.Parallel() lg := hal.LightGroup{} assert.Equal(t, lg.IsOn(), true) }) } func TestLightGroup_TurnOn(t *testing.T) { + t.Parallel() t.Run("returns nil when no errors", func(t *testing.T) { + t.Parallel() // Empty group should not error lg := hal.LightGroup{} err := lg.TurnOn() @@ -210,6 +241,7 @@ func TestLightGroup_TurnOn(t *testing.T) { }) t.Run("collects errors from individual lights", func(t *testing.T) { + t.Parallel() light1 := hal.NewLight("light.1") light2 := hal.NewLight("light.2") lg := hal.LightGroup{light1, light2} @@ -222,13 +254,16 @@ func TestLightGroup_TurnOn(t *testing.T) { } func TestLightGroup_TurnOff(t *testing.T) { + t.Parallel() t.Run("returns nil when no errors", func(t *testing.T) { + t.Parallel() lg := hal.LightGroup{} err := lg.TurnOff() assert.NilError(t, err) }) t.Run("collects errors from individual lights", func(t *testing.T) { + t.Parallel() light1 := hal.NewLight("light.1") light2 := hal.NewLight("light.2") lg := hal.LightGroup{light1, light2} @@ -240,7 +275,9 @@ func TestLightGroup_TurnOff(t *testing.T) { } func TestLightGroup_BindConnection(t *testing.T) { + t.Parallel() t.Run("binds connection to all lights", func(t *testing.T) { + t.Parallel() light1 := hal.NewLight("light.1") light2 := hal.NewLight("light.2") lg := hal.LightGroup{light1, light2} diff --git a/store/sqlite_test.go b/store/sqlite_test.go index 0fe1a25..0961008 100644 --- a/store/sqlite_test.go +++ b/store/sqlite_test.go @@ -8,6 +8,8 @@ import ( ) func TestSQLitePragmaConfiguration(t *testing.T) { + t.Parallel() + // Create a temporary database file tmpFile := "test_sqlite.db" defer os.Remove(tmpFile) diff --git a/sun_internal_test.go b/sun_internal_test.go index c362726..0af71f4 100644 --- a/sun_internal_test.go +++ b/sun_internal_test.go @@ -7,6 +7,8 @@ import ( ) func TestNewSunTimes(t *testing.T) { + t.Parallel() + config := LocationConfig{ Latitude: 37.7749, Longitude: -122.4194, diff --git a/sun_test.go b/sun_test.go index 8499edf..e49fb49 100644 --- a/sun_test.go +++ b/sun_test.go @@ -9,7 +9,9 @@ import ( func TestSunTimes_Sunrise(t *testing.T) { + t.Parallel() t.Run("returns sunrise time for San Francisco", func(t *testing.T) { + t.Parallel() config := hal.LocationConfig{ Latitude: 37.7749, Longitude: -122.4194, @@ -26,6 +28,7 @@ func TestSunTimes_Sunrise(t *testing.T) { }) t.Run("returns different times for different locations", func(t *testing.T) { + t.Parallel() sfConfig := hal.LocationConfig{Latitude: 37.7749, Longitude: -122.4194} // San Francisco nyConfig := hal.LocationConfig{Latitude: 40.7128, Longitude: -74.0060} // New York @@ -41,7 +44,9 @@ func TestSunTimes_Sunrise(t *testing.T) { } func TestSunTimes_Sunset(t *testing.T) { + t.Parallel() t.Run("returns sunset time for San Francisco", func(t *testing.T) { + t.Parallel() config := hal.LocationConfig{ Latitude: 37.7749, Longitude: -122.4194, @@ -59,7 +64,9 @@ func TestSunTimes_Sunset(t *testing.T) { } func TestSunTimes_IsDayTime(t *testing.T) { + t.Parallel() t.Run("correctly determines day/night for known time", func(t *testing.T) { + t.Parallel() config := hal.LocationConfig{ Latitude: 37.7749, Longitude: -122.4194, @@ -73,6 +80,7 @@ func TestSunTimes_IsDayTime(t *testing.T) { }) t.Run("day/night are opposite", func(t *testing.T) { + t.Parallel() config := hal.LocationConfig{ Latitude: 37.7749, Longitude: -122.4194, @@ -88,7 +96,9 @@ func TestSunTimes_IsDayTime(t *testing.T) { } func TestSunTimes_IsNightTime(t *testing.T) { + t.Parallel() t.Run("is opposite of IsDayTime", func(t *testing.T) { + t.Parallel() config := hal.LocationConfig{ Latitude: 37.7749, Longitude: -122.4194, @@ -104,7 +114,9 @@ func TestSunTimes_IsNightTime(t *testing.T) { } func TestSunTimes_EdgeCases(t *testing.T) { + t.Parallel() t.Run("handles extreme northern latitude", func(t *testing.T) { + t.Parallel() // Test with extreme latitude (northern Norway) config := hal.LocationConfig{ Latitude: 78.2156, @@ -130,6 +142,7 @@ func TestSunTimes_EdgeCases(t *testing.T) { }) t.Run("handles zero coordinates", func(t *testing.T) { + t.Parallel() config := hal.LocationConfig{ Latitude: 0, Longitude: 0, diff --git a/util_internal_test.go b/util_internal_test.go index 09575da..5cb4a14 100644 --- a/util_internal_test.go +++ b/util_internal_test.go @@ -75,7 +75,10 @@ func TestGetStringOrStringSlice(t *testing.T) { } func TestGetShortFunctionName(t *testing.T) { + t.Parallel() + t.Run("extracts function name from function reference", func(t *testing.T) { + t.Parallel() testFunc := func() {} name := getShortFunctionName(testFunc) @@ -86,11 +89,13 @@ func TestGetShortFunctionName(t *testing.T) { }) t.Run("handles named function", func(t *testing.T) { + t.Parallel() name := getShortFunctionName(getShortFunctionName) assert.Equal(t, name, "getShortFunctionName") }) t.Run("handles method", func(t *testing.T) { + t.Parallel() light := NewLight("test") name := getShortFunctionName(light.TurnOn) // Method names may have "-fm" suffix in Go runtime @@ -99,12 +104,16 @@ func TestGetShortFunctionName(t *testing.T) { } func TestGetStringOrStringSliceAdditional(t *testing.T) { + t.Parallel() + t.Run("handles boolean input", func(t *testing.T) { + t.Parallel() result := getStringOrStringSlice(true) assert.DeepEqual(t, result, []string{}) }) t.Run("handles struct input", func(t *testing.T) { + t.Parallel() type testStruct struct { field string } From 409680c773ccad99964f6d0b0c83167126123420 Mon Sep 17 00:00:00 2001 From: Daniel Simmons Date: Tue, 22 Jul 2025 14:18:11 +0200 Subject: [PATCH 4/4] formatting --- sun_internal_test.go | 2 +- sun_test.go | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/sun_internal_test.go b/sun_internal_test.go index 0af71f4..d951f0a 100644 --- a/sun_internal_test.go +++ b/sun_internal_test.go @@ -18,4 +18,4 @@ func TestNewSunTimes(t *testing.T) { assert.Assert(t, sunTimes != nil) assert.Equal(t, sunTimes.location.Latitude, 37.7749) assert.Equal(t, sunTimes.location.Longitude, -122.4194) -} \ No newline at end of file +} diff --git a/sun_test.go b/sun_test.go index e49fb49..b426241 100644 --- a/sun_test.go +++ b/sun_test.go @@ -6,8 +6,6 @@ import ( "github.com/dansimau/hal" "gotest.tools/v3/assert" ) - - func TestSunTimes_Sunrise(t *testing.T) { t.Parallel() t.Run("returns sunrise time for San Francisco", func(t *testing.T) {