tests for periodic job scheduling and DST
This commit is contained in:
parent
42f9d517d8
commit
507c0b8f64
|
@ -0,0 +1,284 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPeriodicConfig_DSTChange_Transitions(t *testing.T) {
|
||||
locName := "America/Los_Angeles"
|
||||
loc, err := time.LoadLocation(locName)
|
||||
require.NoError(t, err)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
pattern string
|
||||
initTime time.Time
|
||||
expected []time.Time
|
||||
}{
|
||||
{
|
||||
"normal time",
|
||||
"0 2 * * * 2019",
|
||||
time.Date(2019, time.February, 7, 1, 0, 0, 0, loc),
|
||||
[]time.Time{
|
||||
time.Date(2019, time.February, 7, 2, 0, 0, 0, loc),
|
||||
time.Date(2019, time.February, 8, 2, 0, 0, 0, loc),
|
||||
time.Date(2019, time.February, 9, 2, 0, 0, 0, loc),
|
||||
},
|
||||
},
|
||||
{
|
||||
"Spring forward but not in switch time",
|
||||
"0 4 * * * 2019",
|
||||
time.Date(2019, time.March, 9, 1, 0, 0, 0, loc),
|
||||
[]time.Time{
|
||||
time.Date(2019, time.March, 9, 4, 0, 0, 0, loc),
|
||||
time.Date(2019, time.March, 10, 4, 0, 0, 0, loc),
|
||||
time.Date(2019, time.March, 11, 4, 0, 0, 0, loc),
|
||||
},
|
||||
},
|
||||
{
|
||||
"Spring forward at a skipped time odd",
|
||||
"2 2 * * * 2019",
|
||||
time.Date(2019, time.March, 9, 1, 0, 0, 0, loc),
|
||||
[]time.Time{
|
||||
time.Date(2019, time.March, 9, 2, 2, 0, 0, loc),
|
||||
// no time in March 10!
|
||||
time.Date(2019, time.March, 11, 2, 2, 0, 0, loc),
|
||||
time.Date(2019, time.March, 12, 2, 2, 0, 0, loc),
|
||||
},
|
||||
},
|
||||
{
|
||||
"Spring forward at a skipped time",
|
||||
"1 2 * * * 2019",
|
||||
time.Date(2019, time.March, 9, 1, 0, 0, 0, loc),
|
||||
[]time.Time{
|
||||
time.Date(2019, time.March, 9, 2, 1, 0, 0, loc),
|
||||
// no time in March 8!
|
||||
time.Date(2019, time.March, 11, 2, 1, 0, 0, loc),
|
||||
time.Date(2019, time.March, 12, 2, 1, 0, 0, loc),
|
||||
},
|
||||
},
|
||||
{
|
||||
"Spring forward at a skipped time boundary",
|
||||
"0 2 * * * 2019",
|
||||
time.Date(2019, time.March, 9, 1, 0, 0, 0, loc),
|
||||
[]time.Time{
|
||||
time.Date(2019, time.March, 9, 2, 0, 0, 0, loc),
|
||||
// no time in March 8!
|
||||
time.Date(2019, time.March, 11, 2, 0, 0, 0, loc),
|
||||
time.Date(2019, time.March, 12, 2, 0, 0, 0, loc),
|
||||
},
|
||||
},
|
||||
{
|
||||
"Spring forward at a boundary of repeating time",
|
||||
"0 1 * * * 2019",
|
||||
time.Date(2019, time.March, 9, 0, 0, 0, 0, loc),
|
||||
[]time.Time{
|
||||
time.Date(2019, time.March, 9, 1, 0, 0, 0, loc),
|
||||
time.Date(2019, time.March, 10, 0, 0, 0, 0, loc).Add(1 * time.Hour),
|
||||
time.Date(2019, time.March, 11, 1, 0, 0, 0, loc),
|
||||
time.Date(2019, time.March, 12, 1, 0, 0, 0, loc),
|
||||
},
|
||||
},
|
||||
{
|
||||
"Fall back: before transition",
|
||||
"30 0 * * * 2019",
|
||||
time.Date(2019, time.November, 3, 0, 0, 0, 0, loc),
|
||||
[]time.Time{
|
||||
time.Date(2019, time.November, 3, 0, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 4, 0, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 5, 0, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 6, 0, 30, 0, 0, loc),
|
||||
},
|
||||
},
|
||||
{
|
||||
"Fall back: after transition",
|
||||
"30 3 * * * 2019",
|
||||
time.Date(2019, time.November, 3, 0, 0, 0, 0, loc),
|
||||
[]time.Time{
|
||||
time.Date(2019, time.November, 3, 3, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 4, 3, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 5, 3, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 6, 3, 30, 0, 0, loc),
|
||||
},
|
||||
},
|
||||
{
|
||||
"Fall back: after transition starting in repeated span before",
|
||||
"30 3 * * * 2019",
|
||||
time.Date(2019, time.November, 3, 0, 10, 0, 0, loc).Add(1 * time.Hour),
|
||||
[]time.Time{
|
||||
time.Date(2019, time.November, 3, 3, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 4, 3, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 5, 3, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 6, 3, 30, 0, 0, loc),
|
||||
},
|
||||
},
|
||||
{
|
||||
"Fall back: after transition starting in repeated span after",
|
||||
"30 3 * * * 2019",
|
||||
time.Date(2019, time.November, 3, 0, 10, 0, 0, loc).Add(2 * time.Hour),
|
||||
[]time.Time{
|
||||
time.Date(2019, time.November, 3, 3, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 4, 3, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 5, 3, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 6, 3, 30, 0, 0, loc),
|
||||
},
|
||||
},
|
||||
{
|
||||
"Fall back: in repeated region",
|
||||
"30 1 * * * 2019",
|
||||
time.Date(2019, time.November, 3, 0, 0, 0, 0, loc),
|
||||
[]time.Time{
|
||||
time.Date(2019, time.November, 3, 0, 30, 0, 0, loc).Add(1 * time.Hour),
|
||||
time.Date(2019, time.November, 3, 0, 30, 0, 0, loc).Add(2 * time.Hour),
|
||||
time.Date(2019, time.November, 4, 1, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 5, 1, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 6, 1, 30, 0, 0, loc),
|
||||
},
|
||||
},
|
||||
{
|
||||
"Fall back: in repeated region boundary",
|
||||
"0 1 * * * 2019",
|
||||
time.Date(2019, time.November, 3, 0, 0, 0, 0, loc),
|
||||
[]time.Time{
|
||||
time.Date(2019, time.November, 3, 0, 0, 0, 0, loc).Add(1 * time.Hour),
|
||||
time.Date(2019, time.November, 3, 0, 0, 0, 0, loc).Add(2 * time.Hour),
|
||||
time.Date(2019, time.November, 4, 1, 0, 0, 0, loc),
|
||||
time.Date(2019, time.November, 5, 1, 0, 0, 0, loc),
|
||||
time.Date(2019, time.November, 6, 1, 0, 0, 0, loc),
|
||||
},
|
||||
},
|
||||
{
|
||||
"Fall back: in repeated region boundary 2",
|
||||
"0 2 * * * 2019",
|
||||
time.Date(2019, time.November, 3, 0, 0, 0, 0, loc),
|
||||
[]time.Time{
|
||||
time.Date(2019, time.November, 3, 0, 0, 0, 0, loc).Add(3 * time.Hour),
|
||||
time.Date(2019, time.November, 4, 2, 0, 0, 0, loc),
|
||||
time.Date(2019, time.November, 5, 2, 0, 0, 0, loc),
|
||||
time.Date(2019, time.November, 6, 2, 0, 0, 0, loc),
|
||||
},
|
||||
},
|
||||
{
|
||||
"Fall back: in repeated region, starting from within region",
|
||||
"30 1 * * * 2019",
|
||||
time.Date(2019, time.November, 3, 0, 40, 0, 0, loc).Add(1 * time.Hour),
|
||||
[]time.Time{
|
||||
time.Date(2019, time.November, 3, 0, 30, 0, 0, loc).Add(2 * time.Hour),
|
||||
time.Date(2019, time.November, 4, 1, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 5, 1, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 6, 1, 30, 0, 0, loc),
|
||||
},
|
||||
},
|
||||
{
|
||||
"Fall back: in repeated region, starting from within region 2",
|
||||
"30 1 * * * 2019",
|
||||
time.Date(2019, time.November, 3, 0, 40, 0, 0, loc).Add(2 * time.Hour),
|
||||
[]time.Time{
|
||||
time.Date(2019, time.November, 4, 1, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 5, 1, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 6, 1, 30, 0, 0, loc),
|
||||
},
|
||||
},
|
||||
{
|
||||
"Fall back: wildcard",
|
||||
"30 * * * * 2019",
|
||||
time.Date(2019, time.November, 3, 0, 0, 0, 0, loc),
|
||||
[]time.Time{
|
||||
time.Date(2019, time.November, 3, 0, 30, 0, 0, loc),
|
||||
time.Date(2019, time.November, 3, 0, 30, 0, 0, loc).Add(1 * time.Hour),
|
||||
time.Date(2019, time.November, 3, 0, 30, 0, 0, loc).Add(2 * time.Hour),
|
||||
time.Date(2019, time.November, 3, 2, 30, 0, 0, loc),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
p := &PeriodicConfig{
|
||||
Enabled: true,
|
||||
SpecType: PeriodicSpecCron,
|
||||
Spec: c.pattern,
|
||||
TimeZone: locName,
|
||||
}
|
||||
p.Canonicalize()
|
||||
|
||||
starting := c.initTime
|
||||
for _, next := range c.expected {
|
||||
n, err := p.Next(starting)
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, next, n, "next time of %v", starting)
|
||||
|
||||
starting = next
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPeriodConfig_DSTSprintForward_Property(t *testing.T) {
|
||||
locName := "America/Los_Angeles"
|
||||
loc, err := time.LoadLocation(locName)
|
||||
require.NoError(t, err)
|
||||
|
||||
cronExprs := []string{
|
||||
"* * * * *",
|
||||
"0 2 * * *",
|
||||
"* 1 * * *",
|
||||
}
|
||||
|
||||
times := []time.Time{
|
||||
// spring forward
|
||||
time.Date(2019, time.March, 11, 0, 0, 0, 0, loc),
|
||||
time.Date(2019, time.March, 10, 0, 0, 0, 0, loc),
|
||||
time.Date(2019, time.March, 11, 0, 0, 0, 0, loc),
|
||||
|
||||
// leap backwards
|
||||
time.Date(2019, time.November, 4, 0, 0, 0, 0, loc),
|
||||
time.Date(2019, time.November, 5, 0, 0, 0, 0, loc),
|
||||
time.Date(2019, time.November, 6, 0, 0, 0, 0, loc),
|
||||
}
|
||||
|
||||
testSpan := 4 * time.Hour
|
||||
|
||||
testCase := func(t *testing.T, cronExpr string, init time.Time) {
|
||||
p := &PeriodicConfig{
|
||||
Enabled: true,
|
||||
SpecType: PeriodicSpecCron,
|
||||
Spec: cronExpr,
|
||||
TimeZone: "America/Los_Angeles",
|
||||
}
|
||||
p.Canonicalize()
|
||||
|
||||
lastNext := init
|
||||
for start := init; start.Before(init.Add(testSpan)); start = start.Add(1 * time.Minute) {
|
||||
next, err := p.Next(start)
|
||||
require.NoError(t, err)
|
||||
require.Truef(t, next.After(start),
|
||||
"next(%v) = %v is not after init time", start, next)
|
||||
|
||||
if start.Before(lastNext) {
|
||||
require.Equalf(t, lastNext, next, "next(%v) = %v is earlier than previously known next %v",
|
||||
start, next, lastNext)
|
||||
}
|
||||
if strings.HasPrefix(cronExpr, "* * ") {
|
||||
require.Equalf(t, next.Sub(start), 1*time.Minute,
|
||||
"next(%v) = %v is the next minute", start, next)
|
||||
}
|
||||
|
||||
lastNext = next
|
||||
}
|
||||
}
|
||||
|
||||
for _, cron := range cronExprs {
|
||||
for _, startTime := range times {
|
||||
t.Run(fmt.Sprintf("%v: %v", cron, startTime), func(t *testing.T) {
|
||||
testCase(t, cron, startTime)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue