1838 lines
37 KiB
Go
1838 lines
37 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|
|
}
|