@@ -3,7 +3,8 @@ package cli_test
3
3
import (
4
4
"bytes"
5
5
"context"
6
- "fmt"
6
+ "database/sql"
7
+ "sort"
7
8
"strings"
8
9
"testing"
9
10
"time"
@@ -14,101 +15,229 @@ import (
14
15
"github.com/coder/coder/v2/cli/clitest"
15
16
"github.com/coder/coder/v2/coderd/coderdtest"
16
17
"github.com/coder/coder/v2/coderd/database"
17
- "github.com/coder/coder/v2/coderd/util/ptr"
18
+ "github.com/coder/coder/v2/coderd/database/dbfake"
19
+ "github.com/coder/coder/v2/coderd/schedule/cron"
18
20
"github.com/coder/coder/v2/codersdk"
21
+ "github.com/coder/coder/v2/pty/ptytest"
22
+ "github.com/coder/coder/v2/testutil"
19
23
)
20
24
25
+ func setupTestSchedule (t * testing.T , sched * cron.Schedule ) (ownerClient , memberClient * codersdk.Client , db database.Store ) {
26
+ ownerClient , db = coderdtest .NewWithDatabase (t , nil )
27
+ owner := coderdtest .CreateFirstUser (t , ownerClient )
28
+ memberClient , memberUser := coderdtest .CreateAnotherUserMutators (t , ownerClient , owner .OrganizationID , nil , func (r * codersdk.CreateUserRequest ) {
29
+ r .Username = "testuser2" // ensure deterministic ordering
30
+ })
31
+ _ , _ = dbfake .WorkspaceWithAgent (t , db , database.Workspace {
32
+ Name : "a-owner" ,
33
+ OwnerID : owner .UserID ,
34
+ OrganizationID : owner .OrganizationID ,
35
+ AutostartSchedule : sql.NullString {String : sched .String (), Valid : true },
36
+ Ttl : sql.NullInt64 {Int64 : 8 * time .Hour .Nanoseconds (), Valid : true },
37
+ })
38
+ _ , _ = dbfake .WorkspaceWithAgent (t , db , database.Workspace {
39
+ Name : "b-owner" ,
40
+ OwnerID : owner .UserID ,
41
+ OrganizationID : owner .OrganizationID ,
42
+ AutostartSchedule : sql.NullString {String : sched .String (), Valid : true },
43
+ })
44
+ _ , _ = dbfake .WorkspaceWithAgent (t , db , database.Workspace {
45
+ Name : "c-member" ,
46
+ OwnerID : memberUser .ID ,
47
+ OrganizationID : owner .OrganizationID ,
48
+ Ttl : sql.NullInt64 {Int64 : 8 * time .Hour .Nanoseconds (), Valid : true },
49
+ })
50
+ _ , _ = dbfake .WorkspaceWithAgent (t , db , database.Workspace {
51
+ Name : "d-member" ,
52
+ OwnerID : memberUser .ID ,
53
+ OrganizationID : owner .OrganizationID ,
54
+ })
55
+
56
+ return ownerClient , memberClient , db
57
+ }
58
+
21
59
func TestScheduleShow (t * testing.T ) {
22
60
t .Parallel ()
23
- t .Run ("Enabled" , func (t * testing.T ) {
61
+
62
+ // Given
63
+ sched , err := cron .Weekly ("CRON_TZ=Europe/Dublin 30 7 * * Mon-Fri" )
64
+ require .NoError (t , err , "invalid schedule" )
65
+ ownerClient , memberClient , _ := setupTestSchedule (t , sched )
66
+ now := time .Now ()
67
+
68
+ // Need this for LatestBuild.Deadline
69
+ ws , err := ownerClient .Workspaces (context .Background (), codersdk.WorkspaceFilter {})
70
+ require .NoError (t , err )
71
+ require .Len (t , ws .Workspaces , 4 )
72
+ // Ensure same order as in CLI output
73
+ sort .Slice (ws .Workspaces , func (i , j int ) bool {
74
+ a := ws .Workspaces [i ].OwnerName + "/" + ws .Workspaces [i ].Name
75
+ b := ws .Workspaces [j ].OwnerName + "/" + ws .Workspaces [j ].Name
76
+ return a < b
77
+ })
78
+
79
+ t .Run ("OwnerNoArgs" , func (t * testing.T ) {
24
80
t .Parallel ()
25
81
26
- var (
27
- tz = "Europe/Dublin"
28
- sched = "30 7 * * 1-5"
29
- schedCron = fmt .Sprintf ("CRON_TZ=%s %s" , tz , sched )
30
- ttl = 8 * time .Hour
31
- client = coderdtest .New (t , & coderdtest.Options {IncludeProvisionerDaemon : true })
32
- user = coderdtest .CreateFirstUser (t , client )
33
- version = coderdtest .CreateTemplateVersion (t , client , user .OrganizationID , nil )
34
- _ = coderdtest .AwaitTemplateVersionJobCompleted (t , client , version .ID )
35
- project = coderdtest .CreateTemplate (t , client , user .OrganizationID , version .ID )
36
- workspace = coderdtest .CreateWorkspace (t , client , user .OrganizationID , project .ID , func (cwr * codersdk.CreateWorkspaceRequest ) {
37
- cwr .AutostartSchedule = ptr .Ref (schedCron )
38
- cwr .TTLMillis = ptr .Ref (ttl .Milliseconds ())
39
- })
40
- _ = coderdtest .AwaitWorkspaceBuildJobCompleted (t , client , workspace .LatestBuild .ID )
41
- cmdArgs = []string {"schedule" , "show" , workspace .Name }
42
- stdoutBuf = & bytes.Buffer {}
43
- )
82
+ // When
83
+ inv , root := clitest .New (t , "schedule" , "show" )
84
+ //nolint:gocritic // Testing that owner user sees all
85
+ clitest .SetupConfig (t , ownerClient , root )
86
+ pty := ptytest .New (t ).Attach (inv )
87
+ ctx := testutil .Context (t , testutil .WaitShort )
88
+ done := make (chan any )
89
+ go func () {
90
+ errC := inv .WithContext (ctx ).Run ()
91
+ assert .NoError (t , errC )
92
+ close (done )
93
+ }()
94
+ <- done
95
+
96
+ // Then
97
+ // 1st workspace: a-owner-ws1 has both autostart and autostop enabled.
98
+ pty .ExpectMatchContext (ctx , ws .Workspaces [0 ].OwnerName + "/" + ws .Workspaces [0 ].Name )
99
+ pty .ExpectMatchContext (ctx , sched .Humanize ())
100
+ pty .ExpectMatchContext (ctx , sched .Next (now ).Format (time .RFC3339 ))
101
+ pty .ExpectMatchContext (ctx , "8h" )
102
+ pty .ExpectMatch (ws .Workspaces [0 ].LatestBuild .Deadline .Time .Format (time .RFC3339 ))
103
+ // 2nd workspace: b-owner-ws2 has only autostart enabled.
104
+ pty .ExpectMatch (ws .Workspaces [1 ].OwnerName + "/" + ws .Workspaces [1 ].Name )
105
+ pty .ExpectMatch (sched .Humanize ())
106
+ pty .ExpectMatch (sched .Next (now ).Format (time .RFC3339 ))
107
+ })
44
108
45
- inv , root := clitest .New (t , cmdArgs ... )
46
- clitest .SetupConfig (t , client , root )
47
- inv .Stdout = stdoutBuf
109
+ t .Run ("OwnerAll" , func (t * testing.T ) {
110
+ t .Parallel ()
48
111
49
- err := inv .Run ()
50
- require .NoError (t , err , "unexpected error" )
51
- lines := strings .Split (strings .TrimSpace (stdoutBuf .String ()), "\n " )
52
- if assert .Len (t , lines , 4 ) {
53
- assert .Contains (t , lines [0 ], "Starts at 7:30AM Mon-Fri (Europe/Dublin)" )
54
- assert .Contains (t , lines [1 ], "Starts next 7:30AM" )
55
- // it should have either IST or GMT
56
- if ! strings .Contains (lines [1 ], "IST" ) && ! strings .Contains (lines [1 ], "GMT" ) {
57
- t .Error ("expected either IST or GMT" )
58
- }
59
- assert .Contains (t , lines [2 ], "Stops at 8h after start" )
60
- assert .NotContains (t , lines [3 ], "Stops next -" )
61
- }
112
+ // When
113
+ inv , root := clitest .New (t , "schedule" , "show" , "--all" )
114
+ //nolint:gocritic // Testing that owner user sees all
115
+ clitest .SetupConfig (t , ownerClient , root )
116
+ pty := ptytest .New (t ).Attach (inv )
117
+ ctx := testutil .Context (t , testutil .WaitShort )
118
+ done := make (chan any )
119
+ go func () {
120
+ errC := inv .WithContext (ctx ).Run ()
121
+ assert .NoError (t , errC )
122
+ close (done )
123
+ }()
124
+ <- done
125
+
126
+ // Then
127
+ // 1st workspace: a-owner-ws1 has both autostart and autostop enabled.
128
+ pty .ExpectMatchContext (ctx , ws .Workspaces [0 ].OwnerName + "/" + ws .Workspaces [0 ].Name )
129
+ pty .ExpectMatchContext (ctx , sched .Humanize ())
130
+ pty .ExpectMatchContext (ctx , sched .Next (now ).Format (time .RFC3339 ))
131
+ pty .ExpectMatchContext (ctx , "8h" )
132
+ pty .ExpectMatch (ws .Workspaces [0 ].LatestBuild .Deadline .Time .Format (time .RFC3339 ))
133
+ // 2nd workspace: b-owner-ws2 has only autostart enabled.
134
+ pty .ExpectMatch (ws .Workspaces [1 ].OwnerName + "/" + ws .Workspaces [1 ].Name )
135
+ pty .ExpectMatch (sched .Humanize ())
136
+ pty .ExpectMatch (sched .Next (now ).Format (time .RFC3339 ))
137
+ // 3rd workspace: c-member-ws3 has only autostop enabled.
138
+ pty .ExpectMatch (ws .Workspaces [2 ].OwnerName + "/" + ws .Workspaces [2 ].Name )
139
+ pty .ExpectMatch ("8h" )
140
+ pty .ExpectMatch (ws .Workspaces [2 ].LatestBuild .Deadline .Time .Format (time .RFC3339 ))
141
+ // 4th workspace: d-member-ws4 has neither autostart nor autostop enabled.
142
+ pty .ExpectMatch (ws .Workspaces [3 ].OwnerName + "/" + ws .Workspaces [3 ].Name )
62
143
})
63
144
64
- t .Run ("Manual " , func (t * testing.T ) {
145
+ t .Run ("OwnerSearchByName " , func (t * testing.T ) {
65
146
t .Parallel ()
66
147
67
- var (
68
- client = coderdtest .New (t , & coderdtest.Options {IncludeProvisionerDaemon : true })
69
- user = coderdtest .CreateFirstUser (t , client )
70
- version = coderdtest .CreateTemplateVersion (t , client , user .OrganizationID , nil )
71
- _ = coderdtest .AwaitTemplateVersionJobCompleted (t , client , version .ID )
72
- project = coderdtest .CreateTemplate (t , client , user .OrganizationID , version .ID )
73
- workspace = coderdtest .CreateWorkspace (t , client , user .OrganizationID , project .ID , func (cwr * codersdk.CreateWorkspaceRequest ) {
74
- cwr .AutostartSchedule = nil
75
- cwr .TTLMillis = nil
76
- })
77
- _ = coderdtest .AwaitWorkspaceBuildJobCompleted (t , client , workspace .LatestBuild .ID )
78
- cmdArgs = []string {"schedule" , "show" , workspace .Name }
79
- stdoutBuf = & bytes.Buffer {}
80
- )
148
+ // When
149
+ inv , root := clitest .New (t , "schedule" , "show" , "--search" , "name:" + ws .Workspaces [1 ].Name )
150
+ //nolint:gocritic // Testing that owner user sees all
151
+ clitest .SetupConfig (t , ownerClient , root )
152
+ pty := ptytest .New (t ).Attach (inv )
153
+ ctx := testutil .Context (t , testutil .WaitShort )
154
+ done := make (chan any )
155
+ go func () {
156
+ errC := inv .WithContext (ctx ).Run ()
157
+ assert .NoError (t , errC )
158
+ close (done )
159
+ }()
160
+ <- done
161
+
162
+ // Then
163
+ // 2nd workspace: b-owner-ws2 has only autostart enabled.
164
+ pty .ExpectMatch (ws .Workspaces [1 ].OwnerName + "/" + ws .Workspaces [1 ].Name )
165
+ pty .ExpectMatch (sched .Humanize ())
166
+ pty .ExpectMatch (sched .Next (now ).Format (time .RFC3339 ))
167
+ })
81
168
82
- inv , root := clitest .New (t , cmdArgs ... )
83
- clitest .SetupConfig (t , client , root )
84
- inv .Stdout = stdoutBuf
169
+ t .Run ("OwnerOneArg" , func (t * testing.T ) {
170
+ t .Parallel ()
85
171
86
- err := inv .Run ()
87
- require .NoError (t , err , "unexpected error" )
88
- lines := strings .Split (strings .TrimSpace (stdoutBuf .String ()), "\n " )
89
- if assert .Len (t , lines , 4 ) {
90
- assert .Contains (t , lines [0 ], "Starts at manual" )
91
- assert .Contains (t , lines [1 ], "Starts next -" )
92
- assert .Contains (t , lines [2 ], "Stops at manual" )
93
- assert .Contains (t , lines [3 ], "Stops next -" )
94
- }
172
+ // When
173
+ inv , root := clitest .New (t , "schedule" , "show" , ws .Workspaces [2 ].OwnerName + "/" + ws .Workspaces [2 ].Name )
174
+ //nolint:gocritic // Testing that owner user sees all
175
+ clitest .SetupConfig (t , ownerClient , root )
176
+ pty := ptytest .New (t ).Attach (inv )
177
+ ctx := testutil .Context (t , testutil .WaitShort )
178
+ done := make (chan any )
179
+ go func () {
180
+ errC := inv .WithContext (ctx ).Run ()
181
+ assert .NoError (t , errC )
182
+ close (done )
183
+ }()
184
+ <- done
185
+
186
+ // Then
187
+ // 3rd workspace: c-member-ws3 has only autostop enabled.
188
+ pty .ExpectMatch (ws .Workspaces [2 ].OwnerName + "/" + ws .Workspaces [2 ].Name )
189
+ pty .ExpectMatch ("8h" )
190
+ pty .ExpectMatch (ws .Workspaces [2 ].LatestBuild .Deadline .Time .Format (time .RFC3339 ))
95
191
})
96
192
97
- t .Run ("NotFound " , func (t * testing.T ) {
193
+ t .Run ("MemberNoArgs " , func (t * testing.T ) {
98
194
t .Parallel ()
99
195
100
- var (
101
- client = coderdtest .New (t , & coderdtest.Options {IncludeProvisionerDaemon : true })
102
- user = coderdtest .CreateFirstUser (t , client )
103
- version = coderdtest .CreateTemplateVersion (t , client , user .OrganizationID , nil )
104
- _ = coderdtest .AwaitTemplateVersionJobCompleted (t , client , version .ID )
105
- )
196
+ // When
197
+ inv , root := clitest .New (t , "schedule" , "show" )
198
+ clitest .SetupConfig (t , memberClient , root )
199
+ pty := ptytest .New (t ).Attach (inv )
200
+ ctx := testutil .Context (t , testutil .WaitShort )
201
+ done := make (chan any )
202
+ go func () {
203
+ errC := inv .WithContext (ctx ).Run ()
204
+ assert .NoError (t , errC )
205
+ close (done )
206
+ }()
207
+ <- done
208
+
209
+ // Then
210
+ // 1st workspace: c-member-ws3 has only autostop enabled.
211
+ pty .ExpectMatch (ws .Workspaces [2 ].OwnerName + "/" + ws .Workspaces [2 ].Name )
212
+ pty .ExpectMatch ("8h" )
213
+ pty .ExpectMatch (ws .Workspaces [2 ].LatestBuild .Deadline .Time .Format (time .RFC3339 ))
214
+ // 2nd workspace: d-member-ws4 has neither autostart nor autostop enabled.
215
+ pty .ExpectMatch (ws .Workspaces [3 ].OwnerName + "/" + ws .Workspaces [3 ].Name )
216
+ })
106
217
107
- inv , root := clitest . New ( t , "schedule " , "show" , "doesnotexist" )
108
- clitest . SetupConfig ( t , client , root )
218
+ t . Run ( "MemberAll " , func ( t * testing. T ) {
219
+ t . Parallel ( )
109
220
110
- err := inv .Run ()
111
- require .ErrorContains (t , err , "status code 404" , "unexpected error" )
221
+ // When
222
+ inv , root := clitest .New (t , "schedule" , "show" , "--all" )
223
+ clitest .SetupConfig (t , memberClient , root )
224
+ pty := ptytest .New (t ).Attach (inv )
225
+ ctx := testutil .Context (t , testutil .WaitShort )
226
+ done := make (chan any )
227
+ go func () {
228
+ errC := inv .WithContext (ctx ).Run ()
229
+ assert .NoError (t , errC )
230
+ close (done )
231
+ }()
232
+ <- done
233
+
234
+ // Then
235
+ // 1st workspace: c-member-ws3 has only autostop enabled.
236
+ pty .ExpectMatch (ws .Workspaces [2 ].OwnerName + "/" + ws .Workspaces [2 ].Name )
237
+ pty .ExpectMatch ("8h" )
238
+ pty .ExpectMatch (ws .Workspaces [2 ].LatestBuild .Deadline .Time .Format (time .RFC3339 ))
239
+ // 2nd workspace: d-member-ws4 has neither autostart nor autostop enabled.
240
+ pty .ExpectMatch (ws .Workspaces [3 ].OwnerName + "/" + ws .Workspaces [3 ].Name )
112
241
})
113
242
}
114
243
0 commit comments