fix(cli): use quartz mock clock in PausedDuringWaitForReady test (#25811)

PausedDuringWaitForReady used the real clock, so the 5s poll in
waitForTaskIdle could race with an in-flight stop build. The SQL
view (tasks_with_status) returns "unknown" for stop builds with
job_status != "succeeded" because the build_status CASE has no
branch for (stop, pending) or (stop, running). On macOS CI, where
the provisioner is slower, the poll fires during this transient
window and hits the TaskStatusUnknown case instead of
TaskStatusPaused, failing with "task entered unknown state" rather
than the expected "was paused".

Convert to the same quartz mock clock pattern that PR #25648
applied to WaitsForWorkingAppState: inject a mock clock via
NewWithClock, trap ticker creation and reset, then advance time
deterministically so the poll fires after the stop build completes.

Closes CODAGT-482
This commit is contained in:
Mathias Fredriksson
2026-05-29 11:25:06 +03:00
committed by GitHub
parent 5b10268827
commit 2af037ce02
+26 -1
View File
@@ -245,8 +245,15 @@ func Test_TaskSend(t *testing.T) {
pauseTask(setupCtx, t, setup.userClient, setup.task) pauseTask(setupCtx, t, setup.userClient, setup.task)
resumeTask(setupCtx, t, setup.userClient, setup.task) resumeTask(setupCtx, t, setup.userClient, setup.task)
// Set up mock clock and traps before starting the command.
// Without a mock clock the poll can race with the stop build
// and see a transient 'unknown' status instead of 'paused'.
mClock := quartz.NewMock(t)
tickTrap := mClock.Trap().NewTicker("task_send", "poll")
resetTrap := mClock.Trap().TickerReset("task_send", "poll")
// When: We attempt to send input to the initializing task. // When: We attempt to send input to the initializing task.
inv, root := clitest.New(t, "task", "send", setup.task.Name, "some task input") inv, root := clitest.NewWithClock(t, mClock, "task", "send", setup.task.Name, "some task input")
clitest.SetupConfig(t, setup.userClient, root) clitest.SetupConfig(t, setup.userClient, root)
ctx := testutil.Context(t, testutil.WaitLong) ctx := testutil.Context(t, testutil.WaitLong)
@@ -259,11 +266,29 @@ func Test_TaskSend(t *testing.T) {
// of waitForTaskIdle. // of waitForTaskIdle.
pty.ExpectMatchContext(ctx, "Waiting for task to become idle") pty.ExpectMatchContext(ctx, "Waiting for task to become idle")
// Wait for ticker creation and release it.
tickCall := tickTrap.MustWait(ctx)
tickCall.MustRelease(ctx)
tickTrap.Close()
// Fire the immediate first poll (time.Nanosecond initial interval).
// This poll sees 'initializing' because no agent is connected.
mClock.Advance(time.Nanosecond).MustWait(ctx)
// Wait for Reset (confirms first poll completed).
resetCall := resetTrap.MustWait(ctx)
resetCall.MustRelease(ctx)
resetTrap.Close()
// Pause the task while waitForTaskIdle is polling. Since // Pause the task while waitForTaskIdle is polling. Since
// no agent is connected, the task stays initializing until // no agent is connected, the task stays initializing until
// we pause it, at which point the status becomes paused. // we pause it, at which point the status becomes paused.
pauseTask(ctx, t, setup.userClient, setup.task) pauseTask(ctx, t, setup.userClient, setup.task)
// Fire second poll at the regular 5s interval. The stop
// build has completed, so the poll sees 'paused'.
mClock.Advance(5 * time.Second).MustWait(ctx)
// Then: The command should fail because the task was paused. // Then: The command should fail because the task was paused.
err := w.Wait() err := w.Wait()
require.Error(t, err) require.Error(t, err)