mirror of
https://github.com/databasus/databasus.git
synced 2026-04-06 00:32:03 +02:00
986 lines
29 KiB
Go
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)
|
|
}
|