Skip to content

Commit c774a77

Browse files
committed
add unit test
1 parent 2f8884c commit c774a77

File tree

2 files changed

+105
-16
lines changed

2 files changed

+105
-16
lines changed

cli/organizationroles.go

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"encoding/json"
55
"fmt"
66
"io"
7-
"os"
87
"slices"
98
"strings"
109

@@ -114,7 +113,8 @@ func (r *RootCmd) editOrganizationRole() *serpent.Command {
114113
)
115114

116115
var (
117-
dryRun bool
116+
dryRun bool
117+
jsonInput bool
118118
)
119119

120120
client := new(codersdk.Client)
@@ -135,6 +135,12 @@ func (r *RootCmd) editOrganizationRole() *serpent.Command {
135135
Flag: "dry-run",
136136
Value: serpent.BoolOf(&dryRun),
137137
},
138+
{
139+
Name: "stdin",
140+
Description: "Reads stdin for the json role definition to upload.",
141+
Flag: "stdin",
142+
Value: serpent.BoolOf(&jsonInput),
143+
},
138144
},
139145
Middleware: serpent.Chain(
140146
serpent.RequireRangeArgs(0, 1),
@@ -148,10 +154,9 @@ func (r *RootCmd) editOrganizationRole() *serpent.Command {
148154
}
149155

150156
var customRole codersdk.Role
151-
fi, _ := os.Stdin.Stat()
152-
if (fi.Mode() & os.ModeCharDevice) == 0 {
157+
if jsonInput {
153158
// JSON Upload mode
154-
bytes, err := io.ReadAll(os.Stdin)
159+
bytes, err := io.ReadAll(inv.Stdin)
155160
if err != nil {
156161
return xerrors.Errorf("reading stdin: %w", err)
157162
}
@@ -170,6 +175,10 @@ func (r *RootCmd) editOrganizationRole() *serpent.Command {
170175
return xerrors.Errorf("json input does not appear to be a valid role")
171176
}
172177
} else {
178+
if len(inv.Args) == 0 {
179+
return xerrors.Errorf("missing role name argument, usage: \"coder organizations roles edit <role_name>\"")
180+
}
181+
173182
interactiveRole, err := interactiveOrgRoleEdit(inv, org.ID, client)
174183
if err != nil {
175184
return xerrors.Errorf("editing role: %w", err)
@@ -182,7 +191,7 @@ func (r *RootCmd) editOrganizationRole() *serpent.Command {
182191
for _, o := range customRole.OrganizationPermissions {
183192
totalOrg += len(o)
184193
}
185-
preview := fmt.Sprintf("perms: %d site, %d over %d orgs, %d user",
194+
preview := fmt.Sprintf("permissions: %d site, %d over %d orgs, %d user",
186195
len(customRole.SitePermissions), totalOrg, len(customRole.OrganizationPermissions), len(customRole.UserPermissions))
187196
_, err = cliui.Prompt(inv, cliui.PromptOptions{
188197
Text: "Are you sure you wish to update the role? " + preview,
@@ -276,7 +285,7 @@ customRoleLoop:
276285
for {
277286
selected, err := cliui.Select(inv, cliui.SelectOptions{
278287
Message: "Select which resources to edit permissions",
279-
Options: append(permissionPreviews(role, allowedResources), done, abort),
288+
Options: append(permissionPreviews(role, orgID, allowedResources), done, abort),
280289
})
281290
if err != nil {
282291
return role, xerrors.Errorf("selecting resource: %w", err)
@@ -293,7 +302,7 @@ customRoleLoop:
293302
actions, err := cliui.MultiSelect(inv, cliui.MultiSelectOptions{
294303
Message: fmt.Sprintf("Select actions to allow across the whole deployment for resources=%q", resource),
295304
Options: slice.ToStrings(codersdk.RBACResourceActions[codersdk.RBACResource(resource)]),
296-
Defaults: defaultActions(role, resource),
305+
Defaults: defaultActions(role, orgID, resource),
297306
})
298307
if err != nil {
299308
return role, xerrors.Errorf("selecting actions for resource %q: %w", resource, err)
@@ -309,6 +318,10 @@ customRoleLoop:
309318
}
310319

311320
func applyOrgResourceActions(role *codersdk.Role, orgID uuid.UUID, resource string, actions []string) {
321+
if role.OrganizationPermissions == nil {
322+
role.OrganizationPermissions = map[string][]codersdk.Permission{}
323+
}
324+
312325
if _, ok := role.OrganizationPermissions[orgID.String()]; !ok {
313326
role.OrganizationPermissions[orgID.String()] = []codersdk.Permission{}
314327
}
@@ -334,27 +347,35 @@ func applyOrgResourceActions(role *codersdk.Role, orgID uuid.UUID, resource stri
334347
role.OrganizationPermissions[orgID.String()] = keep
335348
}
336349

337-
func defaultActions(role *codersdk.Role, resource string) []string {
350+
func defaultActions(role *codersdk.Role, orgID uuid.UUID, resource string) []string {
351+
if role.OrganizationPermissions == nil {
352+
role.OrganizationPermissions = map[string][]codersdk.Permission{}
353+
}
354+
338355
defaults := make([]string, 0)
339-
for _, perm := range role.SitePermissions {
356+
for _, perm := range role.OrganizationPermissions[orgID.String()] {
340357
if string(perm.ResourceType) == resource {
341358
defaults = append(defaults, string(perm.Action))
342359
}
343360
}
344361
return defaults
345362
}
346363

347-
func permissionPreviews(role *codersdk.Role, resources []codersdk.RBACResource) []string {
364+
func permissionPreviews(role *codersdk.Role, orgID uuid.UUID, resources []codersdk.RBACResource) []string {
348365
previews := make([]string, 0, len(resources))
349366
for _, resource := range resources {
350-
previews = append(previews, permissionPreview(role, resource))
367+
previews = append(previews, permissionPreview(role, orgID, resource))
351368
}
352369
return previews
353370
}
354371

355-
func permissionPreview(role *codersdk.Role, resource codersdk.RBACResource) string {
372+
func permissionPreview(role *codersdk.Role, orgID uuid.UUID, resource codersdk.RBACResource) string {
373+
if role.OrganizationPermissions == nil {
374+
role.OrganizationPermissions = map[string][]codersdk.Permission{}
375+
}
376+
356377
count := 0
357-
for _, perm := range role.SitePermissions {
378+
for _, perm := range role.OrganizationPermissions[orgID.String()] {
358379
if perm.ResourceType == resource {
359380
count++
360381
}
@@ -377,8 +398,12 @@ func orgPermissionString(perms map[string][]codersdk.Permission) string {
377398
for _, o := range perms {
378399
totalOrg += len(o)
379400
}
380-
return fmt.Sprintf("%d over %d organizations",
381-
totalOrg, len(perms))
401+
plural := ""
402+
if len(perms) > 1 {
403+
plural = "s"
404+
}
405+
return fmt.Sprintf("%d over %d organization%s",
406+
totalOrg, len(perms), plural)
382407
}
383408

384409
type roleTableRow struct {

enterprise/cli/organization_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package cli
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/coder/coder/v2/cli/clitest"
11+
"github.com/coder/coder/v2/coderd/coderdtest"
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/testutil"
16+
)
17+
18+
func TestEditOrganizationRoles(t *testing.T) {
19+
t.Parallel()
20+
21+
t.Run("JSON", func(t *testing.T) {
22+
t.Parallel()
23+
24+
dv := coderdtest.DeploymentValues(t)
25+
dv.Experiments = []string{string(codersdk.ExperimentCustomRoles)}
26+
client, owner := coderdenttest.New(t, &coderdenttest.Options{
27+
Options: &coderdtest.Options{
28+
DeploymentValues: dv,
29+
},
30+
LicenseOptions: &coderdenttest.LicenseOptions{
31+
Features: license.Features{
32+
codersdk.FeatureCustomRoles: 1,
33+
},
34+
}})
35+
36+
ctx := testutil.Context(t, testutil.WaitMedium)
37+
inv, root := clitest.New(t, "organization", "roles", "edit", "--stdin")
38+
// Use json input, as interactive mode would be challenging to control
39+
inv.Stdin = bytes.NewBufferString(fmt.Sprintf(`{
40+
"name": "new-role",
41+
"organization_id": "%[1]s",
42+
"display_name": "",
43+
"site_permissions": [],
44+
"organization_permissions": {
45+
"%[1]s": [
46+
{
47+
"resource_type": "workspace",
48+
"action": "read"
49+
}
50+
]
51+
},
52+
"user_permissions": [],
53+
"assignable": false,
54+
"built_in": false
55+
}`, owner.OrganizationID.String()))
56+
clitest.SetupConfig(t, client, root)
57+
58+
buf := new(bytes.Buffer)
59+
inv.Stdout = buf
60+
err := inv.WithContext(ctx).Run()
61+
require.NoError(t, err)
62+
require.Contains(t, buf.String(), "new-role")
63+
})
64+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy