@@ -16,106 +16,143 @@ import (
1616
1717func TestWorkspaceBuild (t * testing.T ) {
1818t .Parallel ()
19- t .Run ("TemplateRequiresActiveVersion" ,func (t * testing.T ) {
20- t .Parallel ()
2119
22- ctx := testutil . Context ( t , testutil . WaitMedium )
23- ownerClient , owner := coderdenttest . New (t ,& coderdenttest. Options {
24- Options : & coderdtest .Options {
25- IncludeProvisionerDaemon : true ,
26- } ,
27- LicenseOptions : & coderdenttest. LicenseOptions {
28- Features : license. Features {
29- codersdk . FeatureAccessControl : 1 ,
30- codersdk .FeatureTemplateRBAC : 1 ,
31- codersdk .FeatureAdvancedTemplateScheduling : 1 ,
32- } ,
20+ // Only use this context for setup. Use a separate context for subtests!
21+ setupCtx := testutil . Context (t ,testutil . WaitMedium )
22+ ownerClient , owner := coderdenttest . New ( t , & coderdenttest .Options {
23+ Options : & coderdtest. Options {
24+ IncludeProvisionerDaemon : true ,
25+ },
26+ LicenseOptions : & coderdenttest. LicenseOptions {
27+ Features : license. Features {
28+ codersdk .FeatureAccessControl : 1 ,
29+ codersdk .FeatureTemplateRBAC : 1 ,
30+ codersdk . FeatureAdvancedTemplateScheduling : 1 ,
3331},
34- })
32+ },
33+ })
3534
36- // Create an initial version.
37- oldVersion := coderdtest .CreateTemplateVersion (t ,ownerClient ,owner .OrganizationID ,nil )
38- // Create a template that mandates the promoted version.
39- // This should be enforced for everyone except template admins.
40- template := coderdtest .CreateTemplate (t ,ownerClient ,owner .OrganizationID ,oldVersion .ID )
41- coderdtest .AwaitTemplateVersionJobCompleted (t ,ownerClient ,oldVersion .ID )
42- require .Equal (t ,oldVersion .ID ,template .ActiveVersionID )
43- template = coderdtest .UpdateTemplateMeta (t ,ownerClient ,template .ID , codersdk.UpdateTemplateMeta {
44- RequireActiveVersion :true ,
45- })
46- require .True (t ,template .RequireActiveVersion )
35+ // For this test we create two templates:
36+ // tplA will be used to test creation of new workspaces.
37+ // tplB will be used to test builds on existing workspaces.
38+ // This is done to enable parallelization of the sub-tests without them interfering with each other.
39+ // Both templates mandate the promoted version.
40+ // This should be enforced for everyone except template admins.
41+ tplAv1 := coderdtest .CreateTemplateVersion (t ,ownerClient ,owner .OrganizationID ,nil )
42+ tplA := coderdtest .CreateTemplate (t ,ownerClient ,owner .OrganizationID ,tplAv1 .ID )
43+ coderdtest .AwaitTemplateVersionJobCompleted (t ,ownerClient ,tplAv1 .ID )
44+ require .Equal (t ,tplAv1 .ID ,tplA .ActiveVersionID )
45+ tplA = coderdtest .UpdateTemplateMeta (t ,ownerClient ,tplA .ID , codersdk.UpdateTemplateMeta {
46+ RequireActiveVersion :true ,
47+ })
48+ require .True (t ,tplA .RequireActiveVersion )
49+ tplAv2 := coderdtest .CreateTemplateVersion (t ,ownerClient ,owner .OrganizationID ,nil ,func (ctvr * codersdk.CreateTemplateVersionRequest ) {
50+ ctvr .TemplateID = tplA .ID
51+ })
52+ coderdtest .AwaitTemplateVersionJobCompleted (t ,ownerClient ,tplAv2 .ID )
53+ coderdtest .UpdateActiveTemplateVersion (t ,ownerClient ,tplA .ID ,tplAv2 .ID )
4754
48- // Create a new version that we will promote.
49- activeVersion := coderdtest .CreateTemplateVersion (t ,ownerClient ,owner .OrganizationID ,nil ,func (ctvr * codersdk.CreateTemplateVersionRequest ) {
50- ctvr .TemplateID = template .ID
51- })
52- coderdtest .AwaitTemplateVersionJobCompleted (t ,ownerClient ,activeVersion .ID )
53- coderdtest .UpdateActiveTemplateVersion (t ,ownerClient ,template .ID ,activeVersion .ID )
55+ tplBv1 := coderdtest .CreateTemplateVersion (t ,ownerClient ,owner .OrganizationID ,nil )
56+ tplB := coderdtest .CreateTemplate (t ,ownerClient ,owner .OrganizationID ,tplBv1 .ID )
57+ coderdtest .AwaitTemplateVersionJobCompleted (t ,ownerClient ,tplBv1 .ID )
58+ require .Equal (t ,tplBv1 .ID ,tplB .ActiveVersionID )
59+ tplB = coderdtest .UpdateTemplateMeta (t ,ownerClient ,tplB .ID , codersdk.UpdateTemplateMeta {
60+ RequireActiveVersion :true ,
61+ })
62+ require .True (t ,tplB .RequireActiveVersion )
5463
55- templateAdminClient ,_ := coderdtest .CreateAnotherUser (t ,ownerClient ,owner .OrganizationID ,rbac .RoleTemplateAdmin ())
56- templateACLAdminClient ,templateACLAdmin := coderdtest .CreateAnotherUser (t ,ownerClient ,owner .OrganizationID )
57- templateGroupACLAdminClient ,templateGroupACLAdmin := coderdtest .CreateAnotherUser (t ,ownerClient ,owner .OrganizationID )
58- memberClient ,_ := coderdtest .CreateAnotherUser (t ,ownerClient ,owner .OrganizationID )
64+ templateAdminClient ,_ := coderdtest .CreateAnotherUser (t ,ownerClient ,owner .OrganizationID ,rbac .RoleTemplateAdmin ())
65+ templateACLAdminClient ,templateACLAdmin := coderdtest .CreateAnotherUser (t ,ownerClient ,owner .OrganizationID )
66+ templateGroupACLAdminClient ,templateGroupACLAdmin := coderdtest .CreateAnotherUser (t ,ownerClient ,owner .OrganizationID )
67+ memberClient ,_ := coderdtest .CreateAnotherUser (t ,ownerClient ,owner .OrganizationID )
5968
60- // Create a group so we can also test group template admin ownership.
61- // Add the user who gains template admin via group membership.
62- group := coderdtest .CreateGroup (t ,ownerClient ,owner .OrganizationID ,"test" ,templateGroupACLAdmin )
69+ // Create a group so we can also test group template admin ownership.
70+ // Add the user who gains template admin via group membership.
71+ group := coderdtest .CreateGroup (t ,ownerClient ,owner .OrganizationID ,"test" ,templateGroupACLAdmin )
6372
64- // Update the template for both users and groups.
65- //nolint:gocritic // test setup
66- err := ownerClient .UpdateTemplateACL (ctx ,template .ID , codersdk.UpdateTemplateACL {
73+ // Update the template for both users and groups.
74+ //nolint:gocritic // test setup
75+ for _ ,tpl := range []codersdk.Template {tplA ,tplB } {
76+ err := ownerClient .UpdateTemplateACL (setupCtx ,tpl .ID , codersdk.UpdateTemplateACL {
6777UserPerms :map [string ]codersdk.TemplateRole {
6878templateACLAdmin .ID .String ():codersdk .TemplateRoleAdmin ,
6979},
7080GroupPerms :map [string ]codersdk.TemplateRole {
7181group .ID .String ():codersdk .TemplateRoleAdmin ,
7282},
7383})
74- require .NoError (t ,err )
84+ require .NoError (t ,err ,"updating template ACL for template %q" ,tpl .ID )
85+ }
7586
76- type testcase struct {
77- Name string
78- Client * codersdk.Client
79- ExpectedStatusCode int
80- }
87+ type testcase struct {
88+ Name string
89+ Client * codersdk.Client
90+ ExpectedStatusCode int
91+ }
8192
82- cases := []testcase {
83- {
84- Name :"OwnerOK" ,
85- Client :ownerClient ,
86- ExpectedStatusCode :http .StatusOK ,
87- },
88- {
89- Name :"TemplateAdminOK" ,
90- Client :templateAdminClient ,
91- ExpectedStatusCode :http .StatusOK ,
92- },
93- {
94- Name :"TemplateACLAdminOK" ,
95- Client :templateACLAdminClient ,
96- ExpectedStatusCode :http .StatusOK ,
97- },
98- {
99- Name :"TemplateGroupACLAdminOK" ,
100- Client :templateGroupACLAdminClient ,
101- ExpectedStatusCode :http .StatusOK ,
102- },
103- {
104- Name :"MemberFails" ,
105- Client :memberClient ,
106- ExpectedStatusCode :http .StatusForbidden ,
107- },
108- }
93+ cases := []testcase {
94+ {
95+ Name :"OwnerOK" ,
96+ Client :ownerClient ,
97+ ExpectedStatusCode :http .StatusOK ,
98+ },
99+ {
100+ Name :"TemplateAdminOK" ,
101+ Client :templateAdminClient ,
102+ ExpectedStatusCode :http .StatusOK ,
103+ },
104+ {
105+ Name :"TemplateACLAdminOK" ,
106+ Client :templateACLAdminClient ,
107+ ExpectedStatusCode :http .StatusOK ,
108+ },
109+ {
110+ Name :"TemplateGroupACLAdminOK" ,
111+ Client :templateGroupACLAdminClient ,
112+ ExpectedStatusCode :http .StatusOK ,
113+ },
114+ {
115+ Name :"MemberFailsToCreate" ,
116+ Client :memberClient ,
117+ ExpectedStatusCode :http .StatusForbidden ,
118+ },
119+ }
120+
121+ // Create pre-existing workspaces for each of the test cases.
122+ var extantWorkspaces []codersdk.Workspace
123+ for _ ,c := range cases {
124+ extantWs ,err := c .Client .CreateUserWorkspace (setupCtx ,codersdk .Me , codersdk.CreateWorkspaceRequest {
125+ TemplateVersionID :tplB .ActiveVersionID ,
126+ Name :testutil .GetRandomNameHyphenated (t ),
127+ AutomaticUpdates :codersdk .AutomaticUpdatesNever ,
128+ })
129+ require .NoError (t ,err ,"setup workspace for case %q" ,c .Name )
130+ coderdtest .AwaitWorkspaceBuildJobCompleted (t ,c .Client ,extantWs .LatestBuild .ID )
131+ extantWorkspaces = append (extantWorkspaces ,extantWs )
132+ }
133+
134+ // Create a new version of template B and promote it to be the active version.
135+ tplBv2 := coderdtest .CreateTemplateVersion (t ,ownerClient ,owner .OrganizationID ,nil ,func (ctvr * codersdk.CreateTemplateVersionRequest ) {
136+ ctvr .TemplateID = tplB .ID
137+ })
138+ coderdtest .AwaitTemplateVersionJobCompleted (t ,ownerClient ,tplBv2 .ID )
139+ coderdtest .UpdateActiveTemplateVersion (t ,ownerClient ,tplB .ID ,tplBv2 .ID )
140+
141+ t .Run ("NewWorkspace" ,func (t * testing.T ) {
142+ t .Parallel ()
109143
110144for _ ,c := range cases {
111145t .Run (c .Name ,func (t * testing.T ) {
112- _ ,err = c .Client .CreateUserWorkspace (ctx ,codersdk .Me , codersdk.CreateWorkspaceRequest {
113- TemplateVersionID :oldVersion .ID ,
114- Name :"abc123" ,
146+ t .Parallel ()
147+ ctx := testutil .Context (t ,testutil .WaitMedium )
148+ ws ,err := c .Client .CreateUserWorkspace (ctx ,codersdk .Me , codersdk.CreateWorkspaceRequest {
149+ TemplateVersionID :tplAv1 .ID ,
150+ Name :testutil .GetRandomNameHyphenated (t ),
115151AutomaticUpdates :codersdk .AutomaticUpdatesNever ,
116152})
117153if c .ExpectedStatusCode == http .StatusOK {
118154require .NoError (t ,err )
155+ require .Equal (t ,tplAv1 .ID ,ws .LatestBuild .TemplateVersionID ,"workspace did not use expected version for case %q" ,c .Name )
119156}else {
120157require .Error (t ,err )
121158cerr ,ok := codersdk .AsError (err )
@@ -125,4 +162,37 @@ func TestWorkspaceBuild(t *testing.T) {
125162})
126163}
127164})
165+
166+ t .Run ("ExistingWorkspace" ,func (t * testing.T ) {
167+ t .Parallel ()
168+
169+ for idx ,c := range cases {
170+ t .Run (c .Name ,func (t * testing.T ) {
171+ t .Parallel ()
172+ ctx := testutil .Context (t ,testutil .WaitMedium )
173+ // Stopping the workspace must always succeed.
174+ wb ,err := c .Client .CreateWorkspaceBuild (ctx ,extantWorkspaces [idx ].ID , codersdk.CreateWorkspaceBuildRequest {
175+ Transition :codersdk .WorkspaceTransitionStop ,
176+ })
177+ require .NoError (t ,err ,"stopping workspace for case %q" ,c .Name )
178+ coderdtest .AwaitWorkspaceBuildJobCompleted (t ,c .Client ,wb .ID )
179+
180+ // Attempt to start the workspace with the given version.
181+ wb ,err = c .Client .CreateWorkspaceBuild (ctx ,extantWorkspaces [idx ].ID , codersdk.CreateWorkspaceBuildRequest {
182+ Transition :codersdk .WorkspaceTransitionStart ,
183+ TemplateVersionID :tplBv1 .ID ,
184+ })
185+ if c .ExpectedStatusCode == http .StatusOK {
186+ require .NoError (t ,err ,"starting workspace for case %q" ,c .Name )
187+ coderdtest .AwaitWorkspaceBuildJobCompleted (t ,c .Client ,wb .ID )
188+ require .Equal (t ,tplBv1 .ID ,wb .TemplateVersionID ,"workspace did not use expected version for case %q" ,c .Name )
189+ }else {
190+ require .Error (t ,err ,"starting workspace for case %q" ,c .Name )
191+ cerr ,ok := codersdk .AsError (err )
192+ require .True (t ,ok )
193+ require .Equal (t ,c .ExpectedStatusCode ,cerr .StatusCode ())
194+ }
195+ })
196+ }
197+ })
128198}