summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/hashicorp/hcl/decoder_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/hcl/decoder_test.go')
-rw-r--r--vendor/github.com/hashicorp/hcl/decoder_test.go1165
1 files changed, 1165 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/hcl/decoder_test.go b/vendor/github.com/hashicorp/hcl/decoder_test.go
new file mode 100644
index 000000000..480be4fa6
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl/decoder_test.go
@@ -0,0 +1,1165 @@
+package hcl
+
+import (
+ "io/ioutil"
+ "path/filepath"
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/davecgh/go-spew/spew"
+ "github.com/hashicorp/hcl/hcl/ast"
+ "github.com/hashicorp/hcl/testhelper"
+)
+
+func TestDecode_interface(t *testing.T) {
+ cases := []struct {
+ File string
+ Err bool
+ Out interface{}
+ }{
+ {
+ "basic.hcl",
+ false,
+ map[string]interface{}{
+ "foo": "bar",
+ "bar": "${file(\"bing/bong.txt\")}",
+ },
+ },
+ {
+ "basic_squish.hcl",
+ false,
+ map[string]interface{}{
+ "foo": "bar",
+ "bar": "${file(\"bing/bong.txt\")}",
+ "foo-bar": "baz",
+ },
+ },
+ {
+ "empty.hcl",
+ false,
+ map[string]interface{}{
+ "resource": []map[string]interface{}{
+ map[string]interface{}{
+ "foo": []map[string]interface{}{
+ map[string]interface{}{},
+ },
+ },
+ },
+ },
+ },
+ {
+ "tfvars.hcl",
+ false,
+ map[string]interface{}{
+ "regularvar": "Should work",
+ "map.key1": "Value",
+ "map.key2": "Other value",
+ },
+ },
+ {
+ "escape.hcl",
+ false,
+ map[string]interface{}{
+ "foo": "bar\"baz\\n",
+ "qux": "back\\slash",
+ "bar": "new\nline",
+ "qax": `slash\:colon`,
+ "nested": `${HH\\:mm\\:ss}`,
+ "nestedquotes": `${"\"stringwrappedinquotes\""}`,
+ },
+ },
+ {
+ "float.hcl",
+ false,
+ map[string]interface{}{
+ "a": 1.02,
+ },
+ },
+ {
+ "multiline_bad.hcl",
+ true,
+ nil,
+ },
+ {
+ "multiline_literal.hcl",
+ true,
+ nil,
+ },
+ {
+ "multiline_literal_with_hil.hcl",
+ false,
+ map[string]interface{}{"multiline_literal_with_hil": testhelper.Unix2dos(`${hello
+ world}`)},
+ },
+ {
+ "multiline_no_marker.hcl",
+ true,
+ nil,
+ },
+ {
+ "multiline.hcl",
+ false,
+ map[string]interface{}{"foo": testhelper.Unix2dos("bar\nbaz\n")},
+ },
+ {
+ "multiline_indented.hcl",
+ false,
+ map[string]interface{}{"foo": testhelper.Unix2dos(" bar\n baz\n")},
+ },
+ {
+ "multiline_no_hanging_indent.hcl",
+ false,
+ map[string]interface{}{"foo": testhelper.Unix2dos(" baz\n bar\n foo\n")},
+ },
+ {
+ "multiline_no_eof.hcl",
+ false,
+ map[string]interface{}{"foo": testhelper.Unix2dos("bar\nbaz\n"), "key": "value"},
+ },
+ {
+ "multiline.json",
+ false,
+ map[string]interface{}{"foo": "bar\nbaz"},
+ },
+ {
+ "null_strings.json",
+ false,
+ map[string]interface{}{
+ "module": []map[string]interface{}{
+ map[string]interface{}{
+ "app": []map[string]interface{}{
+ map[string]interface{}{"foo": ""},
+ },
+ },
+ },
+ },
+ },
+ {
+ "scientific.json",
+ false,
+ map[string]interface{}{
+ "a": 1e-10,
+ "b": 1e+10,
+ "c": 1e10,
+ "d": 1.2e-10,
+ "e": 1.2e+10,
+ "f": 1.2e10,
+ },
+ },
+ {
+ "scientific.hcl",
+ false,
+ map[string]interface{}{
+ "a": 1e-10,
+ "b": 1e+10,
+ "c": 1e10,
+ "d": 1.2e-10,
+ "e": 1.2e+10,
+ "f": 1.2e10,
+ },
+ },
+ {
+ "terraform_heroku.hcl",
+ false,
+ map[string]interface{}{
+ "name": "terraform-test-app",
+ "config_vars": []map[string]interface{}{
+ map[string]interface{}{
+ "FOO": "bar",
+ },
+ },
+ },
+ },
+ {
+ "structure_multi.hcl",
+ false,
+ map[string]interface{}{
+ "foo": []map[string]interface{}{
+ map[string]interface{}{
+ "baz": []map[string]interface{}{
+ map[string]interface{}{"key": 7},
+ },
+ },
+ map[string]interface{}{
+ "bar": []map[string]interface{}{
+ map[string]interface{}{"key": 12},
+ },
+ },
+ },
+ },
+ },
+ {
+ "structure_multi.json",
+ false,
+ map[string]interface{}{
+ "foo": []map[string]interface{}{
+ map[string]interface{}{
+ "baz": []map[string]interface{}{
+ map[string]interface{}{"key": 7},
+ },
+ },
+ map[string]interface{}{
+ "bar": []map[string]interface{}{
+ map[string]interface{}{"key": 12},
+ },
+ },
+ },
+ },
+ },
+ {
+ "list_of_lists.hcl",
+ false,
+ map[string]interface{}{
+ "foo": []interface{}{
+ []interface{}{"foo"},
+ []interface{}{"bar"},
+ },
+ },
+ },
+ {
+ "list_of_maps.hcl",
+ false,
+ map[string]interface{}{
+ "foo": []interface{}{
+ map[string]interface{}{"somekey1": "someval1"},
+ map[string]interface{}{"somekey2": "someval2", "someextrakey": "someextraval"},
+ },
+ },
+ },
+ {
+ "assign_deep.hcl",
+ false,
+ map[string]interface{}{
+ "resource": []interface{}{
+ map[string]interface{}{
+ "foo": []interface{}{
+ map[string]interface{}{
+ "bar": []map[string]interface{}{
+ map[string]interface{}{}}}}}}},
+ },
+ {
+ "structure_list.hcl",
+ false,
+ map[string]interface{}{
+ "foo": []map[string]interface{}{
+ map[string]interface{}{
+ "key": 7,
+ },
+ map[string]interface{}{
+ "key": 12,
+ },
+ },
+ },
+ },
+ {
+ "structure_list.json",
+ false,
+ map[string]interface{}{
+ "foo": []map[string]interface{}{
+ map[string]interface{}{
+ "key": 7,
+ },
+ map[string]interface{}{
+ "key": 12,
+ },
+ },
+ },
+ },
+ {
+ "structure_list_deep.json",
+ false,
+ map[string]interface{}{
+ "bar": []map[string]interface{}{
+ map[string]interface{}{
+ "foo": []map[string]interface{}{
+ map[string]interface{}{
+ "name": "terraform_example",
+ "ingress": []map[string]interface{}{
+ map[string]interface{}{
+ "from_port": 22,
+ },
+ map[string]interface{}{
+ "from_port": 80,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+
+ {
+ "structure_list_empty.json",
+ false,
+ map[string]interface{}{
+ "foo": []interface{}{},
+ },
+ },
+
+ {
+ "nested_block_comment.hcl",
+ false,
+ map[string]interface{}{
+ "bar": "value",
+ },
+ },
+
+ {
+ "unterminated_block_comment.hcl",
+ true,
+ nil,
+ },
+
+ {
+ "unterminated_brace.hcl",
+ true,
+ nil,
+ },
+
+ {
+ "nested_provider_bad.hcl",
+ true,
+ nil,
+ },
+
+ {
+ "object_list.json",
+ false,
+ map[string]interface{}{
+ "resource": []map[string]interface{}{
+ map[string]interface{}{
+ "aws_instance": []map[string]interface{}{
+ map[string]interface{}{
+ "db": []map[string]interface{}{
+ map[string]interface{}{
+ "vpc": "foo",
+ "provisioner": []map[string]interface{}{
+ map[string]interface{}{
+ "file": []map[string]interface{}{
+ map[string]interface{}{
+ "source": "foo",
+ "destination": "bar",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+
+ // Terraform GH-8295 sanity test that basic decoding into
+ // interface{} works.
+ {
+ "terraform_variable_invalid.json",
+ false,
+ map[string]interface{}{
+ "variable": []map[string]interface{}{
+ map[string]interface{}{
+ "whatever": "abc123",
+ },
+ },
+ },
+ },
+
+ {
+ "interpolate.json",
+ false,
+ map[string]interface{}{
+ "default": `${replace("europe-west", "-", " ")}`,
+ },
+ },
+
+ {
+ "block_assign.hcl",
+ true,
+ nil,
+ },
+
+ {
+ "escape_backslash.hcl",
+ false,
+ map[string]interface{}{
+ "output": []map[string]interface{}{
+ map[string]interface{}{
+ "one": `${replace(var.sub_domain, ".", "\\.")}`,
+ "two": `${replace(var.sub_domain, ".", "\\\\.")}`,
+ "many": `${replace(var.sub_domain, ".", "\\\\\\\\.")}`,
+ },
+ },
+ },
+ },
+
+ {
+ "git_crypt.hcl",
+ true,
+ nil,
+ },
+
+ {
+ "object_with_bool.hcl",
+ false,
+ map[string]interface{}{
+ "path": []map[string]interface{}{
+ map[string]interface{}{
+ "policy": "write",
+ "permissions": []map[string]interface{}{
+ map[string]interface{}{
+ "bool": []interface{}{false},
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ for _, tc := range cases {
+ t.Logf("Testing: %s", tc.File)
+ d, err := ioutil.ReadFile(filepath.Join(fixtureDir, tc.File))
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ var out interface{}
+ err = Decode(&out, string(d))
+ if (err != nil) != tc.Err {
+ t.Fatalf("Input: %s\n\nError: %s", tc.File, err)
+ }
+
+ if !reflect.DeepEqual(out, tc.Out) {
+ t.Fatalf("Input: %s. Actual, Expected.\n\n%#v\n\n%#v", tc.File, out, tc.Out)
+ }
+
+ var v interface{}
+ err = Unmarshal(d, &v)
+ if (err != nil) != tc.Err {
+ t.Fatalf("Input: %s\n\nError: %s", tc.File, err)
+ }
+
+ if !reflect.DeepEqual(v, tc.Out) {
+ t.Fatalf("Input: %s. Actual, Expected.\n\n%#v\n\n%#v", tc.File, out, tc.Out)
+ }
+ }
+}
+
+func TestDecode_interfaceInline(t *testing.T) {
+ cases := []struct {
+ Value string
+ Err bool
+ Out interface{}
+ }{
+ {"t t e{{}}", true, nil},
+ {"t=0t d {}", true, map[string]interface{}{"t": 0}},
+ {"v=0E0v d{}", true, map[string]interface{}{"v": float64(0)}},
+ }
+
+ for _, tc := range cases {
+ t.Logf("Testing: %q", tc.Value)
+
+ var out interface{}
+ err := Decode(&out, tc.Value)
+ if (err != nil) != tc.Err {
+ t.Fatalf("Input: %q\n\nError: %s", tc.Value, err)
+ }
+
+ if !reflect.DeepEqual(out, tc.Out) {
+ t.Fatalf("Input: %q. Actual, Expected.\n\n%#v\n\n%#v", tc.Value, out, tc.Out)
+ }
+
+ var v interface{}
+ err = Unmarshal([]byte(tc.Value), &v)
+ if (err != nil) != tc.Err {
+ t.Fatalf("Input: %q\n\nError: %s", tc.Value, err)
+ }
+
+ if !reflect.DeepEqual(v, tc.Out) {
+ t.Fatalf("Input: %q. Actual, Expected.\n\n%#v\n\n%#v", tc.Value, out, tc.Out)
+ }
+ }
+}
+
+func TestDecode_equal(t *testing.T) {
+ cases := []struct {
+ One, Two string
+ }{
+ {
+ "basic.hcl",
+ "basic.json",
+ },
+ {
+ "float.hcl",
+ "float.json",
+ },
+ /*
+ {
+ "structure.hcl",
+ "structure.json",
+ },
+ */
+ {
+ "structure.hcl",
+ "structure_flat.json",
+ },
+ {
+ "terraform_heroku.hcl",
+ "terraform_heroku.json",
+ },
+ }
+
+ for _, tc := range cases {
+ p1 := filepath.Join(fixtureDir, tc.One)
+ p2 := filepath.Join(fixtureDir, tc.Two)
+
+ d1, err := ioutil.ReadFile(p1)
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ d2, err := ioutil.ReadFile(p2)
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ var i1, i2 interface{}
+ err = Decode(&i1, string(d1))
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ err = Decode(&i2, string(d2))
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if !reflect.DeepEqual(i1, i2) {
+ t.Fatalf(
+ "%s != %s\n\n%#v\n\n%#v",
+ tc.One, tc.Two,
+ i1, i2)
+ }
+ }
+}
+
+func TestDecode_flatMap(t *testing.T) {
+ var val map[string]map[string]string
+
+ err := Decode(&val, testReadFile(t, "structure_flatmap.hcl"))
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ expected := map[string]map[string]string{
+ "foo": map[string]string{
+ "foo": "bar",
+ "key": "7",
+ },
+ }
+
+ if !reflect.DeepEqual(val, expected) {
+ t.Fatalf("Actual: %#v\n\nExpected: %#v", val, expected)
+ }
+}
+
+func TestDecode_structure(t *testing.T) {
+ type Embedded interface{}
+
+ type V struct {
+ Embedded `hcl:"-"`
+ Key int
+ Foo string
+ }
+
+ var actual V
+
+ err := Decode(&actual, testReadFile(t, "flat.hcl"))
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ expected := V{
+ Key: 7,
+ Foo: "bar",
+ }
+
+ if !reflect.DeepEqual(actual, expected) {
+ t.Fatalf("Actual: %#v\n\nExpected: %#v", actual, expected)
+ }
+}
+
+func TestDecode_structurePtr(t *testing.T) {
+ type V struct {
+ Key int
+ Foo string
+ }
+
+ var actual *V
+
+ err := Decode(&actual, testReadFile(t, "flat.hcl"))
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ expected := &V{
+ Key: 7,
+ Foo: "bar",
+ }
+
+ if !reflect.DeepEqual(actual, expected) {
+ t.Fatalf("Actual: %#v\n\nExpected: %#v", actual, expected)
+ }
+}
+
+func TestDecode_structureArray(t *testing.T) {
+ // This test is extracted from a failure in Consul (consul.io),
+ // hence the interesting structure naming.
+
+ type KeyPolicyType string
+
+ type KeyPolicy struct {
+ Prefix string `hcl:",key"`
+ Policy KeyPolicyType
+ }
+
+ type Policy struct {
+ Keys []KeyPolicy `hcl:"key,expand"`
+ }
+
+ expected := Policy{
+ Keys: []KeyPolicy{
+ KeyPolicy{
+ Prefix: "",
+ Policy: "read",
+ },
+ KeyPolicy{
+ Prefix: "foo/",
+ Policy: "write",
+ },
+ KeyPolicy{
+ Prefix: "foo/bar/",
+ Policy: "read",
+ },
+ KeyPolicy{
+ Prefix: "foo/bar/baz",
+ Policy: "deny",
+ },
+ },
+ }
+
+ files := []string{
+ "decode_policy.hcl",
+ "decode_policy.json",
+ }
+
+ for _, f := range files {
+ var actual Policy
+
+ err := Decode(&actual, testReadFile(t, f))
+ if err != nil {
+ t.Fatalf("Input: %s\n\nerr: %s", f, err)
+ }
+
+ if !reflect.DeepEqual(actual, expected) {
+ t.Fatalf("Input: %s\n\nActual: %#v\n\nExpected: %#v", f, actual, expected)
+ }
+ }
+}
+
+func TestDecode_sliceExpand(t *testing.T) {
+ type testInner struct {
+ Name string `hcl:",key"`
+ Key string
+ }
+
+ type testStruct struct {
+ Services []testInner `hcl:"service,expand"`
+ }
+
+ expected := testStruct{
+ Services: []testInner{
+ testInner{
+ Name: "my-service-0",
+ Key: "value",
+ },
+ testInner{
+ Name: "my-service-1",
+ Key: "value",
+ },
+ },
+ }
+
+ files := []string{
+ "slice_expand.hcl",
+ }
+
+ for _, f := range files {
+ t.Logf("Testing: %s", f)
+
+ var actual testStruct
+ err := Decode(&actual, testReadFile(t, f))
+ if err != nil {
+ t.Fatalf("Input: %s\n\nerr: %s", f, err)
+ }
+
+ if !reflect.DeepEqual(actual, expected) {
+ t.Fatalf("Input: %s\n\nActual: %#v\n\nExpected: %#v", f, actual, expected)
+ }
+ }
+}
+
+func TestDecode_structureMap(t *testing.T) {
+ // This test is extracted from a failure in Terraform (terraform.io),
+ // hence the interesting structure naming.
+
+ type hclVariable struct {
+ Default interface{}
+ Description string
+ Fields []string `hcl:",decodedFields"`
+ }
+
+ type rawConfig struct {
+ Variable map[string]hclVariable
+ }
+
+ expected := rawConfig{
+ Variable: map[string]hclVariable{
+ "foo": hclVariable{
+ Default: "bar",
+ Description: "bar",
+ Fields: []string{"Default", "Description"},
+ },
+
+ "amis": hclVariable{
+ Default: []map[string]interface{}{
+ map[string]interface{}{
+ "east": "foo",
+ },
+ },
+ Fields: []string{"Default"},
+ },
+ },
+ }
+
+ files := []string{
+ "decode_tf_variable.hcl",
+ "decode_tf_variable.json",
+ }
+
+ for _, f := range files {
+ t.Logf("Testing: %s", f)
+
+ var actual rawConfig
+ err := Decode(&actual, testReadFile(t, f))
+ if err != nil {
+ t.Fatalf("Input: %s\n\nerr: %s", f, err)
+ }
+
+ if !reflect.DeepEqual(actual, expected) {
+ t.Fatalf("Input: %s\n\nActual: %#v\n\nExpected: %#v", f, actual, expected)
+ }
+ }
+}
+
+func TestDecode_structureMapInvalid(t *testing.T) {
+ // Terraform GH-8295
+
+ type hclVariable struct {
+ Default interface{}
+ Description string
+ Fields []string `hcl:",decodedFields"`
+ }
+
+ type rawConfig struct {
+ Variable map[string]*hclVariable
+ }
+
+ var actual rawConfig
+ err := Decode(&actual, testReadFile(t, "terraform_variable_invalid.json"))
+ if err == nil {
+ t.Fatal("expected error")
+ }
+}
+
+func TestDecode_interfaceNonPointer(t *testing.T) {
+ var value interface{}
+ err := Decode(value, testReadFile(t, "basic_int_string.hcl"))
+ if err == nil {
+ t.Fatal("should error")
+ }
+}
+
+func TestDecode_intString(t *testing.T) {
+ var value struct {
+ Count int
+ }
+
+ err := Decode(&value, testReadFile(t, "basic_int_string.hcl"))
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if value.Count != 3 {
+ t.Fatalf("bad: %#v", value.Count)
+ }
+}
+
+func TestDecode_intStringAliased(t *testing.T) {
+ var value struct {
+ Count time.Duration
+ }
+
+ err := Decode(&value, testReadFile(t, "basic_int_string.hcl"))
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if value.Count != time.Duration(3) {
+ t.Fatalf("bad: %#v", value.Count)
+ }
+}
+
+func TestDecode_Node(t *testing.T) {
+ // given
+ var value struct {
+ Content ast.Node
+ Nested struct {
+ Content ast.Node
+ }
+ }
+
+ content := `
+content {
+ hello = "world"
+}
+`
+
+ // when
+ err := Decode(&value, content)
+
+ // then
+ if err != nil {
+ t.Errorf("unable to decode content, %v", err)
+ return
+ }
+
+ // verify ast.Node can be decoded later
+ var v map[string]interface{}
+ err = DecodeObject(&v, value.Content)
+ if err != nil {
+ t.Errorf("unable to decode content, %v", err)
+ return
+ }
+
+ if v["hello"] != "world" {
+ t.Errorf("expected mapping to be returned")
+ }
+}
+
+func TestDecode_NestedNode(t *testing.T) {
+ // given
+ var value struct {
+ Nested struct {
+ Content ast.Node
+ }
+ }
+
+ content := `
+nested "content" {
+ hello = "world"
+}
+`
+
+ // when
+ err := Decode(&value, content)
+
+ // then
+ if err != nil {
+ t.Errorf("unable to decode content, %v", err)
+ return
+ }
+
+ // verify ast.Node can be decoded later
+ var v map[string]interface{}
+ err = DecodeObject(&v, value.Nested.Content)
+ if err != nil {
+ t.Errorf("unable to decode content, %v", err)
+ return
+ }
+
+ if v["hello"] != "world" {
+ t.Errorf("expected mapping to be returned")
+ }
+}
+
+// https://github.com/hashicorp/hcl/issues/60
+func TestDecode_topLevelKeys(t *testing.T) {
+ type Template struct {
+ Source string
+ }
+
+ templates := struct {
+ Templates []*Template `hcl:"template"`
+ }{}
+
+ err := Decode(&templates, `
+ template {
+ source = "blah"
+ }
+
+ template {
+ source = "blahblah"
+ }`)
+
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if templates.Templates[0].Source != "blah" {
+ t.Errorf("bad source: %s", templates.Templates[0].Source)
+ }
+
+ if templates.Templates[1].Source != "blahblah" {
+ t.Errorf("bad source: %s", templates.Templates[1].Source)
+ }
+}
+
+func TestDecode_flattenedJSON(t *testing.T) {
+ // make sure we can also correctly extract a Name key too
+ type V struct {
+ Name string `hcl:",key"`
+ Description string
+ Default map[string]string
+ }
+ type Vars struct {
+ Variable []*V
+ }
+
+ cases := []struct {
+ JSON string
+ Out interface{}
+ Expected interface{}
+ }{
+ { // Nested object, no sibling keys
+ JSON: `
+{
+ "var_name": {
+ "default": {
+ "key1": "a",
+ "key2": "b"
+ }
+ }
+}
+ `,
+ Out: &[]*V{},
+ Expected: &[]*V{
+ &V{
+ Name: "var_name",
+ Default: map[string]string{"key1": "a", "key2": "b"},
+ },
+ },
+ },
+
+ { // Nested object with a sibling key (this worked previously)
+ JSON: `
+{
+ "var_name": {
+ "description": "Described",
+ "default": {
+ "key1": "a",
+ "key2": "b"
+ }
+ }
+}
+ `,
+ Out: &[]*V{},
+ Expected: &[]*V{
+ &V{
+ Name: "var_name",
+ Description: "Described",
+ Default: map[string]string{"key1": "a", "key2": "b"},
+ },
+ },
+ },
+
+ { // Multiple nested objects, one with a sibling key
+ JSON: `
+{
+ "variable": {
+ "var_1": {
+ "default": {
+ "key1": "a",
+ "key2": "b"
+ }
+ },
+ "var_2": {
+ "description": "Described",
+ "default": {
+ "key1": "a",
+ "key2": "b"
+ }
+ }
+ }
+}
+ `,
+ Out: &Vars{},
+ Expected: &Vars{
+ Variable: []*V{
+ &V{
+ Name: "var_1",
+ Default: map[string]string{"key1": "a", "key2": "b"},
+ },
+ &V{
+ Name: "var_2",
+ Description: "Described",
+ Default: map[string]string{"key1": "a", "key2": "b"},
+ },
+ },
+ },
+ },
+
+ { // Nested object to maps
+ JSON: `
+{
+ "variable": {
+ "var_name": {
+ "description": "Described",
+ "default": {
+ "key1": "a",
+ "key2": "b"
+ }
+ }
+ }
+}
+ `,
+ Out: &[]map[string]interface{}{},
+ Expected: &[]map[string]interface{}{
+ {
+ "variable": []map[string]interface{}{
+ {
+ "var_name": []map[string]interface{}{
+ {
+ "description": "Described",
+ "default": []map[string]interface{}{
+ {
+ "key1": "a",
+ "key2": "b",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+
+ { // Nested object to maps without a sibling key should decode the same as above
+ JSON: `
+{
+ "variable": {
+ "var_name": {
+ "default": {
+ "key1": "a",
+ "key2": "b"
+ }
+ }
+ }
+}
+ `,
+ Out: &[]map[string]interface{}{},
+ Expected: &[]map[string]interface{}{
+ {
+ "variable": []map[string]interface{}{
+ {
+ "var_name": []map[string]interface{}{
+ {
+ "default": []map[string]interface{}{
+ {
+ "key1": "a",
+ "key2": "b",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+
+ { // Nested objects, one with a sibling key, and one without
+ JSON: `
+{
+ "variable": {
+ "var_1": {
+ "default": {
+ "key1": "a",
+ "key2": "b"
+ }
+ },
+ "var_2": {
+ "description": "Described",
+ "default": {
+ "key1": "a",
+ "key2": "b"
+ }
+ }
+ }
+}
+ `,
+ Out: &[]map[string]interface{}{},
+ Expected: &[]map[string]interface{}{
+ {
+ "variable": []map[string]interface{}{
+ {
+ "var_1": []map[string]interface{}{
+ {
+ "default": []map[string]interface{}{
+ {
+ "key1": "a",
+ "key2": "b",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "variable": []map[string]interface{}{
+ {
+ "var_2": []map[string]interface{}{
+ {
+ "description": "Described",
+ "default": []map[string]interface{}{
+ {
+ "key1": "a",
+ "key2": "b",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ for i, tc := range cases {
+ err := Decode(tc.Out, tc.JSON)
+ if err != nil {
+ t.Fatalf("[%d] err: %s", i, err)
+ }
+
+ if !reflect.DeepEqual(tc.Out, tc.Expected) {
+ t.Fatalf("[%d]\ngot: %s\nexpected: %s\n", i, spew.Sdump(tc.Out), spew.Sdump(tc.Expected))
+ }
+ }
+}