2020-02-18 06:07:24 -06:00
package terraform
import (
"reflect"
"strings"
"testing"
"github.com/davecgh/go-spew/spew"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hcltest"
2021-06-24 16:53:43 -05:00
"github.com/hashicorp/terraform/internal/lang/marks"
2021-05-17 12:11:06 -05:00
"github.com/hashicorp/terraform/internal/tfdiags"
2020-02-18 06:07:24 -06:00
"github.com/zclconf/go-cty/cty"
)
2020-04-08 16:09:17 -05:00
func TestEvaluateForEachExpression_valid ( t * testing . T ) {
2020-02-18 06:07:24 -06:00
tests := map [ string ] struct {
Expr hcl . Expression
ForEachMap map [ string ] cty . Value
} {
"empty set" : {
hcltest . MockExprLiteral ( cty . SetValEmpty ( cty . String ) ) ,
map [ string ] cty . Value { } ,
} ,
"multi-value string set" : {
hcltest . MockExprLiteral ( cty . SetVal ( [ ] cty . Value { cty . StringVal ( "a" ) , cty . StringVal ( "b" ) } ) ) ,
map [ string ] cty . Value {
"a" : cty . StringVal ( "a" ) ,
"b" : cty . StringVal ( "b" ) ,
} ,
} ,
"empty map" : {
hcltest . MockExprLiteral ( cty . MapValEmpty ( cty . Bool ) ) ,
map [ string ] cty . Value { } ,
} ,
"map" : {
hcltest . MockExprLiteral ( cty . MapVal ( map [ string ] cty . Value {
"a" : cty . BoolVal ( true ) ,
"b" : cty . BoolVal ( false ) ,
} ) ) ,
map [ string ] cty . Value {
"a" : cty . BoolVal ( true ) ,
"b" : cty . BoolVal ( false ) ,
} ,
} ,
"map containing unknown values" : {
hcltest . MockExprLiteral ( cty . MapVal ( map [ string ] cty . Value {
"a" : cty . UnknownVal ( cty . Bool ) ,
"b" : cty . UnknownVal ( cty . Bool ) ,
} ) ) ,
map [ string ] cty . Value {
"a" : cty . UnknownVal ( cty . Bool ) ,
"b" : cty . UnknownVal ( cty . Bool ) ,
} ,
} ,
2020-12-17 10:27:12 -06:00
"map containing sensitive values, but strings are literal" : {
hcltest . MockExprLiteral ( cty . MapVal ( map [ string ] cty . Value {
2021-06-24 16:53:43 -05:00
"a" : cty . BoolVal ( true ) . Mark ( marks . Sensitive ) ,
2020-12-17 10:27:12 -06:00
"b" : cty . BoolVal ( false ) ,
} ) ) ,
map [ string ] cty . Value {
2021-06-24 16:53:43 -05:00
"a" : cty . BoolVal ( true ) . Mark ( marks . Sensitive ) ,
2020-12-17 10:27:12 -06:00
"b" : cty . BoolVal ( false ) ,
} ,
} ,
2020-02-18 06:07:24 -06:00
}
for name , test := range tests {
t . Run ( name , func ( t * testing . T ) {
ctx := & MockEvalContext { }
ctx . installSimpleEval ( )
2020-04-08 16:09:17 -05:00
forEachMap , diags := evaluateForEachExpression ( test . Expr , ctx )
2020-02-18 06:07:24 -06:00
if len ( diags ) != 0 {
t . Errorf ( "unexpected diagnostics %s" , spew . Sdump ( diags ) )
}
if ! reflect . DeepEqual ( forEachMap , test . ForEachMap ) {
t . Errorf (
"wrong map value\ngot: %swant: %s" ,
spew . Sdump ( forEachMap ) , spew . Sdump ( test . ForEachMap ) ,
)
}
} )
}
}
2020-04-08 16:09:17 -05:00
func TestEvaluateForEachExpression_errors ( t * testing . T ) {
2020-02-18 06:07:24 -06:00
tests := map [ string ] struct {
Expr hcl . Expression
Summary , DetailSubstring string
} {
"null set" : {
hcltest . MockExprLiteral ( cty . NullVal ( cty . Set ( cty . String ) ) ) ,
"Invalid for_each argument" ,
` the given "for_each" argument value is null ` ,
} ,
"string" : {
hcltest . MockExprLiteral ( cty . StringVal ( "i am definitely a set" ) ) ,
"Invalid for_each argument" ,
"must be a map, or set of strings, and you have provided a value of type string" ,
} ,
"list" : {
hcltest . MockExprLiteral ( cty . ListVal ( [ ] cty . Value { cty . StringVal ( "a" ) , cty . StringVal ( "a" ) } ) ) ,
"Invalid for_each argument" ,
"must be a map, or set of strings, and you have provided a value of type list" ,
} ,
"tuple" : {
hcltest . MockExprLiteral ( cty . TupleVal ( [ ] cty . Value { cty . StringVal ( "a" ) , cty . StringVal ( "b" ) } ) ) ,
"Invalid for_each argument" ,
"must be a map, or set of strings, and you have provided a value of type tuple" ,
} ,
"unknown string set" : {
hcltest . MockExprLiteral ( cty . UnknownVal ( cty . Set ( cty . String ) ) ) ,
"Invalid for_each argument" ,
"depends on resource attributes that cannot be determined until apply" ,
} ,
"unknown map" : {
hcltest . MockExprLiteral ( cty . UnknownVal ( cty . Map ( cty . Bool ) ) ) ,
"Invalid for_each argument" ,
"depends on resource attributes that cannot be determined until apply" ,
} ,
2020-12-17 10:27:12 -06:00
"marked map" : {
hcltest . MockExprLiteral ( cty . MapVal ( map [ string ] cty . Value {
"a" : cty . BoolVal ( true ) ,
"b" : cty . BoolVal ( false ) ,
2021-06-24 16:53:43 -05:00
} ) . Mark ( marks . Sensitive ) ) ,
2020-12-17 10:27:12 -06:00
"Invalid for_each argument" ,
"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." ,
} ,
2020-02-18 06:07:24 -06:00
"set containing booleans" : {
hcltest . MockExprLiteral ( cty . SetVal ( [ ] cty . Value { cty . BoolVal ( true ) } ) ) ,
"Invalid for_each set argument" ,
"supports maps and sets of strings, but you have provided a set containing type bool" ,
} ,
"set containing null" : {
hcltest . MockExprLiteral ( cty . SetVal ( [ ] cty . Value { cty . NullVal ( cty . String ) } ) ) ,
"Invalid for_each set argument" ,
"must not contain null values" ,
} ,
"set containing unknown value" : {
hcltest . MockExprLiteral ( cty . SetVal ( [ ] cty . Value { cty . UnknownVal ( cty . String ) } ) ) ,
"Invalid for_each argument" ,
"depends on resource attributes that cannot be determined until apply" ,
} ,
2020-06-29 08:12:36 -05:00
"set containing dynamic unknown value" : {
hcltest . MockExprLiteral ( cty . SetVal ( [ ] cty . Value { cty . UnknownVal ( cty . DynamicPseudoType ) } ) ) ,
"Invalid for_each argument" ,
"depends on resource attributes that cannot be determined until apply" ,
} ,
2020-12-17 10:27:12 -06:00
"set containing marked values" : {
2021-06-24 16:53:43 -05:00
hcltest . MockExprLiteral ( cty . SetVal ( [ ] cty . Value { cty . StringVal ( "beep" ) . Mark ( marks . Sensitive ) , cty . StringVal ( "boop" ) } ) ) ,
2020-12-17 10:27:12 -06:00
"Invalid for_each argument" ,
"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." ,
} ,
2020-02-18 06:07:24 -06:00
}
for name , test := range tests {
t . Run ( name , func ( t * testing . T ) {
ctx := & MockEvalContext { }
ctx . installSimpleEval ( )
2020-04-08 16:09:17 -05:00
_ , diags := evaluateForEachExpression ( test . Expr , ctx )
2020-02-18 06:07:24 -06:00
if len ( diags ) != 1 {
t . Fatalf ( "got %d diagnostics; want 1" , diags )
}
if got , want := diags [ 0 ] . Severity ( ) , tfdiags . Error ; got != want {
t . Errorf ( "wrong diagnostic severity %#v; want %#v" , got , want )
}
if got , want := diags [ 0 ] . Description ( ) . Summary , test . Summary ; got != want {
t . Errorf ( "wrong diagnostic summary %#v; want %#v" , got , want )
}
if got , want := diags [ 0 ] . Description ( ) . Detail , test . DetailSubstring ; ! strings . Contains ( got , want ) {
t . Errorf ( "wrong diagnostic detail %#v; want %#v" , got , want )
}
2020-10-28 19:52:03 -05:00
if fromExpr := diags [ 0 ] . FromExpr ( ) ; fromExpr != nil {
if fromExpr . Expression == nil {
t . Errorf ( "diagnostic does not refer to an expression" )
}
if fromExpr . EvalContext == nil {
t . Errorf ( "diagnostic does not refer to an EvalContext" )
}
} else {
t . Errorf ( "diagnostic does not support FromExpr\ngot: %s" , spew . Sdump ( diags [ 0 ] ) )
}
2020-02-18 06:07:24 -06:00
} )
}
}
2020-04-08 16:09:17 -05:00
func TestEvaluateForEachExpressionKnown ( t * testing . T ) {
2020-02-18 06:07:24 -06:00
tests := map [ string ] hcl . Expression {
"unknown string set" : hcltest . MockExprLiteral ( cty . UnknownVal ( cty . Set ( cty . String ) ) ) ,
"unknown map" : hcltest . MockExprLiteral ( cty . UnknownVal ( cty . Map ( cty . Bool ) ) ) ,
}
for name , expr := range tests {
t . Run ( name , func ( t * testing . T ) {
ctx := & MockEvalContext { }
ctx . installSimpleEval ( )
2020-10-28 19:52:03 -05:00
forEachVal , diags := evaluateForEachExpressionValue ( expr , ctx , true )
2020-02-18 06:07:24 -06:00
if len ( diags ) != 0 {
t . Errorf ( "unexpected diagnostics %s" , spew . Sdump ( diags ) )
}
2020-04-08 16:09:17 -05:00
if forEachVal . IsKnown ( ) {
t . Error ( "got known, want unknown" )
2020-02-18 06:07:24 -06:00
}
} )
}
}