@@ -2,6 +2,7 @@ package coderd_test
22
33import (
44 "context"
5+ "database/sql"
56 "encoding/json"
67 "io"
78 "net/http"
@@ -1184,6 +1185,7 @@ func TestTasksNotification(t *testing.T) {
11841185 isNotificationSent bool
11851186 notificationTemplate uuid.UUID
11861187 taskPrompt string
1188+ agentLifecycle database.WorkspaceAgentLifecycleState
11871189 }{
11881190 // Should not send a notification when the agent app is not an AI task.
11891191 {
@@ -1231,6 +1233,7 @@ func TestTasksNotification(t *testing.T) {
12311233 isNotificationSent : true ,
12321234 notificationTemplate : notifications .TemplateTaskIdle ,
12331235 taskPrompt : "InitialTemplateTaskIdle" ,
1236+ agentLifecycle : database .WorkspaceAgentLifecycleStateReady ,
12341237 },
12351238 // Should send TemplateTaskWorking when the AI task transitions to 'Working' from 'Idle'.
12361239 {
@@ -1244,6 +1247,7 @@ func TestTasksNotification(t *testing.T) {
12441247 isNotificationSent : true ,
12451248 notificationTemplate : notifications .TemplateTaskWorking ,
12461249 taskPrompt : "TemplateTaskWorkingFromIdle" ,
1250+ agentLifecycle : database .WorkspaceAgentLifecycleStateReady ,
12471251 },
12481252 // Should send TemplateTaskIdle when the AI task transitions to 'Idle'.
12491253 {
@@ -1254,6 +1258,7 @@ func TestTasksNotification(t *testing.T) {
12541258 isNotificationSent : true ,
12551259 notificationTemplate : notifications .TemplateTaskIdle ,
12561260 taskPrompt : "TemplateTaskIdle" ,
1261+ agentLifecycle : database .WorkspaceAgentLifecycleStateReady ,
12571262 },
12581263 // Long task prompts should be truncated to 160 characters.
12591264 {
@@ -1264,6 +1269,7 @@ func TestTasksNotification(t *testing.T) {
12641269 isNotificationSent : true ,
12651270 notificationTemplate : notifications .TemplateTaskIdle ,
12661271 taskPrompt : "This is a very long task prompt that should be truncated to 160 characters. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." ,
1272+ agentLifecycle : database .WorkspaceAgentLifecycleStateReady ,
12671273 },
12681274 // Should send TemplateTaskCompleted when the AI task transitions to 'Complete'.
12691275 {
@@ -1274,6 +1280,7 @@ func TestTasksNotification(t *testing.T) {
12741280 isNotificationSent : true ,
12751281 notificationTemplate : notifications .TemplateTaskCompleted ,
12761282 taskPrompt : "TemplateTaskCompleted" ,
1283+ agentLifecycle : database .WorkspaceAgentLifecycleStateReady ,
12771284 },
12781285 // Should send TemplateTaskFailed when the AI task transitions to 'Failure'.
12791286 {
@@ -1284,6 +1291,7 @@ func TestTasksNotification(t *testing.T) {
12841291 isNotificationSent : true ,
12851292 notificationTemplate : notifications .TemplateTaskFailed ,
12861293 taskPrompt : "TemplateTaskFailed" ,
1294+ agentLifecycle : database .WorkspaceAgentLifecycleStateReady ,
12871295 },
12881296 // Should send TemplateTaskCompleted when the AI task transitions from 'Idle' to 'Complete'.
12891297 {
@@ -1294,6 +1302,7 @@ func TestTasksNotification(t *testing.T) {
12941302 isNotificationSent : true ,
12951303 notificationTemplate : notifications .TemplateTaskCompleted ,
12961304 taskPrompt : "TemplateTaskCompletedFromIdle" ,
1305+ agentLifecycle : database .WorkspaceAgentLifecycleStateReady ,
12971306 },
12981307 // Should send TemplateTaskFailed when the AI task transitions from 'Idle' to 'Failure'.
12991308 {
@@ -1304,6 +1313,7 @@ func TestTasksNotification(t *testing.T) {
13041313 isNotificationSent : true ,
13051314 notificationTemplate : notifications .TemplateTaskFailed ,
13061315 taskPrompt : "TemplateTaskFailedFromIdle" ,
1316+ agentLifecycle : database .WorkspaceAgentLifecycleStateReady ,
13071317 },
13081318 // Should NOT send notification when transitioning from 'Complete' to 'Complete' (no change).
13091319 {
@@ -1323,6 +1333,37 @@ func TestTasksNotification(t *testing.T) {
13231333 isNotificationSent : false ,
13241334 taskPrompt : "NoNotificationFailureToFailure" ,
13251335 },
1336+ // Should NOT send notification when agent is in 'starting' lifecycle state (agent startup).
1337+ {
1338+ name : "AgentStarting_NoNotification" ,
1339+ latestAppStatuses : nil ,
1340+ newAppStatus : codersdk .WorkspaceAppStatusStateIdle ,
1341+ isAITask : true ,
1342+ isNotificationSent : false ,
1343+ taskPrompt : "AgentStarting_NoNotification" ,
1344+ agentLifecycle : database .WorkspaceAgentLifecycleStateStarting ,
1345+ },
1346+ // Should NOT send notification when agent is in 'created' lifecycle state (agent not started).
1347+ {
1348+ name : "AgentCreated_NoNotification" ,
1349+ latestAppStatuses : []codersdk.WorkspaceAppStatusState {codersdk .WorkspaceAppStatusStateWorking },
1350+ newAppStatus : codersdk .WorkspaceAppStatusStateIdle ,
1351+ isAITask : true ,
1352+ isNotificationSent : false ,
1353+ taskPrompt : "AgentCreated_NoNotification" ,
1354+ agentLifecycle : database .WorkspaceAgentLifecycleStateCreated ,
1355+ },
1356+ // Should send notification when agent is in 'ready' lifecycle state (agent fully started).
1357+ {
1358+ name : "AgentReady_SendNotification" ,
1359+ latestAppStatuses : []codersdk.WorkspaceAppStatusState {codersdk .WorkspaceAppStatusStateWorking },
1360+ newAppStatus : codersdk .WorkspaceAppStatusStateIdle ,
1361+ isAITask : true ,
1362+ isNotificationSent : true ,
1363+ notificationTemplate : notifications .TemplateTaskIdle ,
1364+ taskPrompt : "AgentReady_SendNotification" ,
1365+ agentLifecycle : database .WorkspaceAgentLifecycleStateReady ,
1366+ },
13261367 } {
13271368 t .Run (tc .name , func (t * testing.T ) {
13281369 t .Parallel ()
@@ -1367,6 +1408,32 @@ func TestTasksNotification(t *testing.T) {
13671408 }
13681409 workspaceBuild := workspaceBuilder .Do ()
13691410
1411+ // Given: set the agent lifecycle state if specified
1412+ if tc .agentLifecycle != "" {
1413+ workspace := coderdtest .MustWorkspace (t , client , workspaceBuild .Workspace .ID )
1414+ agentID := workspace .LatestBuild .Resources [0 ].Agents [0 ].ID
1415+
1416+ var (
1417+ startedAt sql.NullTime
1418+ readyAt sql.NullTime
1419+ )
1420+ if tc .agentLifecycle == database .WorkspaceAgentLifecycleStateReady {
1421+ startedAt = sql.NullTime {Time : dbtime .Now (), Valid : true }
1422+ readyAt = sql.NullTime {Time : dbtime .Now (), Valid : true }
1423+ } else if tc .agentLifecycle == database .WorkspaceAgentLifecycleStateStarting {
1424+ startedAt = sql.NullTime {Time : dbtime .Now (), Valid : true }
1425+ }
1426+
1427+ // nolint:gocritic // This is a system restricted operation for test setup.
1428+ err := db .UpdateWorkspaceAgentLifecycleStateByID (dbauthz .AsSystemRestricted (ctx ), database.UpdateWorkspaceAgentLifecycleStateByIDParams {
1429+ ID : agentID ,
1430+ LifecycleState : tc .agentLifecycle ,
1431+ StartedAt : startedAt ,
1432+ ReadyAt : readyAt ,
1433+ })
1434+ require .NoError (t , err )
1435+ }
1436+
13701437 // Given: the workspace agent app has previous statuses
13711438 agentClient := agentsdk .New (client .URL , agentsdk .WithFixedToken (workspaceBuild .AgentToken ))
13721439 if len (tc .latestAppStatuses ) > 0 {
0 commit comments