Skip to content

Commit 5180fc3

Browse files
committed
add more tests
1 parent 8db4817 commit 5180fc3

File tree

9 files changed

+209
-18
lines changed

9 files changed

+209
-18
lines changed

coderd/coderdtest/coderdtest.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -915,7 +915,7 @@ func CreateWorkspace(t *testing.T, client *codersdk.Client, organization uuid.UU
915915
}
916916

917917
// TransitionWorkspace is a convenience method for transitioning a workspace from one state to another.
918-
func MustTransitionWorkspace(t *testing.T, client *codersdk.Client, workspaceID uuid.UUID, from, to database.WorkspaceTransition) codersdk.Workspace {
918+
func MustTransitionWorkspace(t *testing.T, client *codersdk.Client, workspaceID uuid.UUID, from, to database.WorkspaceTransition, muts ...func(req *codersdk.CreateWorkspaceBuildRequest)) codersdk.Workspace {
919919
t.Helper()
920920
ctx := context.Background()
921921
workspace, err := client.Workspace(ctx, workspaceID)
@@ -925,10 +925,19 @@ func MustTransitionWorkspace(t *testing.T, client *codersdk.Client, workspaceID
925925
template, err := client.Template(ctx, workspace.TemplateID)
926926
require.NoError(t, err, "fetch workspace template")
927927

928-
build, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
928+
req := codersdk.CreateWorkspaceBuildRequest{
929+
// TODO (JonA): I get this is for convenience but we shoul probably
930+
// change this. Tripped me up why my test was passing when it shouldn't
931+
// have.
929932
TemplateVersionID: template.ActiveVersionID,
930933
Transition: codersdk.WorkspaceTransition(to),
931-
})
934+
}
935+
936+
for _, mut := range muts {
937+
mut(&req)
938+
}
939+
940+
build, err := client.CreateWorkspaceBuild(ctx, workspace.ID, req)
932941
require.NoError(t, err, "unexpected error transitioning workspace to %s", to)
933942

934943
_ = AwaitWorkspaceBuildJobCompleted(t, client, build.ID)

coderd/database/dbfake/dbfake.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5703,6 +5703,7 @@ func (q *FakeQuerier) UpdateTemplateScheduleByID(_ context.Context, arg database
57035703
tpl.FailureTTL = arg.FailureTTL
57045704
tpl.TimeTilDormant = arg.TimeTilDormant
57055705
tpl.TimeTilDormantAutoDelete = arg.TimeTilDormantAutoDelete
5706+
tpl.RequirePromotedVersion = arg.RequirePromotedVersion
57065707
q.templates[idx] = tpl
57075708
return nil
57085709
}

coderd/templates.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,8 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
584584
req.AutostopRequirement.Weeks == scheduleOpts.AutostopRequirement.Weeks &&
585585
req.FailureTTLMillis == time.Duration(template.FailureTTL).Milliseconds() &&
586586
req.TimeTilDormantMillis == time.Duration(template.TimeTilDormant).Milliseconds() &&
587-
req.TimeTilDormantAutoDeleteMillis == time.Duration(template.TimeTilDormantAutoDelete).Milliseconds() {
587+
req.TimeTilDormantAutoDeleteMillis == time.Duration(template.TimeTilDormantAutoDelete).Milliseconds() &&
588+
req.RequirePromotedVersion == template.RequirePromotedVersion {
588589
return nil
589590
}
590591

@@ -627,7 +628,8 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
627628
inactivityTTL != time.Duration(template.TimeTilDormant) ||
628629
timeTilDormantAutoDelete != time.Duration(template.TimeTilDormantAutoDelete) ||
629630
req.AllowUserAutostart != template.AllowUserAutostart ||
630-
req.AllowUserAutostop != template.AllowUserAutostop {
631+
req.AllowUserAutostop != template.AllowUserAutostop ||
632+
req.RequirePromotedVersion != template.RequirePromotedVersion {
631633
updated, err = (*api.TemplateScheduleStore.Load()).Set(ctx, tx, updated, schedule.TemplateScheduleOptions{
632634
// Some of these values are enterprise-only, but the
633635
// TemplateScheduleStore will handle avoiding setting them if
@@ -645,6 +647,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
645647
TimeTilDormantAutoDelete: timeTilDormantAutoDelete,
646648
UpdateWorkspaceLastUsedAt: req.UpdateWorkspaceLastUsedAt,
647649
UpdateWorkspaceDormantAt: req.UpdateWorkspaceDormantAt,
650+
RequirePromotedVersion: req.RequirePromotedVersion,
648651
})
649652
if err != nil {
650653
return xerrors.Errorf("set template schedule options: %w", err)
@@ -787,5 +790,6 @@ func (api *API) convertTemplate(
787790
DaysOfWeek: codersdk.BitmapToWeekdays(uint8(template.AutostopRequirementDaysOfWeek)),
788791
Weeks: autostopRequirementWeeks,
789792
},
793+
RequirePromotedVersion: template.RequirePromotedVersion,
790794
}
791795
}

coderd/wsbuilder/wsbuilder.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,11 @@ func (b *Builder) buildTx(authFunc func(action rbac.Action, object rbac.Objecter
343343
MaxDeadline: time.Time{}, // set by provisioner upon completion
344344
})
345345
if err != nil {
346-
return BuildError{http.StatusInternalServerError, "insert workspace build", err}
346+
code := http.StatusInternalServerError
347+
if rbac.IsUnauthorizedError(err) {
348+
code = http.StatusUnauthorized
349+
}
350+
return BuildError{code, "insert workspace build", err}
347351
}
348352

349353
names, values, err := b.getParameters()

codersdk/templates.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ type Template struct {
5050
FailureTTLMillis int64 `json:"failure_ttl_ms"`
5151
TimeTilDormantMillis int64 `json:"time_til_dormant_ms"`
5252
TimeTilDormantAutoDeleteMillis int64 `json:"time_til_dormant_autodelete_ms"`
53+
54+
RequirePromotedVersion bool `json:"require_promoted_version"`
5355
}
5456

5557
// WeekdaysToBitmap converts a list of weekdays to a bitmap in accordance with

enterprise/coderd/schedule/template.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ func (s *EnterpriseTemplateScheduleStore) Get(ctx context.Context, db database.S
8989
FailureTTL: time.Duration(tpl.FailureTTL),
9090
TimeTilDormant: time.Duration(tpl.TimeTilDormant),
9191
TimeTilDormantAutoDelete: time.Duration(tpl.TimeTilDormantAutoDelete),
92+
RequirePromotedVersion: tpl.RequirePromotedVersion,
9293
}, nil
9394
}
9495

enterprise/coderd/templates_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/coder/coder/v2/cryptorand"
1919
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
2020
"github.com/coder/coder/v2/enterprise/coderd/license"
21+
"github.com/coder/coder/v2/enterprise/coderd/schedule"
2122
"github.com/coder/coder/v2/provisioner/echo"
2223
"github.com/coder/coder/v2/testutil"
2324
)
@@ -488,6 +489,41 @@ func TestTemplates(t *testing.T) {
488489
require.Equal(t, updatedDormantWS.DormantAt, dormantWorkspace.DormantAt)
489490
require.True(t, updatedDormantWS.LastUsedAt.After(dormantWorkspace.LastUsedAt))
490491
})
492+
493+
t.Run("RequirePromotedVersion", func(t *testing.T) {
494+
t.Parallel()
495+
client, user := coderdenttest.New(t, &coderdenttest.Options{
496+
Options: &coderdtest.Options{
497+
IncludeProvisionerDaemon: true,
498+
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore()),
499+
},
500+
LicenseOptions: &coderdenttest.LicenseOptions{
501+
Features: license.Features{
502+
codersdk.FeatureAdvancedTemplateScheduling: 1,
503+
},
504+
},
505+
})
506+
507+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
508+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
509+
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
510+
require.False(t, template.RequirePromotedVersion)
511+
512+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
513+
defer cancel()
514+
515+
updatedTemplate, err := client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{
516+
RequirePromotedVersion: true,
517+
})
518+
require.NoError(t, err)
519+
require.True(t, updatedTemplate.RequirePromotedVersion)
520+
521+
// Assert that fetching a template is no different from the response
522+
// when updating.
523+
template, err = client.Template(ctx, template.ID)
524+
require.NoError(t, err)
525+
require.Equal(t, updatedTemplate, template)
526+
})
491527
}
492528

