Skip to content

Commit

Permalink
Adding support for sub-tests (#4)
Browse files Browse the repository at this point in the history
* Adding support for sub-tests

* Fix subtest implementation

* Errof -> Fatalf

* Formatting small fix
  • Loading branch information
Nicholas M. Iodice committed Jan 11, 2021
1 parent d2dd8b2 commit d9205c0
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 28 deletions.
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

0 comments on commit d9205c0

Please sign in to comment.