kubeform.dev/terraform-backend-sdk@v0.0.0-20220310143633-45f07fe731c5/terraform/eval_for_each_test.go (about)

     1  package terraform
     2  
     3  import (
     4  	"reflect"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/davecgh/go-spew/spew"
     9  	"github.com/hashicorp/hcl/v2"
    10  	"github.com/hashicorp/hcl/v2/hcltest"
    11  	"kubeform.dev/terraform-backend-sdk/lang/marks"
    12  	"kubeform.dev/terraform-backend-sdk/tfdiags"
    13  	"github.com/zclconf/go-cty/cty"
    14  )
    15  
    16  func TestEvaluateForEachExpression_valid(t *testing.T) {
    17  	tests := map[string]struct {
    18  		Expr       hcl.Expression
    19  		ForEachMap map[string]cty.Value
    20  	}{
    21  		"empty set": {
    22  			hcltest.MockExprLiteral(cty.SetValEmpty(cty.String)),
    23  			map[string]cty.Value{},
    24  		},
    25  		"multi-value string set": {
    26  			hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b")})),
    27  			map[string]cty.Value{
    28  				"a": cty.StringVal("a"),
    29  				"b": cty.StringVal("b"),
    30  			},
    31  		},
    32  		"empty map": {
    33  			hcltest.MockExprLiteral(cty.MapValEmpty(cty.Bool)),
    34  			map[string]cty.Value{},
    35  		},
    36  		"map": {
    37  			hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{
    38  				"a": cty.BoolVal(true),
    39  				"b": cty.BoolVal(false),
    40  			})),
    41  			map[string]cty.Value{
    42  				"a": cty.BoolVal(true),
    43  				"b": cty.BoolVal(false),
    44  			},
    45  		},
    46  		"map containing unknown values": {
    47  			hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{
    48  				"a": cty.UnknownVal(cty.Bool),
    49  				"b": cty.UnknownVal(cty.Bool),
    50  			})),
    51  			map[string]cty.Value{
    52  				"a": cty.UnknownVal(cty.Bool),
    53  				"b": cty.UnknownVal(cty.Bool),
    54  			},
    55  		},
    56  		"map containing sensitive values, but strings are literal": {
    57  			hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{
    58  				"a": cty.BoolVal(true).Mark(marks.Sensitive),
    59  				"b": cty.BoolVal(false),
    60  			})),
    61  			map[string]cty.Value{
    62  				"a": cty.BoolVal(true).Mark(marks.Sensitive),
    63  				"b": cty.BoolVal(false),
    64  			},
    65  		},
    66  	}
    67  
    68  	for name, test := range tests {
    69  		t.Run(name, func(t *testing.T) {
    70  			ctx := &MockEvalContext{}
    71  			ctx.installSimpleEval()
    72  			forEachMap, diags := evaluateForEachExpression(test.Expr, ctx)
    73  
    74  			if len(diags) != 0 {
    75  				t.Errorf("unexpected diagnostics %s", spew.Sdump(diags))
    76  			}
    77  
    78  			if !reflect.DeepEqual(forEachMap, test.ForEachMap) {
    79  				t.Errorf(
    80  					"wrong map value\ngot:  %swant: %s",
    81  					spew.Sdump(forEachMap), spew.Sdump(test.ForEachMap),
    82  				)
    83  			}
    84  
    85  		})
    86  	}
    87  }
    88  
    89  func TestEvaluateForEachExpression_errors(t *testing.T) {
    90  	tests := map[string]struct {
    91  		Expr                     hcl.Expression
    92  		Summary, DetailSubstring string
    93  	}{
    94  		"null set": {
    95  			hcltest.MockExprLiteral(cty.NullVal(cty.Set(cty.String))),
    96  			"Invalid for_each argument",
    97  			`the given "for_each" argument value is null`,
    98  		},
    99  		"string": {
   100  			hcltest.MockExprLiteral(cty.StringVal("i am definitely a set")),
   101  			"Invalid for_each argument",
   102  			"must be a map, or set of strings, and you have provided a value of type string",
   103  		},
   104  		"list": {
   105  			hcltest.MockExprLiteral(cty.ListVal([]cty.Value{cty.StringVal("a"), cty.StringVal("a")})),
   106  			"Invalid for_each argument",
   107  			"must be a map, or set of strings, and you have provided a value of type list",
   108  		},
   109  		"tuple": {
   110  			hcltest.MockExprLiteral(cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b")})),
   111  			"Invalid for_each argument",
   112  			"must be a map, or set of strings, and you have provided a value of type tuple",
   113  		},
   114  		"unknown string set": {
   115  			hcltest.MockExprLiteral(cty.UnknownVal(cty.Set(cty.String))),
   116  			"Invalid for_each argument",
   117  			"depends on resource attributes that cannot be determined until apply",
   118  		},
   119  		"unknown map": {
   120  			hcltest.MockExprLiteral(cty.UnknownVal(cty.Map(cty.Bool))),
   121  			"Invalid for_each argument",
   122  			"depends on resource attributes that cannot be determined until apply",
   123  		},
   124  		"marked map": {
   125  			hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{
   126  				"a": cty.BoolVal(true),
   127  				"b": cty.BoolVal(false),
   128  			}).Mark(marks.Sensitive)),
   129  			"Invalid for_each argument",
   130  			"Sensitive values, or values derived from sensitive values, cannot be used as for_each arguments. If used, the sensitive value could be exposed as a resource instance key.",
   131  		},
   132  		"set containing booleans": {
   133  			hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.BoolVal(true)})),
   134  			"Invalid for_each set argument",
   135  			"supports maps and sets of strings, but you have provided a set containing type bool",
   136  		},
   137  		"set containing null": {
   138  			hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.NullVal(cty.String)})),
   139  			"Invalid for_each set argument",
   140  			"must not contain null values",
   141  		},
   142  		"set containing unknown value": {
   143  			hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.UnknownVal(cty.String)})),
   144  			"Invalid for_each argument",
   145  			"depends on resource attributes that cannot be determined until apply",
   146  		},
   147  		"set containing dynamic unknown value": {
   148  			hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.UnknownVal(cty.DynamicPseudoType)})),
   149  			"Invalid for_each argument",
   150  			"depends on resource attributes that cannot be determined until apply",
   151  		},
   152  		"set containing marked values": {
   153  			hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.StringVal("beep").Mark(marks.Sensitive), cty.StringVal("boop")})),
   154  			"Invalid for_each argument",
   155  			"Sensitive values, or values derived from sensitive values, cannot be used as for_each arguments. If used, the sensitive value could be exposed as a resource instance key.",
   156  		},
   157  	}
   158  
   159  	for name, test := range tests {
   160  		t.Run(name, func(t *testing.T) {
   161  			ctx := &MockEvalContext{}
   162  			ctx.installSimpleEval()
   163  			_, diags := evaluateForEachExpression(test.Expr, ctx)
   164  
   165  			if len(diags) != 1 {
   166  				t.Fatalf("got %d diagnostics; want 1", diags)
   167  			}
   168  			if got, want := diags[0].Severity(), tfdiags.Error; got != want {
   169  				t.Errorf("wrong diagnostic severity %#v; want %#v", got, want)
   170  			}
   171  			if got, want := diags[0].Description().Summary, test.Summary; got != want {
   172  				t.Errorf("wrong diagnostic summary %#v; want %#v", got, want)
   173  			}
   174  			if got, want := diags[0].Description().Detail, test.DetailSubstring; !strings.Contains(got, want) {
   175  				t.Errorf("wrong diagnostic detail %#v; want %#v", got, want)
   176  			}
   177  			if fromExpr := diags[0].FromExpr(); fromExpr != nil {
   178  				if fromExpr.Expression == nil {
   179  					t.Errorf("diagnostic does not refer to an expression")
   180  				}
   181  				if fromExpr.EvalContext == nil {
   182  					t.Errorf("diagnostic does not refer to an EvalContext")
   183  				}
   184  			} else {
   185  				t.Errorf("diagnostic does not support FromExpr\ngot: %s", spew.Sdump(diags[0]))
   186  			}
   187  		})
   188  	}
   189  }
   190  
   191  func TestEvaluateForEachExpressionKnown(t *testing.T) {
   192  	tests := map[string]hcl.Expression{
   193  		"unknown string set": hcltest.MockExprLiteral(cty.UnknownVal(cty.Set(cty.String))),
   194  		"unknown map":        hcltest.MockExprLiteral(cty.UnknownVal(cty.Map(cty.Bool))),
   195  	}
   196  
   197  	for name, expr := range tests {
   198  		t.Run(name, func(t *testing.T) {
   199  			ctx := &MockEvalContext{}
   200  			ctx.installSimpleEval()
   201  			forEachVal, diags := evaluateForEachExpressionValue(expr, ctx, true)
   202  
   203  			if len(diags) != 0 {
   204  				t.Errorf("unexpected diagnostics %s", spew.Sdump(diags))
   205  			}
   206  
   207  			if forEachVal.IsKnown() {
   208  				t.Error("got known, want unknown")
   209  			}
   210  		})
   211  	}
   212  }