| 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) |
| } |
| }) |
| } |
| } |