Skip to content

Commit

Permalink
[AV-65758] Acceptance Tests - User Resource (#93)
Browse files Browse the repository at this point in the history
  • Loading branch information
matty271828 authored Dec 8, 2023
1 parent d78cfeb commit 91ea25c
Show file tree
Hide file tree
Showing 2 changed files with 235 additions and 2 deletions.
233 changes: 233 additions & 0 deletions internal/resources/acceptance_tests/user_acceptance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
package acceptance_tests

import (
"fmt"
"net/http"
"testing"

"github.com/couchbasecloud/terraform-provider-couchbase-capella/internal/api"
providerschema "github.com/couchbasecloud/terraform-provider-couchbase-capella/internal/schema"
cfg "github.com/couchbasecloud/terraform-provider-couchbase-capella/internal/testing"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
)

func TestAccUserResource(t *testing.T) {
resourceName := "acc_user_" + cfg.GenerateRandomResourceName()
resourceReference := "couchbase-capella_user." + resourceName
projectResourceName := "acc_project_" + cfg.GenerateRandomResourceName()
projectResourceReference := "couchbase-capella_project." + projectResourceName

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { cfg.TestAccPreCheck(t) },
ProtoV6ProviderFactories: cfg.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and Read
{
Config: testAccUserResourceConfig(cfg.Cfg, resourceName, projectResourceName, projectResourceReference),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceReference, "name", "acc_test_user_name"),
resource.TestCheckResourceAttr(resourceReference, "email", "[email protected]"),
resource.TestCheckResourceAttr(resourceReference, "organization_roles.0", "organizationOwner"),
),
},
// Import state
{
ResourceName: resourceReference,
ImportStateIdFunc: generateUserImportIdForResource(resourceReference),
ImportState: true,
ImportStateVerify: true,
},
// Update and Read
{
Config: testAccUserResourceConfigUpdate(cfg.Cfg, resourceName, projectResourceName, projectResourceReference),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceReference, "name", "acc_test_user_name"),
resource.TestCheckResourceAttr(resourceReference, "email", "[email protected]"),
resource.TestCheckResourceAttr(resourceReference, "organization_roles.0", "organizationMember"),
resource.TestCheckResourceAttr(resourceReference, "resources.0.type", "project"),
resource.TestCheckResourceAttr(resourceReference, "resources.0.roles.0", "projectViewer"),
),
},
// NOTE: No delete case is provided - this occurs automatically
},
})
}

// This function takes a resource reference string and returns a resource.TestCheckFunc. The returned function, when used
// in Terraform acceptance tests, ensures the successful deletion of the specified cluster resource. It retrieves
// the resource by name from the Terraform state, initiates the deletion, checks the status of the deletion, and
// confirms that the resource no longer exists. If the resource is successfully deleted, it returns nil; otherwise,
// it returns an error.
func testAccDeleteUserResource(resourceReference string) resource.TestCheckFunc {
return func(s *terraform.State) error {
// retrieve the resource by name from state
var rawState map[string]string
for _, m := range s.Modules {
if len(m.Resources) > 0 {
if v, ok := m.Resources[resourceReference]; ok {
rawState = v.Primary.Attributes
}
}
}

data, err := cfg.TestClient()
if err != nil {
return err
}
err = deleteUserFromServer(data, rawState["organization_id"], rawState["id"])
if err != nil {
return err
}
fmt.Printf("delete initiated")
err = readUserFromServer(data, rawState["organization_id"], rawState["id"])
resourceNotFound, errString := api.CheckResourceNotFoundError(err)
if !resourceNotFound {
return fmt.Errorf(errString)
}
fmt.Printf("successfully deleted")
return nil
}
}

// deleteUserFromServer deletes user from server
func deleteUserFromServer(data *providerschema.Data, organizationId, clusterId string) error {
url := fmt.Sprintf("%s/v4/organizations/%s/users/%s", data.HostURL, organizationId, clusterId)
cfg := api.EndpointCfg{Url: url, Method: http.MethodDelete, SuccessStatus: http.StatusNoContent}
_, err := data.Client.Execute(
cfg,
nil,
data.Token,
nil,
)
if err != nil {
return err
}
return nil
}

