1
16
17 package v2_test
18
19 import (
20 "bytes"
21 "testing"
22 "text/template"
23
24 "github.com/stretchr/testify/require"
25 "k8s.io/apimachinery/pkg/runtime/schema"
26 v2 "k8s.io/kubectl/pkg/explain/v2"
27 )
28
29 func TestFuncs(t *testing.T) {
30 testcases := []struct {
31 Name string
32 FuncName string
33 Source string
34 Context any
35 Expect string
36 Error string
37 }{
38 {
39 Name: "err",
40 FuncName: "fail",
41 Source: `{{fail .}}`,
42 Context: "this is a test",
43 Error: "this is a test",
44 },
45 {
46 Name: "basic",
47 FuncName: "wrap",
48 Source: `{{wrap 3 .}}`,
49 Context: "this is a really good test",
50 Expect: "this\nis\na\nreally\ngood\ntest",
51 },
52 {
53 Name: "basic",
54 FuncName: "split",
55 Source: `{{split . "/"}}`,
56 Context: "this/is/a/slash/separated/thing",
57 Expect: "[this is a slash separated thing]",
58 },
59 {
60 Name: "basic",
61 FuncName: "join",
62 Source: `{{join "/" "this" "is" "a" "slash" "separated" "thing"}}`,
63 Expect: "this/is/a/slash/separated/thing",
64 },
65 {
66 Name: "basic",
67 FuncName: "include",
68 Source: `{{define "myTemplate"}}{{.}}{{end}}{{$var := include "myTemplate" .}}{{$var}}`,
69 Context: "hello, world!",
70 Expect: "hello, world!",
71 },
72 {
73 Name: "nil",
74 FuncName: "first",
75 Source: `{{first .}}`,
76 Context: nil,
77 Error: "list is empty",
78 },
79 {
80 Name: "empty",
81 FuncName: "first",
82 Source: `{{first .}}`,
83 Context: []string{},
84 Error: "list is empty",
85 },
86 {
87 Name: "basic",
88 FuncName: "first",
89 Source: `{{first .}}`,
90 Context: []string{"first", "second", "third"},
91 Expect: "first",
92 },
93 {
94 Name: "wrongtype",
95 FuncName: "first",
96 Source: `{{first .}}`,
97 Context: "test",
98 Error: "first cannot be used on type: string",
99 },
100 {
101 Name: "nil",
102 FuncName: "last",
103 Source: `{{last .}}`,
104 Context: nil,
105 Error: "list is empty",
106 },
107 {
108 Name: "empty",
109 FuncName: "last",
110 Source: `{{last .}}`,
111 Context: []string{},
112 Error: "list is empty",
113 },
114 {
115 Name: "basic",
116 FuncName: "last",
117 Source: `{{last .}}`,
118 Context: []string{"first", "second", "third"},
119 Expect: "third",
120 },
121 {
122 Name: "wrongtype",
123 FuncName: "last",
124 Source: `{{last .}}`,
125 Context: "test",
126 Error: "last cannot be used on type: string",
127 },
128 {
129 Name: "none",
130 FuncName: "indent",
131 Source: `{{indent 0 .}}`,
132 Context: "this is a string",
133 Expect: "this is a string",
134 },
135 {
136 Name: "some",
137 FuncName: "indent",
138 Source: `{{indent 2 .}}`,
139 Context: "this is a string",
140 Expect: " this is a string",
141 },
142 {
143 Name: "empty",
144 FuncName: "dict",
145 Source: `{{dict | toJson}}`,
146 Expect: "{}",
147 },
148 {
149 Name: "single value",
150 FuncName: "dict",
151 Source: `{{dict "key" "value" | toJson}}`,
152 Expect: `{"key":"value"}`,
153 },
154 {
155 Name: "twoValues",
156 FuncName: "dict",
157 Source: `{{dict "key1" "val1" "key2" "val2" | toJson}}`,
158 Expect: `{"key1":"val1","key2":"val2"}`,
159 },
160 {
161 Name: "oddNumberArgs",
162 FuncName: "dict",
163 Source: `{{dict "key1" 1 "key2" | toJson}}`,
164 Error: "error calling dict: expected even # of arguments",
165 },
166 {
167 Name: "IntegerValue",
168 FuncName: "dict",
169 Source: `{{dict "key1" 1 | toJson}}`,
170 Expect: `{"key1":1}`,
171 },
172 {
173 Name: "MixedValues",
174 FuncName: "dict",
175 Source: `{{dict "key1" 1 "key2" "val2" "key3" (dict "key1" "val1") | toJson}}`,
176 Expect: `{"key1":1,"key2":"val2","key3":{"key1":"val1"}}`,
177 },
178 {
179 Name: "nil",
180 FuncName: "contains",
181 Source: `{{contains . "value"}}`,
182 Context: nil,
183 Expect: `false`,
184 },
185 {
186 Name: "empty",
187 FuncName: "contains",
188 Source: `{{contains . "value"}}`,
189 Context: []string{},
190 Expect: `false`,
191 },
192 {
193 Name: "basic",
194 FuncName: "contains",
195 Source: `{{contains . "value"}}`,
196 Context: []string{"value"},
197 Expect: `true`,
198 },
199 {
200 Name: "struct",
201 FuncName: "contains",
202 Source: `{{contains $.haystack $.needle}}`,
203 Context: map[string]any{
204 "needle": schema.GroupVersionKind{Group: "testgroup.k8s.io", Version: "v1", Kind: "Kind"},
205 "haystack": []schema.GroupVersionKind{
206 {Group: "randomgroup.k8s.io", Version: "v1", Kind: "OtherKind"},
207 {Group: "testgroup.k8s.io", Version: "v1", Kind: "OtherKind"},
208 {Group: "testgroup.k8s.io", Version: "v1", Kind: "Kind"},
209 },
210 },
211 Expect: `true`,
212 },
213 {
214 Name: "nil",
215 FuncName: "set",
216 Source: `{{set nil "key" "value" | toJson}}`,
217 Expect: `{"key":"value"}`,
218 },
219 {
220 Name: "empty",
221 FuncName: "set",
222 Source: `{{set (dict) "key" "value" | toJson}}`,
223 Expect: `{"key":"value"}`,
224 },
225 {
226 Name: "OddArgs",
227 FuncName: "set",
228 Source: `{{set (dict) "key" "value" "key2" | toJson}}`,
229 Error: `expected even number of arguments`,
230 },
231 {
232 Name: "NonStringKey",
233 FuncName: "set",
234 Source: `{{set (dict) 1 "value" | toJson}}`,
235 Error: `keys must be strings`,
236 },
237 {
238 Name: "NilKey",
239 FuncName: "set",
240 Source: `{{set (dict) nil "value" | toJson}}`,
241 Error: `keys must be strings`,
242 },
243 {
244 Name: "NilValue",
245 FuncName: "set",
246 Source: `{{set (dict) "key" nil | toJson}}`,
247 Expect: `{"key":null}`,
248 },
249 {
250 Name: "OverwriteKey",
251 FuncName: "set",
252 Source: `{{set (dict "key1" "val1" "key2" "val2") "key1" nil | toJson}}`,
253 Expect: `{"key1":null,"key2":"val2"}`,
254 },
255 {
256 Name: "OverwriteKeyWithLefover",
257 FuncName: "set",
258 Source: `{{set (dict "key1" "val1" "key2" "val2" "key3" "val3") "key1" nil | toJson}}`,
259 Expect: `{"key1":null,"key2":"val2","key3":"val3"}`,
260 },
261 {
262 Name: "basic",
263 FuncName: "add",
264 Source: `{{add 1 2}}`,
265 Expect: `3`,
266 },
267 {
268 Name: "basic",
269 FuncName: "sub",
270 Source: `{{sub 1 2}}`,
271 Expect: `-1`,
272 },
273 {
274 Name: "basic",
275 FuncName: "mul",
276 Source: `{{mul 2 3}}`,
277 Expect: `6`,
278 },
279 {
280 Name: "basic",
281 FuncName: "resolveRef",
282 Source: `{{resolveRef "#/components/schemas/myTypeName" . | toJson}}`,
283 Context: map[string]any{
284 "components": map[string]any{
285 "schemas": map[string]any{
286 "myTypeName": map[string]any{
287 "key": "val",
288 },
289 },
290 },
291 },
292 Expect: `{"key":"val"}`,
293 },
294 {
295 Name: "basicNameWithDots",
296 FuncName: "resolveRef",
297 Source: `{{resolveRef "#/components/schemas/myTypeName.with.dots" . | toJson}}`,
298 Context: map[string]any{
299 "components": map[string]any{
300 "schemas": map[string]any{
301 "myTypeName.with.dots": map[string]any{
302 "key": "val",
303 },
304 },
305 },
306 },
307 Expect: `{"key":"val"}`,
308 },
309 {
310 Name: "notFound",
311 FuncName: "resolveRef",
312 Source: `{{resolveRef "#/components/schemas/otherTypeName" . | toJson}}`,
313 Context: map[string]any{
314 "components": map[string]any{
315 "schemas": map[string]any{
316 "myTypeName": map[string]any{
317 "key": "val",
318 },
319 },
320 },
321 },
322 Expect: `null`,
323 },
324 {
325 Name: "url",
326 FuncName: "resolveRef",
327 Source: `{{resolveRef "http://swagger.com/swagger.json#/components/schemas/myTypeName" . | toJson}}`,
328 Context: map[string]any{
329 "components": map[string]any{
330 "schemas": map[string]any{
331 "myTypeName": map[string]any{
332 "key": "val",
333 },
334 },
335 },
336 },
337 Expect: `null`,
338 },
339 }
340
341 for _, tcase := range testcases {
342 t.Run(tcase.FuncName+"/"+tcase.Name, func(t *testing.T) {
343
344 tmpl, err := v2.WithBuiltinTemplateFuncs(template.New("me")).Parse(tcase.Source)
345 require.NoError(t, err)
346
347 buf := bytes.NewBuffer(nil)
348 err = tmpl.Execute(buf, tcase.Context)
349
350 if len(tcase.Error) > 0 {
351 require.ErrorContains(t, err, tcase.Error)
352 } else if output := buf.String(); len(tcase.Expect) > 0 {
353 require.NoError(t, err)
354 require.Contains(t, output, tcase.Expect)
355 }
356 })
357 }
358 }
359
View as plain text