2018-05-22 02:39:26 +02:00
|
|
|
package funcs
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2020-12-10 22:56:06 +01:00
|
|
|
"math"
|
2018-05-22 02:39:26 +02:00
|
|
|
"testing"
|
|
|
|
|
2021-12-01 19:10:54 +01:00
|
|
|
"github.com/hashicorp/terraform/internal/lang/marks"
|
2018-05-22 02:39:26 +02:00
|
|
|
"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),
|
|
|
|
},
|
2018-11-06 02:13:05 +01:00
|
|
|
{
|
|
|
|
cty.UnknownVal(cty.EmptyTuple),
|
|
|
|
cty.NumberIntVal(0),
|
|
|
|
},
|
2018-05-22 02:39:26 +02:00
|
|
|
{
|
|
|
|
cty.TupleVal([]cty.Value{cty.True}),
|
|
|
|
cty.NumberIntVal(1),
|
|
|
|
},
|
2018-11-06 02:13:05 +01:00
|
|
|
{
|
|
|
|
cty.EmptyObjectVal,
|
|
|
|
cty.NumberIntVal(0),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.UnknownVal(cty.EmptyObject),
|
|
|
|
cty.NumberIntVal(0),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{"true": cty.True}),
|
|
|
|
cty.NumberIntVal(1),
|
|
|
|
},
|
2018-05-22 02:39:26 +02:00
|
|
|
{
|
|
|
|
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),
|
|
|
|
},
|
2021-05-07 17:57:37 +02:00
|
|
|
{ // 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),
|
|
|
|
},
|
2018-05-22 02:39:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2018-05-24 20:48:44 +02:00
|
|
|
|
2020-09-22 15:06:42 +02:00
|
|
|
func TestAllTrue(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
Collection cty.Value
|
|
|
|
Want cty.Value
|
|
|
|
Err bool
|
|
|
|
}{
|
|
|
|
{
|
2020-10-23 22:52:48 +02:00
|
|
|
cty.ListValEmpty(cty.Bool),
|
2020-09-22 15:06:42 +02:00
|
|
|
cty.True,
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
2020-10-23 22:52:48 +02:00
|
|
|
cty.ListVal([]cty.Value{cty.True}),
|
2020-09-22 15:06:42 +02:00
|
|
|
cty.True,
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
2020-10-23 22:52:48 +02:00
|
|
|
cty.ListVal([]cty.Value{cty.False}),
|
|
|
|
cty.False,
|
2020-09-22 15:06:42 +02:00
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
2020-10-23 22:52:48 +02:00
|
|
|
cty.ListVal([]cty.Value{cty.True, cty.False}),
|
|
|
|
cty.False,
|
2020-09-22 15:06:42 +02:00
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
2020-10-23 22:52:48 +02:00
|
|
|
cty.ListVal([]cty.Value{cty.False, cty.True}),
|
|
|
|
cty.False,
|
2020-09-22 15:06:42 +02:00
|
|
|
false,
|
|
|
|
},
|
2020-12-10 17:20:57 +01:00
|
|
|
{
|
|
|
|
cty.ListVal([]cty.Value{cty.True, cty.NullVal(cty.Bool)}),
|
|
|
|
cty.False,
|
|
|
|
false,
|
|
|
|
},
|
2020-09-22 15:06:42 +02:00
|
|
|
{
|
2020-10-23 22:52:48 +02:00
|
|
|
cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}),
|
|
|
|
cty.UnknownVal(cty.Bool),
|
2020-12-10 17:20:57 +01:00
|
|
|
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,
|
2020-09-22 15:06:42 +02:00
|
|
|
},
|
|
|
|
{
|
2020-10-23 22:52:48 +02:00
|
|
|
cty.NullVal(cty.List(cty.Bool)),
|
|
|
|
cty.NilVal,
|
|
|
|
true,
|
2020-09-22 15:06:42 +02:00
|
|
|
},
|
2020-10-23 22:52:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}{
|
2020-09-22 15:06:42 +02:00
|
|
|
{
|
2020-10-23 22:52:48 +02:00
|
|
|
cty.ListValEmpty(cty.Bool),
|
2020-09-22 15:06:42 +02:00
|
|
|
cty.False,
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
2020-10-23 22:52:48 +02:00
|
|
|
cty.ListVal([]cty.Value{cty.True}),
|
|
|
|
cty.True,
|
2020-09-22 15:06:42 +02:00
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
2020-10-23 22:52:48 +02:00
|
|
|
cty.ListVal([]cty.Value{cty.False}),
|
2020-09-22 15:06:42 +02:00
|
|
|
cty.False,
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
2020-10-23 22:52:48 +02:00
|
|
|
cty.ListVal([]cty.Value{cty.True, cty.False}),
|
|
|
|
cty.True,
|
|
|
|
false,
|
2020-09-22 15:06:42 +02:00
|
|
|
},
|
|
|
|
{
|
2020-10-23 22:52:48 +02:00
|
|
|
cty.ListVal([]cty.Value{cty.False, cty.True}),
|
|
|
|
cty.True,
|
2020-09-22 15:06:42 +02:00
|
|
|
false,
|
|
|
|
},
|
2020-12-10 17:20:57 +01:00
|
|
|
{
|
|
|
|
cty.ListVal([]cty.Value{cty.NullVal(cty.Bool), cty.True}),
|
|
|
|
cty.True,
|
|
|
|
false,
|
|
|
|
},
|
2020-09-22 15:06:42 +02:00
|
|
|
{
|
2020-10-23 22:52:48 +02:00
|
|
|
cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}),
|
2020-09-22 15:06:42 +02:00
|
|
|
cty.UnknownVal(cty.Bool),
|
2020-12-10 17:20:57 +01:00
|
|
|
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,
|
2020-10-23 22:52:48 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.NullVal(cty.List(cty.Bool)),
|
|
|
|
cty.NilVal,
|
|
|
|
true,
|
2020-09-22 15:06:42 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
2020-10-23 22:52:48 +02:00
|
|
|
t.Run(fmt.Sprintf("anytrue(%#v)", test.Collection), func(t *testing.T) {
|
|
|
|
got, err := AnyTrue(test.Collection)
|
2020-09-22 15:06:42 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-12 19:57:52 +02:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-24 15:38:00 +01:00
|
|
|
func TestIndex(t *testing.T) {
|
2018-05-24 20:48:44 +02:00
|
|
|
tests := []struct {
|
2020-02-24 15:38:00 +01:00
|
|
|
List cty.Value
|
|
|
|
Value cty.Value
|
|
|
|
Want cty.Value
|
|
|
|
Err bool
|
2018-05-24 20:48:44 +02:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
cty.ListVal([]cty.Value{
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.StringVal("a"),
|
|
|
|
cty.StringVal("b"),
|
|
|
|
cty.StringVal("c"),
|
2018-05-24 20:48:44 +02:00
|
|
|
}),
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.StringVal("a"),
|
|
|
|
cty.NumberIntVal(0),
|
2018-05-24 20:48:44 +02:00
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.ListVal([]cty.Value{
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.StringVal("a"),
|
|
|
|
cty.StringVal("b"),
|
|
|
|
cty.UnknownVal(cty.String),
|
2018-05-24 20:48:44 +02:00
|
|
|
}),
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.StringVal("a"),
|
|
|
|
cty.NumberIntVal(0),
|
2018-05-24 20:48:44 +02:00
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.ListVal([]cty.Value{
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.StringVal("a"),
|
|
|
|
cty.StringVal("b"),
|
|
|
|
cty.StringVal("c"),
|
2018-05-24 20:48:44 +02:00
|
|
|
}),
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.StringVal("b"),
|
|
|
|
cty.NumberIntVal(1),
|
2018-05-24 20:48:44 +02:00
|
|
|
false,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{
|
2018-05-24 23:46:03 +02:00
|
|
|
cty.ListVal([]cty.Value{
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.StringVal("a"),
|
|
|
|
cty.StringVal("b"),
|
|
|
|
cty.StringVal("c"),
|
2018-05-24 23:46:03 +02:00
|
|
|
}),
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.StringVal("z"),
|
|
|
|
cty.NilVal,
|
|
|
|
true,
|
2018-05-24 20:48:44 +02:00
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{
|
2018-05-24 20:48:44 +02:00
|
|
|
cty.ListVal([]cty.Value{
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.StringVal("1"),
|
|
|
|
cty.StringVal("2"),
|
|
|
|
cty.StringVal("3"),
|
2018-05-24 20:48:44 +02:00
|
|
|
}),
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.NumberIntVal(1),
|
|
|
|
cty.NumberIntVal(0),
|
|
|
|
true,
|
2018-05-24 20:48:44 +02:00
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{
|
2018-06-06 19:43:58 +02:00
|
|
|
cty.ListVal([]cty.Value{
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.NumberIntVal(1),
|
|
|
|
cty.NumberIntVal(2),
|
|
|
|
cty.NumberIntVal(3),
|
2018-06-06 19:43:58 +02:00
|
|
|
}),
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.NumberIntVal(2),
|
|
|
|
cty.NumberIntVal(1),
|
2018-06-06 19:43:58 +02:00
|
|
|
false,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{
|
2018-06-06 19:43:58 +02:00
|
|
|
cty.ListVal([]cty.Value{
|
2020-02-24 15:38:00 +01:00
|
|
|
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),
|
2018-06-06 19:43:58 +02:00
|
|
|
}),
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.NumberIntVal(1),
|
|
|
|
cty.NumberIntVal(0),
|
2018-06-06 19:43:58 +02:00
|
|
|
false,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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),
|
|
|
|
})
|
|
|
|
|
2018-05-24 23:46:03 +02:00
|
|
|
tests := []struct {
|
2020-02-24 15:38:00 +01:00
|
|
|
Values []cty.Value
|
|
|
|
Want cty.Value
|
|
|
|
Err bool
|
2018-05-24 23:46:03 +02:00
|
|
|
}{
|
|
|
|
{
|
2020-02-24 15:38:00 +01:00
|
|
|
[]cty.Value{
|
|
|
|
simpleMap,
|
|
|
|
cty.StringVal("foo"),
|
|
|
|
},
|
|
|
|
cty.StringVal("bar"),
|
2018-05-24 23:46:03 +02:00
|
|
|
false,
|
|
|
|
},
|
2018-05-26 01:30:50 +02:00
|
|
|
{
|
2020-02-24 15:38:00 +01:00
|
|
|
[]cty.Value{
|
|
|
|
mapWithObjects,
|
|
|
|
cty.StringVal("foo"),
|
|
|
|
},
|
|
|
|
cty.StringVal("bar"),
|
2018-05-26 01:30:50 +02:00
|
|
|
false,
|
|
|
|
},
|
2019-07-11 19:31:22 +02:00
|
|
|
{
|
2020-02-24 15:38:00 +01:00
|
|
|
[]cty.Value{
|
|
|
|
intsMap,
|
|
|
|
cty.StringVal("foo"),
|
|
|
|
},
|
|
|
|
cty.NumberIntVal(42),
|
2019-07-11 19:31:22 +02:00
|
|
|
false,
|
|
|
|
},
|
2018-05-26 01:30:50 +02:00
|
|
|
{
|
2020-02-24 15:38:00 +01:00
|
|
|
[]cty.Value{
|
|
|
|
mapOfMaps,
|
|
|
|
cty.StringVal("foo"),
|
|
|
|
},
|
|
|
|
cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.StringVal("bar"),
|
|
|
|
}),
|
2018-05-26 01:30:50 +02:00
|
|
|
false,
|
|
|
|
},
|
2018-05-25 00:20:22 +02:00
|
|
|
{
|
2020-02-24 15:38:00 +01:00
|
|
|
[]cty.Value{
|
|
|
|
objectOfMaps,
|
|
|
|
cty.StringVal("foo"),
|
|
|
|
},
|
|
|
|
cty.MapVal(map[string]cty.Value{
|
|
|
|
"a": cty.StringVal("bar"),
|
2018-05-25 00:20:22 +02:00
|
|
|
}),
|
|
|
|
false,
|
|
|
|
},
|
2018-06-06 19:43:58 +02:00
|
|
|
{
|
2020-02-24 15:38:00 +01:00
|
|
|
[]cty.Value{
|
|
|
|
mapOfTuples,
|
|
|
|
cty.StringVal("foo"),
|
|
|
|
},
|
|
|
|
cty.TupleVal([]cty.Value{cty.StringVal("bar")}),
|
2018-06-06 19:43:58 +02:00
|
|
|
false,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{ // Invalid key
|
|
|
|
[]cty.Value{
|
|
|
|
simpleMap,
|
|
|
|
cty.StringVal("bar"),
|
|
|
|
},
|
2018-05-25 00:20:22 +02:00
|
|
|
cty.NilVal,
|
|
|
|
true,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{ // Invalid key
|
|
|
|
[]cty.Value{
|
|
|
|
mapWithObjects,
|
|
|
|
cty.StringVal("bar"),
|
|
|
|
},
|
|
|
|
cty.NilVal,
|
|
|
|
true,
|
2018-05-25 21:57:26 +02:00
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{ // Supplied default with valid key
|
|
|
|
[]cty.Value{
|
|
|
|
simpleMap,
|
|
|
|
cty.StringVal("foo"),
|
|
|
|
cty.StringVal(""),
|
|
|
|
},
|
|
|
|
cty.StringVal("bar"),
|
2018-05-25 21:57:26 +02:00
|
|
|
false,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{ // Supplied default with valid (int) key
|
|
|
|
[]cty.Value{
|
|
|
|
simpleMap,
|
|
|
|
cty.StringVal("foo"),
|
|
|
|
cty.NumberIntVal(-1),
|
|
|
|
},
|
|
|
|
cty.StringVal("bar"),
|
2018-05-25 21:57:26 +02:00
|
|
|
false,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{ // Supplied default with valid (int) key
|
|
|
|
[]cty.Value{
|
|
|
|
simpleMap,
|
|
|
|
cty.StringVal("foobar"),
|
|
|
|
cty.NumberIntVal(-1),
|
|
|
|
},
|
|
|
|
cty.StringVal("-1"),
|
2018-05-25 21:57:26 +02:00
|
|
|
false,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{ // Supplied default with valid key
|
|
|
|
[]cty.Value{
|
|
|
|
mapWithObjects,
|
|
|
|
cty.StringVal("foobar"),
|
|
|
|
cty.StringVal(""),
|
|
|
|
},
|
|
|
|
cty.StringVal(""),
|
2018-05-29 19:05:58 +02:00
|
|
|
false,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{ // Supplied default with invalid key
|
|
|
|
[]cty.Value{
|
|
|
|
simpleMap,
|
|
|
|
cty.StringVal("baz"),
|
|
|
|
cty.StringVal(""),
|
|
|
|
},
|
|
|
|
cty.StringVal(""),
|
2019-05-02 00:13:06 +02:00
|
|
|
false,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{ // 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,
|
|
|
|
},
|
2020-10-05 14:48:49 +02:00
|
|
|
{
|
|
|
|
[]cty.Value{
|
|
|
|
mapWithUnknowns,
|
|
|
|
cty.StringVal("foo"),
|
|
|
|
},
|
|
|
|
cty.StringVal("bar"),
|
|
|
|
false,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{
|
|
|
|
[]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
|
2019-05-02 00:13:06 +02:00
|
|
|
false,
|
|
|
|
},
|
2021-05-07 18:51:06 +02:00
|
|
|
{ // 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,
|
|
|
|
},
|
2018-05-25 21:57:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
2020-02-24 15:38:00 +01:00
|
|
|
t.Run(fmt.Sprintf("lookup(%#v)", test.Values), func(t *testing.T) {
|
|
|
|
got, err := Lookup(test.Values...)
|
2018-05-25 21:57:26 +02:00
|
|
|
|
|
|
|
if test.Err {
|
|
|
|
if err == nil {
|
|
|
|
t.Fatal("succeeded; want error")
|
|
|
|
}
|
|
|
|
return
|
|
|
|
} else if err != nil {
|
|
|
|
t.Fatalf("unexpected error: %s", err)
|
|
|
|
}
|
|
|
|
|
2018-05-29 19:05:58 +02:00
|
|
|
if !got.RawEquals(test.Want) {
|
2018-05-25 21:57:26 +02:00
|
|
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-01 19:10:54 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-24 15:38:00 +01:00
|
|
|
func TestMatchkeys(t *testing.T) {
|
2018-05-26 01:15:50 +02:00
|
|
|
tests := []struct {
|
2020-02-24 15:38:00 +01:00
|
|
|
Keys cty.Value
|
|
|
|
Values cty.Value
|
|
|
|
Searchset cty.Value
|
|
|
|
Want cty.Value
|
|
|
|
Err bool
|
2018-05-26 01:15:50 +02:00
|
|
|
}{
|
2020-02-24 15:38:00 +01:00
|
|
|
{ // normal usage
|
2018-05-26 01:15:50 +02:00
|
|
|
cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("a"),
|
|
|
|
cty.StringVal("b"),
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.StringVal("c"),
|
|
|
|
}),
|
|
|
|
cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("ref1"),
|
|
|
|
cty.StringVal("ref2"),
|
|
|
|
cty.StringVal("ref3"),
|
|
|
|
}),
|
|
|
|
cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("ref1"),
|
2018-05-26 01:15:50 +02:00
|
|
|
}),
|
|
|
|
cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("a"),
|
|
|
|
}),
|
|
|
|
false,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{ // normal usage 2, check the order
|
2018-06-06 19:43:58 +02:00
|
|
|
cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("a"),
|
|
|
|
cty.StringVal("b"),
|
2020-02-24 15:38:00 +01:00
|
|
|
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"),
|
2018-06-06 19:43:58 +02:00
|
|
|
}),
|
2018-05-26 01:15:50 +02:00
|
|
|
cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("a"),
|
|
|
|
cty.StringVal("b"),
|
|
|
|
}),
|
2020-02-24 15:38:00 +01:00
|
|
|
false,
|
|
|
|
},
|
|
|
|
{ // no matches
|
2018-05-26 01:15:50 +02:00
|
|
|
cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("a"),
|
|
|
|
cty.StringVal("b"),
|
|
|
|
cty.StringVal("c"),
|
|
|
|
}),
|
|
|
|
cty.ListVal([]cty.Value{
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.StringVal("ref1"),
|
|
|
|
cty.StringVal("ref2"),
|
|
|
|
cty.StringVal("ref3"),
|
2018-05-26 01:15:50 +02:00
|
|
|
}),
|
|
|
|
cty.ListVal([]cty.Value{
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.StringVal("ref4"),
|
2018-05-26 01:15:50 +02:00
|
|
|
}),
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.ListValEmpty(cty.String),
|
2018-05-26 01:15:50 +02:00
|
|
|
false,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{ // no matches 2
|
2018-05-26 01:15:50 +02:00
|
|
|
cty.ListVal([]cty.Value{
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.StringVal("a"),
|
|
|
|
cty.StringVal("b"),
|
|
|
|
cty.StringVal("c"),
|
2018-05-26 01:15:50 +02:00
|
|
|
}),
|
|
|
|
cty.ListVal([]cty.Value{
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.StringVal("ref1"),
|
|
|
|
cty.StringVal("ref2"),
|
|
|
|
cty.StringVal("ref3"),
|
2018-05-26 01:15:50 +02:00
|
|
|
}),
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.ListValEmpty(cty.String),
|
|
|
|
cty.ListValEmpty(cty.String),
|
2018-05-26 01:15:50 +02:00
|
|
|
false,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{ // zero case
|
|
|
|
cty.ListValEmpty(cty.String),
|
|
|
|
cty.ListValEmpty(cty.String),
|
|
|
|
cty.ListVal([]cty.Value{cty.StringVal("nope")}),
|
|
|
|
cty.ListValEmpty(cty.String),
|
2018-05-26 01:15:50 +02:00
|
|
|
false,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{ // complex values
|
2018-05-26 01:15:50 +02:00
|
|
|
cty.ListVal([]cty.Value{
|
|
|
|
cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("a"),
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.StringVal("a"),
|
2018-05-26 01:15:50 +02:00
|
|
|
}),
|
|
|
|
}),
|
|
|
|
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"),
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.StringVal("a"),
|
2018-05-26 01:15:50 +02:00
|
|
|
}),
|
|
|
|
}),
|
|
|
|
false,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{ // unknowns
|
2018-06-06 19:43:58 +02:00
|
|
|
cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("a"),
|
|
|
|
cty.StringVal("b"),
|
|
|
|
cty.UnknownVal(cty.String),
|
|
|
|
}),
|
2019-05-01 21:29:12 +02:00
|
|
|
cty.ListVal([]cty.Value{
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.StringVal("ref1"),
|
|
|
|
cty.StringVal("ref2"),
|
|
|
|
cty.UnknownVal(cty.String),
|
2019-05-01 21:29:12 +02:00
|
|
|
}),
|
2018-05-30 16:26:19 +02:00
|
|
|
cty.ListVal([]cty.Value{
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.StringVal("ref1"),
|
2018-05-30 16:26:19 +02:00
|
|
|
}),
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.UnknownVal(cty.List(cty.String)),
|
2018-05-30 16:26:19 +02:00
|
|
|
false,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{ // different types that can be unified
|
|
|
|
cty.ListVal([]cty.Value{
|
2019-05-01 20:22:50 +02:00
|
|
|
cty.StringVal("a"),
|
|
|
|
}),
|
2020-02-24 15:38:00 +01:00
|
|
|
cty.ListVal([]cty.Value{
|
2019-05-01 20:22:50 +02:00
|
|
|
cty.NumberIntVal(1),
|
|
|
|
}),
|
2018-06-06 19:43:58 +02:00
|
|
|
cty.ListVal([]cty.Value{
|
2019-05-01 20:22:50 +02:00
|
|
|
cty.StringVal("a"),
|
|
|
|
}),
|
2018-05-30 16:26:19 +02:00
|
|
|
cty.ListValEmpty(cty.String),
|
2019-05-01 16:19:40 +02:00
|
|
|
false,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{ // complex values: values is a different type from keys and searchset
|
2018-05-31 23:46:24 +02:00
|
|
|
cty.ListVal([]cty.Value{
|
2020-02-24 15:38:00 +01:00
|
|
|
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"),
|
|
|
|
}),
|
|
|
|
}),
|
2019-05-17 02:16:24 +02:00
|
|
|
false,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
// 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,
|
2019-05-17 02:16:24 +02:00
|
|
|
true,
|
|
|
|
},
|
2020-02-24 15:38:00 +01:00
|
|
|
{ // 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,
|
2019-05-17 02:16:24 +02:00
|
|
|
true,
|
|
|
|
},
|
2018-05-31 23:46:24 +02:00
|
|
|
}
|
|
|
|
|
2020-02-24 15:38:00 +01:00
|
|
|
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)
|
2018-05-31 23:46:24 +02:00
|
|
|
|
|
|
|
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)
|
lang/funcs: "one" function
In the Terraform language we typically use lists of zero or one values in
some sense interchangably with single values that might be null, because
various Terraform language constructs are designed to work with
collections rather than with nullable values.
In Terraform v0.12 we made the splat operator [*] have a "special power"
of concisely converting from a possibly-null single value into a
zero-or-one list as a way to make that common operation more concise.
In a sense this "one" function is the opposite operation to that special
power: it goes from a zero-or-one collection (list, set, or tuple) to a
possibly-null single value.
This is a concise alternative to the following clunky conditional
expression, with the additional benefit that the following expression is
also not viable for set values, and it also properly handles the case
where there's unexpectedly more than one value:
length(var.foo) != 0 ? var.foo[0] : null
Instead, we can write:
one(var.foo)
As with the splat operator, this is a tricky tradeoff because it could be
argued that it's not something that'd be immediately intuitive to someone
unfamiliar with Terraform. However, I think that's justified given how
often zero-or-one collections arise in typical Terraform configurations.
Unlike the splat operator, it should at least be easier to search for its
name and find its documentation the first time you see it in a
configuration.
My expectation that this will become a common pattern is also my
justification for giving it a short, concise name. Arguably it could be
better named something like "oneornull", but that's a pretty clunky name
and I'm not convinced it really adds any clarity for someone who isn't
already familiar with it.
2021-01-09 01:02:56 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2018-05-31 23:46:24 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-15 20:27:06 +02:00
|
|
|
func TestSum(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
List cty.Value
|
|
|
|
Want cty.Value
|
2020-12-10 22:56:06 +01:00
|
|
|
Err string
|
2020-04-15 20:27:06 +02:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
cty.ListVal([]cty.Value{
|
|
|
|
cty.NumberIntVal(1),
|
|
|
|
cty.NumberIntVal(2),
|
|
|
|
cty.NumberIntVal(3),
|
|
|
|
}),
|
|
|
|
cty.NumberIntVal(6),
|
2020-12-10 22:56:06 +01:00
|
|
|
"",
|
2020-04-15 20:27:06 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.ListVal([]cty.Value{
|
|
|
|
cty.NumberIntVal(1476),
|
|
|
|
cty.NumberIntVal(2093),
|
|
|
|
cty.NumberIntVal(2092495),
|
|
|
|
cty.NumberIntVal(64589234),
|
|
|
|
cty.NumberIntVal(234),
|
|
|
|
}),
|
|
|
|
cty.NumberIntVal(66685532),
|
2020-12-10 22:56:06 +01:00
|
|
|
"",
|
2020-04-15 20:27:06 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.ListVal([]cty.Value{
|
|
|
|
cty.StringVal("a"),
|
|
|
|
cty.StringVal("b"),
|
|
|
|
cty.StringVal("c"),
|
|
|
|
}),
|
|
|
|
cty.UnknownVal(cty.String),
|
2020-12-10 22:56:06 +01:00
|
|
|
"argument must be list, set, or tuple of number values",
|
2020-04-15 20:27:06 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.ListVal([]cty.Value{
|
|
|
|
cty.NumberIntVal(10),
|
|
|
|
cty.NumberIntVal(-19),
|
|
|
|
cty.NumberIntVal(5),
|
|
|
|
}),
|
|
|
|
cty.NumberIntVal(-4),
|
2020-12-10 22:56:06 +01:00
|
|
|
"",
|
2020-04-15 20:27:06 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.ListVal([]cty.Value{
|
|
|
|
cty.NumberFloatVal(10.2),
|
|
|
|
cty.NumberFloatVal(19.4),
|
|
|
|
cty.NumberFloatVal(5.7),
|
|
|
|
}),
|
|
|
|
cty.NumberFloatVal(35.3),
|
2020-12-10 22:56:06 +01:00
|
|
|
"",
|
2020-04-15 20:27:06 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.ListVal([]cty.Value{
|
|
|
|
cty.NumberFloatVal(-10.2),
|
|
|
|
cty.NumberFloatVal(-19.4),
|
|
|
|
cty.NumberFloatVal(-5.7),
|
|
|
|
}),
|
|
|
|
cty.NumberFloatVal(-35.3),
|
2020-12-10 22:56:06 +01:00
|
|
|
"",
|
2020-04-15 20:27:06 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.ListVal([]cty.Value{cty.NullVal(cty.Number)}),
|
|
|
|
cty.NilVal,
|
2020-12-10 22:56:06 +01:00
|
|
|
"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",
|
2020-04-15 20:27:06 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.SetVal([]cty.Value{
|
|
|
|
cty.StringVal("a"),
|
|
|
|
cty.StringVal("b"),
|
|
|
|
cty.StringVal("c"),
|
|
|
|
}),
|
|
|
|
cty.UnknownVal(cty.String),
|
2020-12-10 22:56:06 +01:00
|
|
|
"argument must be list, set, or tuple of number values",
|
2020-04-15 20:27:06 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.SetVal([]cty.Value{
|
|
|
|
cty.NumberIntVal(10),
|
|
|
|
cty.NumberIntVal(-19),
|
|
|
|
cty.NumberIntVal(5),
|
|
|
|
}),
|
|
|
|
cty.NumberIntVal(-4),
|
2020-12-10 22:56:06 +01:00
|
|
|
"",
|
2020-04-15 20:27:06 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.SetVal([]cty.Value{
|
|
|
|
cty.NumberIntVal(10),
|
|
|
|
cty.NumberIntVal(25),
|
|
|
|
cty.NumberIntVal(30),
|
|
|
|
}),
|
|
|
|
cty.NumberIntVal(65),
|
2020-12-10 22:56:06 +01:00
|
|
|
"",
|
2020-04-15 20:27:06 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.SetVal([]cty.Value{
|
|
|
|
cty.NumberFloatVal(2340.8),
|
|
|
|
cty.NumberFloatVal(10.2),
|
|
|
|
cty.NumberFloatVal(3),
|
|
|
|
}),
|
|
|
|
cty.NumberFloatVal(2354),
|
2020-12-10 22:56:06 +01:00
|
|
|
"",
|
2020-04-15 20:27:06 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.SetVal([]cty.Value{
|
|
|
|
cty.NumberFloatVal(2),
|
|
|
|
}),
|
|
|
|
cty.NumberFloatVal(2),
|
2020-12-10 22:56:06 +01:00
|
|
|
"",
|
2020-04-15 20:27:06 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.SetVal([]cty.Value{
|
|
|
|
cty.NumberFloatVal(-2),
|
|
|
|
cty.NumberFloatVal(-50),
|
|
|
|
cty.NumberFloatVal(-20),
|
|
|
|
cty.NumberFloatVal(-123),
|
|
|
|
cty.NumberFloatVal(-4),
|
|
|
|
}),
|
|
|
|
cty.NumberFloatVal(-199),
|
2020-12-10 22:56:06 +01:00
|
|
|
"",
|
2020-04-15 20:27:06 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.TupleVal([]cty.Value{
|
|
|
|
cty.NumberIntVal(12),
|
|
|
|
cty.StringVal("a"),
|
|
|
|
cty.NumberIntVal(38),
|
|
|
|
}),
|
|
|
|
cty.UnknownVal(cty.String),
|
2020-12-10 22:56:06 +01:00
|
|
|
"argument must be list, set, or tuple of number values",
|
2020-04-15 20:27:06 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.NumberIntVal(12),
|
|
|
|
cty.NilVal,
|
2020-12-10 22:56:06 +01:00
|
|
|
"cannot sum noniterable",
|
2020-04-15 20:27:06 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.ListValEmpty(cty.Number),
|
|
|
|
cty.NilVal,
|
2020-12-10 22:56:06 +01:00
|
|
|
"cannot sum an empty list",
|
2020-04-15 20:27:06 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.MapVal(map[string]cty.Value{"hello": cty.True}),
|
|
|
|
cty.NilVal,
|
2020-12-10 22:56:06 +01:00
|
|
|
"argument must be list, set, or tuple. Received map of bool",
|
2020-04-15 20:27:06 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
cty.UnknownVal(cty.Number),
|
|
|
|
cty.UnknownVal(cty.Number),
|
2020-12-10 22:56:06 +01:00
|
|
|
"",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
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",
|
2020-04-15 20:27:06 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Run(fmt.Sprintf("sum(%#v)", test.List), func(t *testing.T) {
|
|
|
|
got, err := Sum(test.List)
|
|
|
|
|
2020-12-10 22:56:06 +01:00
|
|
|
if test.Err != "" {
|
2020-04-15 20:27:06 +02:00
|
|
|
if err == nil {
|
|
|
|
t.Fatal("succeeded; want error")
|
2020-12-10 22:56:06 +01:00
|
|
|
} else if got, want := err.Error(), test.Err; got != want {
|
|
|
|
t.Fatalf("wrong error\n got: %s\nwant: %s", got, want)
|
2020-04-15 20:27:06 +02:00
|
|
|
}
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-31 23:46:24 +02:00
|
|
|
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,
|
|
|
|
},
|
2018-06-06 19:43:58 +02:00
|
|
|
{ // 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,
|
|
|
|
},
|
2018-05-31 23:46:24 +02:00
|
|
|
{ // bad map - empty value
|
|
|
|
cty.MapVal(map[string]cty.Value{
|
|
|
|
"key1": cty.ListValEmpty(cty.String),
|
|
|
|
}),
|
2019-11-08 18:40:39 +01:00
|
|
|
cty.MapValEmpty(cty.List(cty.String)),
|
|
|
|
false,
|
2018-05-31 23:46:24 +02:00
|
|
|
},
|
|
|
|
{ // bad map - value not a list
|
|
|
|
cty.MapVal(map[string]cty.Value{
|
|
|
|
"key1": cty.StringVal("a"),
|
|
|
|
}),
|
|
|
|
cty.NilVal,
|
|
|
|
true,
|
|
|
|
},
|
2021-04-20 18:29:23 +02:00
|
|
|
{ // 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,
|
|
|
|
},
|
2018-05-31 23:46:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|