mirror of https://github.com/coder/coder.git
596 lines
22 KiB
Go
596 lines
22 KiB
Go
package schedule_test
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/coder/coder/v2/coderd/database"
|
|
"github.com/coder/coder/v2/coderd/database/dbgen"
|
|
"github.com/coder/coder/v2/coderd/database/dbtestutil"
|
|
"github.com/coder/coder/v2/coderd/database/dbtime"
|
|
"github.com/coder/coder/v2/coderd/schedule"
|
|
"github.com/coder/coder/v2/testutil"
|
|
)
|
|
|
|
func TestCalculateAutoStop(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
now := time.Now()
|
|
|
|
// Wednesday the 8th of February 2023 at midnight. This date was
|
|
// specifically chosen as it doesn't fall on a applicable week for both
|
|
// fortnightly and triweekly autostop requirements.
|
|
wednesdayMidnightUTC := time.Date(2023, 2, 8, 0, 0, 0, 0, time.UTC)
|
|
|
|
sydneyQuietHours := "CRON_TZ=Australia/Sydney 0 0 * * *"
|
|
sydneyLoc, err := time.LoadLocation("Australia/Sydney")
|
|
require.NoError(t, err)
|
|
// 10pm on Friday the 10th of February 2023 in Sydney.
|
|
fridayEveningSydney := time.Date(2023, 2, 10, 22, 0, 0, 0, sydneyLoc)
|
|
// 12am on Saturday the 11th of February2023 in Sydney.
|
|
saturdayMidnightSydney := time.Date(2023, 2, 11, 0, 0, 0, 0, sydneyLoc)
|
|
|
|
t.Log("now", now)
|
|
t.Log("wednesdayMidnightUTC", wednesdayMidnightUTC)
|
|
t.Log("fridayEveningSydney", fridayEveningSydney)
|
|
t.Log("saturdayMidnightSydney", saturdayMidnightSydney)
|
|
|
|
dstIn := time.Date(2023, 10, 1, 2, 0, 0, 0, sydneyLoc) // 1 hour backward
|
|
dstInQuietHours := "CRON_TZ=Australia/Sydney 30 2 * * *" // never
|
|
// The expected behavior is that we will pick the next time that falls on
|
|
// quiet hours after the DST transition. In this case, it will be the same
|
|
// time the next day.
|
|
dstInQuietHoursExpectedTime := time.Date(2023, 10, 2, 2, 30, 0, 0, sydneyLoc)
|
|
beforeDstIn := time.Date(2023, 10, 1, 0, 0, 0, 0, sydneyLoc)
|
|
saturdayMidnightAfterDstIn := time.Date(2023, 10, 7, 0, 0, 0, 0, sydneyLoc)
|
|
|
|
// Wednesday after DST starts.
|
|
duringDst := time.Date(2023, 10, 4, 0, 0, 0, 0, sydneyLoc)
|
|
saturdayMidnightAfterDuringDst := saturdayMidnightAfterDstIn
|
|
|
|
dstOut := time.Date(2024, 4, 7, 3, 0, 0, 0, sydneyLoc) // 1 hour forward
|
|
dstOutQuietHours := "CRON_TZ=Australia/Sydney 30 3 * * *" // twice
|
|
dstOutQuietHoursExpectedTime := time.Date(2024, 4, 7, 3, 30, 0, 0, sydneyLoc) // in reality, this is the first occurrence
|
|
beforeDstOut := time.Date(2024, 4, 7, 0, 0, 0, 0, sydneyLoc)
|
|
saturdayMidnightAfterDstOut := time.Date(2024, 4, 13, 0, 0, 0, 0, sydneyLoc)
|
|
|
|
t.Log("dstIn", dstIn)
|
|
t.Log("beforeDstIn", beforeDstIn)
|
|
t.Log("saturdayMidnightAfterDstIn", saturdayMidnightAfterDstIn)
|
|
t.Log("dstOut", dstOut)
|
|
t.Log("beforeDstOut", beforeDstOut)
|
|
t.Log("saturdayMidnightAfterDstOut", saturdayMidnightAfterDstOut)
|
|
|
|
cases := []struct {
|
|
name string
|
|
now time.Time
|
|
templateAllowAutostop bool
|
|
templateDefaultTTL time.Duration
|
|
// TODO(@dean): remove max_ttl tests
|
|
useMaxTTL bool
|
|
templateMaxTTL time.Duration
|
|
templateAutostopRequirement schedule.TemplateAutostopRequirement
|
|
userQuietHoursSchedule string
|
|
// workspaceTTL is usually copied from the template's TTL when the
|
|
// workspace is made, so it takes precedence unless
|
|
// templateAllowAutostop is false.
|
|
workspaceTTL time.Duration
|
|
|
|
// expectedDeadline is copied from expectedMaxDeadline if unset.
|
|
expectedDeadline time.Time
|
|
expectedMaxDeadline time.Time
|
|
errContains string
|
|
}{
|
|
{
|
|
name: "OK",
|
|
now: now,
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 0,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{},
|
|
workspaceTTL: 0,
|
|
expectedDeadline: time.Time{},
|
|
expectedMaxDeadline: time.Time{},
|
|
},
|
|
{
|
|
name: "Delete",
|
|
now: now,
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 0,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{},
|
|
workspaceTTL: 0,
|
|
expectedDeadline: time.Time{},
|
|
expectedMaxDeadline: time.Time{},
|
|
},
|
|
{
|
|
name: "WorkspaceTTL",
|
|
now: now,
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 0,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{},
|
|
workspaceTTL: time.Hour,
|
|
expectedDeadline: now.Add(time.Hour),
|
|
expectedMaxDeadline: time.Time{},
|
|
},
|
|
{
|
|
name: "TemplateDefaultTTLIgnored",
|
|
now: now,
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: time.Hour,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{},
|
|
workspaceTTL: 0,
|
|
expectedDeadline: time.Time{},
|
|
expectedMaxDeadline: time.Time{},
|
|
},
|
|
{
|
|
name: "WorkspaceTTLOverridesTemplateDefaultTTL",
|
|
now: now,
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 2 * time.Hour,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{},
|
|
workspaceTTL: time.Hour,
|
|
expectedDeadline: now.Add(time.Hour),
|
|
expectedMaxDeadline: time.Time{},
|
|
},
|
|
{
|
|
name: "TemplateBlockWorkspaceTTL",
|
|
now: now,
|
|
templateAllowAutostop: false,
|
|
templateDefaultTTL: 3 * time.Hour,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{},
|
|
workspaceTTL: 4 * time.Hour,
|
|
expectedDeadline: now.Add(3 * time.Hour),
|
|
expectedMaxDeadline: time.Time{},
|
|
},
|
|
{
|
|
name: "TemplateAutostopRequirement",
|
|
now: wednesdayMidnightUTC,
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 0,
|
|
userQuietHoursSchedule: sydneyQuietHours,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{
|
|
DaysOfWeek: 0b00100000, // Saturday
|
|
Weeks: 0, // weekly
|
|
},
|
|
workspaceTTL: 0,
|
|
// expectedDeadline is copied from expectedMaxDeadline.
|
|
expectedMaxDeadline: saturdayMidnightSydney.In(time.UTC),
|
|
},
|
|
{
|
|
name: "TemplateAutostopRequirement1HourSkip",
|
|
now: saturdayMidnightSydney.Add(-59 * time.Minute),
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 0,
|
|
userQuietHoursSchedule: sydneyQuietHours,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{
|
|
DaysOfWeek: 0b00100000, // Saturday
|
|
Weeks: 1, // 1 also means weekly
|
|
},
|
|
workspaceTTL: 0,
|
|
// expectedDeadline is copied from expectedMaxDeadline.
|
|
expectedMaxDeadline: saturdayMidnightSydney.Add(7 * 24 * time.Hour).In(time.UTC),
|
|
},
|
|
{
|
|
// The next autostop requirement should be skipped if the
|
|
// workspace is started within 1 hour of it.
|
|
name: "TemplateAutostopRequirementDaily",
|
|
now: fridayEveningSydney,
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 0,
|
|
userQuietHoursSchedule: sydneyQuietHours,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{
|
|
DaysOfWeek: 0b01111111, // daily
|
|
Weeks: 0, // all weeks
|
|
},
|
|
workspaceTTL: 0,
|
|
// expectedDeadline is copied from expectedMaxDeadline.
|
|
expectedMaxDeadline: saturdayMidnightSydney.In(time.UTC),
|
|
},
|
|
{
|
|
name: "TemplateAutostopRequirementFortnightly/Skip",
|
|
now: wednesdayMidnightUTC,
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 0,
|
|
userQuietHoursSchedule: sydneyQuietHours,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{
|
|
DaysOfWeek: 0b00100000, // Saturday
|
|
Weeks: 2, // every 2 weeks
|
|
},
|
|
workspaceTTL: 0,
|
|
// expectedDeadline is copied from expectedMaxDeadline.
|
|
expectedMaxDeadline: saturdayMidnightSydney.AddDate(0, 0, 7).In(time.UTC),
|
|
},
|
|
{
|
|
name: "TemplateAutostopRequirementFortnightly/NoSkip",
|
|
now: wednesdayMidnightUTC.AddDate(0, 0, 7),
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 0,
|
|
userQuietHoursSchedule: sydneyQuietHours,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{
|
|
DaysOfWeek: 0b00100000, // Saturday
|
|
Weeks: 2, // every 2 weeks
|
|
},
|
|
workspaceTTL: 0,
|
|
// expectedDeadline is copied from expectedMaxDeadline.
|
|
expectedMaxDeadline: saturdayMidnightSydney.AddDate(0, 0, 7).In(time.UTC),
|
|
},
|
|
{
|
|
name: "TemplateAutostopRequirementTriweekly/Skip",
|
|
now: wednesdayMidnightUTC,
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 0,
|
|
userQuietHoursSchedule: sydneyQuietHours,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{
|
|
DaysOfWeek: 0b00100000, // Saturday
|
|
Weeks: 3, // every 3 weeks
|
|
},
|
|
workspaceTTL: 0,
|
|
// expectedDeadline is copied from expectedMaxDeadline.
|
|
// The next triweekly autostop requirement happens next week
|
|
// according to the epoch.
|
|
expectedMaxDeadline: saturdayMidnightSydney.AddDate(0, 0, 7).In(time.UTC),
|
|
},
|
|
{
|
|
name: "TemplateAutostopRequirementTriweekly/NoSkip",
|
|
now: wednesdayMidnightUTC.AddDate(0, 0, 7),
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 0,
|
|
userQuietHoursSchedule: sydneyQuietHours,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{
|
|
DaysOfWeek: 0b00100000, // Saturday
|
|
Weeks: 3, // every 3 weeks
|
|
},
|
|
workspaceTTL: 0,
|
|
// expectedDeadline is copied from expectedMaxDeadline.
|
|
expectedMaxDeadline: saturdayMidnightSydney.AddDate(0, 0, 7).In(time.UTC),
|
|
},
|
|
{
|
|
name: "TemplateAutostopRequirementOverridesWorkspaceTTL",
|
|
// now doesn't have to be UTC, but it helps us ensure that
|
|
// timezones are compared correctly in this test.
|
|
now: fridayEveningSydney.In(time.UTC),
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 0,
|
|
userQuietHoursSchedule: sydneyQuietHours,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{
|
|
DaysOfWeek: 0b00100000, // Saturday
|
|
Weeks: 0, // weekly
|
|
},
|
|
workspaceTTL: 3 * time.Hour,
|
|
// expectedDeadline is copied from expectedMaxDeadline.
|
|
expectedMaxDeadline: saturdayMidnightSydney.In(time.UTC),
|
|
},
|
|
{
|
|
name: "TemplateAutostopRequirementOverridesTemplateDefaultTTL",
|
|
now: fridayEveningSydney.In(time.UTC),
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 3 * time.Hour,
|
|
userQuietHoursSchedule: sydneyQuietHours,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{
|
|
DaysOfWeek: 0b00100000, // Saturday
|
|
Weeks: 0, // weekly
|
|
},
|
|
workspaceTTL: 0,
|
|
// expectedDeadline is copied from expectedMaxDeadline.
|
|
expectedMaxDeadline: saturdayMidnightSydney.In(time.UTC),
|
|
},
|
|
{
|
|
name: "TimeBeforeEpoch",
|
|
// The epoch is 2023-01-02 in each timezone. We set the time to
|
|
// 1 second before 11pm the previous day, as this is the latest time
|
|
// we allow due to our 1h leeway logic.
|
|
now: time.Date(2023, 1, 1, 22, 59, 59, 0, sydneyLoc),
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 0,
|
|
userQuietHoursSchedule: sydneyQuietHours,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{
|
|
DaysOfWeek: 0b00100000, // Saturday
|
|
Weeks: 2, // every fortnight
|
|
},
|
|
workspaceTTL: 0,
|
|
errContains: "coder server system clock is incorrect",
|
|
},
|
|
{
|
|
name: "DaylightSavings/OK",
|
|
now: duringDst,
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 0,
|
|
userQuietHoursSchedule: sydneyQuietHours,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{
|
|
DaysOfWeek: 0b00100000, // Saturday
|
|
Weeks: 1, // weekly
|
|
},
|
|
workspaceTTL: 0,
|
|
// expectedDeadline is copied from expectedMaxDeadline.
|
|
expectedMaxDeadline: saturdayMidnightAfterDuringDst,
|
|
},
|
|
{
|
|
name: "DaylightSavings/SwitchMidWeek/In",
|
|
now: beforeDstIn,
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 0,
|
|
userQuietHoursSchedule: sydneyQuietHours,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{
|
|
DaysOfWeek: 0b00100000, // Saturday
|
|
Weeks: 1, // weekly
|
|
},
|
|
workspaceTTL: 0,
|
|
// expectedDeadline is copied from expectedMaxDeadline.
|
|
expectedMaxDeadline: saturdayMidnightAfterDstIn,
|
|
},
|
|
{
|
|
name: "DaylightSavings/SwitchMidWeek/Out",
|
|
now: beforeDstOut,
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 0,
|
|
userQuietHoursSchedule: sydneyQuietHours,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{
|
|
DaysOfWeek: 0b00100000, // Saturday
|
|
Weeks: 1, // weekly
|
|
},
|
|
workspaceTTL: 0,
|
|
// expectedDeadline is copied from expectedMaxDeadline.
|
|
expectedMaxDeadline: saturdayMidnightAfterDstOut,
|
|
},
|
|
{
|
|
name: "DaylightSavings/QuietHoursFallsOnDstSwitch/In",
|
|
now: beforeDstIn.Add(-24 * time.Hour),
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 0,
|
|
userQuietHoursSchedule: dstInQuietHours,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{
|
|
DaysOfWeek: 0b01000000, // Sunday
|
|
Weeks: 1, // weekly
|
|
},
|
|
workspaceTTL: 0,
|
|
// expectedDeadline is copied from expectedMaxDeadline.
|
|
expectedMaxDeadline: dstInQuietHoursExpectedTime,
|
|
},
|
|
{
|
|
name: "DaylightSavings/QuietHoursFallsOnDstSwitch/Out",
|
|
now: beforeDstOut.Add(-24 * time.Hour),
|
|
templateAllowAutostop: true,
|
|
templateDefaultTTL: 0,
|
|
userQuietHoursSchedule: dstOutQuietHours,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{
|
|
DaysOfWeek: 0b01000000, // Sunday
|
|
Weeks: 1, // weekly
|
|
},
|
|
workspaceTTL: 0,
|
|
// expectedDeadline is copied from expectedMaxDeadline.
|
|
expectedMaxDeadline: dstOutQuietHoursExpectedTime,
|
|
},
|
|
|
|
// TODO(@dean): remove max_ttl tests
|
|
{
|
|
name: "AutostopRequirementIgnoresMaxTTL",
|
|
now: fridayEveningSydney.In(time.UTC),
|
|
templateAllowAutostop: false,
|
|
templateDefaultTTL: 0,
|
|
useMaxTTL: false,
|
|
templateMaxTTL: time.Hour, // should be ignored
|
|
userQuietHoursSchedule: sydneyQuietHours,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{
|
|
DaysOfWeek: 0b00100000, // Saturday
|
|
Weeks: 0, // weekly
|
|
},
|
|
workspaceTTL: 0,
|
|
// expectedDeadline is copied from expectedMaxDeadline.
|
|
expectedMaxDeadline: saturdayMidnightSydney.In(time.UTC),
|
|
},
|
|
{
|
|
name: "MaxTTLIgnoresAutostopRequirement",
|
|
now: fridayEveningSydney.In(time.UTC),
|
|
templateAllowAutostop: false,
|
|
templateDefaultTTL: 0,
|
|
useMaxTTL: true,
|
|
templateMaxTTL: time.Hour, // should NOT be ignored
|
|
userQuietHoursSchedule: sydneyQuietHours,
|
|
templateAutostopRequirement: schedule.TemplateAutostopRequirement{
|
|
DaysOfWeek: 0b00100000, // Saturday
|
|
Weeks: 0, // weekly
|
|
},
|
|
workspaceTTL: 0,
|
|
// expectedDeadline is copied from expectedMaxDeadline.
|
|
expectedMaxDeadline: fridayEveningSydney.Add(time.Hour).In(time.UTC),
|
|
},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
c := c
|
|
|
|
t.Run(c.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
db, _ := dbtestutil.NewDB(t)
|
|
ctx := testutil.Context(t, testutil.WaitLong)
|
|
|
|
templateScheduleStore := schedule.MockTemplateScheduleStore{
|
|
GetFn: func(_ context.Context, _ database.Store, _ uuid.UUID) (schedule.TemplateScheduleOptions, error) {
|
|
return schedule.TemplateScheduleOptions{
|
|
UserAutostartEnabled: false,
|
|
UserAutostopEnabled: c.templateAllowAutostop,
|
|
DefaultTTL: c.templateDefaultTTL,
|
|
MaxTTL: c.templateMaxTTL,
|
|
UseAutostopRequirement: !c.useMaxTTL,
|
|
AutostopRequirement: c.templateAutostopRequirement,
|
|
}, nil
|
|
},
|
|
}
|
|
|
|
userQuietHoursScheduleStore := schedule.MockUserQuietHoursScheduleStore{
|
|
GetFn: func(_ context.Context, _ database.Store, _ uuid.UUID) (schedule.UserQuietHoursScheduleOptions, error) {
|
|
if c.userQuietHoursSchedule == "" {
|
|
return schedule.UserQuietHoursScheduleOptions{
|
|
Schedule: nil,
|
|
}, nil
|
|
}
|
|
|
|
sched, err := schedule.Daily(c.userQuietHoursSchedule)
|
|
if !assert.NoError(t, err) {
|
|
return schedule.UserQuietHoursScheduleOptions{}, err
|
|
}
|
|
|
|
return schedule.UserQuietHoursScheduleOptions{
|
|
Schedule: sched,
|
|
UserSet: false,
|
|
}, nil
|
|
},
|
|
}
|
|
|
|
org := dbgen.Organization(t, db, database.Organization{})
|
|
user := dbgen.User(t, db, database.User{
|
|
QuietHoursSchedule: c.userQuietHoursSchedule,
|
|
})
|
|
template := dbgen.Template(t, db, database.Template{
|
|
Name: "template",
|
|
Provisioner: database.ProvisionerTypeEcho,
|
|
OrganizationID: org.ID,
|
|
CreatedBy: user.ID,
|
|
})
|
|
err := db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{
|
|
ID: template.ID,
|
|
UpdatedAt: dbtime.Now(),
|
|
AllowUserAutostart: c.templateAllowAutostop,
|
|
AutostopRequirementDaysOfWeek: int16(c.templateAutostopRequirement.DaysOfWeek),
|
|
AutostopRequirementWeeks: c.templateAutostopRequirement.Weeks,
|
|
})
|
|
require.NoError(t, err)
|
|
template, err = db.GetTemplateByID(ctx, template.ID)
|
|
require.NoError(t, err)
|
|
workspaceTTL := sql.NullInt64{}
|
|
if c.workspaceTTL != 0 {
|
|
workspaceTTL = sql.NullInt64{
|
|
Int64: int64(c.workspaceTTL),
|
|
Valid: true,
|
|
}
|
|
}
|
|
workspace := dbgen.Workspace(t, db, database.Workspace{
|
|
TemplateID: template.ID,
|
|
OrganizationID: org.ID,
|
|
OwnerID: user.ID,
|
|
Ttl: workspaceTTL,
|
|
})
|
|
|
|
autostop, err := schedule.CalculateAutostop(ctx, schedule.CalculateAutostopParams{
|
|
Database: db,
|
|
TemplateScheduleStore: templateScheduleStore,
|
|
UserQuietHoursScheduleStore: userQuietHoursScheduleStore,
|
|
Now: c.now,
|
|
Workspace: workspace,
|
|
})
|
|
if c.errContains != "" {
|
|
require.Error(t, err)
|
|
require.ErrorContains(t, err, c.errContains)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
|
|
// If the max deadline is set, the deadline should also be set.
|
|
// Default to the max deadline if the deadline is not set.
|
|
if c.expectedDeadline.IsZero() {
|
|
c.expectedDeadline = c.expectedMaxDeadline
|
|
}
|
|
|
|
if c.expectedDeadline.IsZero() {
|
|
require.True(t, autostop.Deadline.IsZero())
|
|
} else {
|
|
require.WithinDuration(t, c.expectedDeadline, autostop.Deadline, 15*time.Second, "deadline does not match expected")
|
|
}
|
|
if c.expectedMaxDeadline.IsZero() {
|
|
require.True(t, autostop.MaxDeadline.IsZero())
|
|
} else {
|
|
require.WithinDuration(t, c.expectedMaxDeadline, autostop.MaxDeadline, 15*time.Second, "max deadline does not match expected")
|
|
require.GreaterOrEqual(t, autostop.MaxDeadline.Unix(), autostop.Deadline.Unix(), "max deadline is smaller than deadline")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFindWeek(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
timezones := []string{
|
|
"UTC",
|
|
"America/Los_Angeles",
|
|
"America/New_York",
|
|
"Europe/Dublin",
|
|
"Europe/London",
|
|
"Europe/Paris",
|
|
"Asia/Kolkata", // India (UTC+5:30)
|
|
"Asia/Tokyo",
|
|
"Australia/Sydney",
|
|
"Australia/Brisbane",
|
|
}
|
|
|
|
for _, tz := range timezones {
|
|
tz := tz
|
|
t.Run("Loc/"+tz, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
loc, err := time.LoadLocation(tz)
|
|
require.NoError(t, err)
|
|
|
|
now := time.Now().In(loc)
|
|
currentWeek, err := schedule.WeeksSinceEpoch(now)
|
|
require.NoError(t, err)
|
|
|
|
diffMonday := now.Weekday() - time.Monday
|
|
if now.Weekday() == time.Sunday {
|
|
// Sunday is 0, but Monday is the first day of the week in the
|
|
// code.
|
|
diffMonday = 6
|
|
}
|
|
currentWeekMondayExpected := now.AddDate(0, 0, -int(diffMonday))
|
|
require.Equal(t, time.Monday, currentWeekMondayExpected.Weekday())
|
|
y, m, d := currentWeekMondayExpected.Date()
|
|
// Change to midnight.
|
|
currentWeekMondayExpected = time.Date(y, m, d, 0, 0, 0, 0, loc)
|
|
|
|
currentWeekMonday, err := schedule.GetMondayOfWeek(now.Location(), currentWeek)
|
|
require.NoError(t, err)
|
|
require.Equal(t, time.Monday, currentWeekMonday.Weekday())
|
|
require.Equal(t, currentWeekMondayExpected, currentWeekMonday)
|
|
|
|
t.Log("now", now)
|
|
t.Log("currentWeek", currentWeek)
|
|
t.Log("currentMonday", currentWeekMonday)
|
|
|
|
// Loop through every single Monday and Sunday for the next 100
|
|
// years and make sure the week calculations are correct.
|
|
for i := int64(1); i < 52*100; i++ {
|
|
msg := fmt.Sprintf("week %d", i)
|
|
|
|
monday := currentWeekMonday.AddDate(0, 0, int(i*7))
|
|
y, m, d := monday.Date()
|
|
monday = time.Date(y, m, d, 0, 0, 0, 0, loc)
|
|
require.Equal(t, monday.Weekday(), time.Monday, msg)
|
|
t.Log(msg, "monday", monday)
|
|
|
|
week, err := schedule.WeeksSinceEpoch(monday)
|
|
require.NoError(t, err, msg)
|
|
require.Equal(t, currentWeek+i, week, msg)
|
|
|
|
gotMonday, err := schedule.GetMondayOfWeek(monday.Location(), week)
|
|
require.NoError(t, err, msg)
|
|
require.Equal(t, monday, gotMonday, msg)
|
|
|
|
// Check that we get the same week number for late Sunday.
|
|
sunday := time.Date(y, m, d+6, 23, 59, 59, 0, loc)
|
|
require.Equal(t, sunday.Weekday(), time.Sunday, msg)
|
|
t.Log(msg, "sunday", sunday)
|
|
|
|
week, err = schedule.WeeksSinceEpoch(sunday)
|
|
require.NoError(t, err, msg)
|
|
require.Equal(t, currentWeek+i, week, msg)
|
|
}
|
|
})
|
|
}
|
|
}
|