opentofu/internal/configs/hcl2shim/flatmap_test.go
namgyalangmo cb2e9119aa
Update copyright notice (#1232)
Signed-off-by: namgyalangmo <75657887+namgyalangmo@users.noreply.github.com>
2024-02-08 09:48:59 +00:00

758 lines
16 KiB
Go

// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package hcl2shim
import (
"fmt"
"testing"
"github.com/go-test/deep"
"github.com/zclconf/go-cty/cty"
)
func TestFlatmapValueFromHCL2(t *testing.T) {
tests := []struct {
Value cty.Value
Want map[string]string
}{
{
cty.EmptyObjectVal,
map[string]string{},
},
{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.StringVal("hello"),
}),
map[string]string{
"foo": "hello",
},
},
{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.UnknownVal(cty.Bool),
}),
map[string]string{
"foo": UnknownVariableValue,
},
},
{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.NumberIntVal(12),
}),
map[string]string{
"foo": "12",
},
},
{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.True,
"bar": cty.False,
}),
map[string]string{
"foo": "true",
"bar": "false",
},
},
{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.StringVal("hello"),
"bar": cty.StringVal("world"),
"baz": cty.StringVal("whelp"),
}),
map[string]string{
"foo": "hello",
"bar": "world",
"baz": "whelp",
},
},
{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.ListValEmpty(cty.String),
}),
map[string]string{
"foo.#": "0",
},
},
{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.UnknownVal(cty.List(cty.String)),
}),
map[string]string{
"foo.#": UnknownVariableValue,
},
},
{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.ListVal([]cty.Value{
cty.StringVal("hello"),
}),
}),
map[string]string{
"foo.#": "1",
"foo.0": "hello",
},
},
{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.ListVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("world"),
}),
}),
map[string]string{
"foo.#": "2",
"foo.0": "hello",
"foo.1": "world",
},
},
{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.MapVal(map[string]cty.Value{
"hello": cty.NumberIntVal(12),
"hello.world": cty.NumberIntVal(10),
}),
}),
map[string]string{
"foo.%": "2",
"foo.hello": "12",
"foo.hello.world": "10",
},
},
{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.UnknownVal(cty.Map(cty.String)),
}),
map[string]string{
"foo.%": UnknownVariableValue,
},
},
{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.MapVal(map[string]cty.Value{
"hello": cty.NumberIntVal(12),
"hello.world": cty.NumberIntVal(10),
}),
}),
map[string]string{
"foo.%": "2",
"foo.hello": "12",
"foo.hello.world": "10",
},
},
{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.SetVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("world"),
}),
}),
map[string]string{
"foo.#": "2",
"foo.0": "hello",
"foo.1": "world",
},
},
{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.UnknownVal(cty.Set(cty.Number)),
}),
map[string]string{
"foo.#": UnknownVariableValue,
},
},
{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.ListVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("hello"),
"baz": cty.StringVal("world"),
}),
cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("bloo"),
"baz": cty.StringVal("blaa"),
}),
}),
}),
map[string]string{
"foo.#": "2",
"foo.0.bar": "hello",
"foo.0.baz": "world",
"foo.1.bar": "bloo",
"foo.1.baz": "blaa",
},
},
{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.ListVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("hello"),
"baz": cty.ListVal([]cty.Value{
cty.True,
cty.True,
}),
}),
cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("bloo"),
"baz": cty.ListVal([]cty.Value{
cty.False,
cty.True,
}),
}),
}),
}),
map[string]string{
"foo.#": "2",
"foo.0.bar": "hello",
"foo.0.baz.#": "2",
"foo.0.baz.0": "true",
"foo.0.baz.1": "true",
"foo.1.bar": "bloo",
"foo.1.baz.#": "2",
"foo.1.baz.0": "false",
"foo.1.baz.1": "true",
},
},
{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.ListVal([]cty.Value{
cty.UnknownVal(cty.Object(map[string]cty.Type{
"bar": cty.String,
"baz": cty.List(cty.Bool),
"bap": cty.Map(cty.Number),
})),
}),
}),
map[string]string{
"foo.#": "1",
"foo.0.bar": UnknownVariableValue,
"foo.0.baz.#": UnknownVariableValue,
"foo.0.bap.%": UnknownVariableValue,
},
},
{
cty.NullVal(cty.Object(map[string]cty.Type{
"foo": cty.Set(cty.Object(map[string]cty.Type{
"bar": cty.String,
})),
})),
nil,
},
}
for _, test := range tests {
t.Run(test.Value.GoString(), func(t *testing.T) {
got := FlatmapValueFromHCL2(test.Value)
for _, problem := range deep.Equal(got, test.Want) {
t.Error(problem)
}
})
}
}
func TestFlatmapValueFromHCL2FromFlatmap(t *testing.T) {
tests := []struct {
Name string
Map map[string]string
Type cty.Type
}{
{
"empty flatmap with collections",
map[string]string{},
cty.Object(map[string]cty.Type{
"foo": cty.Map(cty.String),
"bar": cty.Set(cty.String),
}),
},
{
"nil flatmap with collections",
nil,
cty.Object(map[string]cty.Type{
"foo": cty.Map(cty.String),
"bar": cty.Set(cty.String),
}),
},
{
"empty flatmap with nested collections",
map[string]string{},
cty.Object(map[string]cty.Type{
"foo": cty.Object(
map[string]cty.Type{
"baz": cty.Map(cty.String),
},
),
"bar": cty.Set(cty.String),
}),
},
{
"partial flatmap with nested collections",
map[string]string{
"foo.baz.%": "1",
"foo.baz.key": "val",
},
cty.Object(map[string]cty.Type{
"foo": cty.Object(
map[string]cty.Type{
"baz": cty.Map(cty.String),
"biz": cty.Map(cty.String),
},
),
"bar": cty.Set(cty.String),
}),
},
}
for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
val, err := HCL2ValueFromFlatmap(test.Map, test.Type)
if err != nil {
t.Fatal(err)
}
got := FlatmapValueFromHCL2(val)
for _, problem := range deep.Equal(got, test.Map) {
t.Error(problem)
}
})
}
}
func TestHCL2ValueFromFlatmap(t *testing.T) {
tests := []struct {
Flatmap map[string]string
Type cty.Type
Want cty.Value
WantErr string
}{
{
Flatmap: map[string]string{},
Type: cty.EmptyObject,
Want: cty.EmptyObjectVal,
},
{
Flatmap: map[string]string{
"ignored": "foo",
},
Type: cty.EmptyObject,
Want: cty.EmptyObjectVal,
},
{
Flatmap: map[string]string{
"foo": "blah",
"bar": "true",
"baz": "12.5",
"unk": UnknownVariableValue,
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.String,
"bar": cty.Bool,
"baz": cty.Number,
"unk": cty.Bool,
}),
Want: cty.ObjectVal(map[string]cty.Value{
"foo": cty.StringVal("blah"),
"bar": cty.True,
"baz": cty.NumberFloatVal(12.5),
"unk": cty.UnknownVal(cty.Bool),
}),
},
{
Flatmap: map[string]string{
"foo.#": "0",
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.List(cty.String),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"foo": cty.ListValEmpty(cty.String),
}),
},
{
Flatmap: map[string]string{
"foo.#": UnknownVariableValue,
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.List(cty.String),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"foo": cty.UnknownVal(cty.List(cty.String)),
}),
},
{
Flatmap: map[string]string{
"foo.#": "1",
"foo.0": "hello",
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.List(cty.String),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"foo": cty.ListVal([]cty.Value{
cty.StringVal("hello"),
}),
}),
},
{
Flatmap: map[string]string{
"foo.#": "2",
"foo.0": "true",
"foo.1": "false",
"foo.2": "ignored", // (because the count is 2, so this is out of range)
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.List(cty.Bool),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"foo": cty.ListVal([]cty.Value{
cty.True,
cty.False,
}),
}),
},
{
Flatmap: map[string]string{
"foo.#": "2",
"foo.0": "hello",
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.Tuple([]cty.Type{
cty.String,
cty.Bool,
}),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"foo": cty.TupleVal([]cty.Value{
cty.StringVal("hello"),
cty.NullVal(cty.Bool),
}),
}),
},
{
Flatmap: map[string]string{
"foo.#": UnknownVariableValue,
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.Tuple([]cty.Type{
cty.String,
cty.Bool,
}),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"foo": cty.UnknownVal(cty.Tuple([]cty.Type{
cty.String,
cty.Bool,
})),
}),
},
{
Flatmap: map[string]string{
"foo.#": "0",
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.Set(cty.String),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"foo": cty.SetValEmpty(cty.String),
}),
},
{
Flatmap: map[string]string{
"foo.#": UnknownVariableValue,
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.Set(cty.String),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"foo": cty.UnknownVal(cty.Set(cty.String)),
}),
},
{
Flatmap: map[string]string{
"foo.#": "1",
"foo.24534534": "hello",
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.Set(cty.String),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"foo": cty.SetVal([]cty.Value{
cty.StringVal("hello"),
}),
}),
},
{
Flatmap: map[string]string{
"foo.#": "1",
"foo.24534534": "true",
"foo.95645644": "true",
"foo.34533452": "false",
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.Set(cty.Bool),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"foo": cty.SetVal([]cty.Value{
cty.True,
cty.False,
}),
}),
},
{
Flatmap: map[string]string{
"foo.%": "0",
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.Map(cty.String),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"foo": cty.MapValEmpty(cty.String),
}),
},
{
Flatmap: map[string]string{
"foo.%": "2",
"foo.baz": "true",
"foo.bar.baz": "false",
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.Map(cty.Bool),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"foo": cty.MapVal(map[string]cty.Value{
"baz": cty.True,
"bar.baz": cty.False,
}),
}),
},
{
Flatmap: map[string]string{
"foo.%": UnknownVariableValue,
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.Map(cty.Bool),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"foo": cty.UnknownVal(cty.Map(cty.Bool)),
}),
},
{
Flatmap: map[string]string{
"foo.#": "2",
"foo.0.bar": "hello",
"foo.0.baz": "1",
"foo.1.bar": "world",
"foo.1.baz": "false",
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.List(cty.Object(map[string]cty.Type{
"bar": cty.String,
"baz": cty.Bool,
})),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"foo": cty.ListVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("hello"),
"baz": cty.True,
}),
cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("world"),
"baz": cty.False,
}),
}),
}),
},
{
Flatmap: map[string]string{
"foo.#": "2",
"foo.34534534.bar": "hello",
"foo.34534534.baz": "1",
"foo.93453345.bar": "world",
"foo.93453345.baz": "false",
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.Set(cty.Object(map[string]cty.Type{
"bar": cty.String,
"baz": cty.Bool,
})),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"foo": cty.SetVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("hello"),
"baz": cty.True,
}),
cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("world"),
"baz": cty.False,
}),
}),
}),
},
{
Flatmap: map[string]string{
"foo.#": "not-valid",
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.List(cty.String),
}),
WantErr: `invalid count value for "foo." in state: strconv.Atoi: parsing "not-valid": invalid syntax`,
},
{
Flatmap: nil,
Type: cty.Object(map[string]cty.Type{
"foo": cty.Set(cty.Object(map[string]cty.Type{
"bar": cty.String,
})),
}),
Want: cty.NullVal(cty.Object(map[string]cty.Type{
"foo": cty.Set(cty.Object(map[string]cty.Type{
"bar": cty.String,
})),
})),
},
{
Flatmap: map[string]string{
"foo.#": "2",
"foo.0.%": "2",
"foo.0.a": "a",
"foo.0.b": "b",
"foo.1.%": "1",
"foo.1.a": "a",
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.List(cty.Map(cty.String)),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"foo": cty.ListVal([]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.StringVal("a"),
"b": cty.StringVal("b"),
}),
cty.MapVal(map[string]cty.Value{
"a": cty.StringVal("a"),
}),
}),
}),
},
{
Flatmap: map[string]string{
"single.#": "1",
"single.~1.value": "a",
"single.~1.optional": UnknownVariableValue,
"two.#": "2",
"two.~2381914684.value": "a",
"two.~2381914684.optional": UnknownVariableValue,
"two.~2798940671.value": "b",
"two.~2798940671.optional": UnknownVariableValue,
},
Type: cty.Object(map[string]cty.Type{
"single": cty.Set(
cty.Object(map[string]cty.Type{
"value": cty.String,
"optional": cty.String,
}),
),
"two": cty.Set(
cty.Object(map[string]cty.Type{
"optional": cty.String,
"value": cty.String,
}),
),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"single": cty.SetVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"value": cty.StringVal("a"),
"optional": cty.UnknownVal(cty.String),
}),
}),
"two": cty.SetVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"value": cty.StringVal("a"),
"optional": cty.UnknownVal(cty.String),
}),
cty.ObjectVal(map[string]cty.Value{
"value": cty.StringVal("b"),
"optional": cty.UnknownVal(cty.String),
}),
}),
}),
},
{
Flatmap: map[string]string{
"foo.#": "1",
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.Set(cty.Object(map[string]cty.Type{
"bar": cty.String,
})),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"foo": cty.SetVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"bar": cty.NullVal(cty.String),
}),
}),
}),
},
{
Flatmap: map[string]string{
"multi.#": "1",
"multi.2.set.#": "1",
"multi.2.set.3.required": "val",
},
Type: cty.Object(map[string]cty.Type{
"multi": cty.Set(cty.Object(map[string]cty.Type{
"set": cty.Set(cty.Object(map[string]cty.Type{
"required": cty.String,
})),
})),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"multi": cty.SetVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"set": cty.SetVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"required": cty.StringVal("val"),
}),
}),
}),
}),
}),
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("%d %#v as %#v", i, test.Flatmap, test.Type), func(t *testing.T) {
got, err := HCL2ValueFromFlatmap(test.Flatmap, test.Type)
if test.WantErr != "" {
if err == nil {
t.Fatalf("succeeded; want error: %s", test.WantErr)
}
if got, want := err.Error(), test.WantErr; got != want {
t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want)
}
if got == cty.NilVal {
t.Fatalf("result is cty.NilVal; want valid placeholder value")
}
return
} else {
if err != nil {
t.Fatalf("unexpected error: %s", err.Error())
}
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}