mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-30 10:47:14 -06:00
d4716a69e1
This is an analog to the "alltrue" function, using OR as the reduce operator rather than AND. This also includes some simplification of the "alltrue" implementation to implement it similarly as a sort of reduce operation with AND as the reduce operator, but with the same effective behavior.
1444 lines
27 KiB
Go
1444 lines
27 KiB
Go
package funcs
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
"github.com/zclconf/go-cty/cty/function"
|
|
)
|
|
|
|
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),
|
|
},
|
|
}
|
|
|
|
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.UnknownVal(cty.Bool)}),
|
|
cty.UnknownVal(cty.Bool),
|
|
true,
|
|
},
|
|
{
|
|
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.UnknownVal(cty.Bool)}),
|
|
cty.UnknownVal(cty.Bool),
|
|
true,
|
|
},
|
|
{
|
|
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 TestList(t *testing.T) {
|
|
tests := []struct {
|
|
Values []cty.Value
|
|
Want cty.Value
|
|
Err bool
|
|
}{
|
|
{
|
|
[]cty.Value{
|
|
cty.NilVal,
|
|
},
|
|
cty.NilVal,
|
|
true,
|
|
},
|
|
{
|
|
[]cty.Value{
|
|
cty.StringVal("Hello"),
|
|
},
|
|
cty.ListVal([]cty.Value{
|
|
cty.StringVal("Hello"),
|
|
}),
|
|
false,
|
|
},
|
|
{
|
|
[]cty.Value{
|
|
cty.StringVal("Hello"),
|
|
cty.StringVal("World"),
|
|
},
|
|
cty.ListVal([]cty.Value{
|
|
cty.StringVal("Hello"),
|
|
cty.StringVal("World"),
|
|
}),
|
|
false,
|
|
},
|
|
{
|
|
[]cty.Value{
|
|
cty.StringVal("Hello"),
|
|
cty.NumberIntVal(42),
|
|
},
|
|
cty.ListVal([]cty.Value{
|
|
cty.StringVal("Hello"),
|
|
cty.StringVal("42"),
|
|
}),
|
|
false,
|
|
},
|
|
{
|
|
[]cty.Value{
|
|
cty.StringVal("Hello"),
|
|
cty.UnknownVal(cty.String),
|
|
},
|
|
cty.ListVal([]cty.Value{
|
|
cty.StringVal("Hello"),
|
|
cty.UnknownVal(cty.String),
|
|
}),
|
|
false,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(fmt.Sprintf("list(%#v)", test.Values), func(t *testing.T) {
|
|
got, err := List(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(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,
|
|
},
|
|
}
|
|
|
|
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 TestMap(t *testing.T) {
|
|
tests := []struct {
|
|
Values []cty.Value
|
|
Want cty.Value
|
|
Err bool
|
|
}{
|
|
{
|
|
[]cty.Value{
|
|
cty.StringVal("hello"),
|
|
cty.StringVal("world"),
|
|
},
|
|
cty.MapVal(map[string]cty.Value{
|
|
"hello": cty.StringVal("world"),
|
|
}),
|
|
false,
|
|
},
|
|
{
|
|
[]cty.Value{
|
|
cty.StringVal("hello"),
|
|
cty.UnknownVal(cty.String),
|
|
},
|
|
cty.UnknownVal(cty.Map(cty.String)),
|
|
false,
|
|
},
|
|
{
|
|
[]cty.Value{
|
|
cty.StringVal("hello"),
|
|
cty.StringVal("world"),
|
|
cty.StringVal("what's"),
|
|
cty.StringVal("up"),
|
|
},
|
|
cty.MapVal(map[string]cty.Value{
|
|
"hello": cty.StringVal("world"),
|
|
"what's": cty.StringVal("up"),
|
|
}),
|
|
false,
|
|
},
|
|
{
|
|
[]cty.Value{
|
|
cty.StringVal("hello"),
|
|
cty.NumberIntVal(1),
|
|
cty.StringVal("goodbye"),
|
|
cty.NumberIntVal(42),
|
|
},
|
|
cty.MapVal(map[string]cty.Value{
|
|
"hello": cty.NumberIntVal(1),
|
|
"goodbye": cty.NumberIntVal(42),
|
|
}),
|
|
false,
|
|
},
|
|
{ // convert numbers to strings
|
|
[]cty.Value{
|
|
cty.StringVal("hello"),
|
|
cty.NumberIntVal(1),
|
|
cty.StringVal("goodbye"),
|
|
cty.StringVal("42"),
|
|
},
|
|
cty.MapVal(map[string]cty.Value{
|
|
"hello": cty.StringVal("1"),
|
|
"goodbye": cty.StringVal("42"),
|
|
}),
|
|
false,
|
|
},
|
|
{ // convert number keys to strings
|
|
[]cty.Value{
|
|
cty.NumberIntVal(1),
|
|
cty.StringVal("hello"),
|
|
cty.NumberIntVal(2),
|
|
cty.StringVal("goodbye"),
|
|
},
|
|
cty.MapVal(map[string]cty.Value{
|
|
"1": cty.StringVal("hello"),
|
|
"2": cty.StringVal("goodbye"),
|
|
}),
|
|
false,
|
|
},
|
|
{ // map of lists is okay
|
|
[]cty.Value{
|
|
cty.StringVal("hello"),
|
|
cty.ListVal([]cty.Value{
|
|
cty.StringVal("world"),
|
|
}),
|
|
cty.StringVal("what's"),
|
|
cty.ListVal([]cty.Value{
|
|
cty.StringVal("up"),
|
|
}),
|
|
},
|
|
cty.MapVal(map[string]cty.Value{
|
|
"hello": cty.ListVal([]cty.Value{cty.StringVal("world")}),
|
|
"what's": cty.ListVal([]cty.Value{cty.StringVal("up")}),
|
|
}),
|
|
false,
|
|
},
|
|
{ // map of maps is okay
|
|
[]cty.Value{
|
|
cty.StringVal("hello"),
|
|
cty.MapVal(map[string]cty.Value{
|
|
"there": cty.StringVal("world"),
|
|
}),
|
|
cty.StringVal("what's"),
|
|
cty.MapVal(map[string]cty.Value{
|
|
"really": cty.StringVal("up"),
|
|
}),
|
|
},
|
|
cty.MapVal(map[string]cty.Value{
|
|
"hello": cty.MapVal(map[string]cty.Value{
|
|
"there": cty.StringVal("world"),
|
|
}),
|
|
"what's": cty.MapVal(map[string]cty.Value{
|
|
"really": cty.StringVal("up"),
|
|
}),
|
|
}),
|
|
false,
|
|
},
|
|
{ // single argument returns an error
|
|
[]cty.Value{
|
|
cty.StringVal("hello"),
|
|
},
|
|
cty.NilVal,
|
|
true,
|
|
},
|
|
{ // duplicate keys returns an error
|
|
[]cty.Value{
|
|
cty.StringVal("hello"),
|
|
cty.StringVal("world"),
|
|
cty.StringVal("hello"),
|
|
cty.StringVal("universe"),
|
|
},
|
|
cty.NilVal,
|
|
true,
|
|
},
|
|
{ // null key returns an error
|
|
[]cty.Value{
|
|
cty.NullVal(cty.DynamicPseudoType),
|
|
cty.NumberIntVal(5),
|
|
},
|
|
cty.NilVal,
|
|
true,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(fmt.Sprintf("map(%#v)", test.Values), func(t *testing.T) {
|
|
got, err := Map(test.Values...)
|
|
if test.Err {
|
|
if err == nil {
|
|
t.Fatal("succeeded; want error")
|
|
}
|
|
if _, ok := err.(function.PanicError); ok {
|
|
t.Fatalf("unexpected panic: %s", err)
|
|
}
|
|
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 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 TestSum(t *testing.T) {
|
|
tests := []struct {
|
|
List cty.Value
|
|
Want cty.Value
|
|
Err bool
|
|
}{
|
|
{
|
|
cty.ListVal([]cty.Value{
|
|
cty.NumberIntVal(1),
|
|
cty.NumberIntVal(2),
|
|
cty.NumberIntVal(3),
|
|
}),
|
|
cty.NumberIntVal(6),
|
|
false,
|
|
},
|
|
{
|
|
cty.ListVal([]cty.Value{
|
|
cty.NumberIntVal(1476),
|
|
cty.NumberIntVal(2093),
|
|
cty.NumberIntVal(2092495),
|
|
cty.NumberIntVal(64589234),
|
|
cty.NumberIntVal(234),
|
|
}),
|
|
cty.NumberIntVal(66685532),
|
|
false,
|
|
},
|
|
{
|
|
cty.ListVal([]cty.Value{
|
|
cty.StringVal("a"),
|
|
cty.StringVal("b"),
|
|
cty.StringVal("c"),
|
|
}),
|
|
cty.UnknownVal(cty.String),
|
|
true,
|
|
},
|
|
{
|
|
cty.ListVal([]cty.Value{
|
|
cty.NumberIntVal(10),
|
|
cty.NumberIntVal(-19),
|
|
cty.NumberIntVal(5),
|
|
}),
|
|
cty.NumberIntVal(-4),
|
|
false,
|
|
},
|
|
{
|
|
cty.ListVal([]cty.Value{
|
|
cty.NumberFloatVal(10.2),
|
|
cty.NumberFloatVal(19.4),
|
|
cty.NumberFloatVal(5.7),
|
|
}),
|
|
cty.NumberFloatVal(35.3),
|
|
false,
|
|
},
|
|
{
|
|
cty.ListVal([]cty.Value{
|
|
cty.NumberFloatVal(-10.2),
|
|
cty.NumberFloatVal(-19.4),
|
|
cty.NumberFloatVal(-5.7),
|
|
}),
|
|
cty.NumberFloatVal(-35.3),
|
|
false,
|
|
},
|
|
{
|
|
cty.ListVal([]cty.Value{cty.NullVal(cty.Number)}),
|
|
cty.NilVal,
|
|
true,
|
|
},
|
|
{
|
|
cty.SetVal([]cty.Value{
|
|
cty.StringVal("a"),
|
|
cty.StringVal("b"),
|
|
cty.StringVal("c"),
|
|
}),
|
|
cty.UnknownVal(cty.String),
|
|
true,
|
|
},
|
|
{
|
|
cty.SetVal([]cty.Value{
|
|
cty.NumberIntVal(10),
|
|
cty.NumberIntVal(-19),
|
|
cty.NumberIntVal(5),
|
|
}),
|
|
cty.NumberIntVal(-4),
|
|
false,
|
|
},
|
|
{
|
|
cty.SetVal([]cty.Value{
|
|
cty.NumberIntVal(10),
|
|
cty.NumberIntVal(25),
|
|
cty.NumberIntVal(30),
|
|
}),
|
|
cty.NumberIntVal(65),
|
|
false,
|
|
},
|
|
{
|
|
cty.SetVal([]cty.Value{
|
|
cty.NumberFloatVal(2340.8),
|
|
cty.NumberFloatVal(10.2),
|
|
cty.NumberFloatVal(3),
|
|
}),
|
|
cty.NumberFloatVal(2354),
|
|
false,
|
|
},
|
|
{
|
|
cty.SetVal([]cty.Value{
|
|
cty.NumberFloatVal(2),
|
|
}),
|
|
cty.NumberFloatVal(2),
|
|
false,
|
|
},
|
|
{
|
|
cty.SetVal([]cty.Value{
|
|
cty.NumberFloatVal(-2),
|
|
cty.NumberFloatVal(-50),
|
|
cty.NumberFloatVal(-20),
|
|
cty.NumberFloatVal(-123),
|
|
cty.NumberFloatVal(-4),
|
|
}),
|
|
cty.NumberFloatVal(-199),
|
|
false,
|
|
},
|
|
{
|
|
cty.TupleVal([]cty.Value{
|
|
cty.NumberIntVal(12),
|
|
cty.StringVal("a"),
|
|
cty.NumberIntVal(38),
|
|
}),
|
|
cty.UnknownVal(cty.String),
|
|
true,
|
|
},
|
|
{
|
|
cty.NumberIntVal(12),
|
|
cty.NilVal,
|
|
true,
|
|
},
|
|
{
|
|
cty.ListValEmpty(cty.Number),
|
|
cty.NilVal,
|
|
true,
|
|
},
|
|
{
|
|
cty.MapVal(map[string]cty.Value{"hello": cty.True}),
|
|
cty.NilVal,
|
|
true,
|
|
},
|
|
{
|
|
cty.UnknownVal(cty.Number),
|
|
cty.UnknownVal(cty.Number),
|
|
false,
|
|
},
|
|
}
|
|
|
|
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")
|
|
}
|
|
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,
|
|
},
|
|
}
|
|
|
|
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)
|
|
}
|
|
})
|
|
}
|
|
}
|