Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DEVPROD-7550: fix calculating next stop time when editing schedule #7918

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions graphql/mutation_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,7 @@ func (r *mutationResolver) EditSpawnHost(ctx context.Context, spawnHost *EditSpa

if spawnHost.SleepSchedule != nil {

if err = h.UpdateSleepSchedule(ctx, *spawnHost.SleepSchedule); err != nil {
if err = h.UpdateSleepSchedule(ctx, *spawnHost.SleepSchedule, time.Now()); err != nil {
gimletErr, ok := err.(gimlet.ErrorResponse)
if ok {
return nil, mapHTTPStatusToGqlError(ctx, gimletErr.StatusCode, err)
Expand Down Expand Up @@ -815,7 +815,7 @@ func (r *mutationResolver) SpawnHost(ctx context.Context, spawnHostInput *SpawnH
return nil, InternalServerError.Send(ctx, "An error occurred Spawn host is nil")
}
if spawnHostInput.SleepSchedule != nil {
if err = spawnHost.UpdateSleepSchedule(ctx, *spawnHostInput.SleepSchedule); err != nil {
if err = spawnHost.UpdateSleepSchedule(ctx, *spawnHostInput.SleepSchedule, time.Now()); err != nil {
gimletErr, ok := err.(gimlet.ErrorResponse)
if ok {
return nil, mapHTTPStatusToGqlError(ctx, gimletErr.StatusCode, err)
Expand Down
16 changes: 11 additions & 5 deletions model/host/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -3625,7 +3625,7 @@ func (h *Host) UnsetPersistentDNSInfo(ctx context.Context) error {
// contain all the unmodified fields. For example, if this host is on a
// temporary exemption when their daily schedule is updated, the new schedule
// must still have the temporary exemption populated.
func (h *Host) UpdateSleepSchedule(ctx context.Context, schedule SleepScheduleInfo) error {
func (h *Host) UpdateSleepSchedule(ctx context.Context, schedule SleepScheduleInfo, now time.Time) error {
if err := schedule.Validate(); err != nil {
return gimlet.ErrorResponse{
StatusCode: http.StatusBadRequest,
Expand All @@ -3638,22 +3638,28 @@ func (h *Host) UpdateSleepSchedule(ctx context.Context, schedule SleepScheduleIn
schedule.NextStartTime = time.Time{}
schedule.NextStopTime = time.Time{}

now := time.Now()
var err error
schedule.NextStartTime, err = schedule.GetNextScheduledStartTime(now)
nextStart, err := schedule.GetNextScheduledStartTime(now)
if err != nil {
return gimlet.ErrorResponse{
StatusCode: http.StatusInternalServerError,
Message: errors.Wrap(err, "determining next sleep schedule start time").Error(),
}
}
schedule.NextStopTime, err = schedule.GetNextScheduledStopTime(now)
nextStop, err := schedule.GetNextScheduledStopTime(now)
if err != nil {
return gimlet.ErrorResponse{
StatusCode: http.StatusInternalServerError,
Message: errors.Wrap(err, "determining next sleep schedule stop time").Error(),
}
}

// Intentionally set these fields on the sleep schedule only after
// calculating both the next start and next stop times. If the next start
// time is set first on the schedule, the next stop time can be pushed
// further into the future than necessary.
schedule.NextStartTime = nextStart
schedule.NextStopTime = nextStop

if err = setSleepSchedule(ctx, h.Id, schedule); err != nil {
return gimlet.ErrorResponse{
StatusCode: http.StatusInternalServerError,
Expand Down
44 changes: 39 additions & 5 deletions model/host/host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6298,7 +6298,7 @@ func TestUpdateSleepSchedule(t *testing.T) {
TimeZone: userTZ.String(),
}

require.NoError(t, h.UpdateSleepSchedule(ctx, s))
require.NoError(t, h.UpdateSleepSchedule(ctx, s, now))

dbHost, err := FindOneId(ctx, h.Id)
require.NoError(t, err)
Expand Down Expand Up @@ -6333,7 +6333,7 @@ func TestUpdateSleepSchedule(t *testing.T) {
TimeZone: userTZ.String(),
}

require.NoError(t, h.UpdateSleepSchedule(ctx, s))
require.NoError(t, h.UpdateSleepSchedule(ctx, s, now))

dbHost, err := FindOneId(ctx, h.Id)
require.NoError(t, err)
Expand Down Expand Up @@ -6372,7 +6372,7 @@ func TestUpdateSleepSchedule(t *testing.T) {
TemporarilyExemptUntil: temporarilyExemptUntil,
}

require.NoError(t, h.UpdateSleepSchedule(ctx, s))
require.NoError(t, h.UpdateSleepSchedule(ctx, s, now))

dbHost, err := FindOneId(ctx, h.Id)
require.NoError(t, err)
Expand All @@ -6386,15 +6386,49 @@ func TestUpdateSleepSchedule(t *testing.T) {
assert.True(t, temporarilyExemptUntil.Equal(dbHost.SleepSchedule.TemporarilyExemptUntil))
assert.False(t, dbHost.SleepSchedule.ShouldKeepOff)
},
"NextStartAndStopTimesIsBasedOnCurrentTime": func(ctx context.Context, t *testing.T, h *Host) {
require.NoError(t, h.Insert(ctx))

const easternTZ = "America/New_York"
easternTZLoc, err := time.LoadLocation(easternTZ)
require.NoError(t, err)

// Simulate the current time, which is:
// Wednesday February 21, 2024 at 15:00 EST
now, err := time.ParseInLocation(time.DateTime, "2024-02-21 15:00:00", easternTZLoc)
require.NoError(t, err)
now = utility.BSONTime(now.UTC())

s := SleepScheduleInfo{
DailyStartTime: "10:00",
DailyStopTime: "18:00",
TimeZone: userTZ.String(),
}

require.NoError(t, h.UpdateSleepSchedule(ctx, s, now))

dbHost, err := FindOneId(ctx, h.Id)
require.NoError(t, err)
require.NotZero(t, dbHost)
checkRecurringSleepScheduleMatches(t, s, dbHost.SleepSchedule)

expectedNextStartTime, err := time.ParseInLocation(time.DateTime, "2024-02-22 10:00:00", easternTZLoc)
require.NoError(t, err)
assert.WithinDuration(t, expectedNextStartTime, dbHost.SleepSchedule.NextStartTime, 0, "next start time should be at 10:00 local time on the next day")

expectedNextStopTime, err := time.ParseInLocation(time.DateTime, "2024-02-21 18:00:00", easternTZLoc)
require.NoError(t, err)
assert.WithinDuration(t, expectedNextStopTime, dbHost.SleepSchedule.NextStopTime, 0, "next stop time should be at 18:00 local time on the same day")
},
"FailsWithZeroSleepSchedule": func(ctx context.Context, t *testing.T, h *Host) {
require.NoError(t, h.Insert(ctx))
assert.Error(t, h.UpdateSleepSchedule(ctx, SleepScheduleInfo{}))
assert.Error(t, h.UpdateSleepSchedule(ctx, SleepScheduleInfo{}, time.Now()))
},
"FailsWithInvalidSleepSchedule": func(ctx context.Context, t *testing.T, h *Host) {
require.NoError(t, h.Insert(ctx))
assert.Error(t, h.UpdateSleepSchedule(ctx, SleepScheduleInfo{
DailyStartTime: "10:00",
}))
}, time.Now()))
},
} {
t.Run(tName, func(t *testing.T) {
Expand Down
Loading