From c58bdc6390c6f2bd04683e768eaaeeff96216184 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Mon, 7 May 2018 16:47:24 -0700 Subject: [PATCH] core: fix the tests in variables_test.go This test required some adaptation for the new variable handling model in the HCL2-oriented codepaths, where more of the handling is now done in the "command" package. The initial reorganization of this test was not correct, so this is a further revision to adapt the original assertions to the new model as much as possible, removing a few tests that no longer make sense in the new world. --- terraform/variables.go | 95 ++++++++++++++++++++++++++ terraform/variables_test.go | 129 +++++++++++++++++++++++++++--------- 2 files changed, 193 insertions(+), 31 deletions(-) diff --git a/terraform/variables.go b/terraform/variables.go index 49ed7851b..a4318f7aa 100644 --- a/terraform/variables.go +++ b/terraform/variables.go @@ -1,6 +1,8 @@ package terraform import ( + "fmt" + "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/tfdiags" "github.com/zclconf/go-cty/cty" @@ -57,6 +59,18 @@ const ( ValueFromCaller ValueSourceType = 'S' ) +func (v *InputValue) GoString() string { + if (v.SourceRange != tfdiags.SourceRange{}) { + return fmt.Sprintf("&terraform.InputValue{Value: %#v, SourceType: %#v, SourceRange: %#v}", v.Value, v.SourceType, v.SourceRange) + } else { + return fmt.Sprintf("&terraform.InputValue{Value: %#v, SourceType: %#v}", v.Value, v.SourceType) + } +} + +func (v ValueSourceType) GoString() string { + return fmt.Sprintf("terraform.%s", v) +} + //go:generate stringer -type ValueSourceType // InputValues is a map of InputValue instances. @@ -124,3 +138,84 @@ func DefaultVariableValues(configs map[string]*configs.Variable) InputValues { } return ret } + +// SameValues returns true if the given InputValues has the same values as +// the receiever, disregarding the source types and source ranges. +// +// Values are compared using the cty "RawEquals" method, which means that +// unknown values can be considered equal to one another if they are of the +// same type. +func (vv InputValues) SameValues(other InputValues) bool { + if len(vv) != len(other) { + return false + } + + for k, v := range vv { + ov, exists := other[k] + if !exists { + return false + } + if !v.Value.RawEquals(ov.Value) { + return false + } + } + + return true +} + +// HasValues returns true if the reciever has the same values as in the given +// map, disregarding the source types and source ranges. +// +// Values are compared using the cty "RawEquals" method, which means that +// unknown values can be considered equal to one another if they are of the +// same type. +func (vv InputValues) HasValues(vals map[string]cty.Value) bool { + if len(vv) != len(vals) { + return false + } + + for k, v := range vv { + oVal, exists := vals[k] + if !exists { + return false + } + if !v.Value.RawEquals(oVal) { + return false + } + } + + return true +} + +// Identical returns true if the given InputValues has the same values, +// source types, and source ranges as the receiver. +// +// Values are compared using the cty "RawEquals" method, which means that +// unknown values can be considered equal to one another if they are of the +// same type. +// +// This method is primarily for testing. For most practical purposes, it's +// better to use SameValues or HasValues. +func (vv InputValues) Identical(other InputValues) bool { + if len(vv) != len(other) { + return false + } + + for k, v := range vv { + ov, exists := other[k] + if !exists { + return false + } + if !v.Value.RawEquals(ov.Value) { + return false + } + if v.SourceType != ov.SourceType { + return false + } + if v.SourceRange != ov.SourceRange { + return false + } + } + + return true +} diff --git a/terraform/variables_test.go b/terraform/variables_test.go index 586416534..da0ae915e 100644 --- a/terraform/variables_test.go +++ b/terraform/variables_test.go @@ -1,25 +1,52 @@ package terraform import ( - "reflect" "testing" + "github.com/davecgh/go-spew/spew" + "github.com/hashicorp/terraform/tfdiags" + + "github.com/go-test/deep" "github.com/zclconf/go-cty/cty" ) func TestVariables(t *testing.T) { - cases := map[string]struct { + tests := map[string]struct { Module string Override map[string]cty.Value - Expected map[string]cty.Value + Want InputValues }{ "config only": { "vars-basic", nil, - map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.ListValEmpty(cty.String), - "c": cty.MapValEmpty(cty.String), + InputValues{ + "a": &InputValue{ + Value: cty.StringVal("foo"), + SourceType: ValueFromConfig, + SourceRange: tfdiags.SourceRange{ + Filename: "test-fixtures/vars-basic/main.tf", + Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0}, + End: tfdiags.SourcePos{Line: 1, Column: 13, Byte: 12}, + }, + }, + "b": &InputValue{ + Value: cty.ListValEmpty(cty.DynamicPseudoType), + SourceType: ValueFromConfig, + SourceRange: tfdiags.SourceRange{ + Filename: "test-fixtures/vars-basic/main.tf", + Start: tfdiags.SourcePos{Line: 6, Column: 1, Byte: 58}, + End: tfdiags.SourcePos{Line: 6, Column: 13, Byte: 70}, + }, + }, + "c": &InputValue{ + Value: cty.MapValEmpty(cty.DynamicPseudoType), + SourceType: ValueFromConfig, + SourceRange: tfdiags.SourceRange{ + Filename: "test-fixtures/vars-basic/main.tf", + Start: tfdiags.SourcePos{Line: 11, Column: 1, Byte: 111}, + End: tfdiags.SourcePos{Line: 11, Column: 13, Byte: 123}, + }, + }, }, }, @@ -35,24 +62,49 @@ func TestVariables(t *testing.T) { "foo": cty.StringVal("bar"), }), }, - map[string]cty.Value{ - "a": cty.StringVal("bar"), - "b": cty.ListVal([]cty.Value{ - cty.StringVal("foo"), - cty.StringVal("bar"), - }), - "c": cty.MapVal(map[string]cty.Value{ - "foo": cty.StringVal("bar"), - }), + InputValues{ + "a": &InputValue{ + Value: cty.StringVal("bar"), + SourceType: ValueFromCaller, + }, + "b": &InputValue{ + Value: cty.ListVal([]cty.Value{ + cty.StringVal("foo"), + cty.StringVal("bar"), + }), + SourceType: ValueFromCaller, + }, + "c": &InputValue{ + Value: cty.MapVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + }), + SourceType: ValueFromCaller, + }, }, }, "bools: config only": { "vars-basic-bool", nil, - map[string]cty.Value{ - "a": cty.StringVal("1"), - "b": cty.StringVal("0"), + InputValues{ + "a": &InputValue{ + Value: cty.True, + SourceType: ValueFromConfig, + SourceRange: tfdiags.SourceRange{ + Filename: "test-fixtures/vars-basic-bool/main.tf", + Start: tfdiags.SourcePos{Line: 4, Column: 1, Byte: 177}, + End: tfdiags.SourcePos{Line: 4, Column: 13, Byte: 189}, + }, + }, + "b": &InputValue{ + Value: cty.False, + SourceType: ValueFromConfig, + SourceRange: tfdiags.SourceRange{ + Filename: "test-fixtures/vars-basic-bool/main.tf", + Start: tfdiags.SourcePos{Line: 8, Column: 1, Byte: 214}, + End: tfdiags.SourcePos{Line: 8, Column: 13, Byte: 226}, + }, + }, }, }, @@ -62,9 +114,15 @@ func TestVariables(t *testing.T) { "a": cty.StringVal("foo"), "b": cty.StringVal("bar"), }, - map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.StringVal("bar"), + InputValues{ + "a": &InputValue{ + Value: cty.StringVal("foo"), + SourceType: ValueFromCaller, + }, + "b": &InputValue{ + Value: cty.StringVal("bar"), + SourceType: ValueFromCaller, + }, }, }, @@ -74,23 +132,32 @@ func TestVariables(t *testing.T) { "a": cty.False, "b": cty.True, }, - map[string]cty.Value{ - "a": cty.StringVal("0"), - "b": cty.StringVal("1"), + InputValues{ + "a": &InputValue{ + Value: cty.False, + SourceType: ValueFromCaller, + }, + "b": &InputValue{ + Value: cty.True, + SourceType: ValueFromCaller, + }, }, }, } - for name, tc := range cases { + for name, test := range tests { // Wrapped in a func so we can get defers to work t.Run(name, func(t *testing.T) { - m := testModule(t, tc.Module) + m := testModule(t, test.Module) fromConfig := DefaultVariableValues(m.Module.Variables) - overrides := InputValuesFromCaller(tc.Override) - actual := fromConfig.Override(overrides) + overrides := InputValuesFromCaller(test.Override) + got := fromConfig.Override(overrides) - if !reflect.DeepEqual(actual, tc.Expected) { - t.Fatalf("%s\n\nexpected: %#v\n\ngot: %#v", name, tc.Expected, actual) + if !got.Identical(test.Want) { + t.Errorf("wrong result\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(test.Want)) + } + for _, problem := range deep.Equal(got, test.Want) { + t.Errorf(problem) } }) }