// readUserFromServer reads user from server
func readUserFromServer(data *providerschema.Data, organizationId, clusterId string) error {
url := fmt.Sprintf("%s/v4/organizations/%s/users/%s", data.HostURL, organizationId, clusterId)
cfg := api.EndpointCfg{Url: url, Method: http.MethodGet, SuccessStatus: http.StatusOK}
_, err := data.Client.Execute(
cfg,
nil,
data.Token,
nil,
)
if err != nil {
return err
}
return nil
}

func TestAccUserResourceResourceNotFound(t *testing.T) {
resourceName := "acc_user_" + cfg.GenerateRandomResourceName()
resourceReference := "couchbase-capella_user." + resourceName
projectResourceName := "acc_project_" + cfg.GenerateRandomResourceName()
projectResourceReference := "couchbase-capella_project." + projectResourceName
resource.Test(t, resource.TestCase{
PreCheck: func() { cfg.TestAccPreCheck(t) },
ProtoV6ProviderFactories: cfg.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and Read testing
{
Config: testAccUserResourceConfig(cfg.Cfg, resourceName, projectResourceName, projectResourceReference),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceReference, "name", "acc_test_user_name"),
resource.TestCheckResourceAttr(resourceReference, "email", "[email protected]"),
resource.TestCheckResourceAttr(resourceReference, "organization_roles.0", "organizationOwner"),
// Delete the user from the server and wait until deletion is successful
testAccDeleteUserResource(resourceReference),
),
ExpectNonEmptyPlan: true,
RefreshState: false,
},

// Attempt to update - since the orginal has been deleted, a new user will be created.
{
Config: testAccUserResourceConfigUpdate(cfg.Cfg, resourceName, projectResourceName, projectResourceReference),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceReference, "name", "acc_test_user_name"),
resource.TestCheckResourceAttr(resourceReference, "email", "[email protected]"),
resource.TestCheckResourceAttr(resourceReference, "organization_roles.0", "organizationMember"),
resource.TestCheckResourceAttr(resourceReference, "resources.0.type", "project"),
resource.TestCheckResourceAttr(resourceReference, "resources.0.roles.0", "projectViewer"),
),
},
},
})
}

func testAccUserResourceConfig(cfg, resourceReference, projectResourceName, projectResourceReference string) string {
return fmt.Sprintf(`
%[1]s
resource "couchbase-capella_project" "%[3]s" {
organization_id = var.organization_id
name = "acc_test_project_name"
description = "description"
}
resource "couchbase-capella_user" "%[2]s" {
organization_id = var.organization_id
name = "acc_test_user_name"
email = "[email protected]"
organization_roles = [
"organizationOwner"
]
}
`, cfg, resourceReference, projectResourceName, projectResourceReference)
}

func testAccUserResourceConfigUpdate(cfg, resourceReference, projectResourceName, projectResourceReference string) string {
return fmt.Sprintf(`
%[1]s
resource "couchbase-capella_project" "%[3]s" {
organization_id = var.organization_id
name = "acc_test_project_name"
description = "description"
}
resource "couchbase-capella_user" "%[2]s" {
organization_id = var.organization_id
name = "acc_test_user_name"
email = "[email protected]"
organization_roles = [
"organizationMember"
]
resources = [
{
type = "project"
id = %[4]s.id
roles = [
"projectViewer",
]
}
]
}
`, cfg, resourceReference, projectResourceName, projectResourceReference)
}

func generateUserImportIdForResource(resourceReference string) resource.ImportStateIdFunc {
return func(state *terraform.State) (string, error) {
var rawState map[string]string
for _, m := range state.Modules {
if len(m.Resources) > 0 {
if v, ok := m.Resources[resourceReference]; ok {
rawState = v.Primary.Attributes
}
}
}
fmt.Printf("raw state %s", rawState)
return fmt.Sprintf("id=%s,organization_id=%s", rawState["id"], rawState["organization_id"]), nil
}
}
4 changes: 2 additions & 2 deletions internal/resources/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ func (r *User) Read(ctx context.Context, req resource.ReadRequest, resp *resourc
IDs, err := state.Validate()
if err != nil {
resp.Diagnostics.AddError(
"Error Reading Capella AllowList",
"Could not read Capella allow list: "+err.Error(),
"Error Reading Capella User",
"Could not read Capella user: "+state.Id.String()+": "+err.Error(),
)
return
}
Expand Down

0 comments on commit 91ea25c

Please sign in to comment.