package backup import ( "testing" "time" ) func TestNext(t *testing.T) { utc := func(s string) time.Time { v, err := time.Parse("2006-01-02 15:04", s) if err != nil { t.Fatal(err) } return v } cases := []struct{ expr, from, want string }{ {"0 3 * * *", "2026-09-30 02:59", "2026-09-30 03:00"}, {"0 3 * * *", "2026-09-30 03:00", "2026-10-01 03:00"}, {"@daily", "2026-12-31 23:30", "2027-01-01 00:00"}, {"*/15 * * * *", "2026-09-30 10:07", "2026-09-30 10:15"}, {"30 4 * * sun", "2026-09-30 00:00", "2026-10-04 04:30"}, // Wed → Sun {"30 4 * * 7", "2026-09-30 00:00", "2026-10-04 04:30"}, {"0 0 1,15 * *", "2026-09-02 00:00", "2026-09-15 00:00"}, {"0 9 * * mon-fri", "2026-10-02 10:00", "2026-10-05 09:00"}, // Fri after 9 → Mon {"0 0 29 2 *", "2026-03-01 00:00", "2028-02-29 00:00"}, {"0 12 13 * 5", "2026-09-30 00:00", "2026-10-02 12:00"}, // Friday OR the 13th {"5/20 * * * *", "2026-09-30 10:26", "2026-09-30 10:45"}, } for _, c := range cases { s, err := ParseCron(c.expr) if err != nil { t.Fatalf("%s: %v", c.expr, err) } if got := s.Next(utc(c.from)); !got.Equal(utc(c.want)) { t.Errorf("%s from %s = %s, want %s", c.expr, c.from, got.Format("2006-01-02 15:04 Mon"), c.want) } } } func TestParseCronErrors(t *testing.T) { for _, expr := range []string{"", "* * * *", "60 * * * *", "* 24 * * *", "* * 0 * *", "5-1 * * * *", "*/0 * * * *", "a * * * *"} { if _, err := ParseCron(expr); err == nil { t.Errorf("%q: expected an error", expr) } } s, _ := ParseCron("0 0 30 2 *") if !s.Next(time.Now()).IsZero() { t.Error("Feb 30 should never run") } }