Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: update test framework to use latest best practices and reduce boilerplate #100

Merged
merged 12 commits into from
Jun 25, 2024
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ Available targets:

| Name | Source | Version |
|------|--------|---------|
| <a name="module_dns_master"></a> [dns\_master](#module\_dns\_master) | cloudposse/route53-cluster-hostname/aws | 0.12.2 |
| <a name="module_dns_replicas"></a> [dns\_replicas](#module\_dns\_replicas) | cloudposse/route53-cluster-hostname/aws | 0.12.2 |
| <a name="module_ssm_write_db_password"></a> [ssm\_write\_db\_password](#module\_ssm\_write\_db\_password) | cloudposse/ssm-parameter-store/aws | 0.11.0 |
| <a name="module_dns_master"></a> [dns\_master](#module\_dns\_master) | cloudposse/route53-cluster-hostname/aws | 0.13.0 |
| <a name="module_dns_replicas"></a> [dns\_replicas](#module\_dns\_replicas) | cloudposse/route53-cluster-hostname/aws | 0.13.0 |
| <a name="module_ssm_write_db_password"></a> [ssm\_write\_db\_password](#module\_ssm\_write\_db\_password) | cloudposse/ssm-parameter-store/aws | 0.13.0 |
| <a name="module_this"></a> [this](#module\_this) | cloudposse/label/null | 0.25.0 |

## Resources
Expand Down
6 changes: 3 additions & 3 deletions docs/terraform.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@

| Name | Source | Version |
|------|--------|---------|
| <a name="module_dns_master"></a> [dns\_master](#module\_dns\_master) | cloudposse/route53-cluster-hostname/aws | 0.12.2 |
| <a name="module_dns_replicas"></a> [dns\_replicas](#module\_dns\_replicas) | cloudposse/route53-cluster-hostname/aws | 0.12.2 |
| <a name="module_ssm_write_db_password"></a> [ssm\_write\_db\_password](#module\_ssm\_write\_db\_password) | cloudposse/ssm-parameter-store/aws | 0.11.0 |
| <a name="module_dns_master"></a> [dns\_master](#module\_dns\_master) | cloudposse/route53-cluster-hostname/aws | 0.13.0 |
| <a name="module_dns_replicas"></a> [dns\_replicas](#module\_dns\_replicas) | cloudposse/route53-cluster-hostname/aws | 0.13.0 |
| <a name="module_ssm_write_db_password"></a> [ssm\_write\_db\_password](#module\_ssm\_write\_db\_password) | cloudposse/ssm-parameter-store/aws | 0.13.0 |
| <a name="module_this"></a> [this](#module\_this) | cloudposse/label/null | 0.25.0 |

## Resources
Expand Down
7 changes: 3 additions & 4 deletions examples/complete/fixtures.us-east-2.tfvars
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
enabled = true

region = "us-east-2"

availability_zones = ["us-east-2a", "us-east-2b"]
Expand All @@ -12,7 +10,7 @@ name = "documentdb-cluster"

vpc_cidr_block = "172.16.0.0/16"

instance_class = "db.r4.large"
instance_class = "db.t4g.medium"

cluster_size = 1

Expand All @@ -26,7 +24,8 @@ retention_period = 5

preferred_backup_window = "07:00-09:00"

cluster_family = "docdb3.6"
cluster_family = "docdb5.0"
engine_version = "5.0.0"

engine = "docdb"

Expand Down
4 changes: 2 additions & 2 deletions examples/complete/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ provider "aws" {

module "vpc" {
source = "cloudposse/vpc/aws"
version = "2.1.0"
version = "2.1.1"

ipv4_primary_cidr_block = var.vpc_cidr_block

Expand All @@ -25,7 +25,7 @@ module "vpc" {

module "subnets" {
source = "cloudposse/dynamic-subnets/aws"
version = "2.3.0"
version = "2.4.2"

availability_zones = var.availability_zones
vpc_id = module.vpc.vpc_id
Expand Down
6 changes: 3 additions & 3 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ locals {

module "dns_master" {
source = "cloudposse/route53-cluster-hostname/aws"
version = "0.12.2"
version = "0.13.0"

enabled = module.this.enabled && var.zone_id != "" ? true : false
dns_name = local.cluster_dns_name
Expand All @@ -144,7 +144,7 @@ module "dns_master" {

module "dns_replicas" {
source = "cloudposse/route53-cluster-hostname/aws"
version = "0.12.2"
version = "0.13.0"

enabled = module.this.enabled && var.zone_id != "" ? true : false
dns_name = local.replicas_dns_name
Expand All @@ -156,7 +156,7 @@ module "dns_replicas" {

module "ssm_write_db_password" {
source = "cloudposse/ssm-parameter-store/aws"
version = "0.11.0"
version = "0.13.0"

enabled = module.this.enabled && var.ssm_parameter_enabled == true ? true : false
parameter_write = [
Expand Down
4 changes: 2 additions & 2 deletions test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ clean:
all: module examples/complete

## Run basic sanity checks against the module itself
module: export TESTS ?= installed lint get-modules module-pinning get-plugins provider-pinning validate terraform-docs input-descriptions output-descriptions
module: export TESTS ?= installed lint module-pinning provider-pinning validate terraform-docs input-descriptions output-descriptions
module: deps
$(call RUN_TESTS, ../)

## Run tests against example
examples/complete: export TESTS ?= installed lint get-modules get-plugins validate
examples/complete: export TESTS ?= installed lint validate
examples/complete: deps
$(call RUN_TESTS, ../$@)
7 changes: 3 additions & 4 deletions test/src/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export TF_CLI_ARGS_init ?= -get-plugins=true
export TERRAFORM_VERSION ?= $(shell curl -s https://checkpoint-api.hashicorp.com/v1/check/terraform | jq -r -M '.current_version' | cut -d. -f1-2)
export TERRAFORM_VERSION ?= $(shell curl -s https://checkpoint-api.hashicorp.com/v1/check/terraform | jq -r -M '.current_version' | cut -d. -f1)

.DEFAULT_GOAL : all

Expand All @@ -16,7 +15,7 @@ init:
## Run tests
test: init
go mod download
go test -v -timeout 60m -run TestExamplesComplete
go test -v -timeout 60m -run '^Test[^_]'

## Run tests in docker container
docker/test:
Expand All @@ -27,4 +26,4 @@ docker/test:
.PHONY : clean
## Clean up files
clean:
rm -rf ../../examples/complete/*.tfstate*
rm -rf $(TF_DATA_DIR)
89 changes: 89 additions & 0 deletions test/src/aws_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// This file is a work-in-progress proposed set of utility functions
// to be standardized across all Cloud Posse Terraform modules.
// Most, if not all, of these functions will be replaced by
// Terratest functions as they become available.
// This file should be considered a temporary solution as of June 2024 and should not be duplicated

package test

// Support AWS operations
// See https://aws.github.io/aws-sdk-go-v2/docs/getting-started/
// and https://pkg.go.dev/github.com/aws/aws-sdk-go-v2
//
// For type conversions, see https://pkg.go.dev/github.com/aws/[email protected]/aws#hdr-Value_and_Pointer_Conversion_Utilities

import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ssm"
"github.com/stretchr/testify/assert"
"log"
"testing"
)

func AWSConfig() aws.Config {
return AWSConfigWithRegion("us-east-2")
}

func AWSConfigWithRegion(region string) aws.Config {
// Load the Shared AWS Configuration (~/.aws/config)
cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion(region))
if err != nil {
log.Fatal(err)
}

return cfg
}

func AssertSSMParameterEqual(t *testing.T, ssmClient *ssm.Client, output map[string]interface{}, ssmPathOutput string, expectedValueOutput string) {

if assert.NotEmpty(t, output[expectedValueOutput], "Missing "+expectedValueOutput) &&
assert.NotEmpty(t, output[ssmPathOutput], "Missing "+ssmPathOutput) {

withDecryption := true
param, err := ssmClient.GetParameter(context.TODO(), &ssm.GetParameterInput{
Name: aws.String(output[ssmPathOutput].(string)),
WithDecryption: &withDecryption,
})

if assert.Nil(t, err, "Unable to retrieve "+ssmPathOutput+" from SSM Parameter Store") {
assert.Equal(t, output[expectedValueOutput].(string), aws.ToString(param.Parameter.Value))
}
}
}

func AssertSSMParameterEmpty(t *testing.T, ssmClient *ssm.Client, output map[string]interface{}, ssmPathOutput string) {

// No path given
if output[ssmPathOutput] == nil || output[ssmPathOutput] == "" {
return
}

withDecryption := true
param, err := ssmClient.GetParameter(context.TODO(), &ssm.GetParameterInput{
Name: aws.String(output[ssmPathOutput].(string)),
WithDecryption: &withDecryption,
})

// If a path is given, there should be an empty value to go with it
if assert.Nil(t, err, "Unable to retrieve "+ssmPathOutput+" from SSM Parameter Store") {
assert.Empty(t, aws.ToString(param.Parameter.Value), "Found non-empty value for "+ssmPathOutput)
}
}

func AssertSSMParameterNotEmpty(t *testing.T, ssmClient *ssm.Client, output map[string]interface{}, ssmPathOutput string) {

if assert.NotEmpty(t, output[ssmPathOutput], "Missing "+ssmPathOutput) {

withDecryption := true
param, err := ssmClient.GetParameter(context.TODO(), &ssm.GetParameterInput{
Name: aws.String(output[ssmPathOutput].(string)),
WithDecryption: &withDecryption,
})

if assert.Nil(t, err, "Unable to retrieve "+ssmPathOutput+" from SSM Parameter Store") {
assert.NotEmpty(t, aws.ToString(param.Parameter.Value), "Retrieved empty value for "+ssmPathOutput)
}
}
}
82 changes: 82 additions & 0 deletions test/src/default_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// This file is a work-in-progress proposed set of `go` Test wrappers
// to be standardized across all Cloud Posse Terraform modules.
// This is likely to change, especially as we move from local helpers
// to Terratest helpers.
// This file should be considered a temporary solution as of June 2024 and should not be duplicated

package test

import (
"github.com/gruntwork-io/terratest/modules/logger"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/stretchr/testify/assert"
"os"
"regexp"
"strings"
"testing"
)

// Test the Terraform module in examples/complete using Terratest.
func TestExamplesComplete(t *testing.T) {
t.Parallel()

testRunner(t, nil, testExamplesComplete)
}

func TestExamplesCompleteDisabled(t *testing.T) {
t.Parallel()

vars := map[string]interface{}{
"enabled": false,
}
testRunner(t, vars, testExamplesCompleteDisabled)
}

func testExamplesCompleteDisabled(t *testing.T, terraformOptions *terraform.Options, randID string, results string) {
// Should complete successfully without creating or changing any resources.
// Extract the "Resources:" section of the output to make the error message more readable.
re := regexp.MustCompile(`Resources: [^.]+\.`)
match := re.FindString(results)
assert.Equal(t, "Resources: 0 added, 0 changed, 0 destroyed.", match, "Re-applying the same configuration should not change any resources")
}

// To speed up debugging, allow running the tests on an existing deployment,
// without creating and destroying one.
// Run this manually by creating a deployment in examples/complete with:
//
// export EXISTING_DEPLOYMENT_ATTRIBUTE="<your-name>"
// terraform init -upgrade
// terraform apply -var-file fixtures.us-east-2.tfvars -var "attributes=[\"$EXISTING_DEPLOYMENT_ATTRIBUTE\"]"
//
// then in this directory (test/src) run
// go test -run Test_ExistingDeployment

func Test_ExistingDeployment(t *testing.T) {
randID := strings.ToLower(os.Getenv("EXISTING_DEPLOYMENT_ATTRIBUTE"))
if randID == "" {
t.Skip("(This is normal): EXISTING_DEPLOYMENT_ATTRIBUTE is not set, skipping...")
return
}

attributes := []string{randID}

varFiles := []string{"fixtures.us-east-2.tfvars"}

terraformOptions := &terraform.Options{
// The path to where our Terraform code is located
TerraformDir: "../../examples/complete",
Upgrade: true,
// Variables to pass to our Terraform code using -var-file options
VarFiles: varFiles,
Vars: map[string]interface{}{
"attributes": attributes,
},
}

// Keep the output quiet
if !testing.Verbose() {
terraformOptions.Logger = logger.Discard
}

testExamplesComplete(t, terraformOptions, randID, "")
}
Loading