1
2
3
4 package refvar_test
5
6 import (
7 "fmt"
8 "testing"
9
10 "github.com/stretchr/testify/assert"
11 . "sigs.k8s.io/kustomize/api/filters/refvar"
12 )
13
14 type expected struct {
15 count int
16 edited string
17 }
18
19 func TestPrimitiveReplacer(t *testing.T) {
20 varCounts := make(map[string]int)
21 f := MakePrimitiveReplacer(
22 varCounts,
23 map[string]interface{}{
24 "FOO": "bar",
25 "ZOO": "$(FOO)-1",
26 "BLU": "$(ZOO)-2",
27 "EIGHT": 8,
28 "PI": 3.14159,
29 "ZINT": "$(INT)",
30 "BOOL": "true",
31 "HUGENUMBER": int64(9223372036854775807),
32 "CRAZYMAP": map[string]int{"crazy": 200},
33 "ZBOOL": "$(BOOL)",
34 })
35 assert.Equal(t, "$()", f(""))
36 assert.Equal(t, "$( )", f(" "))
37 assert.Equal(t, "$(florida)", f("florida"))
38 assert.Equal(t, "$(0)", f("0"))
39 assert.Equal(t, "bar", f("FOO"))
40 assert.Equal(t, "bar", f("FOO"))
41 assert.Equal(t, "bar", f("FOO"))
42 assert.Equal(t, 8, f("EIGHT"))
43 assert.Equal(t, 8, f("EIGHT"))
44 assert.Equal(t, 3.14159, f("PI"))
45 assert.Equal(t, "true", f("BOOL"))
46 assert.Equal(t, int64(9223372036854775807), f("HUGENUMBER"))
47 assert.Equal(t, "$(FOO)-1", f("ZOO"))
48 assert.Equal(t, "$(CRAZYMAP)", f("CRAZYMAP"))
49 assert.Equal(t,
50 map[string]int{
51 "FOO": 3,
52 "EIGHT": 2,
53 "BOOL": 1,
54 "PI": 1,
55 "ZOO": 1,
56 "HUGENUMBER": 1,
57 },
58 varCounts)
59 }
60
61 func TestMapReference(t *testing.T) {
62 type env struct {
63 Name string
64 Value interface{}
65 }
66 envs := []env{
67 {
68 Name: "FOO",
69 Value: "bar",
70 },
71 {
72 Name: "ZOO",
73 Value: "$(FOO)-1",
74 },
75 {
76 Name: "BLU",
77 Value: "$(ZOO)-2",
78 },
79 {
80 Name: "INT",
81 Value: 2,
82 },
83 {
84 Name: "ZINT",
85 Value: "$(INT)",
86 },
87 {
88 Name: "BOOL",
89 Value: true,
90 },
91 {
92 Name: "ZBOOL",
93 Value: "$(BOOL)",
94 },
95 }
96
97 varMap := map[string]interface{}{
98 "FOO": "bar",
99 "ZOO": "$(FOO)-1",
100 "BLU": "$(ZOO)-2",
101 "INT": "2",
102 "ZINT": "$(INT)",
103 "BOOL": "true",
104 "ZBOOL": "$(BOOL)",
105 }
106
107 varCounts := make(map[string]int)
108 for _, env := range envs {
109 varMap[env.Name] = DoReplacements(
110 fmt.Sprintf("%v", env.Value),
111 MakePrimitiveReplacer(varCounts, varMap))
112 }
113
114 expectedEnv := map[string]expected{
115 "FOO": {count: 1, edited: "bar"},
116 "ZOO": {count: 1, edited: "bar-1"},
117 "BLU": {count: 0, edited: "bar-1-2"},
118 "INT": {count: 1, edited: "2"},
119 "ZINT": {count: 0, edited: "2"},
120 "BOOL": {count: 1, edited: "true"},
121 "ZBOOL": {count: 0, edited: "true"},
122 }
123
124 for k, v := range expectedEnv {
125 if e, a := v, varMap[k]; e.edited != a || e.count != varCounts[k] {
126 t.Errorf("Expected %v count=%d, got %v count=%d",
127 e.edited, e.count, a, varCounts[k])
128 } else {
129 delete(varMap, k)
130 }
131 }
132
133 if len(varMap) != 0 {
134 t.Errorf("Unexpected keys in declared env: %v", varMap)
135 }
136 }
137
138 func TestMapping(t *testing.T) {
139 cases := []struct {
140 name string
141 input string
142 expected string
143 counts map[string]int
144 }{
145 {
146 name: "whole string",
147 input: "$(VAR_A)",
148 expected: "A",
149 counts: map[string]int{"VAR_A": 1},
150 },
151 {
152 name: "repeat",
153 input: "$(VAR_A)-$(VAR_A)",
154 expected: "A-A",
155 counts: map[string]int{"VAR_A": 2},
156 },
157 {
158 name: "multiple repeats",
159 input: "$(VAR_A)-$(VAR_B)-$(VAR_B)-$(VAR_B)-$(VAR_A)",
160 expected: "A-B-B-B-A",
161 counts: map[string]int{"VAR_A": 2, "VAR_B": 3},
162 },
163 {
164 name: "beginning",
165 input: "$(VAR_A)-1",
166 expected: "A-1",
167 counts: map[string]int{"VAR_A": 1},
168 },
169 {
170 name: "middle",
171 input: "___$(VAR_B)___",
172 expected: "___B___",
173 counts: map[string]int{"VAR_B": 1},
174 },
175 {
176 name: "end",
177 input: "___$(VAR_C)",
178 expected: "___C",
179 counts: map[string]int{"VAR_C": 1},
180 },
181 {
182 name: "compound",
183 input: "$(VAR_A)_$(VAR_B)_$(VAR_C)",
184 expected: "A_B_C",
185 counts: map[string]int{"VAR_A": 1, "VAR_B": 1, "VAR_C": 1},
186 },
187 {
188 name: "escape & expand",
189 input: "$$(VAR_B)_$(VAR_A)",
190 expected: "$(VAR_B)_A",
191 counts: map[string]int{"VAR_A": 1},
192 },
193 {
194 name: "compound escape",
195 input: "$$(VAR_A)_$$(VAR_B)",
196 expected: "$(VAR_A)_$(VAR_B)",
197 },
198 {
199 name: "mixed in escapes",
200 input: "f000-$$VAR_A",
201 expected: "f000-$VAR_A",
202 },
203 {
204 name: "backslash escape ignored",
205 input: "foo\\$(VAR_C)bar",
206 expected: "foo\\Cbar",
207 counts: map[string]int{"VAR_C": 1},
208 },
209 {
210 name: "backslash escape ignored",
211 input: "foo\\\\$(VAR_C)bar",
212 expected: "foo\\\\Cbar",
213 counts: map[string]int{"VAR_C": 1},
214 },
215 {
216 name: "lots of backslashes",
217 input: "foo\\\\\\\\$(VAR_A)bar",
218 expected: "foo\\\\\\\\Abar",
219 counts: map[string]int{"VAR_A": 1},
220 },
221 {
222 name: "nested var references",
223 input: "$(VAR_A$(VAR_B))",
224 expected: "$(VAR_A$(VAR_B))",
225 },
226 {
227 name: "nested var references second type",
228 input: "$(VAR_A$(VAR_B)",
229 expected: "$(VAR_A$(VAR_B)",
230 },
231 {
232 name: "value is a reference",
233 input: "$(VAR_REF)",
234 expected: "$(VAR_A)",
235 counts: map[string]int{"VAR_REF": 1},
236 },
237 {
238 name: "value is a reference x 2",
239 input: "%%$(VAR_REF)--$(VAR_REF)%%",
240 expected: "%%$(VAR_A)--$(VAR_A)%%",
241 counts: map[string]int{"VAR_REF": 2},
242 },
243 {
244 name: "empty var",
245 input: "foo$(VAR_EMPTY)bar",
246 expected: "foobar",
247 counts: map[string]int{"VAR_EMPTY": 1},
248 },
249 {
250 name: "unterminated expression",
251 input: "foo$(VAR_Awhoops!",
252 expected: "foo$(VAR_Awhoops!",
253 },
254 {
255 name: "expression without operator",
256 input: "f00__(VAR_A)__",
257 expected: "f00__(VAR_A)__",
258 },
259 {
260 name: "shell special vars pass through",
261 input: "$?_boo_$!",
262 expected: "$?_boo_$!",
263 },
264 {
265 name: "bare operators are ignored",
266 input: "$VAR_A",
267 expected: "$VAR_A",
268 },
269 {
270 name: "undefined vars are passed through",
271 input: "$(VAR_DNE)",
272 expected: "$(VAR_DNE)",
273 },
274 {
275 name: "multiple (even) operators, var undefined",
276 input: "$$$$$$(BIG_MONEY)",
277 expected: "$$$(BIG_MONEY)",
278 },
279 {
280 name: "multiple (even) operators, var defined",
281 input: "$$$$$$(VAR_A)",
282 expected: "$$$(VAR_A)",
283 },
284 {
285 name: "multiple (odd) operators, var undefined",
286 input: "$$$$$$$(GOOD_ODDS)",
287 expected: "$$$$(GOOD_ODDS)",
288 },
289 {
290 name: "multiple (odd) operators, var defined",
291 input: "$$$$$$$(VAR_A)",
292 expected: "$$$A",
293 counts: map[string]int{"VAR_A": 1},
294 },
295 {
296 name: "missing open expression",
297 input: "$VAR_A)",
298 expected: "$VAR_A)",
299 },
300 {
301 name: "shell syntax ignored",
302 input: "${VAR_A}",
303 expected: "${VAR_A}",
304 },
305 {
306 name: "trailing incomplete expression not consumed",
307 input: "$(VAR_B)_______$(A",
308 expected: "B_______$(A",
309 counts: map[string]int{"VAR_B": 1},
310 },
311 {
312 name: "trailing incomplete expression, no content, is not consumed",
313 input: "$(VAR_C)_______$(",
314 expected: "C_______$(",
315 counts: map[string]int{"VAR_C": 1},
316 },
317 {
318 name: "operator at end of input string is preserved",
319 input: "$(VAR_A)foobarzab$",
320 expected: "Afoobarzab$",
321 counts: map[string]int{"VAR_A": 1},
322 },
323 {
324 name: "shell escaped incomplete expr",
325 input: "foo-\\$(VAR_A",
326 expected: "foo-\\$(VAR_A",
327 },
328 {
329 name: "lots of $( in middle",
330 input: "--$($($($($--",
331 expected: "--$($($($($--",
332 },
333 {
334 name: "lots of $( in beginning",
335 input: "$($($($($--foo$(",
336 expected: "$($($($($--foo$(",
337 },
338 {
339 name: "lots of $( at end",
340 input: "foo0--$($($($(",
341 expected: "foo0--$($($($(",
342 },
343 {
344 name: "escaped operators in variable names are not escaped",
345 input: "$(foo$$var)",
346 expected: "$(foo$$var)",
347 },
348 {
349 name: "newline not expanded",
350 input: "\n",
351 expected: "\n",
352 },
353 }
354 for _, tc := range cases {
355 counts := make(map[string]int)
356 expanded := DoReplacements(
357 fmt.Sprintf("%v", tc.input),
358 MakePrimitiveReplacer(counts, map[string]interface{}{
359 "VAR_A": "A",
360 "VAR_B": "B",
361 "VAR_C": "C",
362 "VAR_REF": "$(VAR_A)",
363 "VAR_EMPTY": "",
364 }))
365 if e, a := tc.expected, expanded; e != a {
366 t.Errorf("%v: expected %q, got %q", tc.name, e, a)
367 }
368 if len(counts) != len(tc.counts) {
369 t.Errorf("%v: len(counts)=%d != len(tc.counts)=%d",
370 tc.name, len(counts), len(tc.counts))
371 }
372 if len(tc.counts) > 0 {
373 for k, expectedCount := range tc.counts {
374 if c, ok := counts[k]; ok {
375 if c != expectedCount {
376 t.Errorf(
377 "%v: k=%s, expected count %d, got %d",
378 tc.name, k, expectedCount, c)
379 }
380 } else {
381 t.Errorf(
382 "%v: k=%s, expected count %d, got zero",
383 tc.name, k, expectedCount)
384 }
385 }
386 }
387 }
388 }
389
View as plain text