Skip to content

Commit a8a50a4

Browse files
committed
refactor unit tests, add tests for autostop
1 parent b8530ef commit a8a50a4

File tree

1 file changed

+241
-133
lines changed

1 file changed

+241
-133
lines changed

coderd/autostart/lifecycle/lifecycle_executor_test.go

Lines changed: 241 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -10,146 +10,254 @@ import (
1010
"github.com/coder/coder/coderd/database"
1111
"github.com/coder/coder/codersdk"
1212

13+
"github.com/google/uuid"
1314
"github.com/stretchr/testify/require"
1415
)
1516

16-
func Test_Executor_Run(t *testing.T) {
17+
func Test_Executor_Autostart_OK(t *testing.T) {
1718
t.Parallel()
1819

19-
t.Run("OK", func(t *testing.T) {
20-
t.Parallel()
21-
22-
var (
23-
ctx = context.Background()
24-
err error
25-
tickCh = make(chan time.Time)
26-
client = coderdtest.New(t, &coderdtest.Options{
27-
LifecycleTicker: tickCh,
28-
})
29-
// Given: we have a user with a workspace
30-
_ = coderdtest.NewProvisionerDaemon(t, client)
31-
user = coderdtest.CreateFirstUser(t, client)
32-
version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
33-
template = coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
34-
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
35-
workspace = coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
36-
_ = coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
37-
)
38-
// Given: workspace is stopped
39-
build, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
40-
TemplateVersionID: template.ActiveVersionID,
41-
Transition: database.WorkspaceTransitionStop,
20+
var (
21+
ctx = context.Background()
22+
err error
23+
tickCh = make(chan time.Time)
24+
client = coderdtest.New(t, &coderdtest.Options{
25+
LifecycleTicker: tickCh,
4226
})
43-
require.NoError(t, err, "stop workspace")
44-
// Given: we wait for the stop to complete
45-
_ = coderdtest.AwaitWorkspaceBuildJob(t, client, build.ID)
46-
47-
// Given: we update the workspace with its new state
48-
workspace = coderdtest.MustWorkspace(t, client, workspace.ID)
49-
// Given: we ensure the workspace is now in a stopped state
50-
require.Equal(t, database.WorkspaceTransitionStop, workspace.LatestBuild.Transition)
51-
52-
// Given: the workspace initially has autostart disabled
53-
require.Empty(t, workspace.AutostartSchedule)
54-
55-
// When: we enable workspace autostart
56-
sched, err := schedule.Weekly("* * * * *")
57-
require.NoError(t, err)
58-
require.NoError(t, client.UpdateWorkspaceAutostart(ctx, workspace.ID, codersdk.UpdateWorkspaceAutostartRequest{
59-
Schedule: sched.String(),
60-
}))
61-
62-
// When: the lifecycle executor ticks
63-
go func() {
64-
tickCh <- time.Now().UTC().Add(time.Minute)
65-
}()
66-
67-
// Then: the workspace should be started
68-
require.Eventually(t, func() bool {
69-
ws := coderdtest.MustWorkspace(t, client, workspace.ID)
70-
return ws.LatestBuild.Job.Status == codersdk.ProvisionerJobSucceeded &&
71-
ws.LatestBuild.Transition == database.WorkspaceTransitionStart
72-
}, 5*time.Second, 250*time.Millisecond)
73-
})
27+
// Given: we have a user with a workspace
28+
workspace = MustProvisionWorkspace(t, client)
29+
)
30+
// Given: workspace is stopped
31+
MustTransitionWorkspace(t, client, workspace.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)
7432

75-
t.Run("AlreadyRunning", func(t *testing.T) {
76-
t.Parallel()
77-
78-
var (
79-
ctx = context.Background()
80-
err error
81-
tickCh = make(chan time.Time)
82-
client = coderdtest.New(t, &coderdtest.Options{
83-
LifecycleTicker: tickCh,
84-
})
85-
// Given: we have a user with a workspace
86-
_ = coderdtest.NewProvisionerDaemon(t, client)
87-
user = coderdtest.CreateFirstUser(t, client)
88-
version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
89-
template = coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
90-
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
91-
workspace = coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
92-
_ = coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
93-
)
94-
95-
// Given: we ensure the workspace is now in a stopped state
96-
require.Equal(t, database.WorkspaceTransitionStart, workspace.LatestBuild.Transition)
97-
98-
// Given: the workspace initially has autostart disabled
99-
require.Empty(t, workspace.AutostartSchedule)
100-
101-
// When: we enable workspace autostart
102-
sched, err := schedule.Weekly("* * * * *")
103-
require.NoError(t, err)
104-
require.NoError(t, client.UpdateWorkspaceAutostart(ctx, workspace.ID, codersdk.UpdateWorkspaceAutostartRequest{
105-
Schedule: sched.String(),
106-
}))
107-
108-
// When: the lifecycle executor ticks
109-
go func() {
110-
tickCh <- time.Now().UTC().Add(time.Minute)
111-
}()
112-
113-
// Then: the workspace should not be started.
114-
require.Never(t, func() bool {
115-
ws := coderdtest.MustWorkspace(t, client, workspace.ID)
116-
return ws.LatestBuild.ID != workspace.LatestBuild.ID
117-
}, 5*time.Second, 250*time.Millisecond)
118-
})
33+
// Given: the workspace initially has autostart disabled
34+
require.Empty(t, workspace.AutostartSchedule)
35+
36+
// When: we enable workspace autostart
37+
sched, err := schedule.Weekly("* * * * *")
38+
require.NoError(t, err)
39+
require.NoError(t, client.UpdateWorkspaceAutostart(ctx, workspace.ID, codersdk.UpdateWorkspaceAutostartRequest{
40+
Schedule: sched.String(),
41+
}))
42+
43+
// When: the lifecycle executor ticks
44+
go func() {
45+
tickCh <- time.Now().UTC().Add(time.Minute)
46+
}()
47+
48+
// Then: the workspace should be started
49+
require.Eventually(t, func() bool {
50+
ws := coderdtest.MustWorkspace(t, client, workspace.ID)
51+
return ws.LatestBuild.Job.Status == codersdk.ProvisionerJobSucceeded &&
52+
ws.LatestBuild.Transition == database.WorkspaceTransitionStart
53+
}, 5*time.Second, 250*time.Millisecond)
54+
}
55+
56+
func Test_Executor_Autostart_AlreadyRunning(t *testing.T) {
57+
t.Parallel()
58+
59+
var (
60+
ctx = context.Background()
61+
err error
62+
tickCh = make(chan time.Time)
63+
client = coderdtest.New(t, &coderdtest.Options{
64+
LifecycleTicker: tickCh,
65+
})
66+
// Given: we have a user with a workspace
67+
workspace = MustProvisionWorkspace(t, client)
68+
)
69+
70+
// Given: we ensure the workspace is running
71+
require.Equal(t, database.WorkspaceTransitionStart, workspace.LatestBuild.Transition)
72+
73+
// Given: the workspace initially has autostart disabled
74+
require.Empty(t, workspace.AutostartSchedule)
75+
76+
// When: we enable workspace autostart
77+
sched, err := schedule.Weekly("* * * * *")
78+
require.NoError(t, err)
79+
require.NoError(t, client.UpdateWorkspaceAutostart(ctx, workspace.ID, codersdk.UpdateWorkspaceAutostartRequest{
80+
Schedule: sched.String(),
81+
}))
82+
83+
// When: the lifecycle executor ticks
84+
go func() {
85+
tickCh <- time.Now().UTC().Add(time.Minute)
86+
}()
87+
88+
// Then: the workspace should not be started.
89+
require.Never(t, func() bool {
90+
ws := coderdtest.MustWorkspace(t, client, workspace.ID)
91+
return ws.LatestBuild.ID != workspace.LatestBuild.ID && ws.LatestBuild.Transition == database.WorkspaceTransitionStart
92+
}, 5*time.Second, 250*time.Millisecond)
93+
}
94+
95+
func Test_Executor_Autostart_NotEnabled(t *testing.T) {
96+
t.Parallel()
97+
98+
var (
99+
tickCh = make(chan time.Time)
100+
client = coderdtest.New(t, &coderdtest.Options{
101+
LifecycleTicker: tickCh,
102+
})
103+
// Given: we have a user with a workspace
104+
workspace = MustProvisionWorkspace(t, client)
105+
)
106+
107+
// Given: workspace is stopped
108+
MustTransitionWorkspace(t, client, workspace.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)
109+
110+
// Given: the workspace has autostart disabled
111+
require.Empty(t, workspace.AutostartSchedule)
112+
113+
// When: the lifecycle executor ticks
114+
go func() {
115+
tickCh <- time.Now().UTC().Add(time.Minute)
116+
}()
117+
118+
// Then: the workspace should not be started.
119+
require.Never(t, func() bool {
120+
ws := coderdtest.MustWorkspace(t, client, workspace.ID)
121+
return ws.LatestBuild.ID != workspace.LatestBuild.ID && ws.LatestBuild.Transition == database.WorkspaceTransitionStart
122+
}, 5*time.Second, 250*time.Millisecond)
123+
}
124+
125+
func Test_Executor_Autostop_OK(t *testing.T) {
126+
t.Parallel()
127+
128+
var (
129+
ctx = context.Background()
130+
err error
131+
tickCh = make(chan time.Time)
132+
client = coderdtest.New(t, &coderdtest.Options{
133+
LifecycleTicker: tickCh,
134+
})
135+
// Given: we have a user with a workspace
136+
workspace = MustProvisionWorkspace(t, client)
137+
)
138+
// Given: workspace is running
139+
require.Equal(t, database.WorkspaceTransitionStart, workspace.LatestBuild.Transition)
119140

120-
t.Run("NotEnabled", func(t *testing.T) {
121-
t.Parallel()
122-
123-
var (
124-
tickCh = make(chan time.Time)
125-
client = coderdtest.New(t, &coderdtest.Options{
126-
LifecycleTicker: tickCh,
127-
})
128-
// Given: we have a user with a workspace
129-
_ = coderdtest.NewProvisionerDaemon(t, client)
130-
user = coderdtest.CreateFirstUser(t, client)
131-
version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
132-
template = coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
133-
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
134-
workspace = coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
135-
_ = coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
136-
)
137-
138-
// Given: we ensure the workspace is now in a stopped state
139-
require.Equal(t, database.WorkspaceTransitionStart, workspace.LatestBuild.Transition)
140-
141-
// Given: the workspace has autostart disabled
142-
require.Empty(t, workspace.AutostartSchedule)
143-
144-
// When: the lifecycle executor ticks
145-
go func() {
146-
tickCh <- time.Now().UTC().Add(time.Minute)
147-
}()
148-
149-
// Then: the workspace should not be started.
150-
require.Never(t, func() bool {
151-
ws := coderdtest.MustWorkspace(t, client, workspace.ID)
152-
return ws.LatestBuild.ID != workspace.LatestBuild.ID
153-
}, 5*time.Second, 250*time.Millisecond)
141+
// Given: the workspace initially has autostop disabled
142+
require.Empty(t, workspace.AutostopSchedule)
143+
144+
// When: we enable workspace autostop
145+
sched, err := schedule.Weekly("* * * * *")
146+
require.NoError(t, err)
147+
require.NoError(t, client.UpdateWorkspaceAutostop(ctx, workspace.ID, codersdk.UpdateWorkspaceAutostopRequest{
148+
Schedule: sched.String(),
149+
}))
150+
151+
// When: the lifecycle executor ticks
152+
go func() {
153+
tickCh <- time.Now().UTC().Add(time.Minute)
154+
}()
155+
156+
// Then: the workspace should be started
157+
require.Eventually(t, func() bool {
158+
ws := coderdtest.MustWorkspace(t, client, workspace.ID)
159+
return ws.LatestBuild.ID != workspace.LatestBuild.ID && ws.LatestBuild.Transition == database.WorkspaceTransitionStart
160+
}, 5*time.Second, 250*time.Millisecond)
161+
}
162+
func Test_Executor_Autostop_AlreadyStopped(t *testing.T) {
163+
t.Parallel()
164+
165+
var (
166+
ctx = context.Background()
167+
err error
168+
tickCh = make(chan time.Time)
169+
client = coderdtest.New(t, &coderdtest.Options{
170+
LifecycleTicker: tickCh,
171+
})
172+
// Given: we have a user with a workspace
173+
workspace = MustProvisionWorkspace(t, client)
174+
)
175+
176+
// Given: workspace is stopped
177+
MustTransitionWorkspace(t, client, workspace.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)
178+
179+
// Given: the workspace initially has autostart disabled
180+
require.Empty(t, workspace.AutostopSchedule)
181+
182+
// When: we enable workspace autostart
183+
sched, err := schedule.Weekly("* * * * *")
184+
require.NoError(t, err)
185+
require.NoError(t, client.UpdateWorkspaceAutostop(ctx, workspace.ID, codersdk.UpdateWorkspaceAutostopRequest{
186+
Schedule: sched.String(),
187+
}))
188+
189+
// When: the lifecycle executor ticks
190+
go func() {
191+
tickCh <- time.Now().UTC().Add(time.Minute)
192+
}()
193+
194+
// Then: the workspace should not be stopped.
195+
require.Never(t, func() bool {
196+
ws := coderdtest.MustWorkspace(t, client, workspace.ID)
197+
return ws.LatestBuild.ID == workspace.LatestBuild.ID && ws.LatestBuild.Transition == database.WorkspaceTransitionStop
198+
}, 5*time.Second, 250*time.Millisecond)
199+
}
200+
201+
func Test_Executor_Autostop_NotEnabled(t *testing.T) {
202+
t.Parallel()
203+
204+
var (
205+
tickCh = make(chan time.Time)
206+
client = coderdtest.New(t, &coderdtest.Options{
207+
LifecycleTicker: tickCh,
208+
})
209+
// Given: we have a user with a workspace
210+
workspace = MustProvisionWorkspace(t, client)
211+
)
212+
213+
// Given: workspace is running
214+
require.Equal(t, database.WorkspaceTransitionStart, workspace.LatestBuild.Transition)
215+
216+
// Given: the workspace has autostop disabled
217+
require.Empty(t, workspace.AutostopSchedule)
218+
219+
// When: the lifecycle executor ticks
220+
go func() {
221+
tickCh <- time.Now().UTC().Add(time.Minute)
222+
}()
223+
224+
// Then: the workspace should not be stopped.
225+
require.Never(t, func() bool {
226+
ws := coderdtest.MustWorkspace(t, client, workspace.ID)
227+
return ws.LatestBuild.ID == workspace.LatestBuild.ID && ws.LatestBuild.Transition == database.WorkspaceTransitionStop
228+
}, 5*time.Second, 250*time.Millisecond)
229+
}
230+
231+
func MustProvisionWorkspace(t *testing.T, client *codersdk.Client) codersdk.Workspace {
232+
t.Helper()
233+
coderdtest.NewProvisionerDaemon(t, client)
234+
user := coderdtest.CreateFirstUser(t, client)
235+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
236+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
237+
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
238+
ws := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
239+
coderdtest.AwaitWorkspaceBuildJob(t, client, ws.LatestBuild.ID)
240+
return coderdtest.MustWorkspace(t, client, ws.ID)
241+
}
242+
243+
func MustTransitionWorkspace(t *testing.T, client *codersdk.Client, workspaceID uuid.UUID, from, to database.WorkspaceTransition) {
244+
t.Helper()
245+
ctx := context.Background()
246+
workspace, err := client.Workspace(ctx, workspaceID)
247+
require.NoError(t, err, "unexpected error fetching workspace")
248+
require.Equal(t, workspace.LatestBuild.Transition, from, "expected workspace state: %s got: %s", from, workspace.LatestBuild.Transition)
249+
250+
template, err := client.Template(ctx, workspace.TemplateID)
251+
require.NoError(t, err, "fetch workspace template")
252+
253+
build, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
254+
TemplateVersionID: template.ActiveVersionID,
255+
Transition: to,
154256
})
257+
require.NoError(t, err, "unexpected error transitioning workspace to %s", to)
258+
259+
_ = coderdtest.AwaitWorkspaceBuildJob(t, client, build.ID)
260+
261+
updated := coderdtest.MustWorkspace(t, client, workspace.ID)
262+
require.Equal(t, to, updated.LatestBuild.Transition, "expected workspace to be in state %s but got %s", to, updated.LatestBuild.Transition)
155263
}

0 commit comments

Comments
 (0)