Skip to content

Commit cb5b312

Browse files
author
Cristina Sánchez Sánchez
committed
Added model tests
1 parent 84513be commit cb5b312

File tree

4 files changed

+144
-78
lines changed

4 files changed

+144
-78
lines changed

internal/service/clouduserprojectassignment/model.go

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"go.mongodb.org/atlas-sdk/v20250312005/admin"
1010
)
1111

12-
// TODO: `ctx` parameter and `diags` return value can be removed if tf schema has no complex data types (e.g., schema.ListAttribute, schema.SetAttribute)
1312
func NewTFModel(ctx context.Context, projectID string, apiResp *admin.GroupUserResponse) (*TFModel, diag.Diagnostics) {
1413
diags := diag.Diagnostics{}
1514

@@ -23,7 +22,7 @@ func NewTFModel(ctx context.Context, projectID string, apiResp *admin.GroupUserR
2322
Country: types.StringPointerValue(apiResp.Country),
2423
CreatedAt: types.StringPointerValue(conversion.TimePtrToStringPtr(apiResp.CreatedAt)),
2524
FirstName: types.StringPointerValue(apiResp.FirstName),
26-
ProjectId: types.StringValue(projectID),
25+
ProjectId: types.StringValue(projectID),
2726
UserId: types.StringValue(apiResp.GetId()),
2827
InvitationCreatedAt: types.StringPointerValue(conversion.TimePtrToStringPtr(apiResp.InvitationCreatedAt)),
2928
InvitationExpiresAt: types.StringPointerValue(conversion.TimePtrToStringPtr(apiResp.InvitationExpiresAt)),
@@ -37,23 +36,20 @@ func NewTFModel(ctx context.Context, projectID string, apiResp *admin.GroupUserR
3736
}, diags
3837
}
3938

40-
4139
func NewProjectUserReq(ctx context.Context, plan *TFModel) (*admin.GroupUserRequest, diag.Diagnostics) {
4240
var roleNames []string
4341
if !plan.Roles.IsNull() && !plan.Roles.IsUnknown() {
4442
roleNames = conversion.TypesSetToString(ctx, plan.Roles)
4543
}
4644

47-
4845
addProjectUserReq := admin.GroupUserRequest{
49-
Username: plan.Username.ValueString(),
50-
Roles: roleNames,
46+
Username: plan.Username.ValueString(),
47+
Roles: roleNames,
5148
}
5249
return &addProjectUserReq, nil
5350
}
5451

55-
func NewAtlasUpdateReq(ctx context.Context, plan *TFModel, state *TFModel) (addRequests []*admin.AddOrRemoveGroupRole, removeRequests []*admin.AddOrRemoveGroupRole, diags diag.Diagnostics) {
56-
diags = diag.Diagnostics{}
52+
func NewAtlasUpdateReq(ctx context.Context, plan, state *TFModel) (addRequests, removeRequests []*admin.AddOrRemoveGroupRole, diags diag.Diagnostics) {
5753
var currentRoles, desiredRoles []string
5854
if !state.Roles.IsNull() && !state.Roles.IsUnknown() {
5955
currentRoles = conversion.TypesSetToString(ctx, state.Roles)
@@ -82,28 +78,25 @@ func NewAtlasUpdateReq(ctx context.Context, plan *TFModel, state *TFModel) (addR
8278
}
8379

8480
func diffRoles(oldRoles, newRoles []string) (toAdd, toRemove []string) {
85-
8681
oldRolesMap := make(map[string]bool, len(oldRoles))
8782
newRolesMap := make(map[string]bool, len(newRoles))
88-
83+
8984
for _, role := range oldRoles {
9085
oldRolesMap[role] = true
9186
}
92-
87+
9388
for _, role := range newRoles {
9489
newRolesMap[role] = true
9590
if !oldRolesMap[role] {
9691
toAdd = append(toAdd, role)
9792
}
9893
}
99-
94+
10095
for _, role := range oldRoles {
10196
if !newRolesMap[role] {
10297
toRemove = append(toRemove, role)
10398
}
10499
}
105-
100+
106101
return toAdd, toRemove
107102
}
108-
109-

internal/service/clouduserprojectassignment/model_test.go

Lines changed: 112 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,144 @@
11
package clouduserprojectassignment_test
22

33
import (
4-
"context"
54
"testing"
5+
"time"
66

7-
"github.com/stretchr/testify/assert"
7+
"github.com/hashicorp/terraform-plugin-framework/types"
88
"github.com/mongodb/terraform-provider-mongodbatlas/internal/service/clouduserprojectassignment"
9-
// "go.mongodb.org/atlas-sdk/v20231115003/admin" use latest version
9+
"github.com/stretchr/testify/assert"
10+
"go.mongodb.org/atlas-sdk/v20250312005/admin"
11+
)
12+
13+
const (
14+
testUserID = "user-123"
15+
testUsername = "jdoe"
16+
testFirstName = "John"
17+
testLastName = "Doe"
18+
testCountry = "CA"
19+
testMobile = "+1555123456"
20+
testInviter = "admin"
21+
testOrgMembershipStatus = "ACTIVE"
22+
testInviterUsername = ""
23+
24+
testProjectRoleOwner = "PROJECT_OWNER"
25+
testProjectRoleRead = "PROJECT_READ_ONLY"
26+
testProjectRoleMember = "PROJECT_MEMBER"
27+
28+
testProjectID = "project-123"
29+
testOrgID = "org-123"
30+
)
31+
32+
var (
33+
when = time.Date(2020, 1, 2, 3, 4, 5, 0, time.UTC)
34+
testCreatedAt = when.Format(time.RFC3339)
35+
testInvitationCreatedAt = when.Add(-24 * time.Hour).Format(time.RFC3339)
36+
testInvitationExpiresAt = when.Add(24 * time.Hour).Format(time.RFC3339)
37+
testLastAuth = when.Add(-2 * time.Hour).Format(time.RFC3339)
38+
39+
testProjectRoles = []string{testProjectRoleMember, testProjectRoleOwner}
1040
)
1141

1242
type sdkToTFModelTestCase struct {
13-
SDKResp *admin.CloudUserProjectAssignment
43+
SDKResp *admin.GroupUserResponse
1444
expectedTFModel *clouduserprojectassignment.TFModel
1545
}
1646

1747
func TestCloudUserProjectAssignmentSDKToTFModel(t *testing.T) {
18-
testCases := map[string]sdkToTFModelTestCase{ // TODO: consider adding test cases to contemplate all possible API responses
48+
ctx := t.Context()
49+
50+
fullResp := &admin.GroupUserResponse{
51+
Id: testUserID,
52+
Username: testUsername,
53+
FirstName: admin.PtrString(testFirstName),
54+
LastName: admin.PtrString(testLastName),
55+
Country: admin.PtrString(testCountry),
56+
MobileNumber: admin.PtrString(testMobile),
57+
OrgMembershipStatus: testOrgMembershipStatus,
58+
CreatedAt: admin.PtrTime(when),
59+
LastAuth: admin.PtrTime(when.Add(-2 * time.Hour)),
60+
InvitationCreatedAt: admin.PtrTime(when.Add(-24 * time.Hour)),
61+
InvitationExpiresAt: admin.PtrTime(when.Add(24 * time.Hour)),
62+
InviterUsername: admin.PtrString(testInviterUsername),
63+
Roles: testProjectRoles,
64+
}
65+
66+
expectedRoles, _ := types.SetValueFrom(ctx, types.StringType, testProjectRoles)
67+
68+
expectedFullModel := &clouduserprojectassignment.TFModel{
69+
UserId: types.StringValue(testUserID),
70+
Username: types.StringValue(testUsername),
71+
ProjectId: types.StringValue(testProjectID),
72+
FirstName: types.StringValue(testFirstName),
73+
LastName: types.StringValue(testLastName),
74+
Country: types.StringValue(testCountry),
75+
MobileNumber: types.StringValue(testMobile),
76+
OrgMembershipStatus: types.StringValue(testOrgMembershipStatus),
77+
CreatedAt: types.StringValue(testCreatedAt),
78+
LastAuth: types.StringValue(testLastAuth),
79+
InvitationCreatedAt: types.StringValue(testInvitationCreatedAt),
80+
InvitationExpiresAt: types.StringValue(testInvitationExpiresAt),
81+
InviterUsername: types.StringValue(testInviterUsername),
82+
Roles: expectedRoles,
83+
}
84+
85+
testCases := map[string]sdkToTFModelTestCase{
86+
"nil SDK response": {
87+
SDKResp: nil,
88+
expectedTFModel: nil,
89+
},
1990
"Complete SDK response": {
20-
SDKResp: &admin.CloudUserProjectAssignment{
21-
},
22-
expectedTFModel: &clouduserprojectassignment.TFModel{
23-
},
91+
SDKResp: fullResp,
92+
expectedTFModel: expectedFullModel,
2493
},
2594
}
2695

2796
for testName, tc := range testCases {
2897
t.Run(testName, func(t *testing.T) {
29-
resultModel, diags := clouduserprojectassignment.NewTFModel(context.Background(), tc.SDKResp)
30-
if diags.HasError() {
31-
t.Errorf("unexpected errors found: %s", diags.Errors()[0].Summary())
32-
}
33-
assert.Equal(t, tc.expectedTFModel, resultModel, "created terraform model did not match expected output")
98+
resultModel, diags := clouduserprojectassignment.NewTFModel(t.Context(), testProjectID, tc.SDKResp)
99+
assert.False(t, diags.HasError(), "expected no diagnostics")
100+
assert.Equal(t, tc.expectedTFModel, resultModel, "TFModel did not match expected")
34101
})
35102
}
36103
}
37104

105+
func TestNewProjectUserRequest(t *testing.T) {
106+
ctx := t.Context()
107+
expectedRoles, _ := types.SetValueFrom(ctx, types.StringType, testProjectRoles)
38108

39-
type tfToSDKModelTestCase struct {
40-
tfModel *clouduserprojectassignment.TFModel
41-
expectedSDKReq *admin.CloudUserProjectAssignment
42-
}
43-
44-
func TestCloudUserProjectAssignmentTFModelToSDK(t *testing.T) {
45-
testCases := map[string]tfToSDKModelTestCase{
46-
"Complete TF state": {
47-
tfModel: &clouduserprojectassignment.TFModel{
109+
testCases := map[string]struct {
110+
plan *clouduserprojectassignment.TFModel
111+
expected *admin.GroupUserRequest
112+
}{
113+
"Complete model": {
114+
plan: &clouduserprojectassignment.TFModel{
115+
UserId: types.StringValue(testUserID),
116+
Username: types.StringValue(testUsername),
117+
ProjectId: types.StringValue(testProjectID),
118+
FirstName: types.StringValue(testFirstName),
119+
LastName: types.StringValue(testLastName),
120+
Country: types.StringValue(testCountry),
121+
MobileNumber: types.StringValue(testMobile),
122+
OrgMembershipStatus: types.StringValue(testOrgMembershipStatus),
123+
CreatedAt: types.StringValue(testCreatedAt),
124+
LastAuth: types.StringValue(testLastAuth),
125+
InvitationCreatedAt: types.StringValue(testInvitationCreatedAt),
126+
InvitationExpiresAt: types.StringValue(testInvitationExpiresAt),
127+
InviterUsername: types.StringValue(testInviterUsername),
128+
Roles: expectedRoles,
48129
},
49-
expectedSDKReq: &admin.CloudUserProjectAssignment{
130+
expected: &admin.GroupUserRequest{
131+
Username: testUsername,
132+
Roles: testProjectRoles,
50133
},
51134
},
52135
}
53136

54-
for testName, tc := range testCases {
55-
t.Run(testName, func(t *testing.T) {
56-
apiReqResult, diags := clouduserprojectassignment.NewAtlasReq(context.Background(), tc.tfModel)
57-
if diags.HasError() {
58-
t.Errorf("unexpected errors found: %s", diags.Errors()[0].Summary())
59-
}
60-
assert.Equal(t, tc.expectedSDKReq, apiReqResult, "created sdk model did not match expected output")
137+
for name, tc := range testCases {
138+
t.Run(name, func(t *testing.T) {
139+
req, diags := clouduserprojectassignment.NewProjectUserReq(ctx, tc.plan)
140+
assert.False(t, diags.HasError(), "expected no diagnostics")
141+
assert.Equal(t, tc.expected, req)
61142
})
62143
}
63144
}
64-
65-

internal/service/clouduserprojectassignment/resource.go

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,12 @@ import (
88

99
"go.mongodb.org/atlas-sdk/v20250312005/admin"
1010

11-
"github.com/hashicorp/terraform-plugin-framework/resource"
1211
"github.com/hashicorp/terraform-plugin-framework/path"
13-
12+
"github.com/hashicorp/terraform-plugin-framework/resource"
1413

1514
"github.com/mongodb/terraform-provider-mongodbatlas/internal/common/conversion"
1615
"github.com/mongodb/terraform-provider-mongodbatlas/internal/common/validate"
1716
"github.com/mongodb/terraform-provider-mongodbatlas/internal/config"
18-
1917
)
2018

2119
const resourceName = "cloud_user_project_assignment"
@@ -36,7 +34,7 @@ type rs struct {
3634
}
3735

3836
func (r *rs) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
39-
// TODO: Schema and model must be defined in resource_schema.go. Details on scaffolding this file found in contributing/development-best-practices.md under "Scaffolding Schema and Model Definitions"
37+
// TODO: Schema and model must be defined in resource_schema.go. Details on scaffolding this file found in contributing/development-best-practices.md under "Scaffolding Schema and Model Definitions"
4038
resp.Schema = resourceSchema(ctx)
4139
conversion.UpdateSchemaDescription(&resp.Schema)
4240
}
@@ -47,7 +45,6 @@ func (r *rs) Create(ctx context.Context, req resource.CreateRequest, resp *resou
4745
if resp.Diagnostics.HasError() {
4846
return
4947
}
50-
5148

5249
connV2 := r.Client.AtlasV2
5350
projectID := plan.ProjectId.ValueString()
@@ -94,7 +91,7 @@ func (r *rs) Read(ctx context.Context, req resource.ReadRequest, resp *resource.
9491
} else if !state.Username.IsNull() && state.Username.ValueString() != "" { // required for import
9592
username := state.Username.ValueString()
9693
params := &admin.ListProjectUsersApiParams{
97-
GroupId: projectID,
94+
GroupId: projectID,
9895
Username: &username,
9996
}
10097
usersResp, _, err := connV2.MongoDBCloudUsersApi.ListProjectUsersWithParams(ctx, params).Execute()
@@ -105,14 +102,13 @@ func (r *rs) Read(ctx context.Context, req resource.ReadRequest, resp *resource.
105102
}
106103
userResp = &usersResp.GetResults()[0]
107104
}
108-
}
105+
}
109106

110107
if err != nil {
111-
resp.Diagnostics.AddError(fmt.Sprintf("error fetching user(%s) from ProjectID(%s):", userResp.Username,projectID), err.Error())
108+
resp.Diagnostics.AddError(fmt.Sprintf("error fetching user(%s) from ProjectID(%s):", userResp.Username, projectID), err.Error())
112109
return
113110
}
114111

115-
116112
newCloudUserProjectAssignmentModel, diags := NewTFModel(ctx, projectID, userResp)
117113
if diags.HasError() {
118114
resp.Diagnostics.Append(diags...)
@@ -164,12 +160,12 @@ func (r *rs) Update(ctx context.Context, req resource.UpdateRequest, resp *resou
164160
}
165161

166162
var userResp *admin.GroupUserResponse
167-
var err error
163+
var err error
168164
if !state.UserId.IsNull() && state.UserId.ValueString() != "" {
169165
userID := state.UserId.ValueString()
170166
userResp, _, err = connV2.MongoDBCloudUsersApi.GetProjectUser(ctx, projectID, userID).Execute()
171167
if err != nil {
172-
resp.Diagnostics.AddError(fmt.Sprintf("error fetching user(%s) from ProjectID(%s):", userResp.Username,projectID), err.Error())
168+
resp.Diagnostics.AddError(fmt.Sprintf("error fetching user(%s) from ProjectID(%s):", userResp.Username, projectID), err.Error())
173169
return
174170
}
175171
}
@@ -224,4 +220,3 @@ func (r *rs) ImportState(ctx context.Context, req resource.ImportStateRequest, r
224220
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("user_id"), userID)...)
225221
}
226222
}
227-

internal/service/clouduserprojectassignment/resource_test.go

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,26 @@ import (
1010
// TODO: if acceptance test will be run in an existing CI group of resources, the name should include the group in the prefix followed by the name of the resource e.i. TestAccStreamRSStreamInstance_basic
1111
// In addition, if acceptance test contains testing of both resource and data sources, the RS/DS can be omitted.
1212
func TestAccCloudUserProjectAssignmentRS_basic(t *testing.T) {
13-
1413
resource.ParallelTest(t, resource.TestCase{
1514
PreCheck: func() { acc.PreCheckBasic(t) },
1615
ProtoV6ProviderFactories: acc.TestAccProviderV6Factories,
17-
// CheckDestroy: checkDestroyCloudUserProjectAssignment,
16+
// CheckDestroy: checkDestroyCloudUserProjectAssignment,
1817
Steps: []resource.TestStep{ // TODO: verify updates and import in case of resources
19-
// {
20-
// Config: cloudUserProjectAssignmentConfig(),
21-
// Check: cloudUserProjectAssignmentAttributeChecks(),
22-
// },
23-
// {
24-
// Config: cloudUserProjectAssignmentConfig(),
25-
// Check: cloudUserProjectAssignmentAttributeChecks(),
26-
// },
27-
// {
28-
// Config: cloudUserProjectAssignmentConfig(),
29-
// ResourceName: resourceName,
30-
// ImportStateIdFunc: checkCloudUserProjectAssignmentImportStateIDFunc,
31-
// ImportState: true,
32-
// ImportStateVerify: true,
33-
},
18+
// {
19+
// Config: cloudUserProjectAssignmentConfig(),
20+
// Check: cloudUserProjectAssignmentAttributeChecks(),
21+
// },
22+
// {
23+
// Config: cloudUserProjectAssignmentConfig(),
24+
// Check: cloudUserProjectAssignmentAttributeChecks(),
25+
// },
26+
// {
27+
// Config: cloudUserProjectAssignmentConfig(),
28+
// ResourceName: resourceName,
29+
// ImportStateIdFunc: checkCloudUserProjectAssignmentImportStateIDFunc,
30+
// ImportState: true,
31+
// ImportStateVerify: true,
3432
},
33+
},
3534
)
36-
}
35+
}

0 commit comments

Comments
 (0)