mirror of
https://github.com/coder/coder.git
synced 2026-09-21 20:51:01 +08:00
fix: align autostart tests with persisted next_start_at (#26037)
`TestExecutorAutostartOK` and its sibling positive autostart tests compute the autobuild tick from `sched.Next(workspace.LatestBuild.CreatedAt)`, but the server persists `next_start_at` from the build's completion time. When build creation and completion straddle the schedule's next fire time, the persisted value advances past the test's tick, the executor's eligibility query (`next_start_at <= tick`) drops the workspace, and the test fails with an empty transitions map. This surfaced in flaky test runs. Add `coderdtest.NextAutostartTick(t, workspace)` which returns `*workspace.NextStartAt`, and use it across the affected positive autostart paths in `coderd/autobuild`, `coderd`, and `enterprise/coderd`. Generated with assistance from Coder Agents.
This commit is contained in:
@@ -65,8 +65,8 @@ func TestExecutorAutostartOK(t *testing.T) {
|
||||
p, err := coderdtest.GetProvisionerForTags(db, time.Now(), workspace.OrganizationID, map[string]string{})
|
||||
require.NoError(t, err)
|
||||
// When: the autobuild executor ticks after the scheduled time
|
||||
tickTime := coderdtest.NextAutostartTick(t, workspace)
|
||||
go func() {
|
||||
tickTime := sched.Next(workspace.LatestBuild.CreatedAt)
|
||||
coderdtest.UpdateProvisionerLastSeenAt(t, db, p.ID, tickTime)
|
||||
tickCh <- tickTime
|
||||
close(tickCh)
|
||||
@@ -127,7 +127,7 @@ func TestMultipleLifecycleExecutors(t *testing.T) {
|
||||
p, err := coderdtest.GetProvisionerForTags(db, time.Now(), workspace.OrganizationID, nil)
|
||||
require.NoError(t, err)
|
||||
// Get both clients to perform a lifecycle execution tick
|
||||
next := sched.Next(workspace.LatestBuild.CreatedAt)
|
||||
next := coderdtest.NextAutostartTick(t, workspace)
|
||||
coderdtest.UpdateProvisionerLastSeenAt(t, db, p.ID, next)
|
||||
|
||||
startCh := make(chan struct{})
|
||||
@@ -237,7 +237,7 @@ func TestExecutorBuildNumberRaceIsHandled(t *testing.T) {
|
||||
|
||||
p, err := coderdtest.GetProvisionerForTags(realDB, time.Now(), workspace.OrganizationID, nil)
|
||||
require.NoError(t, err)
|
||||
next := sched.Next(workspace.LatestBuild.CreatedAt)
|
||||
next := coderdtest.NextAutostartTick(t, workspace)
|
||||
coderdtest.UpdateProvisionerLastSeenAt(t, realDB, p.ID, next)
|
||||
|
||||
tickCh <- next
|
||||
@@ -351,8 +351,8 @@ func TestExecutorAutostartTemplateUpdated(t *testing.T) {
|
||||
|
||||
t.Log("sending autobuild tick")
|
||||
// When: the autobuild executor ticks after the scheduled time
|
||||
tickTime := coderdtest.NextAutostartTick(t, workspace)
|
||||
go func() {
|
||||
tickTime := sched.Next(workspace.LatestBuild.CreatedAt)
|
||||
coderdtest.UpdateProvisionerLastSeenAt(t, db, p.ID, tickTime)
|
||||
tickCh <- tickTime
|
||||
close(tickCh)
|
||||
@@ -984,8 +984,8 @@ func TestExecutorAutostartMultipleOK(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// When: the autobuild executor ticks past the scheduled time
|
||||
tickTime := coderdtest.NextAutostartTick(t, workspace)
|
||||
go func() {
|
||||
tickTime := sched.Next(workspace.LatestBuild.CreatedAt)
|
||||
coderdtest.UpdateProvisionerLastSeenAt(t, db, p.ID, tickTime)
|
||||
tickCh <- tickTime
|
||||
tickCh2 <- tickTime
|
||||
@@ -1054,8 +1054,8 @@ func TestExecutorAutostartWithParameters(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// When: the autobuild executor ticks after the scheduled time
|
||||
tickTime := coderdtest.NextAutostartTick(t, workspace)
|
||||
go func() {
|
||||
tickTime := sched.Next(workspace.LatestBuild.CreatedAt)
|
||||
coderdtest.UpdateProvisionerLastSeenAt(t, db, p.ID, tickTime)
|
||||
tickCh <- tickTime
|
||||
close(tickCh)
|
||||
@@ -1927,7 +1927,7 @@ func TestExecutorAutostartSkipsWhenNoProvisionersAvailable(t *testing.T) {
|
||||
p, err = coderdtest.GetProvisionerForTags(db, time.Now(), workspace.OrganizationID, provisionerDaemonTags)
|
||||
require.NoError(t, err, "Error getting provisioner for workspace")
|
||||
|
||||
next = sched.Next(workspace.LatestBuild.CreatedAt)
|
||||
next = coderdtest.NextAutostartTick(t, workspace)
|
||||
notStaleTime := next.Add((-1 * provisionerdserver.StaleInterval) + 10*time.Second)
|
||||
coderdtest.UpdateProvisionerLastSeenAt(t, db, p.ID, notStaleTime)
|
||||
// Require that the provisioner time has actually been updated to the expected value.
|
||||
@@ -2051,8 +2051,8 @@ func TestExecutorTaskWorkspace(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// When: the autobuild executor ticks after the scheduled time
|
||||
tickTime := coderdtest.NextAutostartTick(t, workspace)
|
||||
go func() {
|
||||
tickTime := sched.Next(workspace.LatestBuild.CreatedAt)
|
||||
coderdtest.UpdateProvisionerLastSeenAt(t, db, p.ID, tickTime)
|
||||
tickCh <- tickTime
|
||||
close(tickCh)
|
||||
|
||||
@@ -1843,6 +1843,18 @@ func UpdateProvisionerLastSeenAt(t *testing.T, db database.Store, id uuid.UUID,
|
||||
t.Logf("Successfully updated provisioner LastSeenAt")
|
||||
}
|
||||
|
||||
// NextAutostartTick returns workspace.NextStartAt for use as the autobuild
|
||||
// tick. The executor's eligibility query checks next_start_at <= tick.
|
||||
// Computing from build.CreatedAt is racy: next_start_at derives from build
|
||||
// completion time, so it can advance past sched.Next(build.CreatedAt) and
|
||||
// the workspace misses the eligibility window.
|
||||
func NextAutostartTick(t testing.TB, workspace codersdk.Workspace) time.Time {
|
||||
t.Helper()
|
||||
require.NotNil(t, workspace.NextStartAt,
|
||||
"workspace next_start_at is nil; ensure autostart is enabled and the latest build has completed before calling NextAutostartTick")
|
||||
return *workspace.NextStartAt
|
||||
}
|
||||
|
||||
func MustWaitForAnyProvisioner(t *testing.T, db database.Store) {
|
||||
t.Helper()
|
||||
ctx := ctxWithProvisionerPermissions(testutil.Context(t, testutil.WaitShort))
|
||||
|
||||
@@ -6212,8 +6212,8 @@ func TestWorkspaceBuildsEnqueuedMetric(t *testing.T) {
|
||||
p, err := coderdtest.GetProvisionerForTags(db, time.Now(), workspace.OrganizationID, map[string]string{})
|
||||
require.NoError(t, err)
|
||||
|
||||
tickTime := coderdtest.NextAutostartTick(t, workspace)
|
||||
go func() {
|
||||
tickTime := sched.Next(workspace.LatestBuild.CreatedAt)
|
||||
coderdtest.UpdateProvisionerLastSeenAt(t, db, p.ID, tickTime)
|
||||
tickCh <- tickTime
|
||||
close(tickCh)
|
||||
|
||||
@@ -1315,7 +1315,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
|
||||
ws = coderdtest.MustTransitionWorkspace(t, client, ws.ID, codersdk.WorkspaceTransitionStart, codersdk.WorkspaceTransitionStop)
|
||||
|
||||
// Assert that autostart works when the workspace isn't dormant..
|
||||
tickTime := sched.Next(ws.LatestBuild.CreatedAt)
|
||||
tickTime := coderdtest.NextAutostartTick(t, ws)
|
||||
p, err := coderdtest.GetProvisionerForTags(db, time.Now(), ws.OrganizationID, nil)
|
||||
require.NoError(t, err)
|
||||
coderdtest.UpdateProvisionerLastSeenAt(t, db, p.ID, tickTime)
|
||||
@@ -1518,7 +1518,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Kick of an autostart build.
|
||||
tickTime := sched.Next(ws.LatestBuild.CreatedAt)
|
||||
tickTime := coderdtest.NextAutostartTick(t, ws)
|
||||
p, err := coderdtest.GetProvisionerForTags(db, time.Now(), ws.OrganizationID, nil)
|
||||
require.NoError(t, err)
|
||||
coderdtest.UpdateProvisionerLastSeenAt(t, db, p.ID, tickTime)
|
||||
@@ -1545,12 +1545,12 @@ func TestWorkspaceAutobuild(t *testing.T) {
|
||||
|
||||
// Reset the workspace to the stopped state so we can try
|
||||
// to autostart again.
|
||||
coderdtest.MustTransitionWorkspace(t, client, ws.ID, codersdk.WorkspaceTransitionStart, codersdk.WorkspaceTransitionStop, func(req *codersdk.CreateWorkspaceBuildRequest) {
|
||||
ws = coderdtest.MustTransitionWorkspace(t, client, ws.ID, codersdk.WorkspaceTransitionStart, codersdk.WorkspaceTransitionStop, func(req *codersdk.CreateWorkspaceBuildRequest) {
|
||||
req.TemplateVersionID = ws.LatestBuild.TemplateVersionID
|
||||
})
|
||||
|
||||
// Force an autostart transition again.
|
||||
tickTime2 := sched.Next(firstBuild.CreatedAt)
|
||||
tickTime2 := coderdtest.NextAutostartTick(t, ws)
|
||||
coderdtest.UpdateProvisionerLastSeenAt(t, db, p.ID, tickTime2)
|
||||
tickCh <- tickTime2
|
||||
stats = <-statsCh
|
||||
|
||||
Reference in New Issue
Block a user