mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-28 18:01:01 -06:00
d512584497
The omitUnknowns and unknownAsBool functions were previously trying hard to preserve the same collection types in the output as they had in the input, by attempting to keep everything matched up so that the results would be valid. Unfortunately, this turns out to be a harder problem than we originally thought: it was possible for a collection value going in to produce inconsistent element types out (and thus a panic) in the following situations: - when a collection with mixed known and unknown values was passed in to omitUnknowns. - when a collection of collections where the inner collections are a mixture of empty and not empty in unknownAsNull. The results of these functions are only used to marshal to JSON anyway, and JSON serialization can't distinguish between the three sequence types or the two mapping types, so in practice we can just standardize on converting all sequences to tuple and all mappings to object here and not change the resulting output at all, and then we don't have to worry about making sure all of the inner types get preserved exactly. A nice consequence of that relaxation is that we can now do what we originally wanted to do with unknownAsBool, and omit map keys and object attributes altogether if their values would've been false, producing a much more compact result. This is easiest to do now when there's only one known user of this JSON plan output, and we know that user will treat both false and omitted as the same here.
265 lines
5.4 KiB
Go
265 lines
5.4 KiB
Go
package jsonplan
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
func TestOmitUnknowns(t *testing.T) {
|
|
tests := []struct {
|
|
Input cty.Value
|
|
Want cty.Value
|
|
}{
|
|
{
|
|
cty.StringVal("hello"),
|
|
cty.StringVal("hello"),
|
|
},
|
|
{
|
|
cty.NullVal(cty.String),
|
|
cty.NullVal(cty.String),
|
|
},
|
|
{
|
|
cty.UnknownVal(cty.String),
|
|
cty.NilVal,
|
|
},
|
|
{
|
|
cty.ListValEmpty(cty.String),
|
|
cty.EmptyTupleVal,
|
|
},
|
|
{
|
|
cty.ListVal([]cty.Value{cty.StringVal("hello")}),
|
|
cty.TupleVal([]cty.Value{cty.StringVal("hello")}),
|
|
},
|
|
{
|
|
cty.ListVal([]cty.Value{cty.NullVal(cty.String)}),
|
|
cty.TupleVal([]cty.Value{cty.NullVal(cty.String)}),
|
|
},
|
|
{
|
|
cty.ListVal([]cty.Value{cty.UnknownVal(cty.String)}),
|
|
cty.TupleVal([]cty.Value{cty.NullVal(cty.String)}),
|
|
},
|
|
{
|
|
cty.ListVal([]cty.Value{cty.StringVal("hello")}),
|
|
cty.TupleVal([]cty.Value{cty.StringVal("hello")}),
|
|
},
|
|
//
|
|
{
|
|
cty.ListVal([]cty.Value{
|
|
cty.StringVal("hello"),
|
|
cty.UnknownVal(cty.String)}),
|
|
cty.TupleVal([]cty.Value{
|
|
cty.StringVal("hello"),
|
|
cty.NullVal(cty.String),
|
|
}),
|
|
},
|
|
{
|
|
cty.MapVal(map[string]cty.Value{
|
|
"hello": cty.True,
|
|
"world": cty.UnknownVal(cty.Bool),
|
|
}),
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"hello": cty.True,
|
|
}),
|
|
},
|
|
{
|
|
cty.SetVal([]cty.Value{
|
|
cty.StringVal("dev"),
|
|
cty.StringVal("foo"),
|
|
cty.StringVal("stg"),
|
|
cty.UnknownVal(cty.String),
|
|
}),
|
|
cty.TupleVal([]cty.Value{
|
|
cty.StringVal("dev"),
|
|
cty.StringVal("foo"),
|
|
cty.StringVal("stg"),
|
|
}),
|
|
},
|
|
{
|
|
cty.SetVal([]cty.Value{
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"a": cty.UnknownVal(cty.String),
|
|
}),
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"a": cty.StringVal("known"),
|
|
}),
|
|
}),
|
|
cty.TupleVal([]cty.Value{
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"a": cty.StringVal("known"),
|
|
}),
|
|
cty.EmptyObjectVal,
|
|
}),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
got := omitUnknowns(test.Input)
|
|
if !reflect.DeepEqual(got, test.Want) {
|
|
t.Errorf(
|
|
"wrong result\ninput: %#v\ngot: %#v\nwant: %#v",
|
|
test.Input, got, test.Want,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestUnknownAsBool(t *testing.T) {
|
|
tests := []struct {
|
|
Input cty.Value
|
|
Want cty.Value
|
|
}{
|
|
{
|
|
cty.StringVal("hello"),
|
|
cty.False,
|
|
},
|
|
{
|
|
cty.NullVal(cty.String),
|
|
cty.False,
|
|
},
|
|
{
|
|
cty.UnknownVal(cty.String),
|
|
cty.True,
|
|
},
|
|
|
|
{
|
|
cty.NullVal(cty.DynamicPseudoType),
|
|
cty.False,
|
|
},
|
|
{
|
|
cty.NullVal(cty.Object(map[string]cty.Type{"test": cty.String})),
|
|
cty.False,
|
|
},
|
|
{
|
|
cty.DynamicVal,
|
|
cty.True,
|
|
},
|
|
|
|
{
|
|
cty.ListValEmpty(cty.String),
|
|
cty.EmptyTupleVal,
|
|
},
|
|
{
|
|
cty.ListVal([]cty.Value{cty.StringVal("hello")}),
|
|
cty.TupleVal([]cty.Value{cty.False}),
|
|
},
|
|
{
|
|
cty.ListVal([]cty.Value{cty.NullVal(cty.String)}),
|
|
cty.TupleVal([]cty.Value{cty.False}),
|
|
},
|
|
{
|
|
cty.ListVal([]cty.Value{cty.UnknownVal(cty.String)}),
|
|
cty.TupleVal([]cty.Value{cty.True}),
|
|
},
|
|
{
|
|
cty.SetValEmpty(cty.String),
|
|
cty.EmptyTupleVal,
|
|
},
|
|
{
|
|
cty.SetVal([]cty.Value{cty.StringVal("hello")}),
|
|
cty.TupleVal([]cty.Value{cty.False}),
|
|
},
|
|
{
|
|
cty.SetVal([]cty.Value{cty.NullVal(cty.String)}),
|
|
cty.TupleVal([]cty.Value{cty.False}),
|
|
},
|
|
{
|
|
cty.SetVal([]cty.Value{cty.UnknownVal(cty.String)}),
|
|
cty.TupleVal([]cty.Value{cty.True}),
|
|
},
|
|
{
|
|
cty.EmptyTupleVal,
|
|
cty.EmptyTupleVal,
|
|
},
|
|
{
|
|
cty.TupleVal([]cty.Value{cty.StringVal("hello")}),
|
|
cty.TupleVal([]cty.Value{cty.False}),
|
|
},
|
|
{
|
|
cty.TupleVal([]cty.Value{cty.NullVal(cty.String)}),
|
|
cty.TupleVal([]cty.Value{cty.False}),
|
|
},
|
|
{
|
|
cty.TupleVal([]cty.Value{cty.UnknownVal(cty.String)}),
|
|
cty.TupleVal([]cty.Value{cty.True}),
|
|
},
|
|
{
|
|
cty.MapValEmpty(cty.String),
|
|
cty.EmptyObjectVal,
|
|
},
|
|
{
|
|
cty.MapVal(map[string]cty.Value{"greeting": cty.StringVal("hello")}),
|
|
cty.EmptyObjectVal,
|
|
},
|
|
{
|
|
cty.MapVal(map[string]cty.Value{"greeting": cty.NullVal(cty.String)}),
|
|
cty.EmptyObjectVal,
|
|
},
|
|
{
|
|
cty.MapVal(map[string]cty.Value{"greeting": cty.UnknownVal(cty.String)}),
|
|
cty.ObjectVal(map[string]cty.Value{"greeting": cty.True}),
|
|
},
|
|
{
|
|
cty.EmptyObjectVal,
|
|
cty.EmptyObjectVal,
|
|
},
|
|
{
|
|
cty.ObjectVal(map[string]cty.Value{"greeting": cty.StringVal("hello")}),
|
|
cty.EmptyObjectVal,
|
|
},
|
|
{
|
|
cty.ObjectVal(map[string]cty.Value{"greeting": cty.NullVal(cty.String)}),
|
|
cty.EmptyObjectVal,
|
|
},
|
|
{
|
|
cty.ObjectVal(map[string]cty.Value{"greeting": cty.UnknownVal(cty.String)}),
|
|
cty.ObjectVal(map[string]cty.Value{"greeting": cty.True}),
|
|
},
|
|
{
|
|
cty.SetVal([]cty.Value{
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"a": cty.UnknownVal(cty.String),
|
|
}),
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"a": cty.StringVal("known"),
|
|
}),
|
|
}),
|
|
cty.TupleVal([]cty.Value{
|
|
cty.EmptyObjectVal,
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"a": cty.True,
|
|
}),
|
|
}),
|
|
},
|
|
{
|
|
cty.SetVal([]cty.Value{
|
|
cty.MapValEmpty(cty.String),
|
|
cty.MapVal(map[string]cty.Value{
|
|
"a": cty.StringVal("known"),
|
|
}),
|
|
cty.MapVal(map[string]cty.Value{
|
|
"a": cty.UnknownVal(cty.String),
|
|
}),
|
|
}),
|
|
cty.TupleVal([]cty.Value{
|
|
cty.EmptyObjectVal,
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"a": cty.True,
|
|
}),
|
|
cty.EmptyObjectVal,
|
|
}),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
got := unknownAsBool(test.Input)
|
|
if !reflect.DeepEqual(got, test.Want) {
|
|
t.Errorf(
|
|
"wrong result\ninput: %#v\ngot: %#v\nwant: %#v",
|
|
test.Input, got, test.Want,
|
|
)
|
|
}
|
|
}
|
|
}
|