493529
func TestTemplateACL(t *testing.T) {
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package coderd_test
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/coder/coder/v2/coderd/coderdtest"
11+
"github.com/coder/coder/v2/coderd/rbac"
12+
"github.com/coder/coder/v2/codersdk"
13+
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
14+
"github.com/coder/coder/v2/enterprise/coderd/license"
15+
"github.com/coder/coder/v2/enterprise/coderd/schedule"
16+
"github.com/coder/coder/v2/testutil"
17+
)
18+
19+
func TestWorkspaceBuild(t *testing.T) {
20+
t.Parallel()
21+
t.Run("TemplateAdminNoActiveVersion", func(t *testing.T) {
22+
t.Parallel()
23+
24+
ctx := testutil.Context(t, testutil.WaitMedium)
25+
ownerClient, owner := coderdenttest.New(t, &coderdenttest.Options{
26+
Options: &coderdtest.Options{
27+
IncludeProvisionerDaemon: true,
28+
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore()),
29+
},
30+
LicenseOptions: &coderdenttest.LicenseOptions{
31+
Features: license.Features{
32+
codersdk.FeatureAdvancedTemplateScheduling: 1,
33+
codersdk.FeatureTemplateRBAC: 1,
34+
},
35+
},
36+
})
37+
38+
// Create an initial version.
39+
oldVersion := coderdtest.CreateTemplateVersion(t, ownerClient, owner.OrganizationID, nil)
40+
// Create a template that mandates the promoted version.
41+
// This should be enforced for everyone except template admins.
42+
template := coderdtest.CreateTemplate(t, ownerClient, owner.OrganizationID, oldVersion.ID)
43+
coderdtest.AwaitTemplateVersionJobCompleted(t, ownerClient, oldVersion.ID)
44+
require.Equal(t, oldVersion.ID, template.ActiveVersionID)
45+
template, err := ownerClient.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{
46+
RequirePromotedVersion: true,
47+
})
48+
require.NoError(t, err)
49+
50+
// Create a new version that we will promote.
51+
activeVersion := coderdtest.CreateTemplateVersion(t, ownerClient, owner.OrganizationID, nil, func(ctvr *codersdk.CreateTemplateVersionRequest) {
52+
ctvr.TemplateID = template.ID
53+
})
54+
coderdtest.AwaitTemplateVersionJobCompleted(t, ownerClient, activeVersion.ID)
55+
err = ownerClient.UpdateActiveTemplateVersion(ctx, template.ID, codersdk.UpdateActiveTemplateVersion{
56+
ID: activeVersion.ID,
57+
})
58+
require.NoError(t, err)
59+
60+
templateAdminClient, _ := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.RoleTemplateAdmin())
61+
templateACLAdminClient, templateACLAdmin := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID)
62+
templateGroupACLAdminClient, templateGroupACLAdmin := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID)
63+
memberClient, _ := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID)
64+
65+
// Create a group so we can also test group template admin ownership.
66+
group, err := ownerClient.CreateGroup(ctx, owner.OrganizationID, codersdk.CreateGroupRequest{
67+
Name: "test",
68+
})
69+
require.NoError(t, err)
70+
71+
// Add the user who gains template admin via group membership.
72+
group, err = ownerClient.PatchGroup(ctx, group.ID, codersdk.PatchGroupRequest{
73+
AddUsers: []string{templateGroupACLAdmin.ID.String()},
74+
})
75+
require.NoError(t, err)
76+
77+
// Update the template for both users and groups.
78+
err = ownerClient.UpdateTemplateACL(ctx, template.ID, codersdk.UpdateTemplateACL{
79+
UserPerms: map[string]codersdk.TemplateRole{
80+
templateACLAdmin.ID.String(): codersdk.TemplateRoleAdmin,
81+
},
82+
GroupPerms: map[string]codersdk.TemplateRole{
83+
group.ID.String(): codersdk.TemplateRoleAdmin,
84+
},
85+
})
86+
require.NoError(t, err)
87+
88+
type testcase struct {
89+
Name string
90+
Client *codersdk.Client
91+
ExpectedStatusCode int
92+
}
93+
94+
cases := []testcase{
95+
{
96+
Name: "OwnerOK",
97+
Client: ownerClient,
98+
ExpectedStatusCode: http.StatusOK,
99+
},
100+
{
101+
Name: "TemplateAdminOK",
102+
Client: templateAdminClient,
103+
ExpectedStatusCode: http.StatusOK,
104+
},
105+
{
106+
Name: "TemplateACLAdminOK",
107+
Client: templateACLAdminClient,
108+
ExpectedStatusCode: http.StatusOK,
109+
},
110+
{
111+
Name: "TemplateGroupACLAdminOK",
112+
Client: templateGroupACLAdminClient,
113+
ExpectedStatusCode: http.StatusOK,
114+
},
115+
{
116+
Name: "MemberFails",
117+
Client: memberClient,
118+
ExpectedStatusCode: http.StatusUnauthorized,
119+
},
120+
}
121+
122+
for _, c := range cases {
123+
t.Run(c.Name, func(t *testing.T) {
124+
_, err = c.Client.CreateWorkspace(context.Background(), owner.OrganizationID, codersdk.Me, codersdk.CreateWorkspaceRequest{
125+
TemplateVersionID: oldVersion.ID,
126+
Name: "abc123",
127+
AutomaticUpdates: codersdk.AutomaticUpdatesNever,
128+
})
129+
if c.ExpectedStatusCode == http.StatusOK {
130+
require.NoError(t, err)
131+
} else {
132+
require.Error(t, err)
133+
cerr, ok := codersdk.AsError(err)
134+
require.True(t, ok)
135+
require.Equal(t, c.ExpectedStatusCode, cerr.StatusCode())
136+
}
137+
})
138+
}
139+
})
140+
}

