Skip to content

Commit

Permalink
tests/provider: Migrate environment variable functionality into share…
Browse files Browse the repository at this point in the history
…d package with constants and testing (#17197)

Reference: #17083

Other environment variable handling in the code and testing can be migrated in the future for consistency. Please note that if attempting to use the returned value in a test configuration function, it must be done outside the test PreCheck function due to execution during compilation versus run.

Without GITHUB_TOKEN set:

```
=== RUN   TestAccAWSCodePipeline_WithGitHubv1SourceAction
    resource_aws_codepipeline_test.go:470: skipping test; environment variable GITHUB_TOKEN must be set. Usage: token with GitHub permissions to repository for CodePipeline source configuration
--- SKIP: TestAccAWSCodePipeline_WithGitHubv1SourceAction (0.00s)

=== RUN   TestAccAWSCodePipelineWebhook_basic
    resource_aws_codepipeline_webhook_test.go:18: skipping test; environment variable GITHUB_TOKEN must be set. Usage: token with GitHub permissions to repository for CodePipeline webhook creation
--- SKIP: TestAccAWSCodePipelineWebhook_basic (0.00s)
=== RUN   TestAccAWSCodePipelineWebhook_ipAuth
    resource_aws_codepipeline_webhook_test.go:52: skipping test; environment variable GITHUB_TOKEN must be set. Usage: token with GitHub permissions to repository for CodePipeline webhook creation
--- SKIP: TestAccAWSCodePipelineWebhook_ipAuth (0.00s)
=== RUN   TestAccAWSCodePipelineWebhook_unauthenticated
    resource_aws_codepipeline_webhook_test.go:86: skipping test; environment variable GITHUB_TOKEN must be set. Usage: token with GitHub permissions to repository for CodePipeline webhook creation
--- SKIP: TestAccAWSCodePipelineWebhook_unauthenticated (0.00s)
=== RUN   TestAccAWSCodePipelineWebhook_tags
    resource_aws_codepipeline_webhook_test.go:118: skipping test; environment variable GITHUB_TOKEN must be set. Usage: token with GitHub permissions to repository for CodePipeline webhook creation
--- SKIP: TestAccAWSCodePipelineWebhook_tags (0.00s)
=== RUN   TestAccAWSCodePipelineWebhook_UpdateAuthenticationConfiguration_SecretToken
    resource_aws_codepipeline_webhook_test.go:181: skipping test; environment variable GITHUB_TOKEN must be set. Usage: token with GitHub permissions to repository for CodePipeline webhook creation
--- SKIP: TestAccAWSCodePipelineWebhook_UpdateAuthenticationConfiguration_SecretToken (0.00s)
```

Output from acceptance testing:

```
# Verify GITHUB_TOKEN working

--- PASS: TestAccAWSCodePipeline_WithGitHubv1SourceAction (55.64s)

--- PASS: TestAccAWSCodePipelineWebhook_basic (32.74s)
--- PASS: TestAccAWSCodePipelineWebhook_ipAuth (33.24s)
--- PASS: TestAccAWSCodePipelineWebhook_tags (71.80s)
--- PASS: TestAccAWSCodePipelineWebhook_unauthenticated (32.65s)
--- PASS: TestAccAWSCodePipelineWebhook_UpdateAuthenticationConfiguration_SecretToken (54.08s)

# Verify cross-region and cross-account

--- PASS: TestAccAWSDynamoDbTable_Replica_Multiple (1646.42s)

--- PASS: TestAccAWSRoute53ZoneAssociation_CrossAccount (160.38s)

--- PASS: TestAccAWSProvider_AssumeRole_Empty (15.68s)
--- PASS: TestAccAWSProvider_Endpoints (13.10s)
--- PASS: TestAccAWSProvider_IgnoreTags_EmptyConfigurationBlock (12.44s)
--- PASS: TestAccAWSProvider_IgnoreTags_KeyPrefixes_Multiple (12.87s)
--- PASS: TestAccAWSProvider_IgnoreTags_KeyPrefixes_None (12.49s)
--- PASS: TestAccAWSProvider_IgnoreTags_KeyPrefixes_One (12.71s)
--- PASS: TestAccAWSProvider_IgnoreTags_Keys_Multiple (12.57s)
--- PASS: TestAccAWSProvider_IgnoreTags_Keys_None (12.23s)
--- PASS: TestAccAWSProvider_IgnoreTags_Keys_One (12.71s)
--- PASS: TestAccAWSProvider_Region_AwsChina (9.82s)
--- PASS: TestAccAWSProvider_Region_AwsCommercial (10.42s)
--- PASS: TestAccAWSProvider_Region_AwsGovCloudUs (11.37s)
```
  • Loading branch information
bflad authored Feb 4, 2021
1 parent a638958 commit 11cd8e2
Show file tree
Hide file tree
Showing 10 changed files with 378 additions and 61 deletions.
50 changes: 50 additions & 0 deletions aws/internal/envvar/consts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package envvar

// Standard AWS environment variables used in the Terraform AWS Provider testing.
// These are not provided as constants in the AWS Go SDK currently.
const (
// Default static credential identifier for tests (AWS Go SDK does not provide this as constant)
// See also AWS_SECRET_ACCESS_KEY and AWS_PROFILE
AwsAccessKeyId = "AWS_ACCESS_KEY_ID"

// Container credentials endpoint
// See also AWS_ACCESS_KEY_ID and AWS_PROFILE
AwsContainerCredentialsFullUri = "AWS_CONTAINER_CREDENTIALS_FULL_URI"

// Default AWS region for tests (AWS Go SDK does not provide this as constant)
AwsDefaultRegion = "AWS_DEFAULT_REGION"

// Default AWS shared configuration profile for tests (AWS Go SDK does not provide this as constant)
AwsProfile = "AWS_PROFILE"

// Default static credential value for tests (AWS Go SDK does not provide this as constant)
// See also AWS_ACCESS_KEY_ID and AWS_PROFILE
AwsSecretAccessKey = "AWS_SECRET_ACCESS_KEY"
)

// Custom environment variables used in the Terraform AWS Provider testing.
// Additions should also be documented in the Environment Variable Dictionary
// of the Maintainers Guide: docs/MAINTAINING.md
const (
// For tests using an alternate AWS account, the equivalent of AWS_ACCESS_KEY_ID for that account
AwsAlternateAccessKeyId = "AWS_ALTERNATE_ACCESS_KEY_ID"

// For tests using an alternate AWS account, the equivalent of AWS_PROFILE for that account
AwsAlternateProfile = "AWS_PROFILE"

// For tests using an alternate AWS region, the equivalent of AWS_DEFAULT_REGION for that account
AwsAlternateRegion = "AWS_ALTERNATE_REGION"

// For tests using an alternate AWS account, the equivalent of AWS_SECRET_ACCESS_KEY for that account
AwsAlternateSecretAccessKey = "AWS_ALTERNATE_SECRET_ACCESS_KEY"

// For tests using a third AWS region, the equivalent of AWS_DEFAULT_REGION for that region
AwsThirdRegion = "AWS_THIRD_REGION"

// For tests requiring GitHub permissions
GithubToken = "GITHUB_TOKEN"

// For tests requiring restricted IAM permissions, an existing IAM Role to assume
// An inline assume role policy is then used to deny actions for the test
TfAccAssumeRoleArn = "TF_ACC_ASSUME_ROLE_ARN"
)
2 changes: 2 additions & 0 deletions aws/internal/envvar/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// envvar contains constants and helpers for environment variable usage in testing.
package envvar
16 changes: 16 additions & 0 deletions aws/internal/envvar/funcs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package envvar

import (
"os"
)

// GetWithDefault gets an environment variable value if non-empty or returns the default.
func GetWithDefault(variable string, defaultValue string) string {
value := os.Getenv(variable)

if value == "" {
return defaultValue
}

return value
}
50 changes: 50 additions & 0 deletions aws/internal/envvar/funcs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package envvar_test

import (
"os"
"testing"

"github.com/terraform-providers/terraform-provider-aws/aws/internal/envvar"
)

func TestGetWithDefault(t *testing.T) {
envVar := "TESTENVVAR_GETWITHDEFAULT"

t.Run("missing", func(t *testing.T) {
want := "default"

os.Unsetenv(envVar)

got := envvar.GetWithDefault(envVar, want)

if got != want {
t.Fatalf("expected %s, got %s", want, got)
}
})

t.Run("empty", func(t *testing.T) {
want := "default"

os.Setenv(envVar, "")
defer os.Unsetenv(envVar)

got := envvar.GetWithDefault(envVar, want)

if got != want {
t.Fatalf("expected %s, got %s", want, got)
}
})

t.Run("not empty", func(t *testing.T) {
want := "notempty"

os.Setenv(envVar, want)
defer os.Unsetenv(envVar)

got := envvar.GetWithDefault(envVar, "default")

if got != want {
t.Fatalf("expected %s, got %s", want, got)
}
})
}
56 changes: 56 additions & 0 deletions aws/internal/envvar/testing_funcs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package envvar

import (
"os"

"github.com/mitchellh/go-testing-interface"
)

// TestFailIfAllEmpty verifies that at least one environment variable is non-empty or fails the test.
//
// If at lease one environment variable is non-empty, returns the first name and value.
func TestFailIfAllEmpty(t testing.T, names []string, usageMessage string) (string, string) {
t.Helper()

for _, variable := range names {
value := os.Getenv(variable)

if value != "" {
return variable, value
}
}

t.Fatalf("at least one environment variable of %v must be set. Usage: %s", names, usageMessage)

return "", ""
}

// TestFailIfEmpty verifies that an environment variable is non-empty or fails the test.
//
// For acceptance tests, this function must be used outside PreCheck functions to set values for configurations.
func TestFailIfEmpty(t testing.T, name string, usageMessage string) string {
t.Helper()

value := os.Getenv(name)

if value == "" {
t.Fatalf("environment variable %s must be set. Usage: %s", name, usageMessage)
}

return value
}

// TestSkipIfEmpty verifies that an environment variable is non-empty or skips the test.
//
// For acceptance tests, this function must be used outside PreCheck functions to set values for configurations.
func TestSkipIfEmpty(t testing.T, name string, usageMessage string) string {
t.Helper()

value := os.Getenv(name)

if value == "" {
t.Skipf("skipping test; environment variable %s must be set. Usage: %s", name, usageMessage)
}

return value
}
170 changes: 170 additions & 0 deletions aws/internal/envvar/testing_funcs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package envvar_test

import (
"os"
"testing"

testingiface "github.com/mitchellh/go-testing-interface"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/envvar"
)

func TestTestFailIfAllEmpty(t *testing.T) {
envVar1 := "TESTENVVAR_FAILIFALLEMPTY1"
envVar2 := "TESTENVVAR_FAILIFALLEMPTY2"
envVars := []string{envVar1, envVar2}

t.Run("missing", func(t *testing.T) {
defer testingifaceRecover()

for _, envVar := range envVars {
os.Unsetenv(envVar)
}

envvar.TestFailIfAllEmpty(&testingiface.RuntimeT{}, envVars, "usage")

t.Fatal("expected to fail previously")
})

t.Run("all empty", func(t *testing.T) {
defer testingifaceRecover()

os.Setenv(envVar1, "")
os.Setenv(envVar2, "")
defer unsetEnvVars(envVars)

envvar.TestFailIfAllEmpty(&testingiface.RuntimeT{}, envVars, "usage")

t.Fatal("expected to fail previously")
})

t.Run("some empty", func(t *testing.T) {
wantValue := "pickme"

os.Setenv(envVar1, "")
os.Setenv(envVar2, wantValue)
defer unsetEnvVars(envVars)

gotName, gotValue := envvar.TestFailIfAllEmpty(&testingiface.RuntimeT{}, envVars, "usage")

if gotName != envVar2 {
t.Fatalf("expected name: %s, got: %s", envVar2, gotName)
}

if gotValue != wantValue {
t.Fatalf("expected value: %s, got: %s", wantValue, gotValue)
}
})

t.Run("all not empty", func(t *testing.T) {
wantValue := "pickme"

os.Setenv(envVar1, wantValue)
os.Setenv(envVar2, "other")
defer unsetEnvVars(envVars)

gotName, gotValue := envvar.TestFailIfAllEmpty(&testingiface.RuntimeT{}, envVars, "usage")

if gotName != envVar1 {
t.Fatalf("expected name: %s, got: %s", envVar1, gotName)
}

if gotValue != wantValue {
t.Fatalf("expected value: %s, got: %s", wantValue, gotValue)
}
})
}

func TestTestFailIfEmpty(t *testing.T) {
envVar := "TESTENVVAR_FAILIFEMPTY"

t.Run("missing", func(t *testing.T) {
defer testingifaceRecover()

os.Unsetenv(envVar)

envvar.TestFailIfEmpty(&testingiface.RuntimeT{}, envVar, "usage")

t.Fatal("expected to fail previously")
})

t.Run("empty", func(t *testing.T) {
defer testingifaceRecover()

os.Setenv(envVar, "")
defer os.Unsetenv(envVar)

envvar.TestFailIfEmpty(&testingiface.RuntimeT{}, envVar, "usage")

t.Fatal("expected to fail previously")
})

t.Run("not empty", func(t *testing.T) {
want := "notempty"

os.Setenv(envVar, want)
defer os.Unsetenv(envVar)

got := envvar.TestFailIfEmpty(&testingiface.RuntimeT{}, envVar, "usage")

if got != want {
t.Fatalf("expected value: %s, got: %s", want, got)
}
})
}

func TestTestSkipIfEmpty(t *testing.T) {
envVar := "TESTENVVAR_SKIPIFEMPTY"

t.Run("missing", func(t *testing.T) {
mockT := &testingiface.RuntimeT{}

os.Unsetenv(envVar)

envvar.TestSkipIfEmpty(mockT, envVar, "usage")

if !mockT.Skipped() {
t.Fatal("expected to skip previously")
}
})

t.Run("empty", func(t *testing.T) {
mockT := &testingiface.RuntimeT{}

os.Setenv(envVar, "")
defer os.Unsetenv(envVar)

envvar.TestSkipIfEmpty(mockT, envVar, "usage")

if !mockT.Skipped() {
t.Fatal("expected to skip previously")
}
})

t.Run("not empty", func(t *testing.T) {
want := "notempty"

os.Setenv(envVar, want)
defer os.Unsetenv(envVar)

got := envvar.TestSkipIfEmpty(&testingiface.RuntimeT{}, envVar, "usage")

if got != want {
t.Fatalf("expected value: %s, got: %s", want, got)
}
})
}

func testingifaceRecover() {
r := recover()

// this string is hardcoded in github.com/mitchellh/go-testing-interface
if s, ok := r.(string); !ok || s != "testing.T failed, see logs for output (if any)" {
panic(r)
}
}

func unsetEnvVars(envVars []string) {
for _, envVar := range envVars {
os.Unsetenv(envVar)
}
}
Loading

0 comments on commit 11cd8e2

Please sign in to comment.