Files
go-ha/internal/scheduling/daily_test.go

301 lines
7.1 KiB
Go

package scheduling
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFixedTimeTrigger_NextTime(t *testing.T) {
tests := []struct {
name string
hour int
minute int
now time.Time
expected time.Time
}{
{
name: "same day trigger",
hour: 14,
minute: 30,
now: time.Date(2025, 8, 2, 10, 0, 0, 0, time.Local),
expected: time.Date(2025, 8, 2, 14, 30, 0, 0, time.Local),
},
{
name: "next day trigger",
hour: 8,
minute: 0,
now: time.Date(2025, 8, 2, 10, 0, 0, 0, time.Local),
expected: time.Date(2025, 8, 3, 8, 0, 0, 0, time.Local),
},
{
name: "exact time",
hour: 10,
minute: 0,
now: time.Date(2025, 8, 2, 10, 0, 0, 0, time.Local),
expected: time.Date(2025, 8, 3, 10, 0, 0, 0, time.Local),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
trigger := &FixedTimeTrigger{
Hour: tt.hour,
Minute: tt.minute,
}
result := trigger.NextTime(tt.now)
require.NotNil(t, result)
assert.Equal(t, tt.expected, *result)
})
}
}
func TestFixedTimeTrigger_Hash(t *testing.T) {
tests := []struct {
name string
hour int
minute int
expected uint64
}{
{
name: "basic time",
hour: 12,
minute: 30,
expected: 0, // We'll check it's not zero
},
{
name: "midnight",
hour: 0,
minute: 0,
expected: 0, // We'll check it's not zero
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
trigger := &FixedTimeTrigger{
Hour: tt.hour,
Minute: tt.minute,
}
hash := trigger.Hash()
assert.NotZero(t, hash)
assert.IsType(t, uint64(0), hash)
})
}
// Test that different times produce different hashes
trigger1 := &FixedTimeTrigger{Hour: 12, Minute: 30}
trigger2 := &FixedTimeTrigger{Hour: 12, Minute: 31}
trigger3 := &FixedTimeTrigger{Hour: 13, Minute: 30}
hash1 := trigger1.Hash()
hash2 := trigger2.Hash()
hash3 := trigger3.Hash()
assert.NotEqual(t, hash1, hash2)
assert.NotEqual(t, hash1, hash3)
assert.NotEqual(t, hash2, hash3)
// Test that same times produce same hashes
trigger4 := &FixedTimeTrigger{Hour: 12, Minute: 30}
hash4 := trigger4.Hash()
assert.Equal(t, hash1, hash4)
}
func TestSunTrigger_NextTime(t *testing.T) {
// Test with a known location (New York City)
lat, lon := 40.7128, -74.0060
tests := []struct {
name string
sunset bool
offset *time.Duration
now time.Time
expected bool // whether we expect a result
}{
{
name: "sunrise without offset",
sunset: false,
offset: nil,
now: time.Date(2025, 8, 2, 10, 0, 0, 0, time.Local),
expected: true,
},
{
name: "sunset without offset",
sunset: true,
offset: nil,
now: time.Date(2025, 8, 2, 10, 0, 0, 0, time.Local),
expected: true,
},
{
name: "sunrise with positive offset",
sunset: false,
offset: func() *time.Duration { d := 30 * time.Minute; return &d }(),
now: time.Date(2025, 8, 2, 10, 0, 0, 0, time.Local),
expected: true,
},
{
name: "sunset with negative offset",
sunset: true,
offset: func() *time.Duration { d := -1 * time.Hour; return &d }(),
now: time.Date(2025, 8, 2, 10, 0, 0, 0, time.Local),
expected: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
trigger := &SunTrigger{
latitude: lat,
longitude: lon,
sunset: tt.sunset,
offset: tt.offset,
}
result := trigger.NextTime(tt.now)
if tt.expected {
require.NotNil(t, result)
assert.False(t, result.IsZero())
} else {
// For polar regions or extreme dates, sun might not rise/set
// This is acceptable behavior
}
})
}
}
func TestSunTrigger_Hash(t *testing.T) {
lat1, lon1 := 40.7128, -74.0060
lat2, lon2 := 51.5074, -0.1278
tests := []struct {
name string
lat float64
lon float64
sunset bool
offset *time.Duration
}{
{
name: "sunrise without offset",
lat: lat1,
lon: lon1,
sunset: false,
offset: nil,
},
{
name: "sunset with offset",
lat: lat1,
lon: lon1,
sunset: true,
offset: func() *time.Duration { d := 30 * time.Minute; return &d }(),
},
{
name: "different location",
lat: lat2,
lon: lon2,
sunset: false,
offset: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
trigger := &SunTrigger{
latitude: tt.lat,
longitude: tt.lon,
sunset: tt.sunset,
offset: tt.offset,
}
hash := trigger.Hash()
assert.NotZero(t, hash)
assert.IsType(t, uint64(0), hash)
})
}
// Test that different configurations produce different hashes
trigger1 := &SunTrigger{latitude: lat1, longitude: lon1, sunset: false, offset: nil}
trigger2 := &SunTrigger{latitude: lat1, longitude: lon1, sunset: true, offset: nil}
trigger3 := &SunTrigger{latitude: lat2, longitude: lon2, sunset: false, offset: nil}
hash1 := trigger1.Hash()
hash2 := trigger2.Hash()
hash3 := trigger3.Hash()
assert.NotEqual(t, hash1, hash2)
assert.NotEqual(t, hash1, hash3)
assert.NotEqual(t, hash2, hash3)
// Test that same configurations produce same hashes
trigger4 := &SunTrigger{latitude: lat1, longitude: lon1, sunset: false, offset: nil}
hash4 := trigger4.Hash()
assert.Equal(t, hash1, hash4)
}
func TestCompositeDailySchedule_NextTime(t *testing.T) {
trigger1 := &FixedTimeTrigger{Hour: 8, Minute: 0}
trigger2 := &FixedTimeTrigger{Hour: 12, Minute: 0}
trigger3 := &FixedTimeTrigger{Hour: 18, Minute: 0}
composite := &CompositeDailySchedule{
triggers: []Trigger{trigger1, trigger2, trigger3},
}
now := time.Date(2025, 8, 2, 10, 0, 0, 0, time.Local)
result := composite.NextTime(now)
require.NotNil(t, result)
// Should return the earliest trigger after now (12:00)
expected := time.Date(2025, 8, 2, 12, 0, 0, 0, time.Local)
assert.Equal(t, expected, *result)
}
func TestCompositeDailySchedule_Hash(t *testing.T) {
trigger1 := &FixedTimeTrigger{Hour: 8, Minute: 0}
trigger2 := &FixedTimeTrigger{Hour: 12, Minute: 0}
composite1 := &CompositeDailySchedule{
triggers: []Trigger{trigger1, trigger2},
}
composite2 := &CompositeDailySchedule{
triggers: []Trigger{trigger2, trigger1}, // Different order
}
composite3 := &CompositeDailySchedule{
triggers: []Trigger{trigger1}, // Different number of triggers
}
hash1 := composite1.Hash()
hash2 := composite2.Hash()
hash3 := composite3.Hash()
assert.NotZero(t, hash1)
assert.NotZero(t, hash2)
assert.NotZero(t, hash3)
assert.IsType(t, uint64(0), hash1)
// Different orders should produce different hashes
assert.NotEqual(t, hash1, hash2)
assert.NotEqual(t, hash1, hash3)
assert.NotEqual(t, hash2, hash3)
// Same configuration should produce same hash
composite4 := &CompositeDailySchedule{
triggers: []Trigger{trigger1, trigger2},
}
hash4 := composite4.Hash()
assert.Equal(t, hash1, hash4)
}
func TestTriggerInterface(t *testing.T) {
// Test that all trigger types implement the Trigger interface
var _ Trigger = &FixedTimeTrigger{}
var _ Trigger = &SunTrigger{}
var _ Trigger = &CompositeDailySchedule{}
}