mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-19 17:59:05 -05:00
Signed-off-by: Andrei Ciobanu <andrei.ciobanu@opentofu.org> Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
1276 lines
45 KiB
Go
1276 lines
45 KiB
Go
// Copyright (c) The OpenTofu Authors
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright (c) 2023 HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
package evalchecks
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/davecgh/go-spew/spew"
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/hcl/v2/hcltest"
|
|
"github.com/opentofu/opentofu/internal/addrs"
|
|
"github.com/opentofu/opentofu/internal/lang/marks"
|
|
"github.com/opentofu/opentofu/internal/tfdiags"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
// returns a mock ref function for the unit tests
|
|
func mockRefsFunc() ContextFunc {
|
|
return func(_ []*addrs.Reference) (*hcl.EvalContext, tfdiags.Diagnostics) {
|
|
var diags tfdiags.Diagnostics
|
|
evalContext := hcl.EvalContext{}
|
|
return &evalContext, diags
|
|
}
|
|
}
|
|
|
|
func TestEvaluateForEachExpression_multi_errors(t *testing.T) {
|
|
tests := map[string]struct {
|
|
Expr hcl.Expression
|
|
Wanted []struct {
|
|
Summary string
|
|
DetailSubstring string
|
|
CausedBySensitive bool
|
|
}
|
|
}{
|
|
"marked list": {
|
|
hcltest.MockExprLiteral(cty.ListVal([]cty.Value{cty.StringVal("a"), cty.StringVal("a")}).Mark(marks.Sensitive)),
|
|
[]struct {
|
|
Summary string
|
|
DetailSubstring string
|
|
CausedBySensitive bool
|
|
}{
|
|
{
|
|
"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.",
|
|
true,
|
|
},
|
|
{
|
|
"Invalid for_each argument",
|
|
"must be a map, or set of strings, and you have provided a value of type list",
|
|
false,
|
|
},
|
|
},
|
|
},
|
|
"ephemeral marked list": {
|
|
hcltest.MockExprLiteral(cty.ListVal([]cty.Value{cty.StringVal("a"), cty.StringVal("a")}).Mark(marks.Ephemeral)),
|
|
[]struct {
|
|
Summary string
|
|
DetailSubstring string
|
|
CausedBySensitive bool
|
|
}{
|
|
{
|
|
"Invalid for_each argument",
|
|
"Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments. If used, the ephemeral value could be exposed as a resource instance key.",
|
|
true,
|
|
},
|
|
{
|
|
"Invalid for_each argument",
|
|
"must be a map, or set of strings, and you have provided a value of type list",
|
|
false,
|
|
},
|
|
},
|
|
},
|
|
"marked tuple": {
|
|
hcltest.MockExprLiteral(cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.StringVal("a")}).Mark(marks.Sensitive)),
|
|
[]struct {
|
|
Summary string
|
|
DetailSubstring string
|
|
CausedBySensitive bool
|
|
}{
|
|
{
|
|
"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.",
|
|
true,
|
|
},
|
|
{
|
|
"Invalid for_each argument",
|
|
"must be a map, or set of strings, and you have provided a value of type tuple",
|
|
false,
|
|
},
|
|
},
|
|
},
|
|
"ephemeral marked tuple": {
|
|
hcltest.MockExprLiteral(cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.StringVal("a")}).Mark(marks.Ephemeral)),
|
|
[]struct {
|
|
Summary string
|
|
DetailSubstring string
|
|
CausedBySensitive bool
|
|
}{
|
|
{
|
|
"Invalid for_each argument",
|
|
"Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments. If used, the ephemeral value could be exposed as a resource instance key.",
|
|
true,
|
|
},
|
|
{
|
|
"Invalid for_each argument",
|
|
"must be a map, or set of strings, and you have provided a value of type tuple",
|
|
false,
|
|
},
|
|
},
|
|
},
|
|
"marked string": {
|
|
hcltest.MockExprLiteral(cty.StringVal("a").Mark(marks.Sensitive)),
|
|
[]struct {
|
|
Summary string
|
|
DetailSubstring string
|
|
CausedBySensitive bool
|
|
}{
|
|
{
|
|
"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.",
|
|
true,
|
|
},
|
|
{
|
|
"Invalid for_each argument",
|
|
"must be a map, or set of strings, and you have provided a value of type string",
|
|
false,
|
|
},
|
|
},
|
|
},
|
|
"ephemeral marked string": {
|
|
hcltest.MockExprLiteral(cty.StringVal("a").Mark(marks.Ephemeral)),
|
|
[]struct {
|
|
Summary string
|
|
DetailSubstring string
|
|
CausedBySensitive bool
|
|
}{
|
|
{
|
|
"Invalid for_each argument",
|
|
"Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments. If used, the ephemeral value could be exposed as a resource instance key.",
|
|
true,
|
|
},
|
|
{
|
|
"Invalid for_each argument",
|
|
"must be a map, or set of strings, and you have provided a value of type string",
|
|
false,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
_, diags := EvaluateForEachExpression(test.Expr, mockRefsFunc(), nil)
|
|
if len(diags) != len(test.Wanted) {
|
|
t.Fatalf("Expected diagnostics: %s\nGot: %s\n", spew.Sdump(test.Wanted), spew.Sdump(diags))
|
|
}
|
|
for idx := range test.Wanted {
|
|
if got, want := diags[idx].Severity(), tfdiags.Error; got != want {
|
|
t.Errorf("wrong diagnostic severity %#v; want %#v", got, want)
|
|
}
|
|
if got, want := diags[idx].Description().Summary, test.Wanted[idx].Summary; got != want {
|
|
t.Errorf("wrong diagnostic summary\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
if got, want := diags[idx].Description().Detail, test.Wanted[idx].DetailSubstring; !strings.Contains(got, want) {
|
|
t.Errorf("wrong diagnostic detail\ngot: %s\nwant substring: %s", got, want)
|
|
}
|
|
if fromExpr := diags[idx].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[idx]))
|
|
}
|
|
|
|
if got, want := tfdiags.DiagnosticCausedByConfidentialValues(diags[idx]), test.Wanted[idx].CausedBySensitive; got != want {
|
|
t.Errorf("wrong result from tfdiags.DiagnosticCausedByConfidentialValues\ngot: %#v\nwant: %#v", got, want)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestEvaluateForEachExpressionValueTuple(t *testing.T) {
|
|
// Tests for cases where allowTuple is true. This can't be added to the tests on TestEvaluateForEach since they are called with allowTuple as false.
|
|
tests := map[string]struct {
|
|
Value cty.Value
|
|
expectedErrs []expectedErr
|
|
}{
|
|
"valid tuple": {
|
|
Value: cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b")}),
|
|
},
|
|
"empty tuple": {
|
|
Value: cty.EmptyTupleVal,
|
|
},
|
|
"null tuple": {
|
|
Value: cty.NullVal(cty.Tuple([]cty.Type{})),
|
|
expectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "the given \"for_each\" argument value is null",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
},
|
|
"sensitive tuple": {
|
|
Value: cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b")}).Mark(marks.Sensitive),
|
|
expectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "Sensitive values, or values derived from sensitive values, cannot be used as for_each arguments",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: true,
|
|
},
|
|
},
|
|
},
|
|
"ephemeral tuple": {
|
|
Value: cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b")}).Mark(marks.Ephemeral),
|
|
expectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: true,
|
|
},
|
|
},
|
|
},
|
|
"unknown tuple": {
|
|
Value: cty.UnknownVal(cty.Tuple([]cty.Type{cty.String})),
|
|
expectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "tuple includes values derived from resource attributes that cannot be determined until apply",
|
|
CausedByUnknown: true,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
expr := hcltest.MockExprLiteral(test.Value)
|
|
_, diags := EvaluateForEachExpressionValue(expr, mockRefsFunc(), false, true, nil)
|
|
|
|
assertDiagnosticsMatch(t, diags, test.expectedErrs, "plan")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestForEachCommandLineExcludeSuggestion(t *testing.T) {
|
|
noKeyResourceAddr := addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "happycloud_virtual_machine",
|
|
Name: "boop",
|
|
}.Absolute(addrs.RootModuleInstance)
|
|
stringKeyResourceAddr := addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "happycloud_virtual_machine",
|
|
Name: "boop",
|
|
}.Absolute(addrs.RootModuleInstance.Child("foo", addrs.StringKey("bleep bloop")))
|
|
|
|
tests := []struct {
|
|
excludableAddr addrs.Targetable
|
|
goos string
|
|
want string
|
|
}{
|
|
{
|
|
nil,
|
|
"linux",
|
|
`Alternatively, you could use the -target option to first apply only the resources that for_each depends on, and then apply normally to converge.`,
|
|
},
|
|
{
|
|
nil,
|
|
"windows",
|
|
`Alternatively, you could use the -target option to first apply only the resources that for_each depends on, and then apply normally to converge.`,
|
|
},
|
|
{
|
|
nil,
|
|
"darwin",
|
|
`Alternatively, you could use the -target option to first apply only the resources that for_each depends on, and then apply normally to converge.`,
|
|
},
|
|
{
|
|
noKeyResourceAddr,
|
|
"linux",
|
|
`Alternatively, you could use the planning option -exclude=happycloud_virtual_machine.boop to first apply without this object, and then apply normally to converge.`,
|
|
},
|
|
{
|
|
noKeyResourceAddr,
|
|
"windows",
|
|
`Alternatively, you could use the planning option -exclude=happycloud_virtual_machine.boop to first apply without this object, and then apply normally to converge.`,
|
|
},
|
|
{
|
|
noKeyResourceAddr,
|
|
"darwin",
|
|
`Alternatively, you could use the planning option -exclude=happycloud_virtual_machine.boop to first apply without this object, and then apply normally to converge.`,
|
|
},
|
|
{
|
|
stringKeyResourceAddr,
|
|
"linux",
|
|
`Alternatively, you could use the planning option -exclude='module.foo["bleep bloop"].happycloud_virtual_machine.boop' to first apply without this object, and then apply normally to converge.`,
|
|
},
|
|
{
|
|
stringKeyResourceAddr,
|
|
"windows",
|
|
`Alternatively, you could use the planning option -exclude="module.foo[\"bleep bloop\"].happycloud_virtual_machine.boop" to first apply without this object, and then apply normally to converge.`,
|
|
},
|
|
{
|
|
stringKeyResourceAddr,
|
|
"darwin",
|
|
`Alternatively, you could use the planning option -exclude='module.foo["bleep bloop"].happycloud_virtual_machine.boop' to first apply without this object, and then apply normally to converge.`,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(fmt.Sprintf("%v on %s", test.excludableAddr, test.goos), func(t *testing.T) {
|
|
got := forEachCommandLineExcludeSuggestionImpl(test.excludableAddr, test.goos)
|
|
if got != test.want {
|
|
t.Errorf("wrong suggestion\ngot: %s\nwant: %s", got, test.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type expectedErr struct {
|
|
Summary string
|
|
Detail string
|
|
CausedByUnknown bool
|
|
CausedBySensitive bool
|
|
}
|
|
|
|
func assertDiagnosticsMatch(t *testing.T, gotDiags []tfdiags.Diagnostic, wantDiags []expectedErr, phase string) {
|
|
t.Helper()
|
|
if len(gotDiags) != len(wantDiags) {
|
|
t.Errorf("got %d errors in %s phase; expected %d", len(gotDiags), phase, len(wantDiags))
|
|
}
|
|
|
|
for i := range max(len(gotDiags), len(wantDiags)) {
|
|
if i >= len(gotDiags) {
|
|
t.Errorf("on %s phase, got no error on position %d, while expecting error:\n%s\n%s", phase, i, wantDiags[i].Summary, wantDiags[i].Detail)
|
|
} else if i >= len(wantDiags) {
|
|
t.Errorf("on %s phase on position %d, while expecting no error, got:\n%s\n%s", phase, i, gotDiags[i].Description().Summary, gotDiags[i].Description().Detail)
|
|
} else {
|
|
assertDiagnosticMatch(t, phase, gotDiags[i], wantDiags[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func assertDiagnosticMatch(t *testing.T, phase string, gotDiag tfdiags.Diagnostic, wantDiag expectedErr) {
|
|
t.Helper()
|
|
|
|
if got, want := gotDiag.Severity(), tfdiags.Error; got != want {
|
|
t.Errorf("wrong diagnostic severity on %s phase %#v; want %#v", phase, got, want)
|
|
}
|
|
|
|
if got, want := gotDiag.Description().Summary, wantDiag.Summary; got != want {
|
|
t.Errorf("wrong diagnostic summary on %s phase\ngot: %s\nwant: %s", phase, got, want)
|
|
}
|
|
|
|
if got, want := gotDiag.Description().Detail, wantDiag.Detail; !strings.Contains(got, want) {
|
|
t.Errorf("wrong diagnostic detail on %s phase, \ngot: %s\nwant substring: %s", phase, got, want)
|
|
}
|
|
|
|
if got, want := tfdiags.DiagnosticCausedByUnknown(gotDiag), wantDiag.CausedByUnknown; got != want {
|
|
t.Errorf("wrong result from tfdiags.DiagnosticCausedByUnknown on %s phase\ngot: %#v\nwant: %#v", phase, got, want)
|
|
}
|
|
if got, want := tfdiags.DiagnosticCausedByConfidentialValues(gotDiag), wantDiag.CausedBySensitive; got != want {
|
|
t.Errorf("wrong result from tfdiags.DiagnosticCausedByConfidentialValues on %s phase\ngot: %#v\nwant: %#v", phase, got, want)
|
|
}
|
|
|
|
if fromExpr := gotDiag.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(gotDiag))
|
|
}
|
|
}
|
|
|
|
func TestEvaluateForEach(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := map[string]struct {
|
|
Input cty.Value
|
|
ValidateExpectedErrs []expectedErr
|
|
ValidateReturnValue cty.Value
|
|
PlanExpectedErrs []expectedErr
|
|
PlanReturnValue map[string]cty.Value
|
|
}{
|
|
"empty_set": {
|
|
Input: cty.SetValEmpty(cty.String),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.SetValEmpty(cty.String),
|
|
PlanExpectedErrs: nil,
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"empty_set_dynamic": {
|
|
Input: cty.SetValEmpty(cty.DynamicPseudoType),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.SetValEmpty(cty.DynamicPseudoType),
|
|
PlanExpectedErrs: nil,
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"set_of_strings": {
|
|
Input: cty.SetVal([]cty.Value{cty.StringVal("a")}),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.SetVal([]cty.Value{cty.StringVal("a")}),
|
|
PlanExpectedErrs: nil,
|
|
PlanReturnValue: map[string]cty.Value{"a": cty.StringVal("a")},
|
|
},
|
|
"set_of_bool": {
|
|
Input: cty.SetVal([]cty.Value{cty.True}),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "but you have provided a set containing type bool.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.Set(cty.Bool)),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "but you have provided a set containing type bool.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"set_of_null": {
|
|
Input: cty.SetVal([]cty.Value{cty.NullVal(cty.String)}),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "sets must not contain null values",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.Set(cty.String)),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "sets must not contain null values.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"set_of_unknown_strings": {
|
|
Input: cty.SetVal([]cty.Value{cty.UnknownVal(cty.String)}),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.UnknownVal(cty.Set(cty.String)),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "set includes values derived from resource attributes that cannot be determined until apply",
|
|
CausedByUnknown: true,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"set_of_unknown_dynamic": {
|
|
Input: cty.SetVal([]cty.Value{cty.DynamicVal}),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.UnknownVal(cty.Set(cty.DynamicPseudoType)),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "set includes values derived from resource attributes that cannot be determined until apply",
|
|
CausedByUnknown: true,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
// # Tuples
|
|
"empty_tuple": {
|
|
Input: cty.EmptyTupleVal,
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type tuple",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.EmptyTuple),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type tuple",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"tuple_of_strings": {
|
|
Input: cty.TupleVal([]cty.Value{cty.StringVal("a")}),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type tuple",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.Tuple([]cty.Type{cty.String})),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type tuple",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"tuple_of_bool": {
|
|
Input: cty.TupleVal([]cty.Value{cty.True}),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type tuple",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.Tuple([]cty.Type{cty.Bool})),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type tuple",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"tuple_of_null": {
|
|
Input: cty.TupleVal([]cty.Value{cty.NullVal(cty.String)}),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type tuple",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.Tuple([]cty.Type{cty.String})),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type tuple",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"tuple_of_unknown_strings": {
|
|
Input: cty.TupleVal([]cty.Value{cty.UnknownVal(cty.String)}),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type tuple",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.Tuple([]cty.Type{cty.String})),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type tuple",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"tuple_of_unknown_dynamic": {
|
|
Input: cty.TupleVal([]cty.Value{cty.DynamicVal}),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type tuple",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.Tuple([]cty.Type{cty.DynamicPseudoType})),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type tuple",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"empty_map": {
|
|
Input: cty.MapValEmpty(cty.String),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.MapValEmpty(cty.String),
|
|
PlanExpectedErrs: nil,
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"map_of_null": {
|
|
Input: cty.MapVal(map[string]cty.Value{"a": cty.NullVal(cty.String)}),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.MapVal(map[string]cty.Value{"a": cty.NullVal(cty.String)}),
|
|
PlanExpectedErrs: nil,
|
|
PlanReturnValue: map[string]cty.Value{"a": cty.NullVal(cty.String)},
|
|
},
|
|
"map_of_unknown_string": {
|
|
Input: cty.MapVal(map[string]cty.Value{"a": cty.UnknownVal(cty.String)}),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.MapVal(map[string]cty.Value{"a": cty.UnknownVal(cty.String)}),
|
|
PlanExpectedErrs: nil,
|
|
PlanReturnValue: map[string]cty.Value{"a": cty.UnknownVal(cty.String)},
|
|
},
|
|
"map_of_bool": {
|
|
Input: cty.MapVal(map[string]cty.Value{"a": cty.True}),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.MapVal(map[string]cty.Value{"a": cty.True}),
|
|
PlanExpectedErrs: nil,
|
|
PlanReturnValue: map[string]cty.Value{"a": cty.True},
|
|
},
|
|
"map_of_unknown_bool": {
|
|
Input: cty.MapVal(map[string]cty.Value{"a": cty.UnknownVal(cty.Bool)}),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.MapVal(map[string]cty.Value{"a": cty.UnknownVal(cty.Bool)}),
|
|
PlanExpectedErrs: nil,
|
|
PlanReturnValue: map[string]cty.Value{"a": cty.UnknownVal(cty.Bool)},
|
|
},
|
|
"map_of_string": {
|
|
Input: cty.MapVal(map[string]cty.Value{"a": cty.StringVal("b")}),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.MapVal(map[string]cty.Value{"a": cty.StringVal("b")}),
|
|
PlanExpectedErrs: nil,
|
|
PlanReturnValue: map[string]cty.Value{"a": cty.StringVal("b")},
|
|
},
|
|
"empty_object": {
|
|
Input: cty.EmptyObjectVal,
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.EmptyObjectVal,
|
|
PlanExpectedErrs: nil,
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"object_with_null_values": {
|
|
Input: cty.ObjectVal(map[string]cty.Value{"a": cty.NullVal(cty.String)}),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.ObjectVal(map[string]cty.Value{"a": cty.NullVal(cty.String)}),
|
|
PlanExpectedErrs: nil,
|
|
PlanReturnValue: map[string]cty.Value{"a": cty.NullVal(cty.String)},
|
|
},
|
|
"object_with_unknown_string_values": {
|
|
Input: cty.ObjectVal(map[string]cty.Value{"a": cty.UnknownVal(cty.String)}),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.ObjectVal(map[string]cty.Value{"a": cty.UnknownVal(cty.String)}),
|
|
PlanExpectedErrs: nil,
|
|
PlanReturnValue: map[string]cty.Value{"a": cty.UnknownVal(cty.String)},
|
|
},
|
|
"object_with_bool_values": {
|
|
Input: cty.ObjectVal(map[string]cty.Value{"a": cty.BoolVal(true)}),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.ObjectVal(map[string]cty.Value{"a": cty.True}),
|
|
PlanExpectedErrs: nil,
|
|
PlanReturnValue: map[string]cty.Value{"a": cty.True},
|
|
},
|
|
"object_with_unknown_bool_values": {
|
|
Input: cty.ObjectVal(map[string]cty.Value{"a": cty.UnknownVal(cty.Bool)}),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.ObjectVal(map[string]cty.Value{"a": cty.UnknownVal(cty.Bool)}),
|
|
PlanExpectedErrs: nil,
|
|
PlanReturnValue: map[string]cty.Value{"a": cty.UnknownVal(cty.Bool)},
|
|
},
|
|
"object_with_string_values": {
|
|
Input: cty.ObjectVal(map[string]cty.Value{"a": cty.StringVal("b")}),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.ObjectVal(map[string]cty.Value{"a": cty.StringVal("b")}),
|
|
PlanExpectedErrs: nil,
|
|
PlanReturnValue: map[string]cty.Value{"a": cty.StringVal("b")},
|
|
},
|
|
// Other
|
|
"null_set_string": {
|
|
Input: cty.NullVal(cty.Set(cty.String)),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "argument value is null.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.Set(cty.String)),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "argument value is null.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"null_object": {
|
|
Input: cty.NullVal(cty.Object(map[string]cty.Type{
|
|
"route_addrs": cty.String,
|
|
"cidr": cty.String,
|
|
})),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "argument value is null.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.Object(map[string]cty.Type{"cidr": cty.String, "route_addrs": cty.String})),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "argument value is null.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"null_tuple": {
|
|
Input: cty.NullVal(cty.Tuple([]cty.Type{})),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "argument value is null.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "argument must be a map, or set of strings, and you have provided a value of type tuple.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.Tuple([]cty.Type{})),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "argument value is null.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "argument must be a map, or set of strings, and you have provided a value of type tuple.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"sensitive_tuple": {
|
|
Input: cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b")}).Mark(marks.Sensitive),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "Sensitive values, or values derived from sensitive values, cannot be used as for_each arguments",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: true,
|
|
},
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "argument must be a map, or set of strings, and you have provided a value of type tuple.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.Tuple([]cty.Type{cty.String, cty.String})),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "Sensitive values, or values derived from sensitive values, cannot be used as for_each arguments",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: true,
|
|
},
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "argument must be a map, or set of strings, and you have provided a value of type tuple.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"sensitive_set": {
|
|
Input: cty.SetVal([]cty.Value{cty.StringVal("a")}).Mark(marks.Sensitive),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "Sensitive values, or values derived from sensitive values, cannot be used as for_each arguments",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: true,
|
|
}},
|
|
ValidateReturnValue: cty.NullVal(cty.Set(cty.String)),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "Sensitive values, or values derived from sensitive values, cannot be used as for_each arguments",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: true,
|
|
}},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"sensitive_set_elements": {
|
|
Input: cty.SetVal([]cty.Value{cty.StringVal("a").Mark(marks.Sensitive)}),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "Sensitive values, or values derived from sensitive values, cannot be used as for_each arguments",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: true,
|
|
}},
|
|
ValidateReturnValue: cty.NullVal(cty.Set(cty.String)),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "Sensitive values, or values derived from sensitive values, cannot be used as for_each arguments",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: true,
|
|
}},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"ephemeral_tuple": {
|
|
Input: cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b")}).Mark(marks.Ephemeral),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: true,
|
|
},
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "argument must be a map, or set of strings, and you have provided a value of type tuple.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.Tuple([]cty.Type{cty.String, cty.String})),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: true,
|
|
},
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "argument must be a map, or set of strings, and you have provided a value of type tuple.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"ephemeral_set": {
|
|
Input: cty.SetVal([]cty.Value{cty.StringVal("a")}).Mark(marks.Ephemeral),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: true,
|
|
}},
|
|
ValidateReturnValue: cty.NullVal(cty.Set(cty.String)),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: true,
|
|
}},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"ephemeral_set_elements": {
|
|
Input: cty.SetVal([]cty.Value{cty.StringVal("a").Mark(marks.Ephemeral)}),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: true,
|
|
}},
|
|
ValidateReturnValue: cty.NullVal(cty.Set(cty.String)),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: true,
|
|
}},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"string": {
|
|
Input: cty.StringVal("i am definitely a set"),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type string",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.String),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type string",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"number": {
|
|
Input: cty.MustParseNumberVal("1e+50"),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type number",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.Number),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type number",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"bool": {
|
|
Input: cty.True,
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type bool",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.Bool),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type bool",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"list": {
|
|
Input: cty.ListVal([]cty.Value{cty.StringVal("a"), cty.StringVal("a")}),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type list of string",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.List(cty.String)),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "must be a map, or set of strings, and you have provided a value of type list of string",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
// Top-Level Unknowns (basically all the above with unknown on the top-level)
|
|
"unknown_empty_set": {
|
|
Input: cty.UnknownVal(cty.Set(cty.String)),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.UnknownVal(cty.Set(cty.String)),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "set includes values derived from resource attributes that cannot be determined until apply",
|
|
CausedByUnknown: true,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"unknown_set_of_object": {
|
|
Input: cty.UnknownVal(cty.Set(cty.Object(map[string]cty.Type{
|
|
"route_addrs": cty.String,
|
|
"cidr": cty.String,
|
|
}))),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "set containing type object.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.UnknownVal(cty.Set(cty.Object(map[string]cty.Type{"cidr": cty.String, "route_addrs": cty.String}))),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "set includes values derived from resource attributes that cannot be determined until apply",
|
|
CausedByUnknown: true,
|
|
CausedBySensitive: false,
|
|
},
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "set containing type object.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"unknown_set_of_strings": {
|
|
Input: cty.UnknownVal(cty.Set(cty.String)),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.UnknownVal(cty.Set(cty.String)),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "set includes values derived from resource attributes that cannot be determined until apply",
|
|
CausedByUnknown: true,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"unknown_set_of_bool": {
|
|
Input: cty.UnknownVal(cty.Set(cty.Bool)),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "set containing type bool.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.UnknownVal(cty.Set(cty.Bool)),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "set includes values derived from resource attributes that cannot be determined until apply",
|
|
CausedByUnknown: true,
|
|
CausedBySensitive: false,
|
|
},
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "set containing type bool.",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"unknown_set_of_dynamic": {
|
|
Input: cty.UnknownVal(cty.Set(cty.DynamicPseudoType)),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.UnknownVal(cty.Set(cty.DynamicPseudoType)),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "set includes values derived from resource attributes that cannot be determined until apply",
|
|
CausedByUnknown: true,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"unknown_empty_map": {
|
|
Input: cty.UnknownVal(cty.Map(cty.String)),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.UnknownVal(cty.Map(cty.String)),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "map includes keys derived from resource attributes that cannot be determined until apply",
|
|
CausedByUnknown: true,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"unknown_map_of_strings": {
|
|
Input: cty.UnknownVal(cty.Map(cty.String)),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.UnknownVal(cty.Map(cty.String)),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "map includes keys derived from resource attributes that cannot be determined until apply",
|
|
CausedByUnknown: true,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"unknown_of_unknowns": {
|
|
Input: cty.DynamicVal,
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.DynamicVal,
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "value includes keys or set values from resource attributes that cannot be determined until apply",
|
|
CausedByUnknown: true,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"unknown_map_of_bool": {
|
|
Input: cty.UnknownVal(cty.Map(cty.Bool)),
|
|
ValidateExpectedErrs: nil,
|
|
ValidateReturnValue: cty.UnknownVal(cty.Map(cty.Bool)),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "map includes keys derived from resource attributes that cannot be determined until apply",
|
|
CausedByUnknown: true,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"unknown_tuple_of_strings": {
|
|
Input: cty.UnknownVal(cty.Tuple([]cty.Type{cty.String})),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "argument must be a map, or set of strings, and you have provided a value of type tuple",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.Tuple([]cty.Type{cty.String})),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "value includes keys or set values from resource attributes that cannot be determined until apply",
|
|
CausedByUnknown: true,
|
|
CausedBySensitive: false,
|
|
},
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "argument must be a map, or set of strings, and you have provided a value of type tuple",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"unknown_tuple_of_dynamic": {
|
|
Input: cty.UnknownVal(cty.Tuple([]cty.Type{cty.DynamicPseudoType})),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "argument must be a map, or set of strings, and you have provided a value of type tuple",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.Tuple([]cty.Type{cty.DynamicPseudoType})),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "value includes keys or set values from resource attributes that cannot be determined until apply",
|
|
CausedByUnknown: true,
|
|
CausedBySensitive: false,
|
|
},
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "argument must be a map, or set of strings, and you have provided a value of type tuple",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
"unknown_tuple_of_bool": {
|
|
Input: cty.UnknownVal(cty.Tuple([]cty.Type{cty.Bool})),
|
|
ValidateExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "argument must be a map, or set of strings, and you have provided a value of type tuple",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
ValidateReturnValue: cty.NullVal(cty.Tuple([]cty.Type{cty.Bool})),
|
|
PlanExpectedErrs: []expectedErr{
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "value includes keys or set values from resource attributes that cannot be determined until apply",
|
|
CausedByUnknown: true,
|
|
CausedBySensitive: false,
|
|
},
|
|
{
|
|
Summary: "Invalid for_each argument",
|
|
Detail: "argument must be a map, or set of strings, and you have provided a value of type tuple",
|
|
CausedByUnknown: false,
|
|
CausedBySensitive: false,
|
|
},
|
|
},
|
|
PlanReturnValue: map[string]cty.Value{},
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
// Validate Phase
|
|
allowUnknown := true
|
|
allowTuple := false
|
|
expr := hcltest.MockExprLiteral(test.Input)
|
|
validateReturn, validateDiags := EvaluateForEachExpressionValue(expr, mockRefsFunc(), allowUnknown, allowTuple, nil)
|
|
|
|
if !validateReturn.RawEquals(test.ValidateReturnValue) {
|
|
t.Errorf("got %#v in validate phase; want %#v", validateReturn, test.ValidateReturnValue)
|
|
}
|
|
|
|
if test.ValidateExpectedErrs != nil || len(validateDiags) > 0 {
|
|
assertDiagnosticsMatch(t, validateDiags, test.ValidateExpectedErrs, "validate")
|
|
}
|
|
|
|
// Plan Phase
|
|
planReturn, planDiags := EvaluateForEachExpression(expr, mockRefsFunc(), nil)
|
|
|
|
if !reflect.DeepEqual(planReturn, test.PlanReturnValue) {
|
|
t.Errorf("got %#v in plan phase; want %#v", planReturn, test.PlanReturnValue)
|
|
}
|
|
|
|
if test.PlanExpectedErrs != nil || len(planDiags) > 0 {
|
|
assertDiagnosticsMatch(t, planDiags, test.PlanExpectedErrs, "plan")
|
|
}
|
|
})
|
|
}
|
|
}
|