| package funcs |
| |
| import ( |
| "fmt" |
| "math" |
| "testing" |
| |
| "github.com/hashicorp/terraform/internal/lang/marks" |
| "github.com/zclconf/go-cty/cty" |
| ) |
| |
| func TestLength(t *testing.T) { |
| tests := []struct { |
| Value cty.Value |
| Want cty.Value |
| }{ |
| { |
| cty.ListValEmpty(cty.Number), |
| cty.NumberIntVal(0), |
| }, |
| { |
| cty.ListVal([]cty.Value{cty.True}), |
| cty.NumberIntVal(1), |
| }, |
| { |
| cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}), |
| cty.NumberIntVal(1), |
| }, |
| { |
| cty.SetValEmpty(cty.Number), |
| cty.NumberIntVal(0), |
| }, |
| { |
| cty.SetVal([]cty.Value{cty.True}), |
| cty.NumberIntVal(1), |
| }, |
| { |
| cty.MapValEmpty(cty.Bool), |
| cty.NumberIntVal(0), |
| }, |
| { |
| cty.MapVal(map[string]cty.Value{"hello": cty.True}), |
| cty.NumberIntVal(1), |
| }, |
| { |
| cty.EmptyTupleVal, |
| cty.NumberIntVal(0), |
| }, |
| { |
| cty.UnknownVal(cty.EmptyTuple), |
| cty.NumberIntVal(0), |
| }, |
| { |
| cty.TupleVal([]cty.Value{cty.True}), |
| cty.NumberIntVal(1), |
| }, |
| { |
| cty.EmptyObjectVal, |
| cty.NumberIntVal(0), |
| }, |
| { |
| cty.UnknownVal(cty.EmptyObject), |
| cty.NumberIntVal(0), |
| }, |
| { |
| cty.ObjectVal(map[string]cty.Value{"true": cty.True}), |
| cty.NumberIntVal(1), |
| }, |
| { |
| cty.UnknownVal(cty.List(cty.Bool)), |
| cty.UnknownVal(cty.Number), |
| }, |
| { |
| cty.DynamicVal, |
| cty.UnknownVal(cty.Number), |
| }, |
| { |
| cty.StringVal("hello"), |
| cty.NumberIntVal(5), |
| }, |
| { |
| cty.StringVal(""), |
| cty.NumberIntVal(0), |
| }, |
| { |
| cty.StringVal("1"), |
| cty.NumberIntVal(1), |
| }, |
| { |
| cty.StringVal("Живой Журнал"), |
| cty.NumberIntVal(12), |
| }, |
| { |
| // note that the dieresis here is intentionally a combining |
| // ligature. |
| cty.StringVal("noël"), |
| cty.NumberIntVal(4), |
| }, |
| { |
| // The Es in this string has three combining acute accents. |
| // This tests something that NFC-normalization cannot collapse |
| // into a single precombined codepoint, since otherwise we might |
| // be cheating and relying on the single-codepoint forms. |
| cty.StringVal("wé́́é́́é́́!"), |
| cty.NumberIntVal(5), |
| }, |
| { |
| // Go's normalization forms don't handle this ligature, so we |
| // will produce the wrong result but this is now a compatibility |
| // constraint and so we'll test it. |
| cty.StringVal("baffle"), |
| cty.NumberIntVal(4), |
| }, |
| { |
| cty.StringVal("😸😾"), |
| cty.NumberIntVal(2), |
| }, |
| { |
| cty.UnknownVal(cty.String), |
| cty.UnknownVal(cty.Number), |
| }, |
| { |
| cty.DynamicVal, |
| cty.UnknownVal(cty.Number), |
| }, |
| { // Marked collections return a marked length |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("hello"), |
| cty.StringVal("world"), |
| }).Mark("secret"), |
| cty.NumberIntVal(2).Mark("secret"), |
| }, |
| { // Marks on values in unmarked collections do not propagate |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("hello").Mark("a"), |
| cty.StringVal("world").Mark("b"), |
| }), |
| cty.NumberIntVal(2), |
| }, |
| { // Marked strings return a marked length |
| cty.StringVal("hello world").Mark("secret"), |
| cty.NumberIntVal(11).Mark("secret"), |
| }, |
| { // Marked tuples return a marked length |
| cty.TupleVal([]cty.Value{ |
| cty.StringVal("hello"), |
| cty.StringVal("world"), |
| }).Mark("secret"), |
| cty.NumberIntVal(2).Mark("secret"), |
| }, |
| { // Marks on values in unmarked tuples do not propagate |
| cty.TupleVal([]cty.Value{ |
| cty.StringVal("hello").Mark("a"), |
| cty.StringVal("world").Mark("b"), |
| }), |
| cty.NumberIntVal(2), |
| }, |
| { // Marked objects return a marked length |
| cty.ObjectVal(map[string]cty.Value{ |
| "a": cty.StringVal("hello"), |
| "b": cty.StringVal("world"), |
| "c": cty.StringVal("nice to meet you"), |
| }).Mark("secret"), |
| cty.NumberIntVal(3).Mark("secret"), |
| }, |
| { // Marks on object attribute values do not propagate |
| cty.ObjectVal(map[string]cty.Value{ |
| "a": cty.StringVal("hello").Mark("a"), |
| "b": cty.StringVal("world").Mark("b"), |
| "c": cty.StringVal("nice to meet you").Mark("c"), |
| }), |
| cty.NumberIntVal(3), |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(fmt.Sprintf("Length(%#v)", test.Value), func(t *testing.T) { |
| got, err := Length(test.Value) |
| |
| if err != nil { |
| t.Fatalf("unexpected error: %s", err) |
| } |
| |
| if !got.RawEquals(test.Want) { |
| t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) |
| } |
| }) |
| } |
| } |
| |
| func TestAllTrue(t *testing.T) { |
| tests := []struct { |
| Collection cty.Value |
| Want cty.Value |
| Err bool |
| }{ |
| { |
| cty.ListValEmpty(cty.Bool), |
| cty.True, |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{cty.True}), |
| cty.True, |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{cty.False}), |
| cty.False, |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{cty.True, cty.False}), |
| cty.False, |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{cty.False, cty.True}), |
| cty.False, |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{cty.True, cty.NullVal(cty.Bool)}), |
| cty.False, |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}), |
| cty.UnknownVal(cty.Bool), |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.UnknownVal(cty.Bool), |
| cty.UnknownVal(cty.Bool), |
| }), |
| cty.UnknownVal(cty.Bool), |
| false, |
| }, |
| { |
| cty.UnknownVal(cty.List(cty.Bool)), |
| cty.UnknownVal(cty.Bool), |
| false, |
| }, |
| { |
| cty.NullVal(cty.List(cty.Bool)), |
| cty.NilVal, |
| true, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(fmt.Sprintf("alltrue(%#v)", test.Collection), func(t *testing.T) { |
| got, err := AllTrue(test.Collection) |
| |
| if test.Err { |
| if err == nil { |
| t.Fatal("succeeded; want error") |
| } |
| return |
| } else if err != nil { |
| t.Fatalf("unexpected error: %s", err) |
| } |
| |
| if !got.RawEquals(test.Want) { |
| t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) |
| } |
| }) |
| } |
| } |
| |
| func TestAnyTrue(t *testing.T) { |
| tests := []struct { |
| Collection cty.Value |
| Want cty.Value |
| Err bool |
| }{ |
| { |
| cty.ListValEmpty(cty.Bool), |
| cty.False, |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{cty.True}), |
| cty.True, |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{cty.False}), |
| cty.False, |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{cty.True, cty.False}), |
| cty.True, |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{cty.False, cty.True}), |
| cty.True, |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{cty.NullVal(cty.Bool), cty.True}), |
| cty.True, |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}), |
| cty.UnknownVal(cty.Bool), |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.UnknownVal(cty.Bool), |
| cty.False, |
| }), |
| cty.UnknownVal(cty.Bool), |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.UnknownVal(cty.Bool), |
| cty.True, |
| }), |
| cty.True, |
| false, |
| }, |
| { |
| cty.UnknownVal(cty.List(cty.Bool)), |
| cty.UnknownVal(cty.Bool), |
| false, |
| }, |
| { |
| cty.NullVal(cty.List(cty.Bool)), |
| cty.NilVal, |
| true, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(fmt.Sprintf("anytrue(%#v)", test.Collection), func(t *testing.T) { |
| got, err := AnyTrue(test.Collection) |
| |
| if test.Err { |
| if err == nil { |
| t.Fatal("succeeded; want error") |
| } |
| return |
| } else if err != nil { |
| t.Fatalf("unexpected error: %s", err) |
| } |
| |
| if !got.RawEquals(test.Want) { |
| t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) |
| } |
| }) |
| } |
| } |
| |
| func TestCoalesce(t *testing.T) { |
| tests := []struct { |
| Values []cty.Value |
| Want cty.Value |
| Err bool |
| }{ |
| { |
| []cty.Value{cty.StringVal("first"), cty.StringVal("second"), cty.StringVal("third")}, |
| cty.StringVal("first"), |
| false, |
| }, |
| { |
| []cty.Value{cty.StringVal(""), cty.StringVal("second"), cty.StringVal("third")}, |
| cty.StringVal("second"), |
| false, |
| }, |
| { |
| []cty.Value{cty.StringVal(""), cty.StringVal("")}, |
| cty.NilVal, |
| true, |
| }, |
| { |
| []cty.Value{cty.True}, |
| cty.True, |
| false, |
| }, |
| { |
| []cty.Value{cty.NullVal(cty.Bool), cty.True}, |
| cty.True, |
| false, |
| }, |
| { |
| []cty.Value{cty.NullVal(cty.Bool), cty.False}, |
| cty.False, |
| false, |
| }, |
| { |
| []cty.Value{cty.NullVal(cty.Bool), cty.False, cty.StringVal("hello")}, |
| cty.StringVal("false"), |
| false, |
| }, |
| { |
| []cty.Value{cty.True, cty.UnknownVal(cty.Bool)}, |
| cty.True, |
| false, |
| }, |
| { |
| []cty.Value{cty.UnknownVal(cty.Bool), cty.True}, |
| cty.UnknownVal(cty.Bool), |
| false, |
| }, |
| { |
| []cty.Value{cty.UnknownVal(cty.Bool), cty.StringVal("hello")}, |
| cty.UnknownVal(cty.String), |
| false, |
| }, |
| { |
| []cty.Value{cty.DynamicVal, cty.True}, |
| cty.UnknownVal(cty.Bool), |
| false, |
| }, |
| { |
| []cty.Value{cty.DynamicVal}, |
| cty.DynamicVal, |
| false, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(fmt.Sprintf("Coalesce(%#v...)", test.Values), func(t *testing.T) { |
| got, err := Coalesce(test.Values...) |
| |
| if test.Err { |
| if err == nil { |
| t.Fatal("succeeded; want error") |
| } |
| return |
| } else if err != nil { |
| t.Fatalf("unexpected error: %s", err) |
| } |
| |
| if !got.RawEquals(test.Want) { |
| t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) |
| } |
| }) |
| } |
| } |
| |
| func TestIndex(t *testing.T) { |
| tests := []struct { |
| List cty.Value |
| Value cty.Value |
| Want cty.Value |
| Err bool |
| }{ |
| { |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| cty.StringVal("c"), |
| }), |
| cty.StringVal("a"), |
| cty.NumberIntVal(0), |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| cty.UnknownVal(cty.String), |
| }), |
| cty.StringVal("a"), |
| cty.NumberIntVal(0), |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| cty.StringVal("c"), |
| }), |
| cty.StringVal("b"), |
| cty.NumberIntVal(1), |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| cty.StringVal("c"), |
| }), |
| cty.StringVal("z"), |
| cty.NilVal, |
| true, |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("1"), |
| cty.StringVal("2"), |
| cty.StringVal("3"), |
| }), |
| cty.NumberIntVal(1), |
| cty.NumberIntVal(0), |
| true, |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.NumberIntVal(1), |
| cty.NumberIntVal(2), |
| cty.NumberIntVal(3), |
| }), |
| cty.NumberIntVal(2), |
| cty.NumberIntVal(1), |
| false, |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.NumberIntVal(1), |
| cty.NumberIntVal(2), |
| cty.NumberIntVal(3), |
| }), |
| cty.NumberIntVal(4), |
| cty.NilVal, |
| true, |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.NumberIntVal(1), |
| cty.NumberIntVal(2), |
| cty.NumberIntVal(3), |
| }), |
| cty.StringVal("1"), |
| cty.NumberIntVal(0), |
| true, |
| }, |
| { |
| cty.TupleVal([]cty.Value{ |
| cty.NumberIntVal(1), |
| cty.NumberIntVal(2), |
| cty.NumberIntVal(3), |
| }), |
| cty.NumberIntVal(1), |
| cty.NumberIntVal(0), |
| false, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(fmt.Sprintf("index(%#v, %#v)", test.List, test.Value), func(t *testing.T) { |
| got, err := Index(test.List, test.Value) |
| |
| if test.Err { |
| if err == nil { |
| t.Fatal("succeeded; want error") |
| } |
| return |
| } else if err != nil { |
| t.Fatalf("unexpected error: %s", err) |
| } |
| |
| if !got.RawEquals(test.Want) { |
| t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) |
| } |
| }) |
| } |
| } |
| |
| func TestLookup(t *testing.T) { |
| simpleMap := cty.MapVal(map[string]cty.Value{ |
| "foo": cty.StringVal("bar"), |
| }) |
| intsMap := cty.MapVal(map[string]cty.Value{ |
| "foo": cty.NumberIntVal(42), |
| }) |
| mapOfLists := cty.MapVal(map[string]cty.Value{ |
| "foo": cty.ListVal([]cty.Value{ |
| cty.StringVal("bar"), |
| cty.StringVal("baz"), |
| }), |
| }) |
| mapOfMaps := cty.MapVal(map[string]cty.Value{ |
| "foo": cty.MapVal(map[string]cty.Value{ |
| "a": cty.StringVal("bar"), |
| }), |
| "baz": cty.MapVal(map[string]cty.Value{ |
| "b": cty.StringVal("bat"), |
| }), |
| }) |
| mapOfTuples := cty.MapVal(map[string]cty.Value{ |
| "foo": cty.TupleVal([]cty.Value{cty.StringVal("bar")}), |
| "baz": cty.TupleVal([]cty.Value{cty.StringVal("bat")}), |
| }) |
| objectOfMaps := cty.ObjectVal(map[string]cty.Value{ |
| "foo": cty.MapVal(map[string]cty.Value{ |
| "a": cty.StringVal("bar"), |
| }), |
| "baz": cty.MapVal(map[string]cty.Value{ |
| "b": cty.StringVal("bat"), |
| }), |
| }) |
| mapWithUnknowns := cty.MapVal(map[string]cty.Value{ |
| "foo": cty.StringVal("bar"), |
| "baz": cty.UnknownVal(cty.String), |
| }) |
| mapWithObjects := cty.ObjectVal(map[string]cty.Value{ |
| "foo": cty.StringVal("bar"), |
| "baz": cty.NumberIntVal(42), |
| }) |
| |
| tests := []struct { |
| Values []cty.Value |
| Want cty.Value |
| Err bool |
| }{ |
| { |
| []cty.Value{ |
| simpleMap, |
| cty.StringVal("foo"), |
| }, |
| cty.StringVal("bar"), |
| false, |
| }, |
| { |
| []cty.Value{ |
| mapWithObjects, |
| cty.StringVal("foo"), |
| }, |
| cty.StringVal("bar"), |
| false, |
| }, |
| { |
| []cty.Value{ |
| intsMap, |
| cty.StringVal("foo"), |
| }, |
| cty.NumberIntVal(42), |
| false, |
| }, |
| { |
| []cty.Value{ |
| mapOfMaps, |
| cty.StringVal("foo"), |
| }, |
| cty.MapVal(map[string]cty.Value{ |
| "a": cty.StringVal("bar"), |
| }), |
| false, |
| }, |
| { |
| []cty.Value{ |
| objectOfMaps, |
| cty.StringVal("foo"), |
| }, |
| cty.MapVal(map[string]cty.Value{ |
| "a": cty.StringVal("bar"), |
| }), |
| false, |
| }, |
| { |
| []cty.Value{ |
| mapOfTuples, |
| cty.StringVal("foo"), |
| }, |
| cty.TupleVal([]cty.Value{cty.StringVal("bar")}), |
| false, |
| }, |
| { // Invalid key |
| []cty.Value{ |
| simpleMap, |
| cty.StringVal("bar"), |
| }, |
| cty.NilVal, |
| true, |
| }, |
| { // Invalid key |
| []cty.Value{ |
| mapWithObjects, |
| cty.StringVal("bar"), |
| }, |
| cty.NilVal, |
| true, |
| }, |
| { // Supplied default with valid key |
| []cty.Value{ |
| simpleMap, |
| cty.StringVal("foo"), |
| cty.StringVal(""), |
| }, |
| cty.StringVal("bar"), |
| false, |
| }, |
| { // Supplied default with valid (int) key |
| []cty.Value{ |
| simpleMap, |
| cty.StringVal("foo"), |
| cty.NumberIntVal(-1), |
| }, |
| cty.StringVal("bar"), |
| false, |
| }, |
| { // Supplied default with valid (int) key |
| []cty.Value{ |
| simpleMap, |
| cty.StringVal("foobar"), |
| cty.NumberIntVal(-1), |
| }, |
| cty.StringVal("-1"), |
| false, |
| }, |
| { // Supplied default with valid key |
| []cty.Value{ |
| mapWithObjects, |
| cty.StringVal("foobar"), |
| cty.StringVal(""), |
| }, |
| cty.StringVal(""), |
| false, |
| }, |
| { // Supplied default with invalid key |
| []cty.Value{ |
| simpleMap, |
| cty.StringVal("baz"), |
| cty.StringVal(""), |
| }, |
| cty.StringVal(""), |
| false, |
| }, |
| { // Supplied default with type mismatch: expects a map return |
| []cty.Value{ |
| mapOfMaps, |
| cty.StringVal("foo"), |
| cty.StringVal(""), |
| }, |
| cty.NilVal, |
| true, |
| }, |
| { // Supplied non-empty default with invalid key |
| []cty.Value{ |
| simpleMap, |
| cty.StringVal("bar"), |
| cty.StringVal("xyz"), |
| }, |
| cty.StringVal("xyz"), |
| false, |
| }, |
| { // too many args |
| []cty.Value{ |
| simpleMap, |
| cty.StringVal("foo"), |
| cty.StringVal("bar"), |
| cty.StringVal("baz"), |
| }, |
| cty.NilVal, |
| true, |
| }, |
| { // cannot search a map of lists |
| []cty.Value{ |
| mapOfLists, |
| cty.StringVal("baz"), |
| }, |
| cty.NilVal, |
| true, |
| }, |
| { |
| []cty.Value{ |
| mapWithUnknowns, |
| cty.StringVal("baz"), |
| }, |
| cty.UnknownVal(cty.String), |
| false, |
| }, |
| { |
| []cty.Value{ |
| mapWithUnknowns, |
| cty.StringVal("foo"), |
| }, |
| cty.StringVal("bar"), |
| false, |
| }, |
| { |
| []cty.Value{ |
| simpleMap, |
| cty.UnknownVal(cty.String), |
| }, |
| cty.UnknownVal(cty.String), |
| false, |
| }, |
| { |
| []cty.Value{ |
| cty.ObjectVal(map[string]cty.Value{ |
| "foo": cty.StringVal("a"), |
| "bar": cty.StringVal("b"), |
| }), |
| cty.UnknownVal(cty.String), |
| }, |
| cty.DynamicVal, // if the key is unknown then we don't know which object attribute and thus can't know the type |
| false, |
| }, |
| { // successful marked collection lookup returns marked value |
| []cty.Value{ |
| cty.MapVal(map[string]cty.Value{ |
| "boop": cty.StringVal("beep"), |
| }).Mark("a"), |
| cty.StringVal("boop"), |
| cty.StringVal("nope"), |
| }, |
| cty.StringVal("beep").Mark("a"), |
| false, |
| }, |
| { // apply collection marks to unknown return vaue |
| []cty.Value{ |
| cty.MapVal(map[string]cty.Value{ |
| "boop": cty.StringVal("beep"), |
| "frob": cty.UnknownVal(cty.String), |
| }).Mark("a"), |
| cty.StringVal("frob"), |
| cty.StringVal("nope"), |
| }, |
| cty.UnknownVal(cty.String).Mark("a"), |
| false, |
| }, |
| { // propagate collection marks to default when returning |
| []cty.Value{ |
| cty.MapVal(map[string]cty.Value{ |
| "boop": cty.StringVal("beep"), |
| }).Mark("a"), |
| cty.StringVal("frob"), |
| cty.StringVal("nope").Mark("b"), |
| }, |
| cty.StringVal("nope").WithMarks(cty.NewValueMarks("a", "b")), |
| false, |
| }, |
| { // on unmarked collection, return only marks from found value |
| []cty.Value{ |
| cty.MapVal(map[string]cty.Value{ |
| "boop": cty.StringVal("beep").Mark("a"), |
| "frob": cty.StringVal("honk").Mark("b"), |
| }), |
| cty.StringVal("frob"), |
| cty.StringVal("nope").Mark("c"), |
| }, |
| cty.StringVal("honk").Mark("b"), |
| false, |
| }, |
| { // on unmarked collection, return default exactly on missing |
| []cty.Value{ |
| cty.MapVal(map[string]cty.Value{ |
| "boop": cty.StringVal("beep").Mark("a"), |
| "frob": cty.StringVal("honk").Mark("b"), |
| }), |
| cty.StringVal("squish"), |
| cty.StringVal("nope").Mark("c"), |
| }, |
| cty.StringVal("nope").Mark("c"), |
| false, |
| }, |
| { // retain marks on default if converted |
| []cty.Value{ |
| cty.MapVal(map[string]cty.Value{ |
| "boop": cty.StringVal("beep").Mark("a"), |
| "frob": cty.StringVal("honk").Mark("b"), |
| }), |
| cty.StringVal("squish"), |
| cty.NumberIntVal(5).Mark("c"), |
| }, |
| cty.StringVal("5").Mark("c"), |
| false, |
| }, |
| { // propagate marks from key |
| []cty.Value{ |
| cty.MapVal(map[string]cty.Value{ |
| "boop": cty.StringVal("beep"), |
| "frob": cty.StringVal("honk"), |
| }), |
| cty.StringVal("boop").Mark("a"), |
| cty.StringVal("nope"), |
| }, |
| cty.StringVal("beep").Mark("a"), |
| false, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(fmt.Sprintf("lookup(%#v)", test.Values), func(t *testing.T) { |
| got, err := Lookup(test.Values...) |
| |
| if test.Err { |
| if err == nil { |
| t.Fatal("succeeded; want error") |
| } |
| return |
| } else if err != nil { |
| t.Fatalf("unexpected error: %s", err) |
| } |
| |
| if !got.RawEquals(test.Want) { |
| t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) |
| } |
| }) |
| } |
| } |
| |
| func TestLookup_error(t *testing.T) { |
| simpleMap := cty.MapVal(map[string]cty.Value{ |
| "foo": cty.StringVal("bar"), |
| }) |
| |
| tests := map[string]struct { |
| Values []cty.Value |
| WantErr string |
| }{ |
| "failed to find non-sensitive key": { |
| []cty.Value{ |
| simpleMap, |
| cty.StringVal("boop"), |
| }, |
| `lookup failed to find key "boop"`, |
| }, |
| "failed to find sensitive key": { |
| []cty.Value{ |
| simpleMap, |
| cty.StringVal("boop").Mark(marks.Sensitive), |
| }, |
| "lookup failed to find key (sensitive value)", |
| }, |
| } |
| |
| for name, test := range tests { |
| t.Run(name, func(t *testing.T) { |
| _, err := Lookup(test.Values...) |
| |
| if err == nil { |
| t.Fatal("succeeded; want error") |
| } |
| |
| if err.Error() != test.WantErr { |
| t.Errorf("wrong error\ngot: %#v\nwant: %#v", err, test.WantErr) |
| } |
| }) |
| } |
| } |
| |
| func TestMatchkeys(t *testing.T) { |
| tests := []struct { |
| Keys cty.Value |
| Values cty.Value |
| Searchset cty.Value |
| Want cty.Value |
| Err bool |
| }{ |
| { // normal usage |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| cty.StringVal("c"), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("ref1"), |
| cty.StringVal("ref2"), |
| cty.StringVal("ref3"), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("ref1"), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| }), |
| false, |
| }, |
| { // normal usage 2, check the order |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| cty.StringVal("c"), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("ref1"), |
| cty.StringVal("ref2"), |
| cty.StringVal("ref3"), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("ref2"), |
| cty.StringVal("ref1"), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| }), |
| false, |
| }, |
| { // no matches |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| cty.StringVal("c"), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("ref1"), |
| cty.StringVal("ref2"), |
| cty.StringVal("ref3"), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("ref4"), |
| }), |
| cty.ListValEmpty(cty.String), |
| false, |
| }, |
| { // no matches 2 |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| cty.StringVal("c"), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("ref1"), |
| cty.StringVal("ref2"), |
| cty.StringVal("ref3"), |
| }), |
| cty.ListValEmpty(cty.String), |
| cty.ListValEmpty(cty.String), |
| false, |
| }, |
| { // zero case |
| cty.ListValEmpty(cty.String), |
| cty.ListValEmpty(cty.String), |
| cty.ListVal([]cty.Value{cty.StringVal("nope")}), |
| cty.ListValEmpty(cty.String), |
| false, |
| }, |
| { // complex values |
| cty.ListVal([]cty.Value{ |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("a"), |
| }), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("a"), |
| }), |
| }), |
| false, |
| }, |
| { // unknowns |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| cty.UnknownVal(cty.String), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("ref1"), |
| cty.StringVal("ref2"), |
| cty.UnknownVal(cty.String), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("ref1"), |
| }), |
| cty.UnknownVal(cty.List(cty.String)), |
| false, |
| }, |
| { // different types that can be unified |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.NumberIntVal(1), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| }), |
| cty.ListValEmpty(cty.String), |
| false, |
| }, |
| { // complex values: values is a different type from keys and searchset |
| cty.ListVal([]cty.Value{ |
| cty.MapVal(map[string]cty.Value{ |
| "foo": cty.StringVal("bar"), |
| }), |
| cty.MapVal(map[string]cty.Value{ |
| "foo": cty.StringVal("baz"), |
| }), |
| cty.MapVal(map[string]cty.Value{ |
| "foo": cty.StringVal("beep"), |
| }), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| cty.StringVal("c"), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("c"), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.MapVal(map[string]cty.Value{ |
| "foo": cty.StringVal("bar"), |
| }), |
| cty.MapVal(map[string]cty.Value{ |
| "foo": cty.StringVal("beep"), |
| }), |
| }), |
| false, |
| }, |
| // errors |
| { // different types |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| }), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| }), |
| cty.NilVal, |
| true, |
| }, |
| { // lists of different length |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| }), |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| }), |
| cty.NilVal, |
| true, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(fmt.Sprintf("matchkeys(%#v, %#v, %#v)", test.Keys, test.Values, test.Searchset), func(t *testing.T) { |
| got, err := Matchkeys(test.Keys, test.Values, test.Searchset) |
| |
| if test.Err { |
| if err == nil { |
| t.Fatal("succeeded; want error") |
| } |
| return |
| } else if err != nil { |
| t.Fatalf("unexpected error: %s", err) |
| } |
| |
| if !got.RawEquals(test.Want) { |
| t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) |
| } |
| }) |
| } |
| } |
| |
| func TestOne(t *testing.T) { |
| tests := []struct { |
| List cty.Value |
| Want cty.Value |
| Err string |
| }{ |
| { |
| cty.ListVal([]cty.Value{ |
| cty.NumberIntVal(1), |
| }), |
| cty.NumberIntVal(1), |
| "", |
| }, |
| { |
| cty.ListValEmpty(cty.Number), |
| cty.NullVal(cty.Number), |
| "", |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.NumberIntVal(1), |
| cty.NumberIntVal(2), |
| cty.NumberIntVal(3), |
| }), |
| cty.NilVal, |
| "must be a list, set, or tuple value with either zero or one elements", |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.UnknownVal(cty.Number), |
| }), |
| cty.UnknownVal(cty.Number), |
| "", |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.UnknownVal(cty.Number), |
| cty.UnknownVal(cty.Number), |
| }), |
| cty.NilVal, |
| "must be a list, set, or tuple value with either zero or one elements", |
| }, |
| { |
| cty.UnknownVal(cty.List(cty.String)), |
| cty.UnknownVal(cty.String), |
| "", |
| }, |
| { |
| cty.NullVal(cty.List(cty.String)), |
| cty.NilVal, |
| "argument must not be null", |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.NumberIntVal(1), |
| }).Mark("boop"), |
| cty.NumberIntVal(1).Mark("boop"), |
| "", |
| }, |
| { |
| cty.ListValEmpty(cty.Bool).Mark("boop"), |
| cty.NullVal(cty.Bool).Mark("boop"), |
| "", |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.NumberIntVal(1).Mark("boop"), |
| }), |
| cty.NumberIntVal(1).Mark("boop"), |
| "", |
| }, |
| |
| { |
| cty.SetVal([]cty.Value{ |
| cty.NumberIntVal(1), |
| }), |
| cty.NumberIntVal(1), |
| "", |
| }, |
| { |
| cty.SetValEmpty(cty.Number), |
| cty.NullVal(cty.Number), |
| "", |
| }, |
| { |
| cty.SetVal([]cty.Value{ |
| cty.NumberIntVal(1), |
| cty.NumberIntVal(2), |
| cty.NumberIntVal(3), |
| }), |
| cty.NilVal, |
| "must be a list, set, or tuple value with either zero or one elements", |
| }, |
| { |
| cty.SetVal([]cty.Value{ |
| cty.UnknownVal(cty.Number), |
| }), |
| cty.UnknownVal(cty.Number), |
| "", |
| }, |
| { |
| cty.SetVal([]cty.Value{ |
| cty.UnknownVal(cty.Number), |
| cty.UnknownVal(cty.Number), |
| }), |
| // The above would be valid if those two unknown values were |
| // equal known values, so this returns unknown rather than failing. |
| cty.UnknownVal(cty.Number), |
| "", |
| }, |
| { |
| cty.UnknownVal(cty.Set(cty.String)), |
| cty.UnknownVal(cty.String), |
| "", |
| }, |
| { |
| cty.NullVal(cty.Set(cty.String)), |
| cty.NilVal, |
| "argument must not be null", |
| }, |
| { |
| cty.SetVal([]cty.Value{ |
| cty.NumberIntVal(1), |
| }).Mark("boop"), |
| cty.NumberIntVal(1).Mark("boop"), |
| "", |
| }, |
| { |
| cty.SetValEmpty(cty.Bool).Mark("boop"), |
| cty.NullVal(cty.Bool).Mark("boop"), |
| "", |
| }, |
| { |
| cty.SetVal([]cty.Value{ |
| cty.NumberIntVal(1).Mark("boop"), |
| }), |
| cty.NumberIntVal(1).Mark("boop"), |
| "", |
| }, |
| |
| { |
| cty.TupleVal([]cty.Value{ |
| cty.NumberIntVal(1), |
| }), |
| cty.NumberIntVal(1), |
| "", |
| }, |
| { |
| cty.EmptyTupleVal, |
| cty.NullVal(cty.DynamicPseudoType), |
| "", |
| }, |
| { |
| cty.TupleVal([]cty.Value{ |
| cty.NumberIntVal(1), |
| cty.NumberIntVal(2), |
| cty.NumberIntVal(3), |
| }), |
| cty.NilVal, |
| "must be a list, set, or tuple value with either zero or one elements", |
| }, |
| { |
| cty.TupleVal([]cty.Value{ |
| cty.UnknownVal(cty.Number), |
| }), |
| cty.UnknownVal(cty.Number), |
| "", |
| }, |
| { |
| cty.TupleVal([]cty.Value{ |
| cty.UnknownVal(cty.Number), |
| cty.UnknownVal(cty.Number), |
| }), |
| cty.NilVal, |
| "must be a list, set, or tuple value with either zero or one elements", |
| }, |
| { |
| cty.UnknownVal(cty.EmptyTuple), |
| // Could actually return null here, but don't for consistency with unknown lists |
| cty.UnknownVal(cty.DynamicPseudoType), |
| "", |
| }, |
| { |
| cty.UnknownVal(cty.Tuple([]cty.Type{cty.Bool})), |
| cty.UnknownVal(cty.Bool), |
| "", |
| }, |
| { |
| cty.UnknownVal(cty.Tuple([]cty.Type{cty.Bool, cty.Number})), |
| cty.NilVal, |
| "must be a list, set, or tuple value with either zero or one elements", |
| }, |
| { |
| cty.NullVal(cty.EmptyTuple), |
| cty.NilVal, |
| "argument must not be null", |
| }, |
| { |
| cty.NullVal(cty.Tuple([]cty.Type{cty.Bool})), |
| cty.NilVal, |
| "argument must not be null", |
| }, |
| { |
| cty.NullVal(cty.Tuple([]cty.Type{cty.Bool, cty.Number})), |
| cty.NilVal, |
| "argument must not be null", |
| }, |
| { |
| cty.TupleVal([]cty.Value{ |
| cty.NumberIntVal(1), |
| }).Mark("boop"), |
| cty.NumberIntVal(1).Mark("boop"), |
| "", |
| }, |
| { |
| cty.EmptyTupleVal.Mark("boop"), |
| cty.NullVal(cty.DynamicPseudoType).Mark("boop"), |
| "", |
| }, |
| { |
| cty.TupleVal([]cty.Value{ |
| cty.NumberIntVal(1).Mark("boop"), |
| }), |
| cty.NumberIntVal(1).Mark("boop"), |
| "", |
| }, |
| |
| { |
| cty.DynamicVal, |
| cty.DynamicVal, |
| "", |
| }, |
| { |
| cty.NullVal(cty.DynamicPseudoType), |
| cty.NilVal, |
| "argument must not be null", |
| }, |
| { |
| cty.MapValEmpty(cty.String), |
| cty.NilVal, |
| "must be a list, set, or tuple value with either zero or one elements", |
| }, |
| { |
| cty.EmptyObjectVal, |
| cty.NilVal, |
| "must be a list, set, or tuple value with either zero or one elements", |
| }, |
| { |
| cty.True, |
| cty.NilVal, |
| "must be a list, set, or tuple value with either zero or one elements", |
| }, |
| { |
| cty.UnknownVal(cty.Bool), |
| cty.NilVal, |
| "must be a list, set, or tuple value with either zero or one elements", |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(fmt.Sprintf("one(%#v)", test.List), func(t *testing.T) { |
| got, err := One(test.List) |
| |
| if test.Err != "" { |
| if err == nil { |
| t.Fatal("succeeded; want error") |
| } else if got, want := err.Error(), test.Err; got != want { |
| t.Fatalf("wrong error\n got: %s\nwant: %s", got, want) |
| } |
| return |
| } else if err != nil { |
| t.Fatalf("unexpected error: %s", err) |
| } |
| |
| if !test.Want.RawEquals(got) { |
| t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) |
| } |
| }) |
| } |
| } |
| |
| func TestSum(t *testing.T) { |
| tests := []struct { |
| List cty.Value |
| Want cty.Value |
| Err string |
| }{ |
| { |
| cty.ListVal([]cty.Value{ |
| cty.NumberIntVal(1), |
| cty.NumberIntVal(2), |
| cty.NumberIntVal(3), |
| }), |
| cty.NumberIntVal(6), |
| "", |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.NumberIntVal(1476), |
| cty.NumberIntVal(2093), |
| cty.NumberIntVal(2092495), |
| cty.NumberIntVal(64589234), |
| cty.NumberIntVal(234), |
| }), |
| cty.NumberIntVal(66685532), |
| "", |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| cty.StringVal("c"), |
| }), |
| cty.UnknownVal(cty.String), |
| "argument must be list, set, or tuple of number values", |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.NumberIntVal(10), |
| cty.NumberIntVal(-19), |
| cty.NumberIntVal(5), |
| }), |
| cty.NumberIntVal(-4), |
| "", |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.NumberFloatVal(10.2), |
| cty.NumberFloatVal(19.4), |
| cty.NumberFloatVal(5.7), |
| }), |
| cty.NumberFloatVal(35.3), |
| "", |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.NumberFloatVal(-10.2), |
| cty.NumberFloatVal(-19.4), |
| cty.NumberFloatVal(-5.7), |
| }), |
| cty.NumberFloatVal(-35.3), |
| "", |
| }, |
| { |
| cty.ListVal([]cty.Value{cty.NullVal(cty.Number)}), |
| cty.NilVal, |
| "argument must be list, set, or tuple of number values", |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.NumberIntVal(5), |
| cty.NullVal(cty.Number), |
| }), |
| cty.NilVal, |
| "argument must be list, set, or tuple of number values", |
| }, |
| { |
| cty.SetVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| cty.StringVal("c"), |
| }), |
| cty.UnknownVal(cty.String), |
| "argument must be list, set, or tuple of number values", |
| }, |
| { |
| cty.SetVal([]cty.Value{ |
| cty.NumberIntVal(10), |
| cty.NumberIntVal(-19), |
| cty.NumberIntVal(5), |
| }), |
| cty.NumberIntVal(-4), |
| "", |
| }, |
| { |
| cty.SetVal([]cty.Value{ |
| cty.NumberIntVal(10), |
| cty.NumberIntVal(25), |
| cty.NumberIntVal(30), |
| }), |
| cty.NumberIntVal(65), |
| "", |
| }, |
| { |
| cty.SetVal([]cty.Value{ |
| cty.NumberFloatVal(2340.8), |
| cty.NumberFloatVal(10.2), |
| cty.NumberFloatVal(3), |
| }), |
| cty.NumberFloatVal(2354), |
| "", |
| }, |
| { |
| cty.SetVal([]cty.Value{ |
| cty.NumberFloatVal(2), |
| }), |
| cty.NumberFloatVal(2), |
| "", |
| }, |
| { |
| cty.SetVal([]cty.Value{ |
| cty.NumberFloatVal(-2), |
| cty.NumberFloatVal(-50), |
| cty.NumberFloatVal(-20), |
| cty.NumberFloatVal(-123), |
| cty.NumberFloatVal(-4), |
| }), |
| cty.NumberFloatVal(-199), |
| "", |
| }, |
| { |
| cty.TupleVal([]cty.Value{ |
| cty.NumberIntVal(12), |
| cty.StringVal("a"), |
| cty.NumberIntVal(38), |
| }), |
| cty.UnknownVal(cty.String), |
| "argument must be list, set, or tuple of number values", |
| }, |
| { |
| cty.NumberIntVal(12), |
| cty.NilVal, |
| "cannot sum noniterable", |
| }, |
| { |
| cty.ListValEmpty(cty.Number), |
| cty.NilVal, |
| "cannot sum an empty list", |
| }, |
| { |
| cty.MapVal(map[string]cty.Value{"hello": cty.True}), |
| cty.NilVal, |
| "argument must be list, set, or tuple. Received map of bool", |
| }, |
| { |
| cty.UnknownVal(cty.Number), |
| cty.UnknownVal(cty.Number), |
| "", |
| }, |
| { |
| cty.UnknownVal(cty.List(cty.Number)), |
| cty.UnknownVal(cty.Number), |
| "", |
| }, |
| { // known list containing unknown values |
| cty.ListVal([]cty.Value{cty.UnknownVal(cty.Number)}), |
| cty.UnknownVal(cty.Number), |
| "", |
| }, |
| { // numbers too large to represent as float64 |
| cty.ListVal([]cty.Value{ |
| cty.MustParseNumberVal("1e+500"), |
| cty.MustParseNumberVal("1e+500"), |
| }), |
| cty.MustParseNumberVal("2e+500"), |
| "", |
| }, |
| { // edge case we have a special error handler for |
| cty.ListVal([]cty.Value{ |
| cty.NumberFloatVal(math.Inf(1)), |
| cty.NumberFloatVal(math.Inf(-1)), |
| }), |
| cty.NilVal, |
| "can't compute sum of opposing infinities", |
| }, |
| { |
| cty.ListVal([]cty.Value{ |
| cty.StringVal("1"), |
| cty.StringVal("2"), |
| cty.StringVal("3"), |
| }), |
| cty.NumberIntVal(6), |
| "", |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(fmt.Sprintf("sum(%#v)", test.List), func(t *testing.T) { |
| got, err := Sum(test.List) |
| |
| if test.Err != "" { |
| if err == nil { |
| t.Fatal("succeeded; want error") |
| } else if got, want := err.Error(), test.Err; got != want { |
| t.Fatalf("wrong error\n got: %s\nwant: %s", got, want) |
| } |
| return |
| } else if err != nil { |
| t.Fatalf("unexpected error: %s", err) |
| } |
| |
| if !got.RawEquals(test.Want) { |
| t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) |
| } |
| }) |
| } |
| } |
| |
| func TestTranspose(t *testing.T) { |
| tests := []struct { |
| Values cty.Value |
| Want cty.Value |
| Err bool |
| }{ |
| { |
| cty.MapVal(map[string]cty.Value{ |
| "key1": cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| }), |
| "key2": cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| cty.StringVal("c"), |
| }), |
| "key3": cty.ListVal([]cty.Value{ |
| cty.StringVal("c"), |
| }), |
| "key4": cty.ListValEmpty(cty.String), |
| }), |
| cty.MapVal(map[string]cty.Value{ |
| "a": cty.ListVal([]cty.Value{ |
| cty.StringVal("key1"), |
| cty.StringVal("key2"), |
| }), |
| "b": cty.ListVal([]cty.Value{ |
| cty.StringVal("key1"), |
| cty.StringVal("key2"), |
| }), |
| "c": cty.ListVal([]cty.Value{ |
| cty.StringVal("key2"), |
| cty.StringVal("key3"), |
| }), |
| }), |
| false, |
| }, |
| { // map - unknown value |
| cty.MapVal(map[string]cty.Value{ |
| "key1": cty.UnknownVal(cty.List(cty.String)), |
| }), |
| cty.UnknownVal(cty.Map(cty.List(cty.String))), |
| false, |
| }, |
| { // bad map - empty value |
| cty.MapVal(map[string]cty.Value{ |
| "key1": cty.ListValEmpty(cty.String), |
| }), |
| cty.MapValEmpty(cty.List(cty.String)), |
| false, |
| }, |
| { // bad map - value not a list |
| cty.MapVal(map[string]cty.Value{ |
| "key1": cty.StringVal("a"), |
| }), |
| cty.NilVal, |
| true, |
| }, |
| { // marks (deep or shallow) on any elements will propegate to the entire return value |
| cty.MapVal(map[string]cty.Value{ |
| "key1": cty.ListVal([]cty.Value{ |
| cty.StringVal("a").Mark("beep"), // mark on the inner list element |
| cty.StringVal("b"), |
| }), |
| "key2": cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| cty.StringVal("c"), |
| }).Mark("boop"), // mark on the map element |
| "key3": cty.ListVal([]cty.Value{ |
| cty.StringVal("c"), |
| }), |
| "key4": cty.ListValEmpty(cty.String), |
| }), |
| cty.MapVal(map[string]cty.Value{ |
| "a": cty.ListVal([]cty.Value{ |
| cty.StringVal("key1"), |
| cty.StringVal("key2"), |
| }), |
| "b": cty.ListVal([]cty.Value{ |
| cty.StringVal("key1"), |
| cty.StringVal("key2"), |
| }), |
| "c": cty.ListVal([]cty.Value{ |
| cty.StringVal("key2"), |
| cty.StringVal("key3")}), |
| }).WithMarks(cty.NewValueMarks("beep", "boop")), |
| false, |
| }, |
| { // Marks on the input value will be applied to the return value |
| cty.MapVal(map[string]cty.Value{ |
| "key1": cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| }), |
| "key2": cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| cty.StringVal("c"), |
| }), |
| "key3": cty.ListVal([]cty.Value{ |
| cty.StringVal("c"), |
| }), |
| }).Mark("beep"), // mark on the entire input value |
| cty.MapVal(map[string]cty.Value{ |
| "a": cty.ListVal([]cty.Value{ |
| cty.StringVal("key1"), |
| cty.StringVal("key2"), |
| }), |
| "b": cty.ListVal([]cty.Value{ |
| cty.StringVal("key1"), |
| cty.StringVal("key2"), |
| }), |
| "c": cty.ListVal([]cty.Value{ |
| cty.StringVal("key2"), |
| cty.StringVal("key3"), |
| }), |
| }).Mark("beep"), |
| false, |
| }, |
| { // Marks on the entire input value AND inner elements (deep or shallow) ALL apply to the return |
| cty.MapVal(map[string]cty.Value{ |
| "key1": cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| }).Mark("beep"), // mark on the map element |
| "key2": cty.ListVal([]cty.Value{ |
| cty.StringVal("a"), |
| cty.StringVal("b"), |
| cty.StringVal("c"), |
| }), |
| "key3": cty.ListVal([]cty.Value{ |
| cty.StringVal("c").Mark("boop"), // mark on the inner list element |
| }), |
| }).Mark("bloop"), // mark on the entire input value |
| cty.MapVal(map[string]cty.Value{ |
| "a": cty.ListVal([]cty.Value{ |
| cty.StringVal("key1"), |
| cty.StringVal("key2"), |
| }), |
| "b": cty.ListVal([]cty.Value{ |
| cty.StringVal("key1"), |
| cty.StringVal("key2"), |
| }), |
| "c": cty.ListVal([]cty.Value{ |
| cty.StringVal("key2"), |
| cty.StringVal("key3"), |
| }), |
| }).WithMarks(cty.NewValueMarks("beep", "boop", "bloop")), |
| false, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(fmt.Sprintf("transpose(%#v)", test.Values), func(t *testing.T) { |
| got, err := Transpose(test.Values) |
| |
| if test.Err { |
| if err == nil { |
| t.Fatal("succeeded; want error") |
| } |
| return |
| } else if err != nil { |
| t.Fatalf("unexpected error: %s", err) |
| } |
| |
| if !got.RawEquals(test.Want) { |
| t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) |
| } |
| }) |
| } |
| } |