internal/task/scheduler_test.go
Rama main · réplica de lectura del repositorio original.
package task
import (
"testing"
"time"
)
func TestInitializeScheduleForOneTimeRun(t *testing.T) {
scheduler := Scheduler{}
runAt := time.Date(2026, 4, 23, 10, 0, 0, 0, time.UTC)
schedule, errorValue := scheduler.InitializeSchedule(Schedule{
ScheduleID: "schedule-1",
Name: "one-time",
Kind: ScheduleKindOnce,
RunAt: &runAt,
}, time.Date(2026, 4, 23, 9, 0, 0, 0, time.UTC))
if errorValue != nil {
t.Fatalf("expected one-time task schedule to initialize: %v", errorValue)
}
if schedule.NextRunAt == nil {
t.Fatal("expected one-time task schedule to include a next run time")
}
if !schedule.NextRunAt.Equal(runAt) {
t.Fatalf("expected next run time to match scheduled time, got %s", schedule.NextRunAt.Format(time.RFC3339))
}
}
func TestInitializeScheduleForIntervalRun(t *testing.T) {
scheduler := Scheduler{}
runAt := time.Date(2026, 4, 23, 9, 0, 0, 0, time.UTC)
schedule, errorValue := scheduler.InitializeSchedule(Schedule{
ScheduleID: "schedule-2",
Name: "interval",
Kind: ScheduleKindInterval,
RunAt: &runAt,
IntervalSecond: 900,
}, time.Date(2026, 4, 23, 9, 7, 0, 0, time.UTC))
if errorValue != nil {
t.Fatalf("expected interval task schedule to initialize: %v", errorValue)
}
if schedule.NextRunAt == nil {
t.Fatal("expected interval task schedule to include a next run time")
}
expectedNextRunAt := time.Date(2026, 4, 23, 9, 15, 0, 0, time.UTC)
if !schedule.NextRunAt.Equal(expectedNextRunAt) {
t.Fatalf("expected interval next run time to be %s, got %s", expectedNextRunAt.Format(time.RFC3339), schedule.NextRunAt.Format(time.RFC3339))
}
}
func TestInitializeScheduleForIntervalRunWithoutRunAtStartsNow(t *testing.T) {
scheduler := Scheduler{}
referenceTime := time.Date(2026, 4, 23, 9, 7, 0, 0, time.UTC)
schedule, errorValue := scheduler.InitializeSchedule(Schedule{
ScheduleID: "schedule-immediate",
Name: "interval",
Kind: ScheduleKindInterval,
IntervalSecond: 60,
}, referenceTime)
if errorValue != nil {
t.Fatalf("expected interval task schedule to initialize: %v", errorValue)
}
if schedule.NextRunAt == nil || !schedule.NextRunAt.Equal(referenceTime) {
t.Fatalf("expected interval next run time to start at %s, got %+v", referenceTime.Format(time.RFC3339), schedule.NextRunAt)
}
}
func TestAdvanceScheduleStopsAtMaxRunCount(t *testing.T) {
scheduler := Scheduler{}
runAt := time.Date(2026, 4, 23, 9, 0, 0, 0, time.UTC)
schedule, errorValue := scheduler.AdvanceSchedule(Schedule{
ScheduleID: "schedule-limited",
Name: "limited interval",
Kind: ScheduleKindInterval,
RunAt: &runAt,
IntervalSecond: 60,
MaxRunCount: 10,
CompletedRunCount: 9,
}, time.Date(2026, 4, 23, 9, 10, 0, 0, time.UTC))
if errorValue != nil {
t.Fatalf("expected limited interval schedule to advance: %v", errorValue)
}
if schedule.CompletedRunCount != 10 {
t.Fatalf("expected completed run count to reach limit, got %+v", schedule)
}
if schedule.NextRunAt != nil {
t.Fatalf("expected limited interval schedule to stop, got %+v", schedule.NextRunAt)
}
}
func TestAdvanceScheduleForCronRun(t *testing.T) {
scheduler := Scheduler{}
executedAt := time.Date(2026, 4, 23, 10, 15, 0, 0, time.UTC)
schedule, errorValue := scheduler.AdvanceSchedule(Schedule{
ScheduleID: "schedule-3",
Name: "cron",
Kind: ScheduleKindCron,
CronExpression: "*/15 * * * *",
}, executedAt)
if errorValue != nil {
t.Fatalf("expected cron task schedule to advance: %v", errorValue)
}
if schedule.NextRunAt == nil {
t.Fatal("expected cron task schedule to include a next run time")
}
expectedNextRunAt := time.Date(2026, 4, 23, 10, 30, 0, 0, time.UTC)
if !schedule.NextRunAt.Equal(expectedNextRunAt) {
t.Fatalf("expected cron next run time to be %s, got %s", expectedNextRunAt.Format(time.RFC3339), schedule.NextRunAt.Format(time.RFC3339))
}
if schedule.LastRunAt == nil {
t.Fatal("expected cron task schedule to include a last run time")
}
if !schedule.LastRunAt.Equal(executedAt) {
t.Fatalf("expected last run time to be %s, got %s", executedAt.Format(time.RFC3339), schedule.LastRunAt.Format(time.RFC3339))
}
}
func TestAdvanceScheduleForCronRunUsesScheduleTimeZone(t *testing.T) {
scheduler := Scheduler{}
executedAt := time.Date(2026, 5, 5, 22, 0, 0, 0, time.UTC)
schedule, errorValue := scheduler.AdvanceSchedule(Schedule{
ScheduleID: "schedule-seoul-morning",
Name: "morning research",
Kind: ScheduleKindCron,
CronExpression: "0 7 * * *",
TimeZone: "Asia/Seoul",
}, executedAt)
if errorValue != nil {
t.Fatalf("expected cron task schedule to advance: %v", errorValue)
}
expectedNextRunAt := time.Date(2026, 5, 6, 22, 0, 0, 0, time.UTC)
if schedule.NextRunAt == nil || !schedule.NextRunAt.Equal(expectedNextRunAt) {
t.Fatalf("expected next run time to be %s, got %+v", expectedNextRunAt.Format(time.RFC3339), schedule.NextRunAt)
}
}
func TestIsScheduleDue(t *testing.T) {
scheduler := Scheduler{}
nextRunAt := time.Date(2026, 4, 23, 10, 0, 0, 0, time.UTC)
isDue := scheduler.IsScheduleDue(Schedule{
ScheduleID: "schedule-4",
Name: "due",
Kind: ScheduleKindCron,
NextRunAt: &nextRunAt,
}, time.Date(2026, 4, 23, 10, 0, 0, 0, time.UTC))
if !isDue {
t.Fatal("expected task schedule to be due at its next run time")
}
}
func TestScheduleOutlivesItsStartWindowOnlyPastTheWindow(t *testing.T) {
occurrenceAt := time.Date(2026, 11, 1, 23, 0, 0, 0, time.UTC)
schedule := Schedule{Kind: ScheduleKindCron, CronExpression: "0 8 * * *", NextRunAt: &occurrenceAt}
if (Scheduler{}).HasOutlivedItsStartWindow(schedule, occurrenceAt.Add(ScheduledRunStartWindow)) {
t.Fatal("a run at the edge of its start window is still on time")
}
if !(Scheduler{}).HasOutlivedItsStartWindow(schedule, occurrenceAt.Add(ScheduledRunStartWindow+time.Second)) {
t.Fatal("a run past its start window has outlived it")
}
if (Scheduler{}).HasOutlivedItsStartWindow(schedule, occurrenceAt.Add(-time.Hour)) {
t.Fatal("a run that is not due yet has not outlived anything")
}
}