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

Adding support for sub-tests #4

Merged
merged 4 commits into from
Jan 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions integration/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,30 @@ func RunIntegrationTests(fixture *IntegrationTestFixture) {
// - The output has the correct number of items
// - Run any user-supplied assertions over the output
func validateTerraformOutput(fixture *IntegrationTestFixture, output TerraformOutput) {
validateTerraformOutputCount(fixture, output)
validateTerraformOutputKeyValues(fixture, output)
fixture.GoTest.Run("Terraform Output Count", func(t *testing.T) {
validateTerraformOutputCount(t, fixture, output)
})

fixture.GoTest.Run("Terraform Output Key Values", func(t *testing.T) {
validateTerraformOutputKeyValues(t, fixture, output)
})

// run user-provided assertions over the TF output
for _, outputAssertion := range fixture.TfOutputAssertions {
outputAssertion(fixture.GoTest, output)
for i, outputAssertion := range fixture.TfOutputAssertions {
fixture.GoTest.Run(fmt.Sprintf("Custom Validation Function (%d)", i), func(t *testing.T) {
outputAssertion(t, output)
})
}
}

// Validates that the terraform output contains the expected number of items
func validateTerraformOutputCount(fixture *IntegrationTestFixture, output TerraformOutput) {
func validateTerraformOutputCount(t *testing.T, fixture *IntegrationTestFixture, output TerraformOutput) {
if len(output) != fixture.ExpectedTfOutputCount {
fixture.GoTest.Fatal(fmt.Errorf(
t.Fatalf(
"Output unexpectedly had %d entries instead of %d",
len(output),
fixture.ExpectedTfOutputCount,
))
)
}
}

Expand All @@ -74,23 +81,23 @@ func validateTerraformOutputCount(fixture *IntegrationTestFixture, output Terraf
// - Handles comparison of generic data types automatically
// - Handles differences in key ordering for maps
// - Handles all handling of generics, which is tricky in Go
func validateTerraformOutputKeyValues(fixture *IntegrationTestFixture, output TerraformOutput) {
func validateTerraformOutputKeyValues(t *testing.T, fixture *IntegrationTestFixture, output TerraformOutput) {
for expectedKey, expectedValue := range fixture.ExpectedTfOutput {
actualValue, isFound := output[expectedKey]
if !isFound {
fixture.GoTest.Fatal(fmt.Errorf("Output unexpectedly did not contain key %s", expectedKey))
fixture.GoTest.Fatalf("Output unexpectedly did not contain key %s", expectedKey)
}

expectedAsJSON := jsonOrFail(fixture, expectedValue)
actualAsJSON := jsonOrFail(fixture, actualValue)

if expectedAsJSON != actualAsJSON {
fixture.GoTest.Fatal(fmt.Errorf(
t.Fatalf(
"Output value for '%s' was expected to be '%s' but was '%s'",
expectedKey,
expectedAsJSON,
actualAsJSON,
))
)
}
}
}
Expand Down
51 changes: 34 additions & 17 deletions unit/unit.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ package unit

import (
"encoding/json"
"errors"
"fmt"
"os"
"os/exec"
Expand Down Expand Up @@ -76,13 +75,29 @@ func RunUnitTests(fixture *UnitTestFixture) {
// - The plan passes any user-defined assertions
func validateTerraformPlanFile(fixture *UnitTestFixture, tfPlanFilePath string) {
plan := parseTerraformPlan(fixture, tfPlanFilePath)
validatePlanCreateProperties(fixture, plan)
validatePlanResourceKeyValues(fixture, plan)

fixture.GoTest.Run("Terraform Plan Is Not Empty", func(t *testing.T) {
validatePlanNotEmpty(t, plan)
})

fixture.GoTest.Run("Terraform Plan Output Count", func(t *testing.T) {
validatePlanResourceCount(t, fixture, plan)
})

fixture.GoTest.Run("Terraform Plan Is Not Destructive", func(t *testing.T) {
validatePlanHasNoDeletes(t, plan)
})

fixture.GoTest.Run("Terraform Plan Key Values", func(t *testing.T) {
validatePlanResourceKeyValues(t, fixture, plan)
})

// run user-provided assertions
if fixture.PlanAssertions != nil {
for _, planAssertion := range fixture.PlanAssertions {
planAssertion(fixture.GoTest, plan)
for i, planAssertion := range fixture.PlanAssertions {
fixture.GoTest.Run(fmt.Sprintf("Custom Validation Function (%d)", i), func(t *testing.T) {
planAssertion(t, plan)
})
}
}
}
Expand Down Expand Up @@ -116,40 +131,42 @@ func parseTerraformPlan(fixture *UnitTestFixture, filePath string) TerraformPlan
return plan
}

// Validates high level attributes of a terraform plan creat properties. This includes:
// - The plan is not empty
// - The plan contains the correct number of resources
// - The plan is not executing any destructive actions
func validatePlanCreateProperties(fixture *UnitTestFixture, plan TerraformPlan) {
// Validates that the plan is not empty
func validatePlanNotEmpty(t *testing.T, plan TerraformPlan) {
if len(plan.ResourceChanges) == 0 {
fixture.GoTest.Fatal(errors.New("Plan diff was unexpectedly empty"))
t.Fatalf("Plan diff was unexpectedly empty")
}
}

// Validates that the plan has the correct number of resources in it
func validatePlanResourceCount(t *testing.T, fixture *UnitTestFixture, plan TerraformPlan) {
if len(plan.ResourceChanges) != fixture.ExpectedResourceCount {
fixture.GoTest.Fatal(fmt.Errorf(
"Plan unexpectedly had %d resources instead of %d", len(plan.ResourceChanges), fixture.ExpectedResourceCount))
t.Fatalf(
"Plan unexpectedly had %d resources instead of %d", len(plan.ResourceChanges), fixture.ExpectedResourceCount)
}
}

// Validates that the plan is not executing any destructive actions
func validatePlanHasNoDeletes(t *testing.T, plan TerraformPlan) {
// a unit test should never create a destructive action like deleting a resource
allowedActions := map[string]bool{"create": true, "read": true}
for _, resource := range plan.ResourceChanges {
for _, action := range resource.Change.Actions {
if !allowedActions[action] {
fixture.GoTest.Fatal(
fmt.Errorf("Plan unexpectedly actions other than `create`: %s", resource.Change.Actions))
t.Fatalf("Plan unexpectedly actions other than `create`: %s", resource.Change.Actions)
}
}
}
}

// verifies that the attribute value mappings for each resource specified by the client exist
// as a subset of the actual values defined in the terraform plan.
func validatePlanResourceKeyValues(fixture *UnitTestFixture, plan TerraformPlan) {
func validatePlanResourceKeyValues(t *testing.T, fixture *UnitTestFixture, plan TerraformPlan) {
theRealPlanAsMap := planToMap(plan)
theExpectedPlanAsMap := resourceDescriptionToMap(fixture.ExpectedResourceAttributeValues)

if err := verifyTargetsExistInMap(theRealPlanAsMap, theExpectedPlanAsMap, ""); err != nil {
fixture.GoTest.Fatal(err)
t.Fatal(err)
}
}

Expand Down