Skip to content

Commit

Permalink
terraform test: Collect variables from default var file within testin…
Browse files Browse the repository at this point in the history
…g directory (#34341)

* Add logic to collect variables for terrafrom test

* Add tests for test variable collection

* Update the test variable collection implementation

* Update internal/backend/local/test.go

Co-authored-by: Liam Cervante <liam.cervante@hashicorp.com>

* Update internal/backend/local/test.go

Co-authored-by: Liam Cervante <liam.cervante@hashicorp.com>

* Move test variables into var file

* resolve diff from cross-branch switch

* go fmt

---------

Co-authored-by: Liam Cervante <liam.cervante@hashicorp.com>
  • Loading branch information
JKIPIKA and liamcervante committed Dec 4, 2023
1 parent 41435f8 commit c98e355
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 12 deletions.
18 changes: 16 additions & 2 deletions internal/backend/local/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,14 @@ const (
type TestSuiteRunner struct {
Config *configs.Config

GlobalVariables map[string]backend.UnparsedVariableValue
Opts *terraform.ContextOpts
TestingDirectory string

// Global variables comes from the main configuration directory,
// and the Global Test Variables are loaded from the test directory.
GlobalVariables map[string]backend.UnparsedVariableValue
GlobalTestVariables map[string]backend.UnparsedVariableValue

Opts *terraform.ContextOpts

View views.Test

Expand Down Expand Up @@ -1233,6 +1239,14 @@ func (runner *TestFileRunner) initVariables(file *moduletest.File) {
for name, value := range runner.Suite.GlobalVariables {
runner.globalVariables[name] = value
}
if path.Dir(file.Name) == runner.Suite.TestingDirectory {
// If the file is in the testing directory, then also include any
// variables that are defined within the default variable file also in
// the test directory.
for name, value := range runner.Suite.GlobalTestVariables {
runner.globalVariables[name] = value
}
}
for name, expr := range file.Config.Variables {
runner.globalVariables[name] = unparsedTestVariableValue{expr}
}
Expand Down
46 changes: 46 additions & 0 deletions internal/command/meta_vars.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"

"github.com/hashicorp/hcl/v2"
Expand All @@ -23,6 +24,51 @@ import (
// for root module input variables.
const VarEnvPrefix = "TF_VAR_"

// collectVariableValuesForTests inspects the various places that test
// values can come from and constructs a map ready to be passed to the
// backend as part of a backend.Operation.
//
// This method returns diagnostics relating to the collection of the values,
// but the values themselves may produce additional diagnostics when finally
// parsed.
func (m *Meta) collectVariableValuesForTests(testsFilePath string) (map[string]backend.UnparsedVariableValue, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
ret := map[string]backend.UnparsedVariableValue{}

// We collect the variables from the ./tests directory
// there is no other need to process environmental variables
// as this is done via collectVariableValues function
if testsFilePath == "" {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Warning,
"Missing test directory",
"The test directory was unspecified when it should always be set. This is a bug in Terraform - please report it."))
return ret, diags
}

// Firstly we collect variables from .tfvars file
testVarsFilename := filepath.Join(testsFilePath, DefaultVarsFilename)
if _, err := os.Stat(testVarsFilename); err == nil {
moreDiags := m.addVarsFromFile(testVarsFilename, terraform.ValueFromAutoFile, ret)
diags = diags.Append(moreDiags)

}

// Then we collect variables from .tfvars.json file
const defaultVarsFilenameJSON = DefaultVarsFilename + ".json"
testVarsFilenameJSON := filepath.Join(testsFilePath, defaultVarsFilenameJSON)

if _, err := os.Stat(testVarsFilenameJSON); err == nil {
moreDiags := m.addVarsFromFile(testVarsFilenameJSON, terraform.ValueFromAutoFile, ret)
diags = diags.Append(moreDiags)
}

// Also, no need to additionally process variables from command line,
// as this is also done via collectVariableValues

return ret, diags
}

// collectVariableValues inspects the various places that root module input variable
// values can come from and constructs a map ready to be passed to the
// backend as part of a backend.Operation.
Expand Down
30 changes: 20 additions & 10 deletions internal/command/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ func (c *TestCommand) Run(rawArgs []string) int {
}
c.variableArgs = rawFlags{items: &items}

// Collect variables for "terraform test"
testVariables, variableDiags := c.collectVariableValuesForTests(args.TestDirectory)
diags = diags.Append(variableDiags)

variables, variableDiags := c.collectVariableValues()
diags = diags.Append(variableDiags)
if variableDiags.HasErrors() {
Expand Down Expand Up @@ -195,16 +199,22 @@ func (c *TestCommand) Run(rawArgs []string) int {
}
} else {
runner = &local.TestSuiteRunner{
Config: config,
GlobalVariables: variables,
Opts: opts,
View: view,
Stopped: false,
Cancelled: false,
StoppedCtx: stopCtx,
CancelledCtx: cancelCtx,
Filter: args.Filter,
Verbose: args.Verbose,
Config: config,
// The GlobalVariables are loaded from the
// main configuration directory
// The GlobalTestVariables are loaded from the
// test directory
GlobalVariables: variables,
GlobalTestVariables: testVariables,
TestingDirectory: args.TestDirectory,
Opts: opts,
View: view,
Stopped: false,
Cancelled: false,
StoppedCtx: stopCtx,
CancelledCtx: cancelCtx,
Filter: args.Filter,
Verbose: args.Verbose,
}
}

Expand Down
4 changes: 4 additions & 0 deletions internal/command/test_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ func TestTest_Runs(t *testing.T) {
expectedOut: "4 passed, 0 failed.",
code: 0,
},
"tfvars_in_test_dir": {
expectedOut: "2 passed, 0 failed.",
code: 0,
},
"functions_available": {
expectedOut: "1 passed, 0 failed.",
code: 0,
Expand Down
17 changes: 17 additions & 0 deletions internal/command/testdata/test/tfvars_in_test_dir/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
variable "foo" {
description = "This is test variable"
default = "def_value"
}

variable "fooJSON" {
description = "This is test variable"
default = "def_value"
}

output "out_foo" {
value = var.foo
}

output "out_fooJSON" {
value = var.fooJSON
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
run "primary" {
assert {
condition = var.foo == var.test_foo
error_message = "Expected: ${var.test_foo}, Actual: ${var.foo}"
}
}

run "secondary" {
assert {
condition = var.fooJSON == var.test_foo_json
error_message = "Expected: ${var.test_foo_json}, Actual: ${var.fooJSON}"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
foo = "foo_tfvars_value"
test_foo = "foo_tfvars_value"
test_foo_json = "foo_json_tfvars_value"
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"fooJSON": "foo_json_tfvars_value"
}

0 comments on commit c98e355

Please sign in to comment.