Skip to content

Commit 65c854f

Browse files
committed
chore: test creating a group with unmanaged members
1 parent d157e1d commit 65c854f

File tree

3 files changed

+99
-71
lines changed

3 files changed

+99
-71
lines changed

internal/provider/group_resource.go

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -240,19 +240,24 @@ func (r *GroupResource) Update(ctx context.Context, req resource.UpdateRequest,
240240
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to get group, got error: %s", err))
241241
return
242242
}
243-
var newMembers []string
244-
resp.Diagnostics.Append(
245-
data.Members.ElementsAs(ctx, &newMembers, false)...,
246-
)
247-
if resp.Diagnostics.HasError() {
248-
return
249-
}
250243
var add []string
251244
var remove []string
252245
if !data.Members.IsNull() {
253-
add, remove = memberDiff(group.Members, newMembers)
246+
var plannedMembers []UUID
247+
resp.Diagnostics.Append(
248+
data.Members.ElementsAs(ctx, &plannedMembers, false)...,
249+
)
250+
if resp.Diagnostics.HasError() {
251+
return
252+
}
253+
curMembers := make([]uuid.UUID, 0, len(group.Members))
254+
for _, member := range group.Members {
255+
curMembers = append(curMembers, member.ID)
256+
}
257+
add, remove = memberDiff(curMembers, plannedMembers)
254258
}
255259
tflog.Trace(ctx, "updating group", map[string]any{
260+
"id": groupID,
256261
"new_members": add,
257262
"removed_members": remove,
258263
"new_name": data.Name,
@@ -293,7 +298,9 @@ func (r *GroupResource) Delete(ctx context.Context, req resource.DeleteRequest,
293298
client := r.data.Client
294299
groupID := data.ID.ValueUUID()
295300

296-
tflog.Trace(ctx, "deleting group")
301+
tflog.Trace(ctx, "deleting group", map[string]any{
302+
"id": groupID,
303+
})
297304
err := client.DeleteGroup(ctx, groupID)
298305
if err != nil {
299306
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete group, got error: %s", err))
@@ -320,24 +327,3 @@ func (r *GroupResource) ImportState(ctx context.Context, req resource.ImportStat
320327
}
321328
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
322329
}
323-
324-
func memberDiff(curMembers []codersdk.ReducedUser, newMembers []string) (add, remove []string) {
325-
curSet := make(map[string]struct{}, len(curMembers))
326-
newSet := make(map[string]struct{}, len(newMembers))
327-
328-
for _, user := range curMembers {
329-
curSet[user.ID.String()] = struct{}{}
330-
}
331-
for _, userID := range newMembers {
332-
newSet[userID] = struct{}{}
333-
if _, exists := curSet[userID]; !exists {
334-
add = append(add, userID)
335-
}
336-
}
337-
for _, user := range curMembers {
338-
if _, exists := newSet[user.ID.String()]; !exists {
339-
remove = append(remove, user.ID.String())
340-
}
341-
}
342-
return add, remove
343-
}

internal/provider/group_resource_test.go

Lines changed: 58 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -62,49 +62,66 @@ func TestAccGroupResource(t *testing.T) {
6262
cfg3 := cfg2
6363
cfg3.Members = nil
6464

65-
resource.Test(t, resource.TestCase{
66-
PreCheck: func() { testAccPreCheck(t) },
67-
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
68-
Steps: []resource.TestStep{
69-
// Create and Read
70-
{
71-
Config: cfg1.String(t),
72-
Check: resource.ComposeAggregateTestCheckFunc(
73-
resource.TestCheckResourceAttr("coderd_group.test", "name", "example-group"),
74-
resource.TestCheckResourceAttr("coderd_group.test", "display_name", "Example Group"),
75-
resource.TestCheckResourceAttr("coderd_group.test", "avatar_url", "https://google.com"),
76-
resource.TestCheckResourceAttr("coderd_group.test", "quota_allowance", "100"),
77-
resource.TestCheckResourceAttr("coderd_group.test", "organization_id", firstUser.OrganizationIDs[0].String()),
78-
resource.TestCheckResourceAttr("coderd_group.test", "members.#", "1"),
79-
resource.TestCheckResourceAttr("coderd_group.test", "members.0", user1.ID.String()),
80-
),
65+
t.Run("CreateImportUpdateReadOk", func(t *testing.T) {
66+
resource.Test(t, resource.TestCase{
67+
PreCheck: func() { testAccPreCheck(t) },
68+
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
69+
Steps: []resource.TestStep{
70+
// Create and Read
71+
{
72+
Config: cfg1.String(t),
73+
Check: resource.ComposeAggregateTestCheckFunc(
74+
resource.TestCheckResourceAttr("coderd_group.test", "name", "example-group"),
75+
resource.TestCheckResourceAttr("coderd_group.test", "display_name", "Example Group"),
76+
resource.TestCheckResourceAttr("coderd_group.test", "avatar_url", "https://google.com"),
77+
resource.TestCheckResourceAttr("coderd_group.test", "quota_allowance", "100"),
78+
resource.TestCheckResourceAttr("coderd_group.test", "organization_id", firstUser.OrganizationIDs[0].String()),
79+
resource.TestCheckResourceAttr("coderd_group.test", "members.#", "1"),
80+
resource.TestCheckResourceAttr("coderd_group.test", "members.0", user1.ID.String()),
81+
),
82+
},
83+
// Import
84+
{
85+
Config: cfg1.String(t),
86+
ResourceName: "coderd_group.test",
87+
ImportState: true,
88+
ImportStateVerify: true,
89+
ImportStateVerifyIgnore: []string{"members"},
90+
},
91+
// Update and Read
92+
{
93+
Config: cfg2.String(t),
94+
Check: resource.ComposeAggregateTestCheckFunc(
95+
resource.TestCheckResourceAttr("coderd_group.test", "name", "example-group-new"),
96+
resource.TestCheckResourceAttr("coderd_group.test", "display_name", "Example Group New"),
97+
resource.TestCheckResourceAttr("coderd_group.test", "members.#", "1"),
98+
resource.TestCheckResourceAttr("coderd_group.test", "members.0", user2.ID.String()),
99+
),
100+
},
101+
// Unmanaged members
102+
{
103+
Config: cfg3.String(t),
104+
Check: resource.ComposeAggregateTestCheckFunc(
105+
resource.TestCheckNoResourceAttr("coderd_group.test", "members"),
106+
),
107+
},
81108
},
82-
// Import
83-
{
84-
Config: cfg1.String(t),
85-
ResourceName: "coderd_group.test",
86-
ImportState: true,
87-
ImportStateVerify: true,
88-
ImportStateVerifyIgnore: []string{"members"},
89-
},
90-
// Update and Read
91-
{
92-
Config: cfg2.String(t),
93-
Check: resource.ComposeAggregateTestCheckFunc(
94-
resource.TestCheckResourceAttr("coderd_group.test", "name", "example-group-new"),
95-
resource.TestCheckResourceAttr("coderd_group.test", "display_name", "Example Group New"),
96-
resource.TestCheckResourceAttr("coderd_group.test", "members.#", "1"),
97-
resource.TestCheckResourceAttr("coderd_group.test", "members.0", user2.ID.String()),
98-
),
99-
},
100-
// Unmanaged members
101-
{
102-
Config: cfg3.String(t),
103-
Check: resource.ComposeAggregateTestCheckFunc(
104-
resource.TestCheckNoResourceAttr("coderd_group.test", "members"),
105-
),
109+
})
110+
})
111+
112+
t.Run("CreateUnmanagedMembersOk", func(t *testing.T) {
113+
resource.Test(t, resource.TestCase{
114+
PreCheck: func() { testAccPreCheck(t) },
115+
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
116+
Steps: []resource.TestStep{
117+
{
118+
Config: cfg3.String(t),
119+
Check: resource.ComposeAggregateTestCheckFunc(
120+
resource.TestCheckNoResourceAttr("coderd_group.test", "members"),
121+
),
122+
},
106123
},
107-
},
124+
})
108125
})
109126
}
110127

internal/provider/util.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"fmt"
77
"os"
88
"path/filepath"
9+
10+
"github.com/google/uuid"
911
)
1012

1113
func PtrTo[T any](v T) *T {
@@ -76,3 +78,26 @@ func computeDirectoryHash(directory string) (string, error) {
7678
}
7779
return hex.EncodeToString(hash.Sum(nil)), nil
7880
}
81+
82+
// memberDiff returns the members to add and remove from the group, given the current members and the planned members.
83+
// plannedMembers is deliberately our custom type, as Terraform cannot automatically produce `[]uuid.UUID` from a set.
84+
func memberDiff(curMembers []uuid.UUID, plannedMembers []UUID) (add, remove []string) {
85+
curSet := make(map[uuid.UUID]struct{}, len(curMembers))
86+
planSet := make(map[uuid.UUID]struct{}, len(plannedMembers))
87+
88+
for _, userID := range curMembers {
89+
curSet[userID] = struct{}{}
90+
}
91+
for _, plannedUserID := range plannedMembers {
92+
planSet[plannedUserID.ValueUUID()] = struct{}{}
93+
if _, exists := curSet[plannedUserID.ValueUUID()]; !exists {
94+
add = append(add, plannedUserID.ValueString())
95+
}
96+
}
97+
for _, curUserID := range curMembers {
98+
if _, exists := planSet[curUserID]; !exists {
99+
remove = append(remove, curUserID.String())
100+
}
101+
}
102+
return add, remove
103+
}

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