Skip to content

Commit b1cb7fe

Browse files
committed
test: create internal test file for unit test
1 parent c7a0c14 commit b1cb7fe

File tree

3 files changed

+257
-129
lines changed

3 files changed

+257
-129
lines changed

coderd/aitasks.go

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -270,23 +270,36 @@ func (api *API) tasksCreate(rw http.ResponseWriter, r *http.Request) {
270270
func taskFromDBTaskAndWorkspace(dbTask database.Task, ws codersdk.Workspace) codersdk.Task {
271271
var taskAgentLifecycle *codersdk.WorkspaceAgentLifecycle
272272
var taskAgentHealth *codersdk.WorkspaceAgentHealth
273+
var taskAppHealth *codersdk.WorkspaceAppHealth
273274

274-
// If we have an agent ID from the task, find the agent details in the
275-
// workspace.
275+
// If we have an agent ID from the task, find the agent details in the workspace.
276+
// TODO(ssncferreira): Use 'workspace_agent_lifecycle_state' and 'workspace_app_health'
277+
// from new 'tasks_with_status' query
276278
if dbTask.WorkspaceAgentID.Valid {
277279
findTaskAgentLoop:
278280
for _, resource := range ws.LatestBuild.Resources {
279281
for _, agent := range resource.Agents {
280282
if agent.ID == dbTask.WorkspaceAgentID.UUID {
281283
taskAgentLifecycle = &agent.LifecycleState
282284
taskAgentHealth = &agent.Health
285+
286+
// Get task app health if it exists
287+
if dbTask.WorkspaceAppID.Valid {
288+
for _, app := range agent.Apps {
289+
if app.ID == dbTask.WorkspaceAppID.UUID {
290+
taskAppHealth = &app.Health
291+
break
292+
}
293+
}
294+
}
295+
283296
break findTaskAgentLoop
284297
}
285298
}
286299
}
287300
}
288301

289-
currentState := deriveTaskCurrentState(dbTask, ws, taskAgentLifecycle)
302+
currentState := deriveTaskCurrentState(dbTask, ws, taskAgentLifecycle, taskAppHealth)
290303

291304
return codersdk.Task{
292305
ID: dbTask.ID,
@@ -319,7 +332,12 @@ func taskFromDBTaskAndWorkspace(dbTask database.Task, ws codersdk.Workspace) cod
319332
// deriveTaskCurrentState determines the current state of a task based on the
320333
// workspace's latest app status and initialization phase.
321334
// Returns nil if no valid state can be determined.
322-
func deriveTaskCurrentState(dbTask database.Task, ws codersdk.Workspace, taskAgentLifecycle *codersdk.WorkspaceAgentLifecycle) *codersdk.TaskStateEntry {
335+
func deriveTaskCurrentState(
336+
dbTask database.Task,
337+
ws codersdk.Workspace,
338+
taskAgentLifecycle *codersdk.WorkspaceAgentLifecycle,
339+
taskAppHealth *codersdk.WorkspaceAppHealth,
340+
) *codersdk.TaskStateEntry {
323341
var currentState *codersdk.TaskStateEntry
324342

325343
// Ignore 'latest app status' if it is older than the latest build and the
@@ -347,11 +365,19 @@ func deriveTaskCurrentState(dbTask database.Task, ws codersdk.Workspace, taskAge
347365
ws.LatestBuild.Status == codersdk.WorkspaceStatusStarting:
348366
message = fmt.Sprintf("Workspace is %s", ws.LatestBuild.Status)
349367
case taskAgentLifecycle != nil:
350-
switch *taskAgentLifecycle {
351-
case codersdk.WorkspaceAgentLifecycleCreated:
368+
switch {
369+
case *taskAgentLifecycle == codersdk.WorkspaceAgentLifecycleCreated:
352370
message = "Agent is connecting"
353-
case codersdk.WorkspaceAgentLifecycleStarting:
371+
case *taskAgentLifecycle == codersdk.WorkspaceAgentLifecycleStarting:
354372
message = "Agent is starting"
373+
case *taskAgentLifecycle == codersdk.WorkspaceAgentLifecycleReady:
374+
if taskAppHealth != nil && *taskAppHealth == codersdk.WorkspaceAppHealthInitializing {
375+
message = "App is initializing"
376+
} else {
377+
// In case the workspace app is not initializing,
378+
// the overall task status should be updated accordingly
379+
message = "Initializing workspace applications"
380+
}
355381
default:
356382
message = "Initializing workspace agent"
357383
}

coderd/aitasks_internal_test.go

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
package coderd
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/google/uuid"
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
11+
"github.com/coder/coder/v2/coderd/util/ptr"
12+
13+
"github.com/coder/coder/v2/coderd/database"
14+
"github.com/coder/coder/v2/codersdk"
15+
)
16+
17+
func TestDeriveTaskCurrentState_Unit(t *testing.T) {
18+
t.Parallel()
19+
20+
now := time.Now()
21+
tests := []struct {
22+
name string
23+
task database.Task
24+
agentLifecycle *codersdk.WorkspaceAgentLifecycle
25+
appHealth *codersdk.WorkspaceAppHealth
26+
latestAppStatus *codersdk.WorkspaceAppStatus
27+
latestBuild codersdk.WorkspaceBuild
28+
expectCurrentState bool
29+
expectedTimestamp time.Time
30+
expectedState codersdk.TaskState
31+
expectedMessage string
32+
}{
33+
{
34+
name: "NoAppStatus",
35+
task: database.Task{
36+
ID: uuid.New(),
37+
Status: database.TaskStatusActive,
38+
},
39+
agentLifecycle: nil,
40+
appHealth: nil,
41+
latestAppStatus: nil,
42+
latestBuild: codersdk.WorkspaceBuild{
43+
Transition: codersdk.WorkspaceTransitionStart,
44+
CreatedAt: now,
45+
},
46+
expectCurrentState: false,
47+
},
48+
{
49+
name: "BuildStartTransition_AppStatus_NewerThanBuild",
50+
task: database.Task{
51+
ID: uuid.New(),
52+
Status: database.TaskStatusActive,
53+
},
54+
agentLifecycle: nil,
55+
appHealth: nil,
56+
latestAppStatus: &codersdk.WorkspaceAppStatus{
57+
State: codersdk.WorkspaceAppStatusStateWorking,
58+
Message: "Task is working",
59+
CreatedAt: now.Add(1 * time.Minute),
60+
},
61+
latestBuild: codersdk.WorkspaceBuild{
62+
Transition: codersdk.WorkspaceTransitionStart,
63+
CreatedAt: now,
64+
},
65+
expectCurrentState: true,
66+
expectedTimestamp: now.Add(1 * time.Minute),
67+
expectedState: codersdk.TaskState(codersdk.WorkspaceAppStatusStateWorking),
68+
expectedMessage: "Task is working",
69+
},
70+
{
71+
name: "BuildStartTransition_StaleAppStatus_OlderThanBuild",
72+
task: database.Task{
73+
ID: uuid.New(),
74+
Status: database.TaskStatusActive,
75+
},
76+
agentLifecycle: nil,
77+
appHealth: nil,
78+
latestAppStatus: &codersdk.WorkspaceAppStatus{
79+
State: codersdk.WorkspaceAppStatusStateComplete,
80+
Message: "Previous task completed",
81+
CreatedAt: now.Add(-1 * time.Minute),
82+
},
83+
latestBuild: codersdk.WorkspaceBuild{
84+
Transition: codersdk.WorkspaceTransitionStart,
85+
CreatedAt: now,
86+
},
87+
expectCurrentState: false,
88+
},
89+
{
90+
name: "BuildStopTransition",
91+
task: database.Task{
92+
ID: uuid.New(),
93+
Status: database.TaskStatusActive,
94+
},
95+
agentLifecycle: nil,
96+
appHealth: nil,
97+
latestAppStatus: &codersdk.WorkspaceAppStatus{
98+
State: codersdk.WorkspaceAppStatusStateComplete,
99+
Message: "Task completed before stop",
100+
CreatedAt: now.Add(-1 * time.Minute),
101+
},
102+
latestBuild: codersdk.WorkspaceBuild{
103+
Transition: codersdk.WorkspaceTransitionStop,
104+
CreatedAt: now,
105+
},
106+
expectCurrentState: true,
107+
expectedTimestamp: now.Add(-1 * time.Minute),
108+
expectedState: codersdk.TaskState(codersdk.WorkspaceAppStatusStateComplete),
109+
expectedMessage: "Task completed before stop",
110+
},
111+
{
112+
name: "TaskInitializing_WorkspacePending",
113+
task: database.Task{
114+
ID: uuid.New(),
115+
Status: database.TaskStatusInitializing,
116+
},
117+
agentLifecycle: nil,
118+
appHealth: nil,
119+
latestAppStatus: nil,
120+
latestBuild: codersdk.WorkspaceBuild{
121+
Status: codersdk.WorkspaceStatusPending,
122+
CreatedAt: now,
123+
},
124+
expectCurrentState: true,
125+
expectedTimestamp: now,
126+
expectedState: codersdk.TaskStateWorking,
127+
expectedMessage: "Workspace is pending",
128+
},
129+
{
130+
name: "TaskInitializing_WorkspaceStarting",
131+
task: database.Task{
132+
ID: uuid.New(),
133+
Status: database.TaskStatusInitializing,
134+
},
135+
agentLifecycle: nil,
136+
appHealth: nil,
137+
latestAppStatus: nil,
138+
latestBuild: codersdk.WorkspaceBuild{
139+
Status: codersdk.WorkspaceStatusStarting,
140+
CreatedAt: now,
141+
},
142+
expectCurrentState: true,
143+
expectedTimestamp: now,
144+
expectedState: codersdk.TaskStateWorking,
145+
expectedMessage: "Workspace is starting",
146+
},
147+
{
148+
name: "TaskInitializing_AgentConnecting",
149+
task: database.Task{
150+
ID: uuid.New(),
151+
Status: database.TaskStatusInitializing,
152+
},
153+
agentLifecycle: ptr.Ref(codersdk.WorkspaceAgentLifecycleCreated),
154+
appHealth: nil,
155+
latestAppStatus: nil,
156+
latestBuild: codersdk.WorkspaceBuild{
157+
Status: codersdk.WorkspaceStatusRunning,
158+
CreatedAt: now,
159+
},
160+
expectCurrentState: true,
161+
expectedTimestamp: now,
162+
expectedState: codersdk.TaskStateWorking,
163+
expectedMessage: "Agent is connecting",
164+
},
165+
{
166+
name: "TaskInitializing_AgentStarting",
167+
task: database.Task{
168+
ID: uuid.New(),
169+
Status: database.TaskStatusInitializing,
170+
},
171+
agentLifecycle: ptr.Ref(codersdk.WorkspaceAgentLifecycleStarting),
172+
appHealth: nil,
173+
latestAppStatus: nil,
174+
latestBuild: codersdk.WorkspaceBuild{
175+
Status: codersdk.WorkspaceStatusRunning,
176+
CreatedAt: now,
177+
},
178+
expectCurrentState: true,
179+
expectedTimestamp: now,
180+
expectedState: codersdk.TaskStateWorking,
181+
expectedMessage: "Agent is starting",
182+
},
183+
{
184+
name: "TaskInitializing_AppInitializing",
185+
task: database.Task{
186+
ID: uuid.New(),
187+
Status: database.TaskStatusInitializing,
188+
},
189+
agentLifecycle: ptr.Ref(codersdk.WorkspaceAgentLifecycleReady),
190+
appHealth: ptr.Ref(codersdk.WorkspaceAppHealthInitializing),
191+
latestAppStatus: nil,
192+
latestBuild: codersdk.WorkspaceBuild{
193+
Status: codersdk.WorkspaceStatusRunning,
194+
CreatedAt: now,
195+
},
196+
expectCurrentState: true,
197+
expectedTimestamp: now,
198+
expectedState: codersdk.TaskStateWorking,
199+
expectedMessage: "App is initializing",
200+
},
201+
}
202+
203+
for _, tt := range tests {
204+
t.Run(tt.name, func(t *testing.T) {
205+
t.Parallel()
206+
207+
ws := codersdk.Workspace{
208+
LatestBuild: tt.latestBuild,
209+
LatestAppStatus: tt.latestAppStatus,
210+
}
211+
212+
currentState := deriveTaskCurrentState(tt.task, ws, tt.agentLifecycle, tt.appHealth)
213+
214+
if tt.expectCurrentState {
215+
require.NotNil(t, currentState)
216+
assert.Equal(t, tt.expectedTimestamp.UTC(), currentState.Timestamp.UTC())
217+
assert.Equal(t, tt.expectedState, currentState.State)
218+
assert.Equal(t, tt.expectedMessage, currentState.Message)
219+
} else {
220+
assert.Nil(t, currentState)
221+
}
222+
})
223+
}
224+
}

0 commit comments

Comments
 (0)