mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
fix(cli): use quartz clock in waitForTaskIdle for immediate first poll (#25648)
waitForTaskIdle used time.NewTicker(5s) which delays the first poll by 5 seconds. Debugger tracing proved the failure mechanism: on slow CI (Windows), the first poll at 5s sees "working" (idle patch has not landed due to goroutine scheduling), needs poll #2 at 10s, but the 25s context expires before it fires. Two changes: 1. Use r.clock.NewTicker (quartz) with time.Nanosecond initial interval and Reset(5s) for immediate first poll. Tests inject a mock clock via clitest.NewWithClock for deterministic control. 2. Rewrite WaitsForWorkingAppState test with quartz traps (NewTicker + TickerReset) for deterministic synchronization instead of racing goroutines. Fix PausedDuringWaitForReady sync point. Closes DEVEX-381
This commit is contained in:
committed by
GitHub
parent
8652ef3e3b
commit
7958ad6d04
+26
-4
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||
"github.com/coder/coder/v2/pty/ptytest"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
"github.com/coder/quartz"
|
||||
)
|
||||
|
||||
func Test_TaskSend(t *testing.T) {
|
||||
@@ -255,10 +256,10 @@ func Test_TaskSend(t *testing.T) {
|
||||
w := clitest.StartWithWaiter(t, inv)
|
||||
|
||||
// Wait for the command to enter the build-watching phase
|
||||
// of waitForTaskReady.
|
||||
pty.ExpectMatchContext(ctx, "Queued")
|
||||
// of waitForTaskIdle.
|
||||
pty.ExpectMatchContext(ctx, "Waiting for task to become idle")
|
||||
|
||||
// Pause the task while waitForTaskReady is polling. Since
|
||||
// Pause the task while waitForTaskIdle is polling. Since
|
||||
// no agent is connected, the task stays initializing until
|
||||
// we pause it, at which point the status becomes paused.
|
||||
pauseTask(ctx, t, setup.userClient, setup.task)
|
||||
@@ -284,14 +285,32 @@ func Test_TaskSend(t *testing.T) {
|
||||
Message: "busy",
|
||||
}))
|
||||
|
||||
// Set up mock clock and traps before starting the command.
|
||||
mClock := quartz.NewMock(t)
|
||||
tickTrap := mClock.Trap().NewTicker("task_send", "poll")
|
||||
resetTrap := mClock.Trap().TickerReset("task_send", "poll")
|
||||
|
||||
// When: We send input while the app is working.
|
||||
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)
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
inv = inv.WithContext(ctx)
|
||||
w := clitest.StartWithWaiter(t, inv)
|
||||
|
||||
// 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).
|
||||
mClock.Advance(time.Nanosecond).MustWait(ctx)
|
||||
|
||||
// Wait for Reset (confirms first poll completed and saw "working").
|
||||
resetCall := resetTrap.MustWait(ctx)
|
||||
resetCall.MustRelease(ctx)
|
||||
resetTrap.Close()
|
||||
|
||||
// Transition the app back to idle so waitForTaskIdle proceeds.
|
||||
require.NoError(t, agentClient.PatchAppStatus(ctx, agentsdk.PatchAppStatus{
|
||||
AppSlug: "task-sidebar",
|
||||
@@ -299,6 +318,9 @@ func Test_TaskSend(t *testing.T) {
|
||||
Message: "ready",
|
||||
}))
|
||||
|
||||
// Fire second poll at the regular 5s interval.
|
||||
mClock.Advance(5 * time.Second).MustWait(ctx)
|
||||
|
||||
// Then: The command should complete successfully.
|
||||
require.NoError(t, w.Wait())
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user