github.com/uchennaokeke444/nomad@v0.11.8/nomad/structs/structs_periodic_test.go (about)

     1  package structs
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  )
    12  
    13  func TestPeriodicConfig_DSTChange_Transitions(t *testing.T) {
    14  	locName := "America/Los_Angeles"
    15  	loc, err := time.LoadLocation(locName)
    16  	require.NoError(t, err)
    17  
    18  	cases := []struct {
    19  		name     string
    20  		pattern  string
    21  		initTime time.Time
    22  		expected []time.Time
    23  	}{
    24  		{
    25  			"normal time",
    26  			"0 2 * * * 2019",
    27  			time.Date(2019, time.February, 7, 1, 0, 0, 0, loc),
    28  			[]time.Time{
    29  				time.Date(2019, time.February, 7, 2, 0, 0, 0, loc),
    30  				time.Date(2019, time.February, 8, 2, 0, 0, 0, loc),
    31  				time.Date(2019, time.February, 9, 2, 0, 0, 0, loc),
    32  			},
    33  		},
    34  		{
    35  			"Spring forward but not in switch time",
    36  			"0 4 * * * 2019",
    37  			time.Date(2019, time.March, 9, 1, 0, 0, 0, loc),
    38  			[]time.Time{
    39  				time.Date(2019, time.March, 9, 4, 0, 0, 0, loc),
    40  				time.Date(2019, time.March, 10, 4, 0, 0, 0, loc),
    41  				time.Date(2019, time.March, 11, 4, 0, 0, 0, loc),
    42  			},
    43  		},
    44  		{
    45  			"Spring forward at a skipped time odd",
    46  			"2 2 * * * 2019",
    47  			time.Date(2019, time.March, 9, 1, 0, 0, 0, loc),
    48  			[]time.Time{
    49  				time.Date(2019, time.March, 9, 2, 2, 0, 0, loc),
    50  				// no time in March 10!
    51  				time.Date(2019, time.March, 11, 2, 2, 0, 0, loc),
    52  				time.Date(2019, time.March, 12, 2, 2, 0, 0, loc),
    53  			},
    54  		},
    55  		{
    56  			"Spring forward at a skipped time",
    57  			"1 2 * * * 2019",
    58  			time.Date(2019, time.March, 9, 1, 0, 0, 0, loc),
    59  			[]time.Time{
    60  				time.Date(2019, time.March, 9, 2, 1, 0, 0, loc),
    61  				// no time in March 8!
    62  				time.Date(2019, time.March, 11, 2, 1, 0, 0, loc),
    63  				time.Date(2019, time.March, 12, 2, 1, 0, 0, loc),
    64  			},
    65  		},
    66  		{
    67  			"Spring forward at a skipped time boundary",
    68  			"0 2 * * * 2019",
    69  			time.Date(2019, time.March, 9, 1, 0, 0, 0, loc),
    70  			[]time.Time{
    71  				time.Date(2019, time.March, 9, 2, 0, 0, 0, loc),
    72  				// no time in March 8!
    73  				time.Date(2019, time.March, 11, 2, 0, 0, 0, loc),
    74  				time.Date(2019, time.March, 12, 2, 0, 0, 0, loc),
    75  			},
    76  		},
    77  		{
    78  			"Spring forward at a boundary of repeating time",
    79  			"0 1 * * * 2019",
    80  			time.Date(2019, time.March, 9, 0, 0, 0, 0, loc),
    81  			[]time.Time{
    82  				time.Date(2019, time.March, 9, 1, 0, 0, 0, loc),
    83  				time.Date(2019, time.March, 10, 0, 0, 0, 0, loc).Add(1 * time.Hour),
    84  				time.Date(2019, time.March, 11, 1, 0, 0, 0, loc),
    85  				time.Date(2019, time.March, 12, 1, 0, 0, 0, loc),
    86  			},
    87  		},
    88  		{
    89  			"Fall back: before transition",
    90  			"30 0 * * * 2019",
    91  			time.Date(2019, time.November, 3, 0, 0, 0, 0, loc),
    92  			[]time.Time{
    93  				time.Date(2019, time.November, 3, 0, 30, 0, 0, loc),
    94  				time.Date(2019, time.November, 4, 0, 30, 0, 0, loc),
    95  				time.Date(2019, time.November, 5, 0, 30, 0, 0, loc),
    96  				time.Date(2019, time.November, 6, 0, 30, 0, 0, loc),
    97  			},
    98  		},
    99  		{
   100  			"Fall back: after transition",
   101  			"30 3 * * * 2019",
   102  			time.Date(2019, time.November, 3, 0, 0, 0, 0, loc),
   103  			[]time.Time{
   104  				time.Date(2019, time.November, 3, 3, 30, 0, 0, loc),
   105  				time.Date(2019, time.November, 4, 3, 30, 0, 0, loc),
   106  				time.Date(2019, time.November, 5, 3, 30, 0, 0, loc),
   107  				time.Date(2019, time.November, 6, 3, 30, 0, 0, loc),
   108  			},
   109  		},
   110  		{
   111  			"Fall back: after transition starting in repeated span before",
   112  			"30 3 * * * 2019",
   113  			time.Date(2019, time.November, 3, 0, 10, 0, 0, loc).Add(1 * time.Hour),
   114  			[]time.Time{
   115  				time.Date(2019, time.November, 3, 3, 30, 0, 0, loc),
   116  				time.Date(2019, time.November, 4, 3, 30, 0, 0, loc),
   117  				time.Date(2019, time.November, 5, 3, 30, 0, 0, loc),
   118  				time.Date(2019, time.November, 6, 3, 30, 0, 0, loc),
   119  			},
   120  		},
   121  		{
   122  			"Fall back: after transition starting in repeated span after",
   123  			"30 3 * * * 2019",
   124  			time.Date(2019, time.November, 3, 0, 10, 0, 0, loc).Add(2 * time.Hour),
   125  			[]time.Time{
   126  				time.Date(2019, time.November, 3, 3, 30, 0, 0, loc),
   127  				time.Date(2019, time.November, 4, 3, 30, 0, 0, loc),
   128  				time.Date(2019, time.November, 5, 3, 30, 0, 0, loc),
   129  				time.Date(2019, time.November, 6, 3, 30, 0, 0, loc),
   130  			},
   131  		},
   132  		{
   133  			"Fall back: in repeated region",
   134  			"30 1 * * * 2019",
   135  			time.Date(2019, time.November, 3, 0, 0, 0, 0, loc),
   136  			[]time.Time{
   137  				time.Date(2019, time.November, 3, 0, 30, 0, 0, loc).Add(1 * time.Hour),
   138  				time.Date(2019, time.November, 3, 0, 30, 0, 0, loc).Add(2 * time.Hour),
   139  				time.Date(2019, time.November, 4, 1, 30, 0, 0, loc),
   140  				time.Date(2019, time.November, 5, 1, 30, 0, 0, loc),
   141  				time.Date(2019, time.November, 6, 1, 30, 0, 0, loc),
   142  			},
   143  		},
   144  		{
   145  			"Fall back: in repeated region boundary",
   146  			"0 1 * * * 2019",
   147  			time.Date(2019, time.November, 3, 0, 0, 0, 0, loc),
   148  			[]time.Time{
   149  				time.Date(2019, time.November, 3, 0, 0, 0, 0, loc).Add(1 * time.Hour),
   150  				time.Date(2019, time.November, 3, 0, 0, 0, 0, loc).Add(2 * time.Hour),
   151  				time.Date(2019, time.November, 4, 1, 0, 0, 0, loc),
   152  				time.Date(2019, time.November, 5, 1, 0, 0, 0, loc),
   153  				time.Date(2019, time.November, 6, 1, 0, 0, 0, loc),
   154  			},
   155  		},
   156  		{
   157  			"Fall back: in repeated region boundary 2",
   158  			"0 2 * * * 2019",
   159  			time.Date(2019, time.November, 3, 0, 0, 0, 0, loc),
   160  			[]time.Time{
   161  				time.Date(2019, time.November, 3, 0, 0, 0, 0, loc).Add(3 * time.Hour),
   162  				time.Date(2019, time.November, 4, 2, 0, 0, 0, loc),
   163  				time.Date(2019, time.November, 5, 2, 0, 0, 0, loc),
   164  				time.Date(2019, time.November, 6, 2, 0, 0, 0, loc),
   165  			},
   166  		},
   167  		{
   168  			"Fall back: in repeated region, starting from within region",
   169  			"30 1 * * * 2019",
   170  			time.Date(2019, time.November, 3, 0, 40, 0, 0, loc).Add(1 * time.Hour),
   171  			[]time.Time{
   172  				time.Date(2019, time.November, 3, 0, 30, 0, 0, loc).Add(2 * time.Hour),
   173  				time.Date(2019, time.November, 4, 1, 30, 0, 0, loc),
   174  				time.Date(2019, time.November, 5, 1, 30, 0, 0, loc),
   175  				time.Date(2019, time.November, 6, 1, 30, 0, 0, loc),
   176  			},
   177  		},
   178  		{
   179  			"Fall back: in repeated region, starting from within region 2",
   180  			"30 1 * * * 2019",
   181  			time.Date(2019, time.November, 3, 0, 40, 0, 0, loc).Add(2 * time.Hour),
   182  			[]time.Time{
   183  				time.Date(2019, time.November, 4, 1, 30, 0, 0, loc),
   184  				time.Date(2019, time.November, 5, 1, 30, 0, 0, loc),
   185  				time.Date(2019, time.November, 6, 1, 30, 0, 0, loc),
   186  			},
   187  		},
   188  		{
   189  			"Fall back: wildcard",
   190  			"30 * * * * 2019",
   191  			time.Date(2019, time.November, 3, 0, 0, 0, 0, loc),
   192  			[]time.Time{
   193  				time.Date(2019, time.November, 3, 0, 30, 0, 0, loc),
   194  				time.Date(2019, time.November, 3, 0, 30, 0, 0, loc).Add(1 * time.Hour),
   195  				time.Date(2019, time.November, 3, 0, 30, 0, 0, loc).Add(2 * time.Hour),
   196  				time.Date(2019, time.November, 3, 2, 30, 0, 0, loc),
   197  			},
   198  		},
   199  	}
   200  
   201  	for _, c := range cases {
   202  		t.Run(c.name, func(t *testing.T) {
   203  			p := &PeriodicConfig{
   204  				Enabled:  true,
   205  				SpecType: PeriodicSpecCron,
   206  				Spec:     c.pattern,
   207  				TimeZone: locName,
   208  			}
   209  			p.Canonicalize()
   210  
   211  			starting := c.initTime
   212  			for _, next := range c.expected {
   213  				n, err := p.Next(starting)
   214  				assert.NoError(t, err)
   215  				assert.Equalf(t, next, n, "next time of %v", starting)
   216  
   217  				starting = next
   218  			}
   219  		})
   220  	}
   221  }
   222  
   223  func TestPeriodConfig_DSTSprintForward_Property(t *testing.T) {
   224  	locName := "America/Los_Angeles"
   225  	loc, err := time.LoadLocation(locName)
   226  	require.NoError(t, err)
   227  
   228  	cronExprs := []string{
   229  		"* * * * *",
   230  		"0 2 * * *",
   231  		"* 1 * * *",
   232  	}
   233  
   234  	times := []time.Time{
   235  		// spring forward
   236  		time.Date(2019, time.March, 11, 0, 0, 0, 0, loc),
   237  		time.Date(2019, time.March, 10, 0, 0, 0, 0, loc),
   238  		time.Date(2019, time.March, 11, 0, 0, 0, 0, loc),
   239  
   240  		// leap backwards
   241  		time.Date(2019, time.November, 4, 0, 0, 0, 0, loc),
   242  		time.Date(2019, time.November, 5, 0, 0, 0, 0, loc),
   243  		time.Date(2019, time.November, 6, 0, 0, 0, 0, loc),
   244  	}
   245  
   246  	testSpan := 4 * time.Hour
   247  
   248  	testCase := func(t *testing.T, cronExpr string, init time.Time) {
   249  		p := &PeriodicConfig{
   250  			Enabled:  true,
   251  			SpecType: PeriodicSpecCron,
   252  			Spec:     cronExpr,
   253  			TimeZone: "America/Los_Angeles",
   254  		}
   255  		p.Canonicalize()
   256  
   257  		lastNext := init
   258  		for start := init; start.Before(init.Add(testSpan)); start = start.Add(1 * time.Minute) {
   259  			next, err := p.Next(start)
   260  			require.NoError(t, err)
   261  			require.Truef(t, next.After(start),
   262  				"next(%v) = %v is not after init time", start, next)
   263  
   264  			if start.Before(lastNext) {
   265  				require.Equalf(t, lastNext, next, "next(%v) = %v is earlier than previously known next %v",
   266  					start, next, lastNext)
   267  			}
   268  			if strings.HasPrefix(cronExpr, "* * ") {
   269  				require.Equalf(t, next.Sub(start), 1*time.Minute,
   270  					"next(%v) = %v is the next minute", start, next)
   271  			}
   272  
   273  			lastNext = next
   274  		}
   275  	}
   276  
   277  	for _, cron := range cronExprs {
   278  		for _, startTime := range times {
   279  			t.Run(fmt.Sprintf("%v: %v", cron, startTime), func(t *testing.T) {
   280  				testCase(t, cron, startTime)
   281  			})
   282  		}
   283  	}
   284  }