1/*
2Copyright The Helm Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package engine
18
19import (
20	"strings"
21	"testing"
22	"text/template"
23
24	"github.com/stretchr/testify/assert"
25)
26
27func TestFuncs(t *testing.T) {
28	//TODO write tests for failure cases
29	tests := []struct {
30		tpl, expect string
31		vars        interface{}
32	}{{
33		tpl:    `{{ toYaml . }}`,
34		expect: `foo: bar`,
35		vars:   map[string]interface{}{"foo": "bar"},
36	}, {
37		tpl:    `{{ toToml . }}`,
38		expect: "foo = \"bar\"\n",
39		vars:   map[string]interface{}{"foo": "bar"},
40	}, {
41		tpl:    `{{ toJson . }}`,
42		expect: `{"foo":"bar"}`,
43		vars:   map[string]interface{}{"foo": "bar"},
44	}, {
45		tpl:    `{{ fromYaml . }}`,
46		expect: "map[hello:world]",
47		vars:   `hello: world`,
48	}, {
49		tpl:    `{{ fromYamlArray . }}`,
50		expect: "[one 2 map[name:helm]]",
51		vars:   "- one\n- 2\n- name: helm\n",
52	}, {
53		tpl:    `{{ fromYamlArray . }}`,
54		expect: "[one 2 map[name:helm]]",
55		vars:   `["one", 2, { "name": "helm" }]`,
56	}, {
57		// Regression for https://github.com/helm/helm/issues/2271
58		tpl:    `{{ toToml . }}`,
59		expect: "[mast]\n  sail = \"white\"\n",
60		vars:   map[string]map[string]string{"mast": {"sail": "white"}},
61	}, {
62		tpl:    `{{ fromYaml . }}`,
63		expect: "map[Error:error unmarshaling JSON: while decoding JSON: json: cannot unmarshal array into Go value of type map[string]interface {}]",
64		vars:   "- one\n- two\n",
65	}, {
66		tpl:    `{{ fromJson .}}`,
67		expect: `map[hello:world]`,
68		vars:   `{"hello":"world"}`,
69	}, {
70		tpl:    `{{ fromJson . }}`,
71		expect: `map[Error:json: cannot unmarshal array into Go value of type map[string]interface {}]`,
72		vars:   `["one", "two"]`,
73	}, {
74		tpl:    `{{ fromJsonArray . }}`,
75		expect: `[one 2 map[name:helm]]`,
76		vars:   `["one", 2, { "name": "helm" }]`,
77	}, {
78		tpl:    `{{ fromJsonArray . }}`,
79		expect: `[json: cannot unmarshal object into Go value of type []interface {}]`,
80		vars:   `{"hello": "world"}`,
81	}, {
82		tpl:    `{{ merge .dict (fromYaml .yaml) }}`,
83		expect: `map[a:map[b:c]]`,
84		vars:   map[string]interface{}{"dict": map[string]interface{}{"a": map[string]interface{}{"b": "c"}}, "yaml": `{"a":{"b":"d"}}`},
85	}, {
86		tpl:    `{{ merge (fromYaml .yaml) .dict }}`,
87		expect: `map[a:map[b:d]]`,
88		vars:   map[string]interface{}{"dict": map[string]interface{}{"a": map[string]interface{}{"b": "c"}}, "yaml": `{"a":{"b":"d"}}`},
89	}, {
90		tpl:    `{{ fromYaml . }}`,
91		expect: `map[Error:error unmarshaling JSON: while decoding JSON: json: cannot unmarshal array into Go value of type map[string]interface {}]`,
92		vars:   `["one", "two"]`,
93	}, {
94		tpl:    `{{ fromYamlArray . }}`,
95		expect: `[error unmarshaling JSON: while decoding JSON: json: cannot unmarshal object into Go value of type []interface {}]`,
96		vars:   `hello: world`,
97	}, {
98		// This should never result in a network lookup. Regression for #7955
99		tpl:    `{{ lookup "v1" "Namespace" "" "unlikelynamespace99999999" }}`,
100		expect: `map[]`,
101		vars:   `["one", "two"]`,
102	}}
103
104	for _, tt := range tests {
105		var b strings.Builder
106		err := template.Must(template.New("test").Funcs(funcMap()).Parse(tt.tpl)).Execute(&b, tt.vars)
107		assert.NoError(t, err)
108		assert.Equal(t, tt.expect, b.String(), tt.tpl)
109	}
110}
111
112// This test to check a function provided by sprig is due to a change in a
113// dependency of sprig. mergo in v0.3.9 changed the way it merges and only does
114// public fields (i.e. those starting with a capital letter). This test, from
115// sprig, fails in the new version. This is a behavior change for mergo that
116// impacts sprig and Helm users. This test will help us to not update to a
117// version of mergo (even accidentally) that causes a breaking change. See
118// sprig changelog and notes for more details.
119// Note, Go modules assume semver is never broken. So, there is no way to tell
120// the tooling to not update to a minor or patch version. `go get -u` could be
121// used to accidentally update mergo. This test and message should catch the
122// problem and explain why it's happening.
123func TestMerge(t *testing.T) {
124	dict := map[string]interface{}{
125		"src2": map[string]interface{}{
126			"h": 10,
127			"i": "i",
128			"j": "j",
129		},
130		"src1": map[string]interface{}{
131			"a": 1,
132			"b": 2,
133			"d": map[string]interface{}{
134				"e": "four",
135			},
136			"g": []int{6, 7},
137			"i": "aye",
138			"j": "jay",
139			"k": map[string]interface{}{
140				"l": false,
141			},
142		},
143		"dst": map[string]interface{}{
144			"a": "one",
145			"c": 3,
146			"d": map[string]interface{}{
147				"f": 5,
148			},
149			"g": []int{8, 9},
150			"i": "eye",
151			"k": map[string]interface{}{
152				"l": true,
153			},
154		},
155	}
156	tpl := `{{merge .dst .src1 .src2}}`
157	var b strings.Builder
158	err := template.Must(template.New("test").Funcs(funcMap()).Parse(tpl)).Execute(&b, dict)
159	assert.NoError(t, err)
160
161	expected := map[string]interface{}{
162		"a": "one", // key overridden
163		"b": 2,     // merged from src1
164		"c": 3,     // merged from dst
165		"d": map[string]interface{}{ // deep merge
166			"e": "four",
167			"f": 5,
168		},
169		"g": []int{8, 9}, // overridden - arrays are not merged
170		"h": 10,          // merged from src2
171		"i": "eye",       // overridden twice
172		"j": "jay",       // overridden and merged
173		"k": map[string]interface{}{
174			"l": true, // overridden
175		},
176	}
177	assert.Equal(t, expected, dict["dst"])
178}
179