@@ -115,7 +115,7 @@ func TestReportFailedWorkspaceBuilds(t *testing.T) {
115
115
require .Empty (t , notifEnq .Sent )
116
116
})
117
117
118
- t .Run ("FailedBuilds_FirstRun_Report_SecondRunTooEarly_NoReport_ThirdRun_Report " , func (t * testing.T ) {
118
+ t .Run ("FailedBuilds_SecondRun_Report_ThirdRunTooEarly_NoReport_FourthRun_Report " , func (t * testing.T ) {
119
119
t .Parallel ()
120
120
121
121
verifyNotification := func (t * testing.T , recipient database.User , notif * testutil.Notification , tmpl database.Template , failedBuilds , totalBuilds int64 , templateVersions []map [string ]interface {}) {
@@ -290,6 +290,118 @@ func TestReportFailedWorkspaceBuilds(t *testing.T) {
290
290
}
291
291
})
292
292
293
+ t .Run ("TooManyFailedBuilds_SecondRun_Report" , func (t * testing.T ) {
294
+ t .Parallel ()
295
+
296
+ verifyNotification := func (t * testing.T , recipient database.User , notif * testutil.Notification , tmpl database.Template , failedBuilds , totalBuilds int64 , templateVersions []map [string ]interface {}) {
297
+ t .Helper ()
298
+
299
+ require .Equal (t , recipient .ID , notif .UserID )
300
+ require .Equal (t , notifications .TemplateWorkspaceBuildsFailedReport , notif .TemplateID )
301
+ require .Equal (t , tmpl .Name , notif .Labels ["template_name" ])
302
+ require .Equal (t , tmpl .DisplayName , notif .Labels ["template_display_name" ])
303
+ require .Equal (t , failedBuilds , notif .Data ["failed_builds" ])
304
+ require .Equal (t , totalBuilds , notif .Data ["total_builds" ])
305
+ require .Equal (t , "week" , notif .Data ["report_frequency" ])
306
+ require .Equal (t , templateVersions , notif .Data ["template_versions" ])
307
+ }
308
+
309
+ // Setup
310
+ ctx , logger , db , ps , notifEnq , clk := setup (t )
311
+
312
+ // Given
313
+
314
+ // Organization
315
+ org := dbgen .Organization (t , db , database.Organization {})
316
+
317
+ // Template admins
318
+ templateAdmin1 := dbgen .User (t , db , database.User {Username : "template-admin-1" , RBACRoles : []string {rbac .RoleTemplateAdmin ().Name }})
319
+ _ = dbgen .OrganizationMember (t , db , database.OrganizationMember {UserID : templateAdmin1 .ID , OrganizationID : org .ID })
320
+
321
+ // Regular users
322
+ user1 := dbgen .User (t , db , database.User {})
323
+ _ = dbgen .OrganizationMember (t , db , database.OrganizationMember {UserID : user1 .ID , OrganizationID : org .ID })
324
+
325
+ // Templates
326
+ t1 := dbgen .Template (t , db , database.Template {Name : "template-1" , DisplayName : "First Template" , CreatedBy : templateAdmin1 .ID , OrganizationID : org .ID })
327
+
328
+ // Template versions
329
+ t1v1 := dbgen .TemplateVersion (t , db , database.TemplateVersion {Name : "template-1-version-1" , CreatedBy : templateAdmin1 .ID , OrganizationID : org .ID , TemplateID : uuid.NullUUID {UUID : t1 .ID , Valid : true }, JobID : uuid .New ()})
330
+ t1v2 := dbgen .TemplateVersion (t , db , database.TemplateVersion {Name : "template-1-version-2" , CreatedBy : templateAdmin1 .ID , OrganizationID : org .ID , TemplateID : uuid.NullUUID {UUID : t1 .ID , Valid : true }, JobID : uuid .New ()})
331
+
332
+ // Workspaces
333
+ w1 := dbgen .Workspace (t , db , database.Workspace {TemplateID : t1 .ID , OwnerID : user1 .ID , OrganizationID : org .ID })
334
+
335
+ // When: first run
336
+ notifEnq .Clear ()
337
+ err := reportFailedWorkspaceBuilds (ctx , logger , db , notifEnq , clk )
338
+
339
+ // Then
340
+ require .NoError (t , err )
341
+ require .Empty (t , notifEnq .Sent ) // no notifications
342
+
343
+ // One week later...
344
+ clk .Advance (failedWorkspaceBuildsReportFrequency + time .Minute )
345
+ now := clk .Now ()
346
+
347
+ // Workspace builds
348
+ pj0 := dbgen .ProvisionerJob (t , db , ps , database.ProvisionerJob {OrganizationID : org .ID , CompletedAt : sql.NullTime {Time : now .Add (- 24 * time .Hour ), Valid : true }})
349
+ _ = dbgen .WorkspaceBuild (t , db , database.WorkspaceBuild {WorkspaceID : w1 .ID , BuildNumber : 777 , TemplateVersionID : t1v1 .ID , JobID : pj0 .ID , CreatedAt : now .Add (- 24 * time .Hour ), Transition : database .WorkspaceTransitionStart , Reason : database .BuildReasonInitiator })
350
+
351
+ for i := 1 ; i <= 23 ; i ++ {
352
+ at := now .Add (- time .Duration (i ) * time .Hour )
353
+
354
+ pj1 := dbgen .ProvisionerJob (t , db , ps , database.ProvisionerJob {OrganizationID : org .ID , Error : jobError , ErrorCode : jobErrorCode , CompletedAt : sql.NullTime {Time : at , Valid : true }})
355
+ _ = dbgen .WorkspaceBuild (t , db , database.WorkspaceBuild {WorkspaceID : w1 .ID , BuildNumber : int32 (i ), TemplateVersionID : t1v1 .ID , JobID : pj1 .ID , CreatedAt : at , Transition : database .WorkspaceTransitionStart , Reason : database .BuildReasonInitiator })
356
+
357
+ pj2 := dbgen .ProvisionerJob (t , db , ps , database.ProvisionerJob {OrganizationID : org .ID , Error : jobError , ErrorCode : jobErrorCode , CompletedAt : sql.NullTime {Time : at , Valid : true }})
358
+ _ = dbgen .WorkspaceBuild (t , db , database.WorkspaceBuild {WorkspaceID : w1 .ID , BuildNumber : int32 (i ) + 100 , TemplateVersionID : t1v2 .ID , JobID : pj2 .ID , CreatedAt : at , Transition : database .WorkspaceTransitionStart , Reason : database .BuildReasonInitiator })
359
+ }
360
+
361
+ // When
362
+ notifEnq .Clear ()
363
+ err = reportFailedWorkspaceBuilds (ctx , logger , authedDB (t , db , logger ), notifEnq , clk )
364
+
365
+ // Then
366
+ require .NoError (t , err )
367
+
368
+ require .Len (t , notifEnq .Sent , 1 ) // 1 template, 1 template admin
369
+ verifyNotification (t , templateAdmin1 , notifEnq .Sent [0 ], t1 , 46 , 47 , []map [string ]interface {}{
370
+ {
371
+ "failed_builds" : []map [string ]interface {}{
372
+ {"build_number" : int32 (23 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
373
+ {"build_number" : int32 (22 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
374
+ {"build_number" : int32 (21 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
375
+ {"build_number" : int32 (20 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
376
+ {"build_number" : int32 (19 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
377
+ {"build_number" : int32 (18 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
378
+ {"build_number" : int32 (17 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
379
+ {"build_number" : int32 (16 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
380
+ {"build_number" : int32 (15 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
381
+ {"build_number" : int32 (14 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
382
+ },
383
+ "failed_count" : 23 ,
384
+ "template_version_name" : t1v1 .Name ,
385
+ },
386
+ {
387
+ "failed_builds" : []map [string ]interface {}{
388
+ {"build_number" : int32 (123 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
389
+ {"build_number" : int32 (122 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
390
+ {"build_number" : int32 (121 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
391
+ {"build_number" : int32 (120 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
392
+ {"build_number" : int32 (119 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
393
+ {"build_number" : int32 (118 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
394
+ {"build_number" : int32 (117 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
395
+ {"build_number" : int32 (116 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
396
+ {"build_number" : int32 (115 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
397
+ {"build_number" : int32 (114 ), "workspace_name" : w1 .Name , "workspace_owner_username" : user1 .Username },
398
+ },
399
+ "failed_count" : 23 ,
400
+ "template_version_name" : t1v2 .Name ,
401
+ },
402
+ })
403
+ })
404
+
293
405
t .Run ("NoFailedBuilds_NoReport" , func (t * testing.T ) {
294
406
t .Parallel ()
295
407
0 commit comments