internal/ci/cron_test.go
54 lines · 1503 bytes
3 symbols in this file
1package ci
2
3import (
4 "testing"
5 "time"
6)
7
8func at(s string) time.Time {
9 t, err := time.Parse("2006-01-02 15:04", s)
10 if err != nil {
11 panic(err)
12 }
13 return t
14}
15
16func TestCronNext(t *testing.T) {
17 cases := []struct{ expr, from, want string }{
18 {"* * * * *", "2026-08-25 10:30", "2026-08-25 10:31"},
19 {"17 11,23 * * *", "2026-08-25 10:30", "2026-08-25 11:17"},
20 {"17 11,23 * * *", "2026-08-25 11:17", "2026-08-25 23:17"},
21 {"0 6 * * 1", "2026-08-25 00:00", "2026-08-31 06:00"}, // next Monday
22 {"*/15 * * * *", "2026-08-25 10:31", "2026-08-25 10:45"},
23 {"0 0 1 * *", "2026-08-25 10:00", "2026-09-01 00:00"},
24 {"30 4 1-7 * 0", "2026-08-25 10:00", "2026-08-30 04:30"}, // dom OR dow: Sunday wins
25 {"0 12 29 2 *", "2026-03-01 00:00", "2028-02-29 12:00"}, // leap day beyond a year -> zero
26 }
27 for _, c := range cases {
28 cr, err := ParseCron(c.expr)
29 if err != nil {
30 t.Fatalf("%q: %v", c.expr, err)
31 }
32 got := cr.Next(at(c.from))
33 if c.expr == "0 12 29 2 *" {
34 if !got.IsZero() {
35 t.Errorf("%q from %s: want zero, got %s", c.expr, c.from, got)
36 }
37 continue
38 }
39 if !got.Equal(at(c.want)) {
40 t.Errorf("%q from %s: got %s, want %s", c.expr, c.from, got, c.want)
41 }
42 }
43}
44
45func TestCronParseErrors(t *testing.T) {
46 for _, expr := range []string{
47 "", "* * * *", "60 * * * *", "* 24 * * *", "* * 0 * *", "* * * 13 *",
48 "* * * * 8", "a * * * *", "*/0 * * * *", "5-2 * * * *",
49 } {
50 if _, err := ParseCron(expr); err == nil {
51 t.Errorf("%q parsed without error", expr)
52 }
53 }
54}