From e6eb012632948b4959f4705d5fece00a38dd1914 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Mon, 15 Jul 2024 05:51:05 +0000 Subject: [PATCH 1/9] chore: add acceptance tests to user data source --- internal/provider/user_data_source_test.go | 78 ++++++++++++++-------- 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/internal/provider/user_data_source_test.go b/internal/provider/user_data_source_test.go index 6c5c0df..fea867c 100644 --- a/internal/provider/user_data_source_test.go +++ b/internal/provider/user_data_source_test.go @@ -1,57 +1,80 @@ package provider -/* import ( + "context" "html/template" "strings" "testing" + "github.com/coder/coder/v2/codersdk" + "github.com/coder/terraform-provider-coderd/integration" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/stretchr/testify/require" ) func TestAccUserDataSource(t *testing.T) { + ctx := context.Background() + client := integration.StartCoder(ctx, t, "user_acc") + firstUser, err := client.User(ctx, codersdk.Me) + require.NoError(t, err) + user, err := client.CreateUser(ctx, codersdk.CreateUserRequest{ + Email: "example@coder.com", + Username: "example", + Password: "SomeSecurePassword!", + UserLoginType: "password", + OrganizationID: firstUser.OrganizationIDs[0], + }) + require.NoError(t, err) + _, err = client.UpdateUserRoles(ctx, user.Username, codersdk.UpdateRoles{ + Roles: []string{"auditor"}, + }) + require.NoError(t, err) + _, err = client.UpdateUserProfile(ctx, user.Username, codersdk.UpdateUserProfileRequest{ + Username: user.Username, + Name: "Example User", + }) + require.NoError(t, err) + cfg := testAccUserDataSourceConfig{ + URL: client.URL.String(), + Token: client.SessionToken(), + } // User by Username + cfg.Username = user.Username resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { - Config: testAccUserDataSourceConfig{ - Username: "example", - }.String(t), + Config: cfg.String(t), Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("coderd_user.test", "username", "example"), - resource.TestCheckResourceAttr("coderd_user.test", "name", "Example User"), - resource.TestCheckResourceAttr("coderd_user.test", "email", "example@coder.com"), - resource.TestCheckResourceAttr("coderd_user.test", "roles.#", "2"), - resource.TestCheckResourceAttr("coderd_user.test", "roles.0", "auditor"), - resource.TestCheckResourceAttr("coderd_user.test", "roles.1", "owner"), - resource.TestCheckResourceAttr("coderd_user.test", "login_type", "password"), - resource.TestCheckResourceAttr("coderd_user.test", "password", "SomeSecurePassword!"), - resource.TestCheckResourceAttr("coderd_user.test", "suspended", "false"), + resource.TestCheckResourceAttr("data.coderd_user.test", "username", "example"), + resource.TestCheckResourceAttr("data.coderd_user.test", "name", "Example User"), + resource.TestCheckResourceAttr("data.coderd_user.test", "email", "example@coder.com"), + resource.TestCheckResourceAttr("data.coderd_user.test", "roles.#", "1"), + resource.TestCheckResourceAttr("data.coderd_user.test", "roles.0", "auditor"), + resource.TestCheckResourceAttr("data.coderd_user.test", "login_type", "password"), + resource.TestCheckResourceAttr("data.coderd_user.test", "suspended", "false"), ), }, }, }) + cfg.Username = "" + cfg.ID = user.ID.String() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, // User by ID Steps: []resource.TestStep{ { - Config: testAccUserDataSourceConfig{ - ID: "example", - }.String(t), + Config: cfg.String(t), Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("coderd_user.test", "username", "example"), - resource.TestCheckResourceAttr("coderd_user.test", "name", "Example User"), - resource.TestCheckResourceAttr("coderd_user.test", "email", "example@coder.com"), - resource.TestCheckResourceAttr("coderd_user.test", "roles.#", "2"), - resource.TestCheckResourceAttr("coderd_user.test", "roles.0", "auditor"), - resource.TestCheckResourceAttr("coderd_user.test", "roles.1", "owner"), - resource.TestCheckResourceAttr("coderd_user.test", "login_type", "password"), - resource.TestCheckResourceAttr("coderd_user.test", "password", "SomeSecurePassword!"), - resource.TestCheckResourceAttr("coderd_user.test", "suspended", "false"), + resource.TestCheckResourceAttr("data.coderd_user.test", "username", "example"), + resource.TestCheckResourceAttr("data.coderd_user.test", "name", "Example User"), + resource.TestCheckResourceAttr("data.coderd_user.test", "email", "example@coder.com"), + resource.TestCheckResourceAttr("data.coderd_user.test", "roles.#", "1"), + resource.TestCheckResourceAttr("data.coderd_user.test", "roles.0", "auditor"), + resource.TestCheckResourceAttr("data.coderd_user.test", "login_type", "password"), + resource.TestCheckResourceAttr("data.coderd_user.test", "suspended", "false"), ), }, }, @@ -59,8 +82,8 @@ func TestAccUserDataSource(t *testing.T) { } type testAccUserDataSourceConfig struct { - URL string - Token string + URL string + Token string ID string Username string @@ -92,4 +115,3 @@ data "coderd_user" "test" { return buf.String() } -*/ From 34851702a65db48542fa84572194fecceed6b23a Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Mon, 15 Jul 2024 06:38:30 +0000 Subject: [PATCH 2/9] error test --- internal/provider/user_data_source_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/internal/provider/user_data_source_test.go b/internal/provider/user_data_source_test.go index fea867c..9b71e2a 100644 --- a/internal/provider/user_data_source_test.go +++ b/internal/provider/user_data_source_test.go @@ -3,6 +3,7 @@ package provider import ( "context" "html/template" + "regexp" "strings" "testing" @@ -79,6 +80,20 @@ func TestAccUserDataSource(t *testing.T) { }, }, }) + cfg.ID = "" + resource.Test(t, resource.TestCase{ + IsUnitTest: true, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + // Neither ID nor Username + Steps: []resource.TestStep{ + { + Config: cfg.String(t), + ExpectError: regexp.MustCompile(`At least one of these attributes must be configured: \[id,username\]`), + }, + }, + }) + } type testAccUserDataSourceConfig struct { From bd799f3118152db62b745788f54aad235b5ab485 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Mon, 15 Jul 2024 06:57:31 +0000 Subject: [PATCH 3/9] review --- internal/provider/user_data_source_test.go | 115 +++++++++++---------- 1 file changed, 63 insertions(+), 52 deletions(-) diff --git a/internal/provider/user_data_source_test.go b/internal/provider/user_data_source_test.go index 9b71e2a..7d8c8a7 100644 --- a/internal/provider/user_data_source_test.go +++ b/internal/provider/user_data_source_test.go @@ -35,63 +35,74 @@ func TestAccUserDataSource(t *testing.T) { Name: "Example User", }) require.NoError(t, err) - cfg := testAccUserDataSourceConfig{ - URL: client.URL.String(), - Token: client.SessionToken(), - } - // User by Username - cfg.Username = user.Username - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, - Steps: []resource.TestStep{ - { - Config: cfg.String(t), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("data.coderd_user.test", "username", "example"), - resource.TestCheckResourceAttr("data.coderd_user.test", "name", "Example User"), - resource.TestCheckResourceAttr("data.coderd_user.test", "email", "example@coder.com"), - resource.TestCheckResourceAttr("data.coderd_user.test", "roles.#", "1"), - resource.TestCheckResourceAttr("data.coderd_user.test", "roles.0", "auditor"), - resource.TestCheckResourceAttr("data.coderd_user.test", "login_type", "password"), - resource.TestCheckResourceAttr("data.coderd_user.test", "suspended", "false"), - ), + t.Run("UserByUsername", func(t *testing.T) { + cfg := testAccUserDataSourceConfig{ + URL: client.URL.String(), + Token: client.SessionToken(), + Username: user.Username, + } + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: cfg.String(t), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.coderd_user.test", "username", "example"), + resource.TestCheckResourceAttr("data.coderd_user.test", "name", "Example User"), + resource.TestCheckResourceAttr("data.coderd_user.test", "email", "example@coder.com"), + resource.TestCheckResourceAttr("data.coderd_user.test", "roles.#", "1"), + resource.TestCheckResourceAttr("data.coderd_user.test", "roles.0", "auditor"), + resource.TestCheckResourceAttr("data.coderd_user.test", "login_type", "password"), + resource.TestCheckResourceAttr("data.coderd_user.test", "suspended", "false"), + ), + }, }, - }, + }) }) - cfg.Username = "" - cfg.ID = user.ID.String() - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, - // User by ID - Steps: []resource.TestStep{ - { - Config: cfg.String(t), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("data.coderd_user.test", "username", "example"), - resource.TestCheckResourceAttr("data.coderd_user.test", "name", "Example User"), - resource.TestCheckResourceAttr("data.coderd_user.test", "email", "example@coder.com"), - resource.TestCheckResourceAttr("data.coderd_user.test", "roles.#", "1"), - resource.TestCheckResourceAttr("data.coderd_user.test", "roles.0", "auditor"), - resource.TestCheckResourceAttr("data.coderd_user.test", "login_type", "password"), - resource.TestCheckResourceAttr("data.coderd_user.test", "suspended", "false"), - ), + + t.Run("UserByID", func(t *testing.T) { + cfg := testAccUserDataSourceConfig{ + URL: client.URL.String(), + Token: client.SessionToken(), + Username: user.ID.String(), + } + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + // User by ID + Steps: []resource.TestStep{ + { + Config: cfg.String(t), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.coderd_user.test", "username", "example"), + resource.TestCheckResourceAttr("data.coderd_user.test", "name", "Example User"), + resource.TestCheckResourceAttr("data.coderd_user.test", "email", "example@coder.com"), + resource.TestCheckResourceAttr("data.coderd_user.test", "roles.#", "1"), + resource.TestCheckResourceAttr("data.coderd_user.test", "roles.0", "auditor"), + resource.TestCheckResourceAttr("data.coderd_user.test", "login_type", "password"), + resource.TestCheckResourceAttr("data.coderd_user.test", "suspended", "false"), + ), + }, }, - }, + }) }) - cfg.ID = "" - resource.Test(t, resource.TestCase{ - IsUnitTest: true, - PreCheck: func() { testAccPreCheck(t) }, - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, - // Neither ID nor Username - Steps: []resource.TestStep{ - { - Config: cfg.String(t), - ExpectError: regexp.MustCompile(`At least one of these attributes must be configured: \[id,username\]`), + t.Run("NeitherIDNorUsername", func(t *testing.T) { + cfg := testAccUserDataSourceConfig{ + URL: client.URL.String(), + Token: client.SessionToken(), + } + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + // Neither ID nor Username + Steps: []resource.TestStep{ + { + Config: cfg.String(t), + ExpectError: regexp.MustCompile(`At least one of these attributes must be configured: \[id,username\]`), + }, }, - }, + }) }) } From 0f716edd7bca0229fb78239a0280051e12adbf24 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Mon, 15 Jul 2024 06:58:03 +0000 Subject: [PATCH 4/9] fixup --- internal/provider/user_data_source_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/user_data_source_test.go b/internal/provider/user_data_source_test.go index 7d8c8a7..9bfc607 100644 --- a/internal/provider/user_data_source_test.go +++ b/internal/provider/user_data_source_test.go @@ -15,7 +15,7 @@ import ( func TestAccUserDataSource(t *testing.T) { ctx := context.Background() - client := integration.StartCoder(ctx, t, "user_acc") + client := integration.StartCoder(ctx, t, "user_data_acc") firstUser, err := client.User(ctx, codersdk.Me) require.NoError(t, err) user, err := client.CreateUser(ctx, codersdk.CreateUserRequest{ From eece858606a7dbe25d70e1fa625bfa5920e53c54 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Mon, 15 Jul 2024 06:58:33 +0000 Subject: [PATCH 5/9] gen + fmt --- docs/data-sources/user.md | 3 ++- integration/user-test/main.tf | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/data-sources/user.md b/docs/data-sources/user.md index 88c9a1f..2b12670 100644 --- a/docs/data-sources/user.md +++ b/docs/data-sources/user.md @@ -22,11 +22,12 @@ An existing user on the coder deployment ### Read-Only +- `avatar_url` (String) URL of the user's avatar. - `created_at` (Number) Unix timestamp of when the user was created. - `email` (String) Email of the user. - `last_seen_at` (Number) Unix timestamp of when the user was last seen. - `login_type` (String) Type of login for the user. Valid types are 'none', 'password', 'github', and 'oidc'. -- `name` (String) Display name of the user. Defaults to username. +- `name` (String) Display name of the user. - `organization_ids` (Set of String) IDs of organizations the user is associated with. - `roles` (Set of String) Roles assigned to the user. Valid roles are 'owner', 'template-admin', 'user-admin', and 'auditor'. - `suspended` (Boolean) Whether the user is suspended. diff --git a/integration/user-test/main.tf b/integration/user-test/main.tf index 3b05004..922cabb 100644 --- a/integration/user-test/main.tf +++ b/integration/user-test/main.tf @@ -22,11 +22,11 @@ data "coderd_user" "ethan" { } resource "coderd_user" "ethan2" { - username = "${data.coderd_user.ethan.username}2" - name = "${data.coderd_user.ethan.name}2" - email = "${data.coderd_user.ethan.email}.au" - login_type = "${data.coderd_user.ethan.login_type}" - roles = data.coderd_user.ethan.roles - suspended = data.coderd_user.ethan.suspended + username = "${data.coderd_user.ethan.username}2" + name = "${data.coderd_user.ethan.name}2" + email = "${data.coderd_user.ethan.email}.au" + login_type = data.coderd_user.ethan.login_type + roles = data.coderd_user.ethan.roles + suspended = data.coderd_user.ethan.suspended } From f149934467e8e5b821254055ec6961512e21405e Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Mon, 15 Jul 2024 07:20:23 +0000 Subject: [PATCH 6/9] add skip --- internal/provider/user_data_source_test.go | 4 ++++ internal/provider/user_resource_test.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/internal/provider/user_data_source_test.go b/internal/provider/user_data_source_test.go index 9bfc607..bf6bdcb 100644 --- a/internal/provider/user_data_source_test.go +++ b/internal/provider/user_data_source_test.go @@ -3,6 +3,7 @@ package provider import ( "context" "html/template" + "os" "regexp" "strings" "testing" @@ -14,6 +15,9 @@ import ( ) func TestAccUserDataSource(t *testing.T) { + if os.Getenv("TF_ACC") == "" { + t.Skip("Acceptance tests are disabled.") + } ctx := context.Background() client := integration.StartCoder(ctx, t, "user_data_acc") firstUser, err := client.User(ctx, codersdk.Me) diff --git a/internal/provider/user_resource_test.go b/internal/provider/user_resource_test.go index c95ace0..f955310 100644 --- a/internal/provider/user_resource_test.go +++ b/internal/provider/user_resource_test.go @@ -3,6 +3,7 @@ package provider import ( "context" "fmt" + "os" "strings" "testing" "text/template" @@ -13,6 +14,9 @@ import ( ) func TestAccUserResource(t *testing.T) { + if os.Getenv("TF_ACC") == "" { + t.Skip("Acceptance tests are disabled.") + } ctx := context.Background() client := integration.StartCoder(ctx, t, "user_acc") From c524e5434025201905d311e1059f04f07711ec4f Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Tue, 16 Jul 2024 03:02:50 +0000 Subject: [PATCH 7/9] validate retrieved id/username against provided --- internal/provider/user_data_source.go | 7 +++++++ internal/provider/user_data_source_test.go | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/internal/provider/user_data_source.go b/internal/provider/user_data_source.go index 8254fe1..a8654ce 100644 --- a/internal/provider/user_data_source.go +++ b/internal/provider/user_data_source.go @@ -155,6 +155,13 @@ func (d *UserDataSource) Read(ctx context.Context, req datasource.ReadRequest, r resp.Diagnostics.AddError("Client Error", "User is not associated with any organizations") return } + if !data.ID.IsNull() && user.ID.String() != data.ID.ValueString() { + resp.Diagnostics.AddError("Client Error", "Retrieved User's ID does not match the provided ID") + return + } else if !data.Username.IsNull() && user.Username != data.Username.ValueString() { + resp.Diagnostics.AddError("Client Error", "Retrieved User's username does not match the provided username") + return + } data.ID = types.StringValue(user.ID.String()) data.Username = types.StringValue(user.Username) diff --git a/internal/provider/user_data_source_test.go b/internal/provider/user_data_source_test.go index bf6bdcb..b3e3987 100644 --- a/internal/provider/user_data_source_test.go +++ b/internal/provider/user_data_source_test.go @@ -67,9 +67,9 @@ func TestAccUserDataSource(t *testing.T) { t.Run("UserByID", func(t *testing.T) { cfg := testAccUserDataSourceConfig{ - URL: client.URL.String(), - Token: client.SessionToken(), - Username: user.ID.String(), + URL: client.URL.String(), + Token: client.SessionToken(), + ID: user.ID.String(), } resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, From 034e1c23accf96cf01b58b3c5c11348febd791b4 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Tue, 16 Jul 2024 03:04:32 +0000 Subject: [PATCH 8/9] fixup --- integration/user-test/main.tf | 1 - 1 file changed, 1 deletion(-) diff --git a/integration/user-test/main.tf b/integration/user-test/main.tf index 922cabb..aa2e225 100644 --- a/integration/user-test/main.tf +++ b/integration/user-test/main.tf @@ -25,7 +25,6 @@ resource "coderd_user" "ethan2" { username = "${data.coderd_user.ethan.username}2" name = "${data.coderd_user.ethan.name}2" email = "${data.coderd_user.ethan.email}.au" - login_type = data.coderd_user.ethan.login_type roles = data.coderd_user.ethan.roles suspended = data.coderd_user.ethan.suspended } From b697236a50f00cc1fc6e47ede65c4af575674809 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Tue, 16 Jul 2024 03:05:03 +0000 Subject: [PATCH 9/9] fixup2 --- integration/user-test/main.tf | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/integration/user-test/main.tf b/integration/user-test/main.tf index aa2e225..a0cebbe 100644 --- a/integration/user-test/main.tf +++ b/integration/user-test/main.tf @@ -22,10 +22,10 @@ data "coderd_user" "ethan" { } resource "coderd_user" "ethan2" { - username = "${data.coderd_user.ethan.username}2" - name = "${data.coderd_user.ethan.name}2" - email = "${data.coderd_user.ethan.email}.au" - roles = data.coderd_user.ethan.roles - suspended = data.coderd_user.ethan.suspended + username = "${data.coderd_user.ethan.username}2" + name = "${data.coderd_user.ethan.name}2" + email = "${data.coderd_user.ethan.email}.au" + roles = data.coderd_user.ethan.roles + suspended = data.coderd_user.ethan.suspended }