1 package hcl
2
3 import (
4 "github.com/hashicorp/hcl/hcl/token"
5 "io/ioutil"
6 "path/filepath"
7 "reflect"
8 "testing"
9 "time"
10
11 "github.com/davecgh/go-spew/spew"
12 "github.com/hashicorp/hcl/hcl/ast"
13 )
14
15 func TestDecode_interface(t *testing.T) {
16 cases := []struct {
17 File string
18 Err bool
19 Out interface{}
20 }{
21 {
22 "basic.hcl",
23 false,
24 map[string]interface{}{
25 "foo": "bar",
26 "bar": "${file(\"bing/bong.txt\")}",
27 },
28 },
29 {
30 "basic_squish.hcl",
31 false,
32 map[string]interface{}{
33 "foo": "bar",
34 "bar": "${file(\"bing/bong.txt\")}",
35 "foo-bar": "baz",
36 },
37 },
38 {
39 "empty.hcl",
40 false,
41 map[string]interface{}{
42 "resource": []map[string]interface{}{
43 map[string]interface{}{
44 "foo": []map[string]interface{}{
45 map[string]interface{}{},
46 },
47 },
48 },
49 },
50 },
51 {
52 "tfvars.hcl",
53 false,
54 map[string]interface{}{
55 "regularvar": "Should work",
56 "map.key1": "Value",
57 "map.key2": "Other value",
58 },
59 },
60 {
61 "escape.hcl",
62 false,
63 map[string]interface{}{
64 "foo": "bar\"baz\\n",
65 "qux": "back\\slash",
66 "bar": "new\nline",
67 "qax": `slash\:colon`,
68 "nested": `${HH\\:mm\\:ss}`,
69 "nestedquotes": `${"\"stringwrappedinquotes\""}`,
70 },
71 },
72 {
73 "float.hcl",
74 false,
75 map[string]interface{}{
76 "a": 1.02,
77 "b": 2,
78 },
79 },
80 {
81 "multiline_bad.hcl",
82 true,
83 nil,
84 },
85 {
86 "multiline_literal.hcl",
87 true,
88 nil,
89 },
90 {
91 "multiline_literal_with_hil.hcl",
92 false,
93 map[string]interface{}{"multiline_literal_with_hil": "${hello\n world}"},
94 },
95 {
96 "multiline_no_marker.hcl",
97 true,
98 nil,
99 },
100 {
101 "multiline.hcl",
102 false,
103 map[string]interface{}{"foo": "bar\nbaz\n"},
104 },
105 {
106 "multiline_indented.hcl",
107 false,
108 map[string]interface{}{"foo": " bar\n baz\n"},
109 },
110 {
111 "multiline_no_hanging_indent.hcl",
112 false,
113 map[string]interface{}{"foo": " baz\n bar\n foo\n"},
114 },
115 {
116 "multiline_no_eof.hcl",
117 false,
118 map[string]interface{}{"foo": "bar\nbaz\n", "key": "value"},
119 },
120 {
121 "multiline.json",
122 false,
123 map[string]interface{}{"foo": "bar\nbaz"},
124 },
125 {
126 "null_strings.json",
127 false,
128 map[string]interface{}{
129 "module": []map[string]interface{}{
130 map[string]interface{}{
131 "app": []map[string]interface{}{
132 map[string]interface{}{"foo": ""},
133 },
134 },
135 },
136 },
137 },
138 {
139 "scientific.json",
140 false,
141 map[string]interface{}{
142 "a": 1e-10,
143 "b": 1e+10,
144 "c": 1e10,
145 "d": 1.2e-10,
146 "e": 1.2e+10,
147 "f": 1.2e10,
148 },
149 },
150 {
151 "scientific.hcl",
152 false,
153 map[string]interface{}{
154 "a": 1e-10,
155 "b": 1e+10,
156 "c": 1e10,
157 "d": 1.2e-10,
158 "e": 1.2e+10,
159 "f": 1.2e10,
160 },
161 },
162 {
163 "terraform_heroku.hcl",
164 false,
165 map[string]interface{}{
166 "name": "terraform-test-app",
167 "config_vars": []map[string]interface{}{
168 map[string]interface{}{
169 "FOO": "bar",
170 },
171 },
172 },
173 },
174 {
175 "structure_multi.hcl",
176 false,
177 map[string]interface{}{
178 "foo": []map[string]interface{}{
179 map[string]interface{}{
180 "baz": []map[string]interface{}{
181 map[string]interface{}{"key": 7},
182 },
183 },
184 map[string]interface{}{
185 "bar": []map[string]interface{}{
186 map[string]interface{}{"key": 12},
187 },
188 },
189 },
190 },
191 },
192 {
193 "structure_multi.json",
194 false,
195 map[string]interface{}{
196 "foo": []map[string]interface{}{
197 map[string]interface{}{
198 "baz": []map[string]interface{}{
199 map[string]interface{}{"key": 7},
200 },
201 },
202 map[string]interface{}{
203 "bar": []map[string]interface{}{
204 map[string]interface{}{"key": 12},
205 },
206 },
207 },
208 },
209 },
210 {
211 "list_of_lists.hcl",
212 false,
213 map[string]interface{}{
214 "foo": []interface{}{
215 []interface{}{"foo"},
216 []interface{}{"bar"},
217 },
218 },
219 },
220 {
221 "list_of_maps.hcl",
222 false,
223 map[string]interface{}{
224 "foo": []interface{}{
225 map[string]interface{}{"somekey1": "someval1"},
226 map[string]interface{}{"somekey2": "someval2", "someextrakey": "someextraval"},
227 },
228 },
229 },
230 {
231 "assign_deep.hcl",
232 false,
233 map[string]interface{}{
234 "resource": []interface{}{
235 map[string]interface{}{
236 "foo": []interface{}{
237 map[string]interface{}{
238 "bar": []map[string]interface{}{
239 map[string]interface{}{}}}}}}},
240 },
241 {
242 "structure_list.hcl",
243 false,
244 map[string]interface{}{
245 "foo": []map[string]interface{}{
246 map[string]interface{}{
247 "key": 7,
248 },
249 map[string]interface{}{
250 "key": 12,
251 },
252 },
253 },
254 },
255 {
256 "structure_list.json",
257 false,
258 map[string]interface{}{
259 "foo": []map[string]interface{}{
260 map[string]interface{}{
261 "key": 7,
262 },
263 map[string]interface{}{
264 "key": 12,
265 },
266 },
267 },
268 },
269 {
270 "structure_list_deep.json",
271 false,
272 map[string]interface{}{
273 "bar": []map[string]interface{}{
274 map[string]interface{}{
275 "foo": []map[string]interface{}{
276 map[string]interface{}{
277 "name": "terraform_example",
278 "ingress": []map[string]interface{}{
279 map[string]interface{}{
280 "from_port": 22,
281 },
282 map[string]interface{}{
283 "from_port": 80,
284 },
285 },
286 },
287 },
288 },
289 },
290 },
291 },
292
293 {
294 "structure_list_empty.json",
295 false,
296 map[string]interface{}{
297 "foo": []interface{}{},
298 },
299 },
300
301 {
302 "nested_block_comment.hcl",
303 false,
304 map[string]interface{}{
305 "bar": "value",
306 },
307 },
308
309 {
310 "unterminated_block_comment.hcl",
311 true,
312 nil,
313 },
314
315 {
316 "unterminated_brace.hcl",
317 true,
318 nil,
319 },
320
321 {
322 "nested_provider_bad.hcl",
323 true,
324 nil,
325 },
326
327 {
328 "object_list.json",
329 false,
330 map[string]interface{}{
331 "resource": []map[string]interface{}{
332 map[string]interface{}{
333 "aws_instance": []map[string]interface{}{
334 map[string]interface{}{
335 "db": []map[string]interface{}{
336 map[string]interface{}{
337 "vpc": "foo",
338 "provisioner": []map[string]interface{}{
339 map[string]interface{}{
340 "file": []map[string]interface{}{
341 map[string]interface{}{
342 "source": "foo",
343 "destination": "bar",
344 },
345 },
346 },
347 },
348 },
349 },
350 },
351 },
352 },
353 },
354 },
355 },
356
357
358
359 {
360 "terraform_variable_invalid.json",
361 false,
362 map[string]interface{}{
363 "variable": []map[string]interface{}{
364 map[string]interface{}{
365 "whatever": "abc123",
366 },
367 },
368 },
369 },
370
371 {
372 "interpolate.json",
373 false,
374 map[string]interface{}{
375 "default": `${replace("europe-west", "-", " ")}`,
376 },
377 },
378
379 {
380 "block_assign.hcl",
381 true,
382 nil,
383 },
384
385 {
386 "escape_backslash.hcl",
387 false,
388 map[string]interface{}{
389 "output": []map[string]interface{}{
390 map[string]interface{}{
391 "one": `${replace(var.sub_domain, ".", "\\.")}`,
392 "two": `${replace(var.sub_domain, ".", "\\\\.")}`,
393 "many": `${replace(var.sub_domain, ".", "\\\\\\\\.")}`,
394 },
395 },
396 },
397 },
398
399 {
400 "git_crypt.hcl",
401 true,
402 nil,
403 },
404
405 {
406 "object_with_bool.hcl",
407 false,
408 map[string]interface{}{
409 "path": []map[string]interface{}{
410 map[string]interface{}{
411 "policy": "write",
412 "permissions": []map[string]interface{}{
413 map[string]interface{}{
414 "bool": []interface{}{false},
415 },
416 },
417 },
418 },
419 },
420 },
421 }
422
423 for _, tc := range cases {
424 t.Run(tc.File, func(t *testing.T) {
425 d, err := ioutil.ReadFile(filepath.Join(fixtureDir, tc.File))
426 if err != nil {
427 t.Fatalf("err: %s", err)
428 }
429
430 var out interface{}
431 err = Decode(&out, string(d))
432 if (err != nil) != tc.Err {
433 t.Fatalf("Input: %s\n\nError: %s", tc.File, err)
434 }
435
436 if !reflect.DeepEqual(out, tc.Out) {
437 t.Fatalf("Input: %s. Actual, Expected.\n\n%#v\n\n%#v", tc.File, out, tc.Out)
438 }
439
440 var v interface{}
441 err = Unmarshal(d, &v)
442 if (err != nil) != tc.Err {
443 t.Fatalf("Input: %s\n\nError: %s", tc.File, err)
444 }
445
446 if !reflect.DeepEqual(v, tc.Out) {
447 t.Fatalf("Input: %s. Actual, Expected.\n\n%#v\n\n%#v", tc.File, out, tc.Out)
448 }
449 })
450 }
451 }
452
453 func TestDecode_interfaceInline(t *testing.T) {
454 cases := []struct {
455 Value string
456 Err bool
457 Out interface{}
458 }{
459 {"t t e{{}}", true, nil},
460 {"t=0t d {}", true, map[string]interface{}{"t": 0}},
461 {"v=0E0v d{}", true, map[string]interface{}{"v": float64(0)}},
462 }
463
464 for _, tc := range cases {
465 t.Logf("Testing: %q", tc.Value)
466
467 var out interface{}
468 err := Decode(&out, tc.Value)
469 if (err != nil) != tc.Err {
470 t.Fatalf("Input: %q\n\nError: %s", tc.Value, err)
471 }
472
473 if !reflect.DeepEqual(out, tc.Out) {
474 t.Fatalf("Input: %q. Actual, Expected.\n\n%#v\n\n%#v", tc.Value, out, tc.Out)
475 }
476
477 var v interface{}
478 err = Unmarshal([]byte(tc.Value), &v)
479 if (err != nil) != tc.Err {
480 t.Fatalf("Input: %q\n\nError: %s", tc.Value, err)
481 }
482
483 if !reflect.DeepEqual(v, tc.Out) {
484 t.Fatalf("Input: %q. Actual, Expected.\n\n%#v\n\n%#v", tc.Value, out, tc.Out)
485 }
486 }
487 }
488
489 func TestDecode_equal(t *testing.T) {
490 cases := []struct {
491 One, Two string
492 }{
493 {
494 "basic.hcl",
495 "basic.json",
496 },
497 {
498 "float.hcl",
499 "float.json",
500 },
501
507 {
508 "structure.hcl",
509 "structure_flat.json",
510 },
511 {
512 "terraform_heroku.hcl",
513 "terraform_heroku.json",
514 },
515 }
516
517 for _, tc := range cases {
518 p1 := filepath.Join(fixtureDir, tc.One)
519 p2 := filepath.Join(fixtureDir, tc.Two)
520
521 d1, err := ioutil.ReadFile(p1)
522 if err != nil {
523 t.Fatalf("err: %s", err)
524 }
525
526 d2, err := ioutil.ReadFile(p2)
527 if err != nil {
528 t.Fatalf("err: %s", err)
529 }
530
531 var i1, i2 interface{}
532 err = Decode(&i1, string(d1))
533 if err != nil {
534 t.Fatalf("err: %s", err)
535 }
536
537 err = Decode(&i2, string(d2))
538 if err != nil {
539 t.Fatalf("err: %s", err)
540 }
541
542 if !reflect.DeepEqual(i1, i2) {
543 t.Fatalf(
544 "%s != %s\n\n%#v\n\n%#v",
545 tc.One, tc.Two,
546 i1, i2)
547 }
548 }
549 }
550
551 func TestDecode_flatMap(t *testing.T) {
552 var val map[string]map[string]string
553
554 err := Decode(&val, testReadFile(t, "structure_flatmap.hcl"))
555 if err != nil {
556 t.Fatalf("err: %s", err)
557 }
558
559 expected := map[string]map[string]string{
560 "foo": map[string]string{
561 "foo": "bar",
562 "key": "7",
563 },
564 }
565
566 if !reflect.DeepEqual(val, expected) {
567 t.Fatalf("Actual: %#v\n\nExpected: %#v", val, expected)
568 }
569 }
570
571 func TestDecode_structure(t *testing.T) {
572 type Embedded interface{}
573
574 type V struct {
575 Embedded `hcl:"-"`
576 Key int
577 Foo string
578 }
579
580 var actual V
581
582 err := Decode(&actual, testReadFile(t, "flat.hcl"))
583 if err != nil {
584 t.Fatalf("err: %s", err)
585 }
586
587 expected := V{
588 Key: 7,
589 Foo: "bar",
590 }
591
592 if !reflect.DeepEqual(actual, expected) {
593 t.Fatalf("Actual: %#v\n\nExpected: %#v", actual, expected)
594 }
595 }
596
597 func TestDecode_structurePtr(t *testing.T) {
598 type V struct {
599 Key int
600 Foo string
601 }
602
603 var actual *V
604
605 err := Decode(&actual, testReadFile(t, "flat.hcl"))
606 if err != nil {
607 t.Fatalf("err: %s", err)
608 }
609
610 expected := &V{
611 Key: 7,
612 Foo: "bar",
613 }
614
615 if !reflect.DeepEqual(actual, expected) {
616 t.Fatalf("Actual: %#v\n\nExpected: %#v", actual, expected)
617 }
618 }
619
620 func TestDecode_structureArray(t *testing.T) {
621
622
623
624 type KeyPolicyType string
625
626 type KeyPolicy struct {
627 Prefix string `hcl:",key"`
628 Policy KeyPolicyType
629 }
630
631 type Policy struct {
632 Keys []KeyPolicy `hcl:"key,expand"`
633 }
634
635 expected := Policy{
636 Keys: []KeyPolicy{
637 KeyPolicy{
638 Prefix: "",
639 Policy: "read",
640 },
641 KeyPolicy{
642 Prefix: "foo/",
643 Policy: "write",
644 },
645 KeyPolicy{
646 Prefix: "foo/bar/",
647 Policy: "read",
648 },
649 KeyPolicy{
650 Prefix: "foo/bar/baz",
651 Policy: "deny",
652 },
653 },
654 }
655
656 files := []string{
657 "decode_policy.hcl",
658 "decode_policy.json",
659 }
660
661 for _, f := range files {
662 var actual Policy
663
664 err := Decode(&actual, testReadFile(t, f))
665 if err != nil {
666 t.Fatalf("Input: %s\n\nerr: %s", f, err)
667 }
668
669 if !reflect.DeepEqual(actual, expected) {
670 t.Fatalf("Input: %s\n\nActual: %#v\n\nExpected: %#v", f, actual, expected)
671 }
672 }
673 }
674
675 func TestDecode_sliceExpand(t *testing.T) {
676 type testInner struct {
677 Name string `hcl:",key"`
678 Key string
679 }
680
681 type testStruct struct {
682 Services []testInner `hcl:"service,expand"`
683 }
684
685 expected := testStruct{
686 Services: []testInner{
687 testInner{
688 Name: "my-service-0",
689 Key: "value",
690 },
691 testInner{
692 Name: "my-service-1",
693 Key: "value",
694 },
695 },
696 }
697
698 files := []string{
699 "slice_expand.hcl",
700 }
701
702 for _, f := range files {
703 t.Logf("Testing: %s", f)
704
705 var actual testStruct
706 err := Decode(&actual, testReadFile(t, f))
707 if err != nil {
708 t.Fatalf("Input: %s\n\nerr: %s", f, err)
709 }
710
711 if !reflect.DeepEqual(actual, expected) {
712 t.Fatalf("Input: %s\n\nActual: %#v\n\nExpected: %#v", f, actual, expected)
713 }
714 }
715 }
716
717 func TestDecode_structureMap(t *testing.T) {
718
719
720
721 type hclVariable struct {
722 Default interface{}
723 Description string
724 Fields []string `hcl:",decodedFields"`
725 }
726
727 type rawConfig struct {
728 Variable map[string]hclVariable
729 }
730
731 expected := rawConfig{
732 Variable: map[string]hclVariable{
733 "foo": hclVariable{
734 Default: "bar",
735 Description: "bar",
736 Fields: []string{"Default", "Description"},
737 },
738
739 "amis": hclVariable{
740 Default: []map[string]interface{}{
741 map[string]interface{}{
742 "east": "foo",
743 },
744 },
745 Fields: []string{"Default"},
746 },
747 },
748 }
749
750 files := []string{
751 "decode_tf_variable.hcl",
752 "decode_tf_variable.json",
753 }
754
755 for _, f := range files {
756 t.Logf("Testing: %s", f)
757
758 var actual rawConfig
759 err := Decode(&actual, testReadFile(t, f))
760 if err != nil {
761 t.Fatalf("Input: %s\n\nerr: %s", f, err)
762 }
763
764 if !reflect.DeepEqual(actual, expected) {
765 t.Fatalf("Input: %s\n\nActual: %#v\n\nExpected: %#v", f, actual, expected)
766 }
767 }
768 }
769
770 func TestDecode_structureMapInvalid(t *testing.T) {
771
772
773 type hclVariable struct {
774 Default interface{}
775 Description string
776 Fields []string `hcl:",decodedFields"`
777 }
778
779 type rawConfig struct {
780 Variable map[string]*hclVariable
781 }
782
783 var actual rawConfig
784 err := Decode(&actual, testReadFile(t, "terraform_variable_invalid.json"))
785 if err == nil {
786 t.Fatal("expected error")
787 }
788 }
789
790 func TestDecode_structureMapExtraKeys(t *testing.T) {
791 type hclVariable struct {
792 A int
793 B int
794 Found []string `hcl:",decodedFields"`
795 Extra map[string][]token.Pos `hcl:",unusedKeyPositions"`
796 }
797
798 q := hclVariable{
799 A: 1,
800 B: 2,
801 Found: []string{"A", "B"},
802 Extra: map[string][]token.Pos{
803 "extra1": {{
804 Line: 3,
805 Column: 1,
806 Offset: 12,
807 }},
808 "extra2": {{
809 Line: 4,
810 Column: 1,
811 Offset: 23,
812 }},
813 },
814 }
815
816 var p hclVariable
817 ast, _ := Parse(testReadFile(t, "structure_map_extra_keys.hcl"))
818 DecodeObject(&p, ast)
819 if !reflect.DeepEqual(p, q) {
820 t.Fatal("not equal")
821 }
822
823 p.Extra = map[string][]token.Pos{
824 "extra1": {{}},
825 "extra2": {{}},
826 }
827
828 var j hclVariable
829 ast, _ = Parse(testReadFile(t, "structure_map_extra_keys.json"))
830 DecodeObject(&j, ast)
831 if !reflect.DeepEqual(p, j) {
832 t.Fatal("not equal")
833 }
834 }
835
836 func TestDecode_structureMapExtraKeysNestedObjects(t *testing.T) {
837 type hclVariable struct {
838 A int
839 B struct{}
840 Found []string `hcl:",decodedFields"`
841 Extra map[string][]token.Pos `hcl:",unusedKeyPositions"`
842 }
843
844 var j hclVariable
845 ast, _ := Parse(testReadFile(t, "structure_map_nested_keys.json"))
846 DecodeObject(&j, ast)
847 if len(j.Extra) > 0 {
848 t.Fatalf("Extra keys found in map: %x", j.Extra)
849 }
850 }
851
852 func TestDecode_interfaceNonPointer(t *testing.T) {
853 var value interface{}
854 err := Decode(value, testReadFile(t, "basic_int_string.hcl"))
855 if err == nil {
856 t.Fatal("should error")
857 }
858 }
859
860 func TestDecode_intString(t *testing.T) {
861 var value struct {
862 Count int
863 }
864
865 err := Decode(&value, testReadFile(t, "basic_int_string.hcl"))
866 if err != nil {
867 t.Fatalf("err: %s", err)
868 }
869
870 if value.Count != 3 {
871 t.Fatalf("bad: %#v", value.Count)
872 }
873 }
874
875 func TestDecode_float32(t *testing.T) {
876 var value struct {
877 A float32 `hcl:"a"`
878 B float32 `hcl:"b"`
879 }
880
881 err := Decode(&value, testReadFile(t, "float.hcl"))
882 if err != nil {
883 t.Fatalf("err: %s", err)
884 }
885
886 if got, want := value.A, float32(1.02); got != want {
887 t.Fatalf("wrong result %#v; want %#v", got, want)
888 }
889 if got, want := value.B, float32(2); got != want {
890 t.Fatalf("wrong result %#v; want %#v", got, want)
891 }
892 }
893
894 func TestDecode_float64(t *testing.T) {
895 var value struct {
896 A float64 `hcl:"a"`
897 B float64 `hcl:"b"`
898 }
899
900 err := Decode(&value, testReadFile(t, "float.hcl"))
901 if err != nil {
902 t.Fatalf("err: %s", err)
903 }
904
905 if got, want := value.A, float64(1.02); got != want {
906 t.Fatalf("wrong result %#v; want %#v", got, want)
907 }
908 if got, want := value.B, float64(2); got != want {
909 t.Fatalf("wrong result %#v; want %#v", got, want)
910 }
911 }
912
913 func TestDecode_intStringAliased(t *testing.T) {
914 var value struct {
915 Count time.Duration
916 }
917
918 err := Decode(&value, testReadFile(t, "basic_int_string.hcl"))
919 if err != nil {
920 t.Fatalf("err: %s", err)
921 }
922
923 if value.Count != time.Duration(3) {
924 t.Fatalf("bad: %#v", value.Count)
925 }
926 }
927
928 func TestDecode_Node(t *testing.T) {
929
930 var value struct {
931 Content ast.Node
932 Nested struct {
933 Content ast.Node
934 }
935 }
936
937 content := `
938 content {
939 hello = "world"
940 }
941 `
942
943
944 err := Decode(&value, content)
945
946
947 if err != nil {
948 t.Errorf("unable to decode content, %v", err)
949 return
950 }
951
952
953 var v map[string]interface{}
954 err = DecodeObject(&v, value.Content)
955 if err != nil {
956 t.Errorf("unable to decode content, %v", err)
957 return
958 }
959
960 if v["hello"] != "world" {
961 t.Errorf("expected mapping to be returned")
962 }
963 }
964
965 func TestDecode_NestedNode(t *testing.T) {
966
967 var value struct {
968 Nested struct {
969 Content ast.Node
970 }
971 }
972
973 content := `
974 nested "content" {
975 hello = "world"
976 }
977 `
978
979
980 err := Decode(&value, content)
981
982
983 if err != nil {
984 t.Errorf("unable to decode content, %v", err)
985 return
986 }
987
988
989 var v map[string]interface{}
990 err = DecodeObject(&v, value.Nested.Content)
991 if err != nil {
992 t.Errorf("unable to decode content, %v", err)
993 return
994 }
995
996 if v["hello"] != "world" {
997 t.Errorf("expected mapping to be returned")
998 }
999 }
1000
1001
1002 func TestDecode_topLevelKeys(t *testing.T) {
1003 type Template struct {
1004 Source string
1005 }
1006
1007 templates := struct {
1008 Templates []*Template `hcl:"template"`
1009 }{}
1010
1011 err := Decode(&templates, `
1012 template {
1013 source = "blah"
1014 }
1015
1016 template {
1017 source = "blahblah"
1018 }`)
1019
1020 if err != nil {
1021 t.Fatal(err)
1022 }
1023
1024 if templates.Templates[0].Source != "blah" {
1025 t.Errorf("bad source: %s", templates.Templates[0].Source)
1026 }
1027
1028 if templates.Templates[1].Source != "blahblah" {
1029 t.Errorf("bad source: %s", templates.Templates[1].Source)
1030 }
1031 }
1032
1033 func TestDecode_flattenedJSON(t *testing.T) {
1034
1035 type V struct {
1036 Name string `hcl:",key"`
1037 Description string
1038 Default map[string]string
1039 }
1040 type Vars struct {
1041 Variable []*V
1042 }
1043
1044 cases := []struct {
1045 JSON string
1046 Out interface{}
1047 Expected interface{}
1048 }{
1049 {
1050 JSON: `
1051 {
1052 "var_name": {
1053 "default": {
1054 "key1": "a",
1055 "key2": "b"
1056 }
1057 }
1058 }
1059 `,
1060 Out: &[]*V{},
1061 Expected: &[]*V{
1062 &V{
1063 Name: "var_name",
1064 Default: map[string]string{"key1": "a", "key2": "b"},
1065 },
1066 },
1067 },
1068
1069 {
1070 JSON: `
1071 {
1072 "var_name": {
1073 "description": "Described",
1074 "default": {
1075 "key1": "a",
1076 "key2": "b"
1077 }
1078 }
1079 }
1080 `,
1081 Out: &[]*V{},
1082 Expected: &[]*V{
1083 &V{
1084 Name: "var_name",
1085 Description: "Described",
1086 Default: map[string]string{"key1": "a", "key2": "b"},
1087 },
1088 },
1089 },
1090
1091 {
1092 JSON: `
1093 {
1094 "variable": {
1095 "var_1": {
1096 "default": {
1097 "key1": "a",
1098 "key2": "b"
1099 }
1100 },
1101 "var_2": {
1102 "description": "Described",
1103 "default": {
1104 "key1": "a",
1105 "key2": "b"
1106 }
1107 }
1108 }
1109 }
1110 `,
1111 Out: &Vars{},
1112 Expected: &Vars{
1113 Variable: []*V{
1114 &V{
1115 Name: "var_1",
1116 Default: map[string]string{"key1": "a", "key2": "b"},
1117 },
1118 &V{
1119 Name: "var_2",
1120 Description: "Described",
1121 Default: map[string]string{"key1": "a", "key2": "b"},
1122 },
1123 },
1124 },
1125 },
1126
1127 {
1128 JSON: `
1129 {
1130 "variable": {
1131 "var_name": {
1132 "description": "Described",
1133 "default": {
1134 "key1": "a",
1135 "key2": "b"
1136 }
1137 }
1138 }
1139 }
1140 `,
1141 Out: &[]map[string]interface{}{},
1142 Expected: &[]map[string]interface{}{
1143 {
1144 "variable": []map[string]interface{}{
1145 {
1146 "var_name": []map[string]interface{}{
1147 {
1148 "description": "Described",
1149 "default": []map[string]interface{}{
1150 {
1151 "key1": "a",
1152 "key2": "b",
1153 },
1154 },
1155 },
1156 },
1157 },
1158 },
1159 },
1160 },
1161 },
1162
1163 {
1164 JSON: `
1165 {
1166 "variable": {
1167 "var_name": {
1168 "default": {
1169 "key1": "a",
1170 "key2": "b"
1171 }
1172 }
1173 }
1174 }
1175 `,
1176 Out: &[]map[string]interface{}{},
1177 Expected: &[]map[string]interface{}{
1178 {
1179 "variable": []map[string]interface{}{
1180 {
1181 "var_name": []map[string]interface{}{
1182 {
1183 "default": []map[string]interface{}{
1184 {
1185 "key1": "a",
1186 "key2": "b",
1187 },
1188 },
1189 },
1190 },
1191 },
1192 },
1193 },
1194 },
1195 },
1196
1197 {
1198 JSON: `
1199 {
1200 "variable": {
1201 "var_1": {
1202 "default": {
1203 "key1": "a",
1204 "key2": "b"
1205 }
1206 },
1207 "var_2": {
1208 "description": "Described",
1209 "default": {
1210 "key1": "a",
1211 "key2": "b"
1212 }
1213 }
1214 }
1215 }
1216 `,
1217 Out: &[]map[string]interface{}{},
1218 Expected: &[]map[string]interface{}{
1219 {
1220 "variable": []map[string]interface{}{
1221 {
1222 "var_1": []map[string]interface{}{
1223 {
1224 "default": []map[string]interface{}{
1225 {
1226 "key1": "a",
1227 "key2": "b",
1228 },
1229 },
1230 },
1231 },
1232 },
1233 },
1234 },
1235 {
1236 "variable": []map[string]interface{}{
1237 {
1238 "var_2": []map[string]interface{}{
1239 {
1240 "description": "Described",
1241 "default": []map[string]interface{}{
1242 {
1243 "key1": "a",
1244 "key2": "b",
1245 },
1246 },
1247 },
1248 },
1249 },
1250 },
1251 },
1252 },
1253 },
1254 }
1255
1256 for i, tc := range cases {
1257 err := Decode(tc.Out, tc.JSON)
1258 if err != nil {
1259 t.Fatalf("[%d] err: %s", i, err)
1260 }
1261
1262 if !reflect.DeepEqual(tc.Out, tc.Expected) {
1263 t.Fatalf("[%d]\ngot: %s\nexpected: %s\n", i, spew.Sdump(tc.Out), spew.Sdump(tc.Expected))
1264 }
1265 }
1266 }
1267
View as plain text