fix: make GetWorkspacesEligibleForTransition return even less false positives (#15594)

Relates to https://github.com/coder/coder/issues/15082

Further to https://github.com/coder/coder/pull/15429, this reduces the
amount of false-positives returned by the 'is eligible for autostart'
part of the query. We achieve this by calculating the 'next start at'
time of the workspace, storing it in the database, and using it in our
`GetWorkspacesEligibleForTransition` query.

The prior implementation of the 'is eligible for autostart' query would
return _all_ workspaces that at some point in the future _might_ be
eligible for autostart. This now ensures we only return workspaces that
_should_ be eligible for autostart.

We also now pass `currentTick` instead of `t` to the
`GetWorkspacesEligibleForTransition` query as otherwise we'll have one
round of workspaces that are skipped by `isEligibleForTransition` due to
`currentTick` being a truncated version of `t`.
This commit is contained in:
Danielle Maywood
2024-12-02 21:02:36 +00:00
committed by GitHub
parent 2b57dcc68c
commit e21a301682
35 changed files with 1012 additions and 75 deletions

View File

@ -2,11 +2,13 @@ package coderd_test
import (
"context"
"database/sql"
"net/http"
"sync/atomic"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"cdr.dev/slog"
@ -17,8 +19,10 @@ import (
"github.com/coder/coder/v2/coderd/autobuild"
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbauthz"
"github.com/coder/coder/v2/coderd/database/dbfake"
"github.com/coder/coder/v2/coderd/database/dbtestutil"
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/coderd/notifications"
"github.com/coder/coder/v2/coderd/rbac"
agplschedule "github.com/coder/coder/v2/coderd/schedule"
@ -32,6 +36,7 @@ import (
"github.com/coder/coder/v2/enterprise/coderd/schedule"
"github.com/coder/coder/v2/provisioner/echo"
"github.com/coder/coder/v2/testutil"
"github.com/coder/quartz"
)
// agplUserQuietHoursScheduleStore is passed to
@ -295,7 +300,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
AutobuildTicker: ticker,
IncludeProvisionerDaemon: true,
AutobuildStats: statCh,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger),
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{codersdk.FeatureAdvancedTemplateScheduling: 1},
@ -342,7 +347,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
AutobuildTicker: ticker,
IncludeProvisionerDaemon: true,
AutobuildStats: statCh,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger),
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{codersdk.FeatureAdvancedTemplateScheduling: 1},
@ -388,7 +393,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
AutobuildTicker: ticker,
IncludeProvisionerDaemon: true,
AutobuildStats: statCh,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger),
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{codersdk.FeatureAdvancedTemplateScheduling: 1},
@ -432,7 +437,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
Options: &coderdtest.Options{
AutobuildTicker: ticker,
AutobuildStats: statCh,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger),
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
Auditor: auditRecorder,
},
LicenseOptions: &coderdenttest.LicenseOptions{
@ -527,7 +532,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
Options: &coderdtest.Options{
AutobuildTicker: ticker,
AutobuildStats: statCh,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger),
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
Database: db,
Pubsub: pubsub,
Auditor: auditor,
@ -585,7 +590,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
AutobuildTicker: ticker,
IncludeProvisionerDaemon: true,
AutobuildStats: statCh,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger),
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{codersdk.FeatureAdvancedTemplateScheduling: 1},
@ -628,7 +633,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
AutobuildTicker: ticker,
IncludeProvisionerDaemon: true,
AutobuildStats: statCh,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger),
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{codersdk.FeatureAdvancedTemplateScheduling: 1},
@ -671,7 +676,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
AutobuildTicker: ticker,
IncludeProvisionerDaemon: true,
AutobuildStats: statCh,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger),
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{codersdk.FeatureAdvancedTemplateScheduling: 1},
@ -725,7 +730,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
AutobuildTicker: ticker,
IncludeProvisionerDaemon: true,
AutobuildStats: statCh,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger),
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{codersdk.FeatureAdvancedTemplateScheduling: 1},
@ -797,7 +802,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
AutobuildTicker: ticker,
IncludeProvisionerDaemon: true,
AutobuildStats: statCh,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger),
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{codersdk.FeatureAdvancedTemplateScheduling: 1},
@ -861,7 +866,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
AutobuildTicker: tickCh,
IncludeProvisionerDaemon: true,
AutobuildStats: statsCh,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger),
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{codersdk.FeatureAdvancedTemplateScheduling: 1},
@ -941,7 +946,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
AutobuildTicker: ticker,
IncludeProvisionerDaemon: true,
AutobuildStats: statCh,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger),
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{codersdk.FeatureAdvancedTemplateScheduling: 1},
@ -1027,7 +1032,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
AutobuildTicker: tickCh,
IncludeProvisionerDaemon: true,
AutobuildStats: statsCh,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger),
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{codersdk.FeatureAccessControl: 1},
@ -1102,6 +1107,245 @@ func TestWorkspaceAutobuild(t *testing.T) {
ws = coderdtest.MustWorkspace(t, client, ws.ID)
require.Equal(t, version2.ID, ws.LatestBuild.TemplateVersionID)
})
t.Run("NextStartAtIsValid", func(t *testing.T) {
t.Parallel()
var (
tickCh = make(chan time.Time)
statsCh = make(chan autobuild.Stats)
clock = quartz.NewMock(t)
)
// Set the clock to 8AM Monday, 1st January, 2024 to keep
// this test deterministic.
clock.Set(time.Date(2024, 1, 1, 8, 0, 0, 0, time.UTC))
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
client, user := coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{
AutobuildTicker: tickCh,
IncludeProvisionerDaemon: true,
AutobuildStats: statsCh,
Logger: &logger,
Clock: clock,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{codersdk.FeatureAdvancedTemplateScheduling: 1},
},
})
version1 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version1.ID)
// First create a template that only supports Monday-Friday
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version1.ID, func(ctr *codersdk.CreateTemplateRequest) {
ctr.AutostartRequirement = &codersdk.TemplateAutostartRequirement{DaysOfWeek: codersdk.BitmapToWeekdays(0b00011111)}
})
require.Equal(t, version1.ID, template.ActiveVersionID)
// Then create a workspace with a schedule Sunday-Saturday
sched, err := cron.Weekly("CRON_TZ=UTC 0 9 * * 0-6")
require.NoError(t, err)
ws := coderdtest.CreateWorkspace(t, client, template.ID, func(cwr *codersdk.CreateWorkspaceRequest) {
cwr.AutostartSchedule = ptr.Ref(sched.String())
})
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, ws.LatestBuild.ID)
ws = coderdtest.MustTransitionWorkspace(t, client, ws.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)
next := ws.LatestBuild.CreatedAt
// For each day of the week (Monday-Sunday)
// We iterate through each day of the week to ensure the behavior of each
// day of the week is as expected.
for range 7 {
next = sched.Next(next)
clock.Set(next)
tickCh <- next
stats := <-statsCh
ws = coderdtest.MustWorkspace(t, client, ws.ID)
// Our cron schedule specifies Sunday-Saturday but the template only allows
// Monday-Friday so we expect there to be no transitions on the weekend.
if next.Weekday() == time.Saturday || next.Weekday() == time.Sunday {
assert.Len(t, stats.Errors, 0)
assert.Len(t, stats.Transitions, 0)
ws = coderdtest.MustWorkspace(t, client, ws.ID)
} else {
assert.Len(t, stats.Errors, 0)
assert.Len(t, stats.Transitions, 1)
assert.Contains(t, stats.Transitions, ws.ID)
assert.Equal(t, database.WorkspaceTransitionStart, stats.Transitions[ws.ID])
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, ws.LatestBuild.ID)
ws = coderdtest.MustTransitionWorkspace(t, client, ws.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)
}
// Ensure that there is a valid next start at and that is is after
// the preivous start.
require.NotNil(t, ws.NextStartAt)
require.Greater(t, *ws.NextStartAt, next)
// Our autostart requirement disallows sundays and saturdays so
// the next start at should never land on these days.
require.NotEqual(t, time.Saturday, ws.NextStartAt.Weekday())
require.NotEqual(t, time.Sunday, ws.NextStartAt.Weekday())
}
})
t.Run("NextStartAtIsUpdatedWhenTemplateAutostartRequirementsChange", func(t *testing.T) {
t.Parallel()
var (
tickCh = make(chan time.Time)
statsCh = make(chan autobuild.Stats)
clock = quartz.NewMock(t)
)
// Set the clock to 8AM Monday, 1st January, 2024 to keep
// this test deterministic.
clock.Set(time.Date(2024, 1, 1, 8, 0, 0, 0, time.UTC))
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
templateScheduleStore := schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil)
templateScheduleStore.Clock = clock
client, user := coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{
AutobuildTicker: tickCh,
IncludeProvisionerDaemon: true,
AutobuildStats: statsCh,
Logger: &logger,
Clock: clock,
TemplateScheduleStore: templateScheduleStore,
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{codersdk.FeatureAdvancedTemplateScheduling: 1},
},
})
version1 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version1.ID)
// First create a template that only supports Monday-Friday
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version1.ID, func(ctr *codersdk.CreateTemplateRequest) {
ctr.AllowUserAutostart = ptr.Ref(true)
ctr.AutostartRequirement = &codersdk.TemplateAutostartRequirement{DaysOfWeek: codersdk.BitmapToWeekdays(0b00011111)}
})
require.Equal(t, version1.ID, template.ActiveVersionID)
// Then create a workspace with a schedule Monday-Friday
sched, err := cron.Weekly("CRON_TZ=UTC 0 9 * * 1-5")
require.NoError(t, err)
ws := coderdtest.CreateWorkspace(t, client, template.ID, func(cwr *codersdk.CreateWorkspaceRequest) {
cwr.AutostartSchedule = ptr.Ref(sched.String())
})
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, ws.LatestBuild.ID)
ws = coderdtest.MustTransitionWorkspace(t, client, ws.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)
// Our next start at should be Monday
require.NotNil(t, ws.NextStartAt)
require.Equal(t, time.Monday, ws.NextStartAt.Weekday())
// Now update the template to only allow Tuesday-Friday
coderdtest.UpdateTemplateMeta(t, client, template.ID, codersdk.UpdateTemplateMeta{
AutostartRequirement: &codersdk.TemplateAutostartRequirement{
DaysOfWeek: codersdk.BitmapToWeekdays(0b00011110),
},
})
// Verify that our next start at has been updated to Tuesday
ws = coderdtest.MustWorkspace(t, client, ws.ID)
require.NotNil(t, ws.NextStartAt)
require.Equal(t, time.Tuesday, ws.NextStartAt.Weekday())
})
t.Run("NextStartAtIsNullifiedOnScheduleChange", func(t *testing.T) {
t.Parallel()
if !dbtestutil.WillUsePostgres() {
t.Skip("this test uses triggers so does not work with dbmem.go")
}
var (
tickCh = make(chan time.Time)
statsCh = make(chan autobuild.Stats)
)
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
client, db, user := coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
Options: &coderdtest.Options{
AutobuildTicker: tickCh,
IncludeProvisionerDaemon: true,
AutobuildStats: statsCh,
Logger: &logger,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{codersdk.FeatureAdvancedTemplateScheduling: 1},
},
})
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
// Create a template that allows autostart Monday-Sunday
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
ctr.AutostartRequirement = &codersdk.TemplateAutostartRequirement{DaysOfWeek: codersdk.AllDaysOfWeek}
})
require.Equal(t, version.ID, template.ActiveVersionID)
// Create a workspace with a schedule Sunday-Saturday
sched, err := cron.Weekly("CRON_TZ=UTC 0 9 * * 0-6")
require.NoError(t, err)
ws := coderdtest.CreateWorkspace(t, client, template.ID, func(cwr *codersdk.CreateWorkspaceRequest) {
cwr.AutostartSchedule = ptr.Ref(sched.String())
})
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, ws.LatestBuild.ID)
ws = coderdtest.MustTransitionWorkspace(t, client, ws.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)
// Check we have a 'NextStartAt'
require.NotNil(t, ws.NextStartAt)
// Create a new slightly different cron schedule that could
// potentially make NextStartAt invalid.
sched, err = cron.Weekly("CRON_TZ=UTC 0 9 * * 1-6")
require.NoError(t, err)
ctx := testutil.Context(t, testutil.WaitShort)
// We want to test the database nullifies the NextStartAt so we
// make a raw DB call here. We pass in NextStartAt here so we
// can test the database will nullify it and not us.
//nolint: gocritic // We need system context to modify this.
err = db.UpdateWorkspaceAutostart(dbauthz.AsSystemRestricted(ctx), database.UpdateWorkspaceAutostartParams{
ID: ws.ID,
AutostartSchedule: sql.NullString{Valid: true, String: sched.String()},
NextStartAt: sql.NullTime{Valid: true, Time: *ws.NextStartAt},
})
require.NoError(t, err)
ws = coderdtest.MustWorkspace(t, client, ws.ID)
// Check 'NextStartAt' has been nullified
require.Nil(t, ws.NextStartAt)
// Now we let the lifecycle executor run. This should spot that the
// NextStartAt is null and update it for us.
next := dbtime.Now()
tickCh <- next
stats := <-statsCh
assert.Len(t, stats.Errors, 0)
assert.Len(t, stats.Transitions, 0)
// Ensure NextStartAt has been set, and is the expected value
ws = coderdtest.MustWorkspace(t, client, ws.ID)
require.NotNil(t, ws.NextStartAt)
require.Equal(t, sched.Next(next), ws.NextStartAt.UTC())
})
}
func TestTemplateDoesNotAllowUserAutostop(t *testing.T) {
@ -1112,7 +1356,7 @@ func TestTemplateDoesNotAllowUserAutostop(t *testing.T) {
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
client := coderdtest.New(t, &coderdtest.Options{
IncludeProvisionerDaemon: true,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger),
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
})
user := coderdtest.CreateFirstUser(t, client)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
@ -1151,7 +1395,7 @@ func TestTemplateDoesNotAllowUserAutostop(t *testing.T) {
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
client := coderdtest.New(t, &coderdtest.Options{
IncludeProvisionerDaemon: true,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger),
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
})
user := coderdtest.CreateFirstUser(t, client)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
@ -1203,7 +1447,7 @@ func TestExecutorAutostartBlocked(t *testing.T) {
AutobuildTicker: tickCh,
IncludeProvisionerDaemon: true,
AutobuildStats: statsCh,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger),
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{codersdk.FeatureAdvancedTemplateScheduling: 1},
@ -1225,9 +1469,9 @@ func TestExecutorAutostartBlocked(t *testing.T) {
// Given: workspace is stopped
workspace = coderdtest.MustTransitionWorkspace(t, client, workspace.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)
// When: the autobuild executor ticks way into the future
// When: the autobuild executor ticks into the future
go func() {
tickCh <- workspace.LatestBuild.CreatedAt.Add(24 * time.Hour)
tickCh <- workspace.LatestBuild.CreatedAt.Add(2 * time.Hour)
close(tickCh)
}()
@ -1247,7 +1491,7 @@ func TestWorkspacesFiltering(t *testing.T) {
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
client, db, owner := coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
Options: &coderdtest.Options{
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger),
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{codersdk.FeatureAdvancedTemplateScheduling: 1},
@ -1362,7 +1606,7 @@ func TestWorkspaceLock(t *testing.T) {
client, user = coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{
IncludeProvisionerDaemon: true,
TemplateScheduleStore: &schedule.EnterpriseTemplateScheduleStore{},
TemplateScheduleStore: &schedule.EnterpriseTemplateScheduleStore{Clock: quartz.NewReal()},
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
@ -1423,7 +1667,7 @@ func TestResolveAutostart(t *testing.T) {
ownerClient, db, owner := coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
Options: &coderdtest.Options{
TemplateScheduleStore: &schedule.EnterpriseTemplateScheduleStore{},
TemplateScheduleStore: &schedule.EnterpriseTemplateScheduleStore{Clock: quartz.NewReal()},
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{