Files
databasus/backend/internal/features/intervals/model_test.go
2026-03-06 08:10:29 +03:00

986 lines
29 KiB
Go

package intervals
import (
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func TestInterval_ShouldTriggerBackup_Hourly(t *testing.T) {
interval := &Interval{
ID: uuid.New(),
Interval: IntervalHourly,
}
baseTime := time.Date(2024, 1, 15, 12, 0, 0, 0, time.UTC)
t.Run("No previous backup: Trigger backup immediately", func(t *testing.T) {
should := interval.ShouldTriggerBackup(baseTime, nil)
assert.True(t, should)
})
t.Run("Last backup 59 minutes ago: Do not trigger backup", func(t *testing.T) {
lastBackup := baseTime.Add(-59 * time.Minute)
should := interval.ShouldTriggerBackup(baseTime, &lastBackup)
assert.False(t, should)
})
t.Run("Last backup exactly 1 hour ago: Trigger backup", func(t *testing.T) {
lastBackup := baseTime.Add(-1 * time.Hour)
should := interval.ShouldTriggerBackup(baseTime, &lastBackup)
assert.True(t, should)
})
t.Run("Last backup 2 hours ago: Trigger backup", func(t *testing.T) {
lastBackup := baseTime.Add(-2 * time.Hour)
should := interval.ShouldTriggerBackup(baseTime, &lastBackup)
assert.True(t, should)
})
}
func TestInterval_ShouldTriggerBackup_Daily(t *testing.T) {
timeOfDay := "09:00"
interval := &Interval{
ID: uuid.New(),
Interval: IntervalDaily,
TimeOfDay: &timeOfDay,
}
// Base time: January 15, 2024
baseDate := time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC)
t.Run("No previous backup: Trigger backup immediately", func(t *testing.T) {
now := baseDate.Add(10 * time.Hour) // 10:00 AM
should := interval.ShouldTriggerBackup(now, nil)
assert.True(t, should)
})
t.Run("Today 08:59, no backup today: Do not trigger backup", func(t *testing.T) {
now := time.Date(2024, 1, 15, 8, 59, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 14, 9, 0, 0, 0, time.UTC) // Yesterday
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.False(t, should)
})
t.Run("Today exactly 09:00, no backup today: Trigger backup", func(t *testing.T) {
now := time.Date(2024, 1, 15, 9, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 14, 9, 0, 0, 0, time.UTC) // Yesterday
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should)
})
t.Run("Today 09:01, no backup today: Trigger backup", func(t *testing.T) {
now := time.Date(2024, 1, 15, 9, 1, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 14, 9, 0, 0, 0, time.UTC) // Yesterday
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should)
})
t.Run("Backup earlier today at 09:00: Do not trigger another backup", func(t *testing.T) {
now := time.Date(2024, 1, 15, 15, 0, 0, 0, time.UTC) // 3 PM
lastBackup := time.Date(2024, 1, 15, 9, 0, 0, 0, time.UTC) // Today at 9 AM
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.False(t, should)
})
t.Run(
"Backup yesterday at correct time: Trigger backup today at or after 09:00",
func(t *testing.T) {
now := time.Date(2024, 1, 15, 9, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 14, 9, 0, 0, 0, time.UTC) // Yesterday at 9 AM
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should)
},
)
t.Run(
"Backup yesterday at 15:00: Trigger backup today at 09:00",
func(t *testing.T) {
now := time.Date(2024, 1, 15, 9, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 14, 15, 0, 0, 0, time.UTC) // Yesterday at 15:00
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should)
},
)
t.Run(
"Manual backup before scheduled time should not prevent scheduled backup",
func(t *testing.T) {
timeOfDay := "21:00"
interval := &Interval{
ID: uuid.New(),
Interval: IntervalDaily,
TimeOfDay: &timeOfDay,
}
manual := time.Date(2025, 6, 6, 16, 17, 0, 0, time.UTC) // manual earlier
scheduled := time.Date(2025, 6, 6, 21, 0, 0, 0, time.UTC) // scheduled time
should := interval.ShouldTriggerBackup(scheduled, &manual)
assert.True(t, should, "scheduled run should trigger even after earlier manual backup")
},
)
t.Run("Catch up previous time slot", func(t *testing.T) {
timeOfDay := "21:00"
interval := &Interval{
ID: uuid.New(),
Interval: IntervalDaily,
TimeOfDay: &timeOfDay,
}
// It's June-07 15:00 UTC, yesterday's scheduled backup was missed
now := time.Date(2025, 6, 7, 15, 0, 0, 0, time.UTC)
lastBackup := time.Date(2025, 6, 6, 16, 0, 0, 0, time.UTC) // before yesterday's 21:00
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should, "should catch up missed 21:00 backup the next day at 15:00")
})
}
func TestInterval_ShouldTriggerBackup_Weekly(t *testing.T) {
timeOfDay := "15:00"
weekday := 3 // Wednesday (0=Sunday, 1=Monday, ..., 3=Wednesday)
interval := &Interval{
ID: uuid.New(),
Interval: IntervalWeekly,
TimeOfDay: &timeOfDay,
Weekday: &weekday,
}
// Base time: Wednesday, January 17, 2024 (to ensure we're on Wednesday)
wednesday := time.Date(2024, 1, 17, 0, 0, 0, 0, time.UTC)
t.Run("No previous backup: Trigger backup immediately", func(t *testing.T) {
now := wednesday.Add(16 * time.Hour) // 4 PM Wednesday
should := interval.ShouldTriggerBackup(now, nil)
assert.True(t, should)
})
t.Run(
"Today Wednesday at 14:59, no backup this week: Do not trigger backup",
func(t *testing.T) {
now := time.Date(2024, 1, 17, 14, 59, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 10, 15, 0, 0, 0, time.UTC) // Previous week
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.False(t, should)
},
)
t.Run(
"Today Wednesday at exactly 15:00, no backup this week: Trigger backup",
func(t *testing.T) {
now := time.Date(2024, 1, 17, 15, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 10, 15, 0, 0, 0, time.UTC) // Previous week
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should)
},
)
t.Run("Today Wednesday at 15:01, no backup this week: Trigger backup", func(t *testing.T) {
now := time.Date(2024, 1, 17, 15, 1, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 10, 15, 0, 0, 0, time.UTC) // Previous week
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should)
})
t.Run(
"Backup already done at scheduled time (Wednesday 15:00): Do not trigger again",
func(t *testing.T) {
now := time.Date(2024, 1, 18, 10, 0, 0, 0, time.UTC) // Thursday
// Wednesday this week at scheduled time
lastBackup := time.Date(
2024,
1,
17,
15,
0,
0,
0,
time.UTC,
)
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.False(t, should)
},
)
t.Run(
"Manual backup before scheduled time should not prevent scheduled backup",
func(t *testing.T) {
// Wednesday at scheduled time
now := time.Date(
2024,
1,
17,
15,
0,
0,
0,
time.UTC,
)
// Manual backup same day, before scheduled time
lastBackup := time.Date(
2024,
1,
17,
10,
0,
0,
0,
time.UTC,
)
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should)
},
)
t.Run(
"Manual backup after scheduled time should prevent another backup",
func(t *testing.T) {
now := time.Date(2024, 1, 18, 10, 0, 0, 0, time.UTC) // Thursday
// Manual backup after scheduled time
lastBackup := time.Date(
2024,
1,
17,
16,
0,
0,
0,
time.UTC,
)
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.False(t, should)
},
)
t.Run(
"Backup missed completely: Trigger backup immediately after scheduled time",
func(t *testing.T) {
// Thursday after missed Wednesday
now := time.Date(
2024,
1,
18,
10,
0,
0,
0,
time.UTC,
)
lastBackup := time.Date(2024, 1, 10, 15, 0, 0, 0, time.UTC) // Previous week
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should)
},
)
t.Run(
"Backup last week: Trigger backup at this week's scheduled time",
func(t *testing.T) {
// Wednesday at scheduled time
now := time.Date(
2024,
1,
17,
15,
0,
0,
0,
time.UTC,
)
lastBackup := time.Date(2024, 1, 10, 15, 0, 0, 0, time.UTC) // Previous week
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should)
},
)
t.Run(
"User's scenario: Weekly Friday 00:00 backup should trigger even after Wednesday manual backup",
func(t *testing.T) {
timeOfDay := "00:00"
weekday := 5 // Friday (0=Sunday, 1=Monday, ..., 5=Friday)
fridayInterval := &Interval{
ID: uuid.New(),
Interval: IntervalWeekly,
TimeOfDay: &timeOfDay,
Weekday: &weekday,
}
// Friday at 00:00 - scheduled backup time
friday := time.Date(2024, 1, 19, 0, 0, 0, 0, time.UTC) // Friday Jan 19, 2024
// Manual backup was done on Wednesday
wednesdayBackup := time.Date(
2024,
1,
17,
21,
0,
0,
0,
time.UTC,
) // Wednesday Jan 17, 2024 at 21:00
should := fridayInterval.ShouldTriggerBackup(friday, &wednesdayBackup)
assert.True(
t,
should,
"Friday scheduled backup should trigger despite Wednesday manual backup",
)
},
)
}
func TestInterval_ShouldTriggerBackup_Monthly(t *testing.T) {
timeOfDay := "08:00"
dayOfMonth := 10
interval := &Interval{
ID: uuid.New(),
Interval: IntervalMonthly,
TimeOfDay: &timeOfDay,
DayOfMonth: &dayOfMonth,
}
t.Run("No previous backup: Trigger backup immediately", func(t *testing.T) {
now := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC)
should := interval.ShouldTriggerBackup(now, nil)
assert.True(t, should)
})
t.Run(
"Today is the 10th at 07:59, no backup this month: Do not trigger backup",
func(t *testing.T) {
now := time.Date(2024, 1, 10, 7, 59, 0, 0, time.UTC)
lastBackup := time.Date(2023, 12, 10, 8, 0, 0, 0, time.UTC) // Previous month
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.False(t, should)
},
)
t.Run(
"Today is the 10th exactly 08:00, no backup this month: Trigger backup",
func(t *testing.T) {
now := time.Date(2024, 1, 10, 8, 0, 0, 0, time.UTC)
lastBackup := time.Date(2023, 12, 10, 8, 0, 0, 0, time.UTC) // Previous month
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should)
},
)
t.Run(
"Today is the 10th after 08:00, no backup this month: Trigger backup",
func(t *testing.T) {
now := time.Date(2024, 1, 10, 8, 1, 0, 0, time.UTC)
lastBackup := time.Date(2023, 12, 10, 8, 0, 0, 0, time.UTC) // Previous month
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should)
},
)
t.Run(
"Today is the 11th, backup missed on the 10th: Trigger backup immediately",
func(t *testing.T) {
now := time.Date(2024, 1, 11, 10, 0, 0, 0, time.UTC)
lastBackup := time.Date(2023, 12, 10, 8, 0, 0, 0, time.UTC) // Previous month
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should)
},
)
t.Run("Backup already performed at scheduled time: Do not trigger again", func(t *testing.T) {
now := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 10, 8, 0, 0, 0, time.UTC) // This month at scheduled time
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.False(t, should)
})
t.Run(
"Manual backup before scheduled time should not prevent scheduled backup",
func(t *testing.T) {
now := time.Date(2024, 1, 10, 8, 0, 0, 0, time.UTC) // Scheduled time
// Manual backup earlier this month
lastBackup := time.Date(
2024,
1,
5,
10,
0,
0,
0,
time.UTC,
)
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should)
},
)
t.Run(
"Manual backup after scheduled time should prevent another backup",
func(t *testing.T) {
now := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC)
// Manual backup after scheduled time
lastBackup := time.Date(
2024,
1,
10,
9,
0,
0,
0,
time.UTC,
)
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.False(t, should)
},
)
t.Run(
"Backup performed last month on schedule: Trigger backup this month at scheduled time",
func(t *testing.T) {
now := time.Date(2024, 1, 10, 8, 0, 0, 0, time.UTC)
// Previous month at scheduled time
lastBackup := time.Date(
2023,
12,
10,
8,
0,
0,
0,
time.UTC,
)
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should)
},
)
}
func TestInterval_ShouldTriggerBackup_Cron(t *testing.T) {
cronExpr := "0 2 * * *" // Daily at 2:00 AM
interval := &Interval{
ID: uuid.New(),
Interval: IntervalCron,
CronExpression: &cronExpr,
}
t.Run("No previous backup: Trigger backup immediately", func(t *testing.T) {
now := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC)
should := interval.ShouldTriggerBackup(now, nil)
assert.True(t, should)
})
t.Run("Before scheduled cron time: Do not trigger backup", func(t *testing.T) {
now := time.Date(2024, 1, 15, 1, 59, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 14, 2, 0, 0, 0, time.UTC) // Yesterday at 2 AM
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.False(t, should)
})
t.Run("Exactly at scheduled cron time: Trigger backup", func(t *testing.T) {
now := time.Date(2024, 1, 15, 2, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 14, 2, 0, 0, 0, time.UTC) // Yesterday at 2 AM
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should)
})
t.Run("After scheduled cron time: Trigger backup", func(t *testing.T) {
now := time.Date(2024, 1, 15, 3, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 14, 2, 0, 0, 0, time.UTC) // Yesterday at 2 AM
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should)
})
t.Run("Backup already done after scheduled time: Do not trigger again", func(t *testing.T) {
now := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 15, 2, 5, 0, 0, time.UTC) // Today at 2:05 AM
should := interval.ShouldTriggerBackup(now, &lastBackup)
assert.False(t, should)
})
t.Run("Weekly cron expression: 0 3 * * 1 (Monday at 3 AM)", func(t *testing.T) {
weeklyCron := "0 3 * * 1" // Every Monday at 3 AM
weeklyInterval := &Interval{
ID: uuid.New(),
Interval: IntervalCron,
CronExpression: &weeklyCron,
}
// Monday Jan 15, 2024 at 3:00 AM
monday := time.Date(2024, 1, 15, 3, 0, 0, 0, time.UTC)
// Last backup was previous Monday
lastBackup := time.Date(2024, 1, 8, 3, 0, 0, 0, time.UTC)
should := weeklyInterval.ShouldTriggerBackup(monday, &lastBackup)
assert.True(t, should)
})
t.Run("Complex cron expression: 30 4 1,15 * * (1st and 15th at 4:30 AM)", func(t *testing.T) {
complexCron := "30 4 1,15 * *" // 1st and 15th of each month at 4:30 AM
complexInterval := &Interval{
ID: uuid.New(),
Interval: IntervalCron,
CronExpression: &complexCron,
}
// Jan 15, 2024 at 4:30 AM
now := time.Date(2024, 1, 15, 4, 30, 0, 0, time.UTC)
// Last backup was Jan 1
lastBackup := time.Date(2024, 1, 1, 4, 30, 0, 0, time.UTC)
should := complexInterval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should)
})
t.Run("Every 6 hours cron expression: 0 */6 * * *", func(t *testing.T) {
sixHourlyCron := "0 */6 * * *" // Every 6 hours (0:00, 6:00, 12:00, 18:00)
sixHourlyInterval := &Interval{
ID: uuid.New(),
Interval: IntervalCron,
CronExpression: &sixHourlyCron,
}
// 12:00 - next trigger after 6:00
now := time.Date(2024, 1, 15, 12, 0, 0, 0, time.UTC)
// Last backup was at 6:00
lastBackup := time.Date(2024, 1, 15, 6, 0, 0, 0, time.UTC)
should := sixHourlyInterval.ShouldTriggerBackup(now, &lastBackup)
assert.True(t, should)
})
t.Run("Invalid cron expression returns false", func(t *testing.T) {
invalidCron := "invalid cron"
invalidInterval := &Interval{
ID: uuid.New(),
Interval: IntervalCron,
CronExpression: &invalidCron,
}
now := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 14, 10, 0, 0, 0, time.UTC)
should := invalidInterval.ShouldTriggerBackup(now, &lastBackup)
assert.False(t, should)
})
t.Run("Empty cron expression returns false", func(t *testing.T) {
emptyCron := ""
emptyInterval := &Interval{
ID: uuid.New(),
Interval: IntervalCron,
CronExpression: &emptyCron,
}
now := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 14, 10, 0, 0, 0, time.UTC)
should := emptyInterval.ShouldTriggerBackup(now, &lastBackup)
assert.False(t, should)
})
t.Run("Nil cron expression returns false", func(t *testing.T) {
nilInterval := &Interval{
ID: uuid.New(),
Interval: IntervalCron,
CronExpression: nil,
}
now := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 14, 10, 0, 0, 0, time.UTC)
should := nilInterval.ShouldTriggerBackup(now, &lastBackup)
assert.False(t, should)
})
}
func TestInterval_Validate(t *testing.T) {
t.Run("Daily interval requires time of day", func(t *testing.T) {
interval := &Interval{
ID: uuid.New(),
Interval: IntervalDaily,
}
err := interval.Validate()
assert.Error(t, err)
assert.Contains(t, err.Error(), "time of day is required")
})
t.Run("Weekly interval requires weekday", func(t *testing.T) {
timeOfDay := "09:00"
interval := &Interval{
ID: uuid.New(),
Interval: IntervalWeekly,
TimeOfDay: &timeOfDay,
}
err := interval.Validate()
assert.Error(t, err)
assert.Contains(t, err.Error(), "weekday is required")
})
t.Run("Monthly interval requires day of month", func(t *testing.T) {
timeOfDay := "09:00"
interval := &Interval{
ID: uuid.New(),
Interval: IntervalMonthly,
TimeOfDay: &timeOfDay,
}
err := interval.Validate()
assert.Error(t, err)
assert.Contains(t, err.Error(), "day of month is required")
})
t.Run("Hourly interval is valid without additional fields", func(t *testing.T) {
interval := &Interval{
ID: uuid.New(),
Interval: IntervalHourly,
}
err := interval.Validate()
assert.NoError(t, err)
})
t.Run("Valid weekly interval", func(t *testing.T) {
timeOfDay := "09:00"
weekday := 1
interval := &Interval{
ID: uuid.New(),
Interval: IntervalWeekly,
TimeOfDay: &timeOfDay,
Weekday: &weekday,
}
err := interval.Validate()
assert.NoError(t, err)
})
t.Run("Valid monthly interval", func(t *testing.T) {
timeOfDay := "09:00"
dayOfMonth := 15
interval := &Interval{
ID: uuid.New(),
Interval: IntervalMonthly,
TimeOfDay: &timeOfDay,
DayOfMonth: &dayOfMonth,
}
err := interval.Validate()
assert.NoError(t, err)
})
t.Run("Cron interval requires cron expression", func(t *testing.T) {
interval := &Interval{
ID: uuid.New(),
Interval: IntervalCron,
}
err := interval.Validate()
assert.Error(t, err)
assert.Contains(t, err.Error(), "cron expression is required")
})
t.Run("Cron interval with empty expression is invalid", func(t *testing.T) {
emptyCron := ""
interval := &Interval{
ID: uuid.New(),
Interval: IntervalCron,
CronExpression: &emptyCron,
}
err := interval.Validate()
assert.Error(t, err)
assert.Contains(t, err.Error(), "cron expression is required")
})
t.Run("Cron interval with invalid expression is invalid", func(t *testing.T) {
invalidCron := "invalid cron"
interval := &Interval{
ID: uuid.New(),
Interval: IntervalCron,
CronExpression: &invalidCron,
}
err := interval.Validate()
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid cron expression")
})
t.Run("Valid cron interval with daily expression", func(t *testing.T) {
cronExpr := "0 2 * * *" // Daily at 2 AM
interval := &Interval{
ID: uuid.New(),
Interval: IntervalCron,
CronExpression: &cronExpr,
}
err := interval.Validate()
assert.NoError(t, err)
})
t.Run("Valid cron interval with complex expression", func(t *testing.T) {
cronExpr := "30 4 1,15 * *" // 1st and 15th of each month at 4:30 AM
interval := &Interval{
ID: uuid.New(),
Interval: IntervalCron,
CronExpression: &cronExpr,
}
err := interval.Validate()
assert.NoError(t, err)
})
}
func TestInterval_NextTriggerTime_NilLastBackup(t *testing.T) {
now := time.Date(2024, 1, 15, 12, 0, 0, 0, time.UTC)
t.Run("Hourly with nil lastBackup returns nil", func(t *testing.T) {
interval := &Interval{ID: uuid.New(), Interval: IntervalHourly}
result := interval.NextTriggerTime(now, nil)
assert.Nil(t, result)
})
t.Run("Daily with nil lastBackup returns nil", func(t *testing.T) {
timeOfDay := "09:00"
interval := &Interval{ID: uuid.New(), Interval: IntervalDaily, TimeOfDay: &timeOfDay}
result := interval.NextTriggerTime(now, nil)
assert.Nil(t, result)
})
t.Run("Weekly with nil lastBackup returns nil", func(t *testing.T) {
timeOfDay := "15:00"
weekday := 3
interval := &Interval{
ID: uuid.New(),
Interval: IntervalWeekly,
TimeOfDay: &timeOfDay,
Weekday: &weekday,
}
result := interval.NextTriggerTime(now, nil)
assert.Nil(t, result)
})
t.Run("Monthly with nil lastBackup returns nil", func(t *testing.T) {
timeOfDay := "08:00"
dayOfMonth := 10
interval := &Interval{
ID: uuid.New(),
Interval: IntervalMonthly,
TimeOfDay: &timeOfDay,
DayOfMonth: &dayOfMonth,
}
result := interval.NextTriggerTime(now, nil)
assert.Nil(t, result)
})
t.Run("Cron with nil lastBackup returns nil", func(t *testing.T) {
cronExpr := "0 2 * * *"
interval := &Interval{ID: uuid.New(), Interval: IntervalCron, CronExpression: &cronExpr}
result := interval.NextTriggerTime(now, nil)
assert.Nil(t, result)
})
}
func TestInterval_NextTriggerTime_Hourly(t *testing.T) {
interval := &Interval{ID: uuid.New(), Interval: IntervalHourly}
now := time.Date(2024, 1, 15, 12, 0, 0, 0, time.UTC)
t.Run("Returns lastBackup + 1 hour", func(t *testing.T) {
lastBackup := time.Date(2024, 1, 15, 11, 0, 0, 0, time.UTC)
result := interval.NextTriggerTime(now, &lastBackup)
assert.NotNil(t, result)
assert.Equal(t, time.Date(2024, 1, 15, 12, 0, 0, 0, time.UTC), *result)
})
t.Run("Returns future time when last backup was recent", func(t *testing.T) {
lastBackup := time.Date(2024, 1, 15, 11, 30, 0, 0, time.UTC)
result := interval.NextTriggerTime(now, &lastBackup)
assert.NotNil(t, result)
assert.Equal(t, time.Date(2024, 1, 15, 12, 30, 0, 0, time.UTC), *result)
})
}
func TestInterval_NextTriggerTime_Daily(t *testing.T) {
timeOfDay := "09:00"
interval := &Interval{ID: uuid.New(), Interval: IntervalDaily, TimeOfDay: &timeOfDay}
t.Run("Before today's slot: returns today's slot", func(t *testing.T) {
now := time.Date(2024, 1, 15, 8, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 14, 9, 0, 0, 0, time.UTC)
result := interval.NextTriggerTime(now, &lastBackup)
assert.NotNil(t, result)
assert.Equal(t, time.Date(2024, 1, 15, 9, 0, 0, 0, time.UTC), *result)
})
t.Run("After today's slot: returns tomorrow's slot", func(t *testing.T) {
now := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 15, 9, 0, 0, 0, time.UTC)
result := interval.NextTriggerTime(now, &lastBackup)
assert.NotNil(t, result)
assert.Equal(t, time.Date(2024, 1, 16, 9, 0, 0, 0, time.UTC), *result)
})
t.Run("Exactly at today's slot: returns tomorrow's slot", func(t *testing.T) {
now := time.Date(2024, 1, 15, 9, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 14, 9, 0, 0, 0, time.UTC)
result := interval.NextTriggerTime(now, &lastBackup)
assert.NotNil(t, result)
assert.Equal(t, time.Date(2024, 1, 16, 9, 0, 0, 0, time.UTC), *result)
})
}
func TestInterval_NextTriggerTime_Weekly(t *testing.T) {
timeOfDay := "15:00"
weekday := 3 // Wednesday
interval := &Interval{
ID: uuid.New(),
Interval: IntervalWeekly,
TimeOfDay: &timeOfDay,
Weekday: &weekday,
}
t.Run("Before this week's target: returns this week's target", func(t *testing.T) {
// Tuesday Jan 16, 2024
now := time.Date(2024, 1, 16, 10, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 10, 15, 0, 0, 0, time.UTC)
result := interval.NextTriggerTime(now, &lastBackup)
assert.NotNil(t, result)
// Wednesday Jan 17 at 15:00
assert.Equal(t, time.Date(2024, 1, 17, 15, 0, 0, 0, time.UTC), *result)
})
t.Run("After this week's target: returns next week's target", func(t *testing.T) {
// Thursday Jan 18, 2024
now := time.Date(2024, 1, 18, 10, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 17, 15, 0, 0, 0, time.UTC)
result := interval.NextTriggerTime(now, &lastBackup)
assert.NotNil(t, result)
// Next Wednesday Jan 24 at 15:00
assert.Equal(t, time.Date(2024, 1, 24, 15, 0, 0, 0, time.UTC), *result)
})
t.Run("Friday interval: returns correct target", func(t *testing.T) {
fridayTimeOfDay := "00:00"
fridayWeekday := 5 // Friday
fridayInterval := &Interval{
ID: uuid.New(),
Interval: IntervalWeekly,
TimeOfDay: &fridayTimeOfDay,
Weekday: &fridayWeekday,
}
// Wednesday Jan 17, 2024
now := time.Date(2024, 1, 17, 10, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 12, 0, 0, 0, 0, time.UTC)
result := fridayInterval.NextTriggerTime(now, &lastBackup)
assert.NotNil(t, result)
// Friday Jan 19 at 00:00
assert.Equal(t, time.Date(2024, 1, 19, 0, 0, 0, 0, time.UTC), *result)
})
}
func TestInterval_NextTriggerTime_Monthly(t *testing.T) {
timeOfDay := "08:00"
dayOfMonth := 10
interval := &Interval{
ID: uuid.New(),
Interval: IntervalMonthly,
TimeOfDay: &timeOfDay,
DayOfMonth: &dayOfMonth,
}
t.Run("Before this month's target: returns this month's target", func(t *testing.T) {
now := time.Date(2024, 1, 5, 10, 0, 0, 0, time.UTC)
lastBackup := time.Date(2023, 12, 10, 8, 0, 0, 0, time.UTC)
result := interval.NextTriggerTime(now, &lastBackup)
assert.NotNil(t, result)
assert.Equal(t, time.Date(2024, 1, 10, 8, 0, 0, 0, time.UTC), *result)
})
t.Run("After this month's target: returns next month's target", func(t *testing.T) {
now := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 10, 8, 0, 0, 0, time.UTC)
result := interval.NextTriggerTime(now, &lastBackup)
assert.NotNil(t, result)
assert.Equal(t, time.Date(2024, 2, 10, 8, 0, 0, 0, time.UTC), *result)
})
t.Run("Exactly at this month's target: returns next month's target", func(t *testing.T) {
now := time.Date(2024, 1, 10, 8, 0, 0, 0, time.UTC)
lastBackup := time.Date(2023, 12, 10, 8, 0, 0, 0, time.UTC)
result := interval.NextTriggerTime(now, &lastBackup)
assert.NotNil(t, result)
assert.Equal(t, time.Date(2024, 2, 10, 8, 0, 0, 0, time.UTC), *result)
})
}
func TestInterval_NextTriggerTime_Cron(t *testing.T) {
t.Run("Daily cron: returns next trigger after lastBackup", func(t *testing.T) {
cronExpr := "0 2 * * *" // Daily at 2:00 AM
interval := &Interval{ID: uuid.New(), Interval: IntervalCron, CronExpression: &cronExpr}
now := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 14, 2, 0, 0, 0, time.UTC)
result := interval.NextTriggerTime(now, &lastBackup)
assert.NotNil(t, result)
assert.Equal(t, time.Date(2024, 1, 15, 2, 0, 0, 0, time.UTC), *result)
})
t.Run("Complex cron: 1st and 15th at 4:30", func(t *testing.T) {
cronExpr := "30 4 1,15 * *"
interval := &Interval{ID: uuid.New(), Interval: IntervalCron, CronExpression: &cronExpr}
now := time.Date(2024, 1, 10, 10, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 1, 4, 30, 0, 0, time.UTC)
result := interval.NextTriggerTime(now, &lastBackup)
assert.NotNil(t, result)
assert.Equal(t, time.Date(2024, 1, 15, 4, 30, 0, 0, time.UTC), *result)
})
t.Run("Invalid cron expression returns nil", func(t *testing.T) {
invalidCron := "invalid cron"
interval := &Interval{ID: uuid.New(), Interval: IntervalCron, CronExpression: &invalidCron}
now := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 14, 10, 0, 0, 0, time.UTC)
result := interval.NextTriggerTime(now, &lastBackup)
assert.Nil(t, result)
})
t.Run("Empty cron expression returns nil", func(t *testing.T) {
emptyCron := ""
interval := &Interval{ID: uuid.New(), Interval: IntervalCron, CronExpression: &emptyCron}
now := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 14, 10, 0, 0, 0, time.UTC)
result := interval.NextTriggerTime(now, &lastBackup)
assert.Nil(t, result)
})
t.Run("Nil cron expression returns nil", func(t *testing.T) {
interval := &Interval{ID: uuid.New(), Interval: IntervalCron, CronExpression: nil}
now := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 14, 10, 0, 0, 0, time.UTC)
result := interval.NextTriggerTime(now, &lastBackup)
assert.Nil(t, result)
})
}
func TestInterval_NextTriggerTime_UnknownInterval(t *testing.T) {
interval := &Interval{ID: uuid.New(), Interval: IntervalType("UNKNOWN")}
now := time.Date(2024, 1, 15, 12, 0, 0, 0, time.UTC)
lastBackup := time.Date(2024, 1, 14, 12, 0, 0, 0, time.UTC)
result := interval.NextTriggerTime(now, &lastBackup)
assert.Nil(t, result)
}