From 8289f5ef80b2c4185eb9cd4f3aaa9f94fca18d66 Mon Sep 17 00:00:00 2001 From: Sarah French <15078782+SarahFrench@users.noreply.github.com> Date: Mon, 30 Sep 2024 19:16:26 +0100 Subject: [PATCH] Add acceptance tests for how provider handles `scopes` argument (#11860) --- .../framework_provider_scopes_test.go.tmpl | 190 ++++++++++++++++++ .../provider/provider_scopes_test.go | 161 +++++++++++++++ 2 files changed, 351 insertions(+) create mode 100644 mmv1/third_party/terraform/fwprovider/framework_provider_scopes_test.go.tmpl create mode 100644 mmv1/third_party/terraform/provider/provider_scopes_test.go diff --git a/mmv1/third_party/terraform/fwprovider/framework_provider_scopes_test.go.tmpl b/mmv1/third_party/terraform/fwprovider/framework_provider_scopes_test.go.tmpl new file mode 100644 index 000000000000..e9ab1623ed50 --- /dev/null +++ b/mmv1/third_party/terraform/fwprovider/framework_provider_scopes_test.go.tmpl @@ -0,0 +1,190 @@ +package fwprovider_test + +import ( + "fmt" +{{- if ne $.TargetVersionName "ga" }} + "regexp" +{{- end }} + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/transport" +) + +// TestAccFwProvider_scopes is a series of acc tests asserting how the PF provider handles scopes arguments +// It is PF specific because the HCL used provisions PF-implemented resources +// It is a counterpart to TestAccSdkProvider_scopes +func TestAccFwProvider_scopes(t *testing.T) { + testCases := map[string]func(t *testing.T){ + // Configuring the provider using inputs + "default scopes are used when there are no user inputs": testAccFwProvider_scopes_providerDefault, + "scopes can be set in config": testAccFwProvider_scopes_setInConfig, + //no ENVs to test + + // Schema-level validation + "when scopes is set to an empty array in the config the value is ignored and default scopes are used": testAccFwProvider_scopes_emptyArray, + + // Usage + {{- if ne $.TargetVersionName "ga" }} + // Beta-only generation is needed because we need to access a PF-implemented data source linked to resource in an API. + // Currently this only exists in TPGB. + "the scopes argument impacts provisioning resources": testAccFwProvider_scopes_usage, + {{- else }} + // No usage test cases are implemented in the GA provider because the only PF-implemented data sources are Beta-only + {{- end }} + } + + for name, tc := range testCases { + // shadow the tc variable into scope so that when + // the loop continues, if t.Run hasn't executed tc(t) + // yet, we don't have a race condition + // see https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + }) + } +} + +func testAccFwProvider_scopes_providerDefault(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccFwProvider_scopes_unset(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "scopes.#", fmt.Sprintf("%d", len(transport.DefaultClientScopes))), + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "scopes.0", transport.DefaultClientScopes[0]), + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "scopes.1", transport.DefaultClientScopes[1]), + ), + }, + }, + }) +} + +func testAccFwProvider_scopes_setInConfig(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + scopes := []string{"https://www.googleapis.com/auth/cloud-platform"} // first of the two default scopes + context := map[string]interface{}{ + "scopes": fmt.Sprintf("[\"%s\"]", scopes[0]), + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccFwProvider_scopes_inProviderBlock(context), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "scopes.#", fmt.Sprintf("%d", len(scopes))), + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "scopes.0", scopes[0]), + ), + }, + }, + }) +} + +func testAccFwProvider_scopes_emptyArray(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + context := map[string]interface{}{ + "scopes": "[]", + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccFwProvider_scopes_inProviderBlock(context), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "scopes.#", fmt.Sprintf("%d", len(transport.DefaultClientScopes))), + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "scopes.0", transport.DefaultClientScopes[0]), + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "scopes.1", transport.DefaultClientScopes[1]), + ), + }, + }, + }) +} + +{{ if ne $.TargetVersionName `ga` -}} +func testAccFwProvider_scopes_usage(t *testing.T) { + acctest.SkipIfVcr(t) // Skip because Firebase is weird with VCR, and we have to use Firebase resources in the test + + // We include scopes that aren't sufficient to enable provisioning the resources in the config below + context := map[string]interface{}{ + "scopes": "[\"https://www.googleapis.com/auth/pubsub\"]", + "random_suffix": acctest.RandString(t, 10), + + "bundle_id": "apple.app." + acctest.RandString(t, 5), + "display_name": "tf-test Display Name AppleAppConfig DataSource", + "app_store_id": 12345, + "team_id": 1234567890, + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccFwProvider_access_token_useScopes(context), + ExpectError: regexp.MustCompile("Request had insufficient authentication scopes"), + }, + }, + }) +} +{{- end }} + +// testAccFwProvider_scopes_inProviderBlock allows setting the scopes argument in a provider block. +// This function uses data.google_provider_config_plugin_framework because it is implemented with the PF +func testAccFwProvider_scopes_inProviderBlock(context map[string]interface{}) string { + return acctest.Nprintf(` +provider "google" { + scopes = %{scopes} +} + +data "google_provider_config_plugin_framework" "default" {} +`, context) +} + +// testAccFwProvider_scopes_inEnvsOnly allows testing when the scopes argument is not set +func testAccFwProvider_scopes_unset() string { + return ` +data "google_provider_config_plugin_framework" "default" {} +` +} + +{{ if ne $.TargetVersionName `ga` -}} +func testAccFwProvider_access_token_useScopes(context map[string]interface{}) string { + return acctest.Nprintf(` +provider "google" {} // default scopes used + +provider "google" { + alias = "under-scoped" + scopes = %{scopes} +} + +data "google_provider_config_plugin_framework" "default" { +} + +resource "google_firebase_apple_app" "my_app_config" { + project = data.google_provider_config_plugin_framework.default.project + bundle_id = "%{bundle_id}" + display_name = "%{display_name}" + app_store_id = "%{app_store_id}" + team_id = "%{team_id}" +} + +// This is implemented with plugin-framework so tests our use of scopes in a PF specific way +data "google_firebase_apple_app_config" "my_app_config" { + provider = google.under-scoped + app_id = google_firebase_apple_app.my_app_config.app_id +} +`, context) +} +{{- end }} \ No newline at end of file diff --git a/mmv1/third_party/terraform/provider/provider_scopes_test.go b/mmv1/third_party/terraform/provider/provider_scopes_test.go new file mode 100644 index 000000000000..75fd4f754144 --- /dev/null +++ b/mmv1/third_party/terraform/provider/provider_scopes_test.go @@ -0,0 +1,161 @@ +package provider_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/transport" +) + +// TestAccSdkProvider_scopes is a series of acc tests asserting how the SDK provider handles scopes arguments +// It is SDK specific because the HCL used provisions SDK-implemented resources +// It is a counterpart to TestAccFwProvider_scopes +func TestAccSdkProvider_scopes(t *testing.T) { + testCases := map[string]func(t *testing.T){ + // Configuring the provider using inputs + "default scopes are used when there are no user inputs": testAccSdkProvider_scopes_providerDefault, + "scopes can be set in config": testAccSdkProvider_scopes_setInConfig, + //no ENVs to test + + // Schema-level validation + "when scopes is set to an empty array in the config the value is ignored and default scopes are used": testAccSdkProvider_scopes_emptyArray, + + // Usage + "the scopes argument impacts provisioning resources": testAccSdkProvider_scopes_usage, + } + + for name, tc := range testCases { + // shadow the tc variable into scope so that when + // the loop continues, if t.Run hasn't executed tc(t) + // yet, we don't have a race condition + // see https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + }) + } +} + +func testAccSdkProvider_scopes_providerDefault(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccSdkProvider_scopes_unset(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "scopes.#", fmt.Sprintf("%d", len(transport.DefaultClientScopes))), + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "scopes.0", transport.DefaultClientScopes[0]), + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "scopes.1", transport.DefaultClientScopes[1]), + ), + }, + }, + }) +} + +func testAccSdkProvider_scopes_setInConfig(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + scopes := []string{"https://www.googleapis.com/auth/cloud-platform"} // first of the two default scopes + context := map[string]interface{}{ + "scopes": fmt.Sprintf("[\"%s\"]", scopes[0]), + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccSdkProvider_scopes_inProviderBlock(context), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "scopes.#", fmt.Sprintf("%d", len(scopes))), + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "scopes.0", scopes[0]), + ), + }, + }, + }) +} + +func testAccSdkProvider_scopes_emptyArray(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + context := map[string]interface{}{ + "scopes": "[]", + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccSdkProvider_scopes_inProviderBlock(context), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "scopes.#", fmt.Sprintf("%d", len(transport.DefaultClientScopes))), + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "scopes.0", transport.DefaultClientScopes[0]), + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "scopes.1", transport.DefaultClientScopes[1]), + ), + }, + }, + }) +} + +func testAccSdkProvider_scopes_usage(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + // We include scopes that aren't sufficient to enable provisioning the resources in the config below + context := map[string]interface{}{ + "scopes": "[\"https://www.googleapis.com/auth/pubsub\"]", + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccSdkProvider_scopes_affectsProvisioning(context), + ExpectError: regexp.MustCompile("Request had insufficient authentication scopes"), + }, + }, + }) +} + +// testAccSdkProvider_scopes_inProviderBlock allows setting the scopes argument in a provider block. +// This function uses data.google_provider_config_sdk because it is implemented with the SDK +func testAccSdkProvider_scopes_inProviderBlock(context map[string]interface{}) string { + return acctest.Nprintf(` +provider "google" { + scopes = %{scopes} +} + +data "google_provider_config_sdk" "default" {} +`, context) +} + +// testAccSdkProvider_scopes_inEnvsOnly allows testing when the scopes argument is not set +func testAccSdkProvider_scopes_unset() string { + return ` +data "google_provider_config_sdk" "default" {} +` +} + +// testAccSdkProvider_scopes_affectsProvisioning allows testing the impact of the scopes argument on provisioning +func testAccSdkProvider_scopes_affectsProvisioning(context map[string]interface{}) string { + return acctest.Nprintf(` +provider "google" { + scopes = %{scopes} +} + +data "google_provider_config_sdk" "default" {} + +resource "google_service_account" "default" { + account_id = "tf-test-%{random_suffix}" + display_name = "AccTest Service Account" +} +`, context) +}