@@ -2,34 +2,42 @@ package reports
2
2
3
3
import (
4
4
"context"
5
+ "database/sql"
5
6
"testing"
7
+ "time"
6
8
7
- "github.com/prometheus/client_golang/prometheus "
9
+ "github.com/google/uuid "
8
10
"github.com/stretchr/testify/require"
9
11
10
12
"cdr.dev/slog"
11
13
"cdr.dev/slog/sloggers/slogtest"
12
14
"github.com/coder/quartz"
13
15
14
- "github.com/coder/coder/v2/coderd/coderdtest"
15
16
"github.com/coder/coder/v2/coderd/database"
16
17
"github.com/coder/coder/v2/coderd/database/dbauthz"
18
+ "github.com/coder/coder/v2/coderd/database/dbgen"
17
19
"github.com/coder/coder/v2/coderd/database/dbtestutil"
20
+ "github.com/coder/coder/v2/coderd/database/pubsub"
18
21
"github.com/coder/coder/v2/coderd/notifications"
19
22
"github.com/coder/coder/v2/coderd/rbac"
20
23
"github.com/coder/coder/v2/testutil"
21
24
)
22
25
26
+ const dayDuration = 24 * time .Hour
27
+
28
+ var (
29
+ jobError = sql.NullString {String : "badness" , Valid : true }
30
+ jobErrorCode = sql.NullString {String : "ERR-42" , Valid : true }
31
+ )
32
+
23
33
func TestReportFailedWorkspaceBuilds (t * testing.T ) {
24
34
t .Parallel ()
25
35
26
36
t .Run ("InitialState_NoBuilds_NoReport" , func (t * testing.T ) {
27
37
t .Parallel ()
28
38
29
39
// Setup
30
- logger , db , notifEnq , clk := setup (t )
31
- // nolint:gocritic // reportFailedWorkspaceBuilds is called by system.
32
- ctx := dbauthz .AsSystemRestricted (context .Background ())
40
+ ctx , logger , db , _ , notifEnq , clk := setup (t )
33
41
34
42
// When
35
43
err := reportFailedWorkspaceBuilds (ctx , logger , db , notifEnq , clk )
@@ -40,7 +48,116 @@ func TestReportFailedWorkspaceBuilds(t *testing.T) {
40
48
41
49
t .Run ("FailedBuilds_TemplateAdminOptIn_FirstRun_Report_SecondRunTooEarly_NoReport_ThirdRun_Report" , func (t * testing.T ) {
42
50
t .Parallel ()
43
- // TODO
51
+
52
+ // Setup
53
+ ctx , logger , db , ps , notifEnq , clk := setup (t )
54
+
55
+ // Given
56
+ // Organization
57
+ org := dbgen .Organization (t , db , database.Organization {})
58
+
59
+ // Template admins
60
+ templateAdmin1 := dbgen .User (t , db , database.User {Username : "template-admin-1" , RBACRoles : []string {rbac .RoleTemplateAdmin ().Name }})
61
+ _ = dbgen .OrganizationMember (t , db , database.OrganizationMember {UserID : templateAdmin1 .ID , OrganizationID : org .ID })
62
+ templateAdmin2 := dbgen .User (t , db , database.User {Username : "template-admin-2" , RBACRoles : []string {rbac .RoleTemplateAdmin ().Name }})
63
+ _ = dbgen .OrganizationMember (t , db , database.OrganizationMember {UserID : templateAdmin2 .ID , OrganizationID : org .ID })
64
+ _ = dbgen .User (t , db , database.User {Name : "template-admin-3" , RBACRoles : []string {rbac .RoleTemplateAdmin ().Name }})
65
+ // template admin in some other org
66
+
67
+ // Regular users
68
+ user1 := dbgen .User (t , db , database.User {})
69
+ _ = dbgen .OrganizationMember (t , db , database.OrganizationMember {UserID : user1 .ID , OrganizationID : org .ID })
70
+ user2 := dbgen .User (t , db , database.User {})
71
+ _ = dbgen .OrganizationMember (t , db , database.OrganizationMember {UserID : user2 .ID , OrganizationID : org .ID })
72
+ user3 := dbgen .User (t , db , database.User {})
73
+ // user in some other org
74
+
75
+ // Templates
76
+ t1 := dbgen .Template (t , db , database.Template {Name : "template-1" , DisplayName : "First Template" , OrganizationID : org .ID })
77
+ t2 := dbgen .Template (t , db , database.Template {Name : "template-2" , OrganizationID : org .ID })
78
+
79
+ // Template versions
80
+ t1v1 := dbgen .TemplateVersion (t , db , database.TemplateVersion {Name : "template-1-version-1" , OrganizationID : org .ID , TemplateID : uuid.NullUUID {UUID : t1 .ID , Valid : true }, JobID : uuid .New ()})
81
+ t1v2 := dbgen .TemplateVersion (t , db , database.TemplateVersion {Name : "template-1-version-2" , OrganizationID : org .ID , TemplateID : uuid.NullUUID {UUID : t1 .ID , Valid : true }, JobID : uuid .New ()})
82
+ t2v1 := dbgen .TemplateVersion (t , db , database.TemplateVersion {Name : "template-2-version-1" , OrganizationID : org .ID , TemplateID : uuid.NullUUID {UUID : t2 .ID , Valid : true }, JobID : uuid .New ()})
83
+ t2v2 := dbgen .TemplateVersion (t , db , database.TemplateVersion {Name : "template-1-version-1" , OrganizationID : org .ID , TemplateID : uuid.NullUUID {UUID : t2 .ID , Valid : true }, JobID : uuid .New ()})
84
+
85
+ // Workspaces
86
+ w1 := dbgen .Workspace (t , db , database.Workspace {TemplateID : t1 .ID , OwnerID : user1 .ID , OrganizationID : org .ID })
87
+ w2 := dbgen .Workspace (t , db , database.Workspace {TemplateID : t2 .ID , OwnerID : user2 .ID , OrganizationID : org .ID })
88
+ w3 := dbgen .Workspace (t , db , database.Workspace {TemplateID : t1 .ID , OwnerID : user3 .ID , OrganizationID : org .ID })
89
+ w4 := dbgen .Workspace (t , db , database.Workspace {TemplateID : t2 .ID , OwnerID : user2 .ID , OrganizationID : org .ID })
90
+
91
+ now := clk .Now ()
92
+
93
+ // Workspace builds
94
+ w1wb1pj := dbgen .ProvisionerJob (t , db , ps , database.ProvisionerJob {OrganizationID : org .ID , Error : jobError , ErrorCode : jobErrorCode , CompletedAt : sql.NullTime {Time : now .Add (- 6 * dayDuration ), Valid : true }})
95
+ _ = dbgen .WorkspaceBuild (t , db , database.WorkspaceBuild {WorkspaceID : w1 .ID , TemplateVersionID : t1v1 .ID , JobID : w1wb1pj .ID , Transition : database .WorkspaceTransitionStart , Reason : database .BuildReasonInitiator })
96
+ w1wb2pj := dbgen .ProvisionerJob (t , db , ps , database.ProvisionerJob {OrganizationID : org .ID , CompletedAt : sql.NullTime {Time : now .Add (- 5 * dayDuration ), Valid : true }})
97
+ _ = dbgen .WorkspaceBuild (t , db , database.WorkspaceBuild {WorkspaceID : w1 .ID , TemplateVersionID : t1v2 .ID , JobID : w1wb2pj .ID , Transition : database .WorkspaceTransitionStart , Reason : database .BuildReasonInitiator })
98
+ w1wb3pj := dbgen .ProvisionerJob (t , db , ps , database.ProvisionerJob {OrganizationID : org .ID , Error : jobError , ErrorCode : jobErrorCode , CompletedAt : sql.NullTime {Time : now .Add (- 4 * dayDuration ), Valid : true }})
99
+ _ = dbgen .WorkspaceBuild (t , db , database.WorkspaceBuild {WorkspaceID : w1 .ID , TemplateVersionID : t1v2 .ID , JobID : w1wb3pj .ID , Transition : database .WorkspaceTransitionStart , Reason : database .BuildReasonInitiator })
100
+
101
+ w2wb1pj := dbgen .ProvisionerJob (t , db , ps , database.ProvisionerJob {OrganizationID : org .ID , CompletedAt : sql.NullTime {Time : now .Add (- 5 * dayDuration ), Valid : true }})
102
+ _ = dbgen .WorkspaceBuild (t , db , database.WorkspaceBuild {WorkspaceID : w2 .ID , TemplateVersionID : t2v1 .ID , JobID : w2wb1pj .ID , Transition : database .WorkspaceTransitionStart , Reason : database .BuildReasonInitiator })
103
+ w2wb2pj := dbgen .ProvisionerJob (t , db , ps , database.ProvisionerJob {OrganizationID : org .ID , Error : jobError , ErrorCode : jobErrorCode , CompletedAt : sql.NullTime {Time : now .Add (- 4 * dayDuration ), Valid : true }})
104
+ _ = dbgen .WorkspaceBuild (t , db , database.WorkspaceBuild {WorkspaceID : w2 .ID , TemplateVersionID : t2v2 .ID , JobID : w2wb2pj .ID , Transition : database .WorkspaceTransitionStart , Reason : database .BuildReasonInitiator })
105
+ w2wb3pj := dbgen .ProvisionerJob (t , db , ps , database.ProvisionerJob {OrganizationID : org .ID , Error : jobError , ErrorCode : jobErrorCode , CompletedAt : sql.NullTime {Time : now .Add (- 3 * dayDuration ), Valid : true }})
106
+ _ = dbgen .WorkspaceBuild (t , db , database.WorkspaceBuild {WorkspaceID : w2 .ID , TemplateVersionID : t2v2 .ID , JobID : w2wb3pj .ID , Transition : database .WorkspaceTransitionStart , Reason : database .BuildReasonInitiator })
107
+
108
+ w3wb1pj := dbgen .ProvisionerJob (t , db , ps , database.ProvisionerJob {OrganizationID : org .ID , Error : jobError , ErrorCode : jobErrorCode , CompletedAt : sql.NullTime {Time : now .Add (- 3 * dayDuration ), Valid : true }})
109
+ _ = dbgen .WorkspaceBuild (t , db , database.WorkspaceBuild {WorkspaceID : w3 .ID , TemplateVersionID : t1v1 .ID , JobID : w3wb1pj .ID , Transition : database .WorkspaceTransitionStart , Reason : database .BuildReasonInitiator })
110
+
111
+ w4wb1pj := dbgen .ProvisionerJob (t , db , ps , database.ProvisionerJob {OrganizationID : org .ID , Error : jobError , ErrorCode : jobErrorCode , CompletedAt : sql.NullTime {Time : now .Add (- 6 * dayDuration ), Valid : true }})
112
+ _ = dbgen .WorkspaceBuild (t , db , database.WorkspaceBuild {WorkspaceID : w4 .ID , TemplateVersionID : t2v1 .ID , JobID : w4wb1pj .ID , Transition : database .WorkspaceTransitionStart , Reason : database .BuildReasonInitiator })
113
+ w4wb2pj := dbgen .ProvisionerJob (t , db , ps , database.ProvisionerJob {OrganizationID : org .ID , CompletedAt : sql.NullTime {Time : now .Add (- 1 * dayDuration ), Valid : true }})
114
+ _ = dbgen .WorkspaceBuild (t , db , database.WorkspaceBuild {WorkspaceID : w4 .ID , TemplateVersionID : t2v2 .ID , JobID : w4wb2pj .ID , Transition : database .WorkspaceTransitionStart , Reason : database .BuildReasonInitiator })
115
+
116
+ // Database is ready, so we can clear notifications queue
117
+ notifEnq .Clear ()
118
+
119
+ // When
120
+ err := reportFailedWorkspaceBuilds (ctx , logger , db , notifEnq , clk )
121
+
122
+ // Then
123
+ require .NoError (t , err )
124
+
125
+ require .Len (t , notifEnq .Sent , 4 ) // 2 templates, 2 template admins
126
+ require .Equal (t , notifEnq .Sent [0 ].UserID , templateAdmin1 .ID )
127
+ require .Equal (t , notifEnq .Sent [0 ].TemplateID , notifications .TemplateWorkspaceBuildsFailedReport )
128
+ require .Equal (t , notifEnq .Sent [0 ].Labels ["template_name" ], t1 .Name )
129
+ require .Equal (t , notifEnq .Sent [0 ].Labels ["template_display_name" ], t1 .DisplayName )
130
+ require .Equal (t , notifEnq .Sent [0 ].Data ["failed_builds" ], int64 (3 ))
131
+ require .Equal (t , notifEnq .Sent [0 ].Data ["total_builds" ], int64 (4 ))
132
+ require .Equal (t , notifEnq .Sent [0 ].Data ["report_frequency" ], "week" )
133
+ // require.Contains(t, notifEnq.Sent[0].Data["template_versions"], "?")
134
+
135
+ require .Equal (t , notifEnq .Sent [1 ].UserID , templateAdmin2 .ID )
136
+ require .Equal (t , notifEnq .Sent [1 ].TemplateID , notifications .TemplateWorkspaceBuildsFailedReport )
137
+ require .Equal (t , notifEnq .Sent [1 ].Labels ["template_name" ], t1 .Name )
138
+ require .Equal (t , notifEnq .Sent [1 ].Labels ["template_display_name" ], t1 .DisplayName )
139
+ require .Equal (t , notifEnq .Sent [1 ].Data ["failed_builds" ], int64 (3 ))
140
+ require .Equal (t , notifEnq .Sent [1 ].Data ["total_builds" ], int64 (4 ))
141
+ require .Equal (t , notifEnq .Sent [1 ].Data ["report_frequency" ], "week" )
142
+ // require.Contains(t, notifEnq.Sent[1].Data["template_versions"], "?")
143
+
144
+ require .Equal (t , notifEnq .Sent [2 ].UserID , templateAdmin1 .ID )
145
+ require .Equal (t , notifEnq .Sent [2 ].TemplateID , notifications .TemplateWorkspaceBuildsFailedReport )
146
+ require .Equal (t , notifEnq .Sent [2 ].Labels ["template_name" ], t2 .Name )
147
+ require .Equal (t , notifEnq .Sent [2 ].Labels ["template_display_name" ], t2 .DisplayName )
148
+ require .Equal (t , notifEnq .Sent [2 ].Data ["failed_builds" ], int64 (3 ))
149
+ require .Equal (t , notifEnq .Sent [2 ].Data ["total_builds" ], int64 (5 ))
150
+ require .Equal (t , notifEnq .Sent [2 ].Data ["report_frequency" ], "week" )
151
+ // require.Contains(t, notifEnq.Sent[0].Data["template_versions"], "?")
152
+
153
+ require .Equal (t , notifEnq .Sent [3 ].UserID , templateAdmin2 .ID )
154
+ require .Equal (t , notifEnq .Sent [3 ].TemplateID , notifications .TemplateWorkspaceBuildsFailedReport )
155
+ require .Equal (t , notifEnq .Sent [3 ].Labels ["template_name" ], t2 .Name )
156
+ require .Equal (t , notifEnq .Sent [3 ].Labels ["template_display_name" ], t2 .DisplayName )
157
+ require .Equal (t , notifEnq .Sent [3 ].Data ["failed_builds" ], int64 (3 ))
158
+ require .Equal (t , notifEnq .Sent [3 ].Data ["total_builds" ], int64 (5 ))
159
+ require .Equal (t , notifEnq .Sent [3 ].Data ["report_frequency" ], "week" )
160
+ // require.Contains(t, notifEnq.Sent[0].Data["template_versions"], "?")
44
161
})
45
162
46
163
t .Run ("NoFailedBuilds_TemplateAdminIn_NoReport" , func (t * testing.T ) {
@@ -59,13 +176,16 @@ func TestReportFailedWorkspaceBuilds(t *testing.T) {
59
176
})
60
177
}
61
178
62
- func setup (t * testing.T ) (slog.Logger , database.Store , notifications. Enqueuer , quartz.Clock ) {
179
+ func setup (t * testing.T ) (context. Context , slog.Logger , database.Store , pubsub. Pubsub , * testutil. FakeNotificationsEnqueuer , quartz.Clock ) {
63
180
t .Helper ()
64
181
182
+ // nolint:gocritic // reportFailedWorkspaceBuilds is called by system.
183
+ ctx := dbauthz .AsSystemRestricted (context .Background ())
65
184
logger := slogtest .Make (t , & slogtest.Options {})
66
- rdb , _ := dbtestutil .NewDB (t )
67
- db := dbauthz .New (rdb , rbac .NewAuthorizer (prometheus .NewRegistry ()), logger , coderdtest .AccessControlStorePointer ())
185
+ db , ps := dbtestutil .NewDB (t )
186
+ // does not work with works
187
+ // db := dbauthz.New(rdb, rbac.NewAuthorizer(prometheus.NewRegistry()), logger, coderdtest.AccessControlStorePointer())
68
188
notifyEnq := & testutil.FakeNotificationsEnqueuer {}
69
189
clk := quartz .NewMock (t )
70
- return logger , db , notifyEnq , clk
190
+ return ctx , logger , db , ps , notifyEnq , clk
71
191
}
0 commit comments