enterprise/coderd/workspaces_test.go

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -762,11 +762,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
762762
require.NoError(t, err)
763763

764764
// Create a template version1 that passes to get a functioning workspace.
765-
version1 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
766-
Parse: echo.ParseComplete,
767-
ProvisionPlan: echo.PlanComplete,
768-
ProvisionApply: echo.ApplyComplete,
769-
})
765+
version1 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
770766
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version1.ID)
771767

772768
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version1.ID)
@@ -781,11 +777,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
781777

782778
// Create a new version so that we can assert we don't update
783779
// to the latest by default.
784-
version2 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
785-
Parse: echo.ParseComplete,
786-
ProvisionPlan: echo.PlanComplete,
787-
ProvisionApply: echo.ApplyComplete,
788-
}, func(ctvr *codersdk.CreateTemplateVersionRequest) {
780+
version2 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil, func(ctvr *codersdk.CreateTemplateVersionRequest) {
789781
ctvr.TemplateID = template.ID
790782
})
791783
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version2.ID)
@@ -807,7 +799,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
807799
// Validate that we didn't update to the promoted version.
808800
started := coderdtest.MustWorkspace(t, client, ws.ID)
809801
firstBuild := coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, started.LatestBuild.ID)
810-
require.Equal(t, version1.ID, ws.LatestBuild.TemplateVersionID)
802+
require.Equal(t, version1.ID, firstBuild.TemplateVersionID)
811803

812804
// Update the template to require the promoted version.
813805
_, err = client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{
@@ -818,7 +810,9 @@ func TestWorkspaceAutobuild(t *testing.T) {
818810

819811
// Reset the workspace to the stopped state so we can try
820812
// to autostart again.
821-
coderdtest.MustTransitionWorkspace(t, client, ws.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)
813+
coderdtest.MustTransitionWorkspace(t, client, ws.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop, func(req *codersdk.CreateWorkspaceBuildRequest) {
814+
req.TemplateVersionID = ws.LatestBuild.TemplateVersionID
815+
})
822816

823817
// Force an autostart transition again.
824818
tickCh <- sched.Next(firstBuild.CreatedAt)

0 commit comments

Comments
 (0)