1
16
17 package typed_test
18
19 import (
20 "fmt"
21 "testing"
22
23 "sigs.k8s.io/structured-merge-diff/v4/typed"
24 "sigs.k8s.io/structured-merge-diff/v4/value"
25 )
26
27 func TestValidateDeducedType(t *testing.T) {
28 tests := []string{
29 `{"a": null}`,
30 `{"a": ["a", "b"]}`,
31 `{"a": {"b": [], "c": 2, "d": {"f": "string"}}}`,
32 }
33
34 for i, test := range tests {
35 t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
36 v, err := typed.DeducedParseableType.FromYAML(typed.YAMLObject(test))
37 if err != nil {
38 t.Fatalf("Failed to parse yaml: %v", err)
39 }
40 if err := v.Validate(); err != nil {
41 t.Fatalf("Validation failed: %v", err)
42 }
43 })
44 }
45 }
46
47 func TestMergeDeduced(t *testing.T) {
48 triplets := []mergeTriplet{
49 {
50 `{"key":"foo","value":{}}`,
51 `{"key":"foo","value":1}`,
52 `{"key":"foo","value":1}`,
53 }, {
54 `{"key":"foo","value":1}`,
55 `{"key":"foo","value":{}}`,
56 `{"key":"foo","value":{}}`,
57 }, {
58 `{"key":"foo","value":null}`,
59 `{"key":"foo","value":{}}`,
60 `{"key":"foo","value":{}}`,
61 }, {
62 `{"key":"foo"}`,
63 `{"value":true}`,
64 `{"key":"foo","value":true}`,
65 }, {
66 `{}`,
67 `{"inner":{}}`,
68 `{"inner":{}}`,
69 }, {
70 `{}`,
71 `{"inner":null}`,
72 `{"inner":null}`,
73 }, {
74 `{"inner":null}`,
75 `{"inner":{}}`,
76 `{"inner":{}}`,
77 }, {
78 `{"inner":{}}`,
79 `{"inner":null}`,
80 `{"inner":null}`,
81 }, {
82 `{"inner":{}}`,
83 `{"inner":{}}`,
84 `{"inner":{}}`,
85 }, {
86 `{}`,
87 `{"inner":{}}`,
88 `{"inner":{}}`,
89 }, {
90 `{"inner":null}`,
91 `{"inner":{}}`,
92 `{"inner":{}}`,
93 }, {
94 `{"inner":{}}`,
95 `{"inner":null}`,
96 `{"inner":null}`,
97 }, {
98 `{}`,
99 `{"inner":[]}`,
100 `{"inner":[]}`,
101 }, {
102 `{"inner":null}`,
103 `{"inner":[]}`,
104 `{"inner":[]}`,
105 }, {
106 `{"inner":[]}`,
107 `{"inner":null}`,
108 `{"inner":null}`,
109 }, {
110 `{"inner":[]}`,
111 `{"inner":[]}`,
112 `{"inner":[]}`,
113 }, {
114 `{"numeric":1}`,
115 `{"numeric":3.14159}`,
116 `{"numeric":3.14159}`,
117 }, {
118 `{"numeric":3.14159}`,
119 `{"numeric":1}`,
120 `{"numeric":1}`,
121 }, {
122 `{"string":"aoeu"}`,
123 `{"bool":true}`,
124 `{"string":"aoeu","bool":true}`,
125 }, {
126 `{"atomic":["a","b","c"]}`,
127 `{"atomic":["a","b"]}`,
128 `{"atomic":["a","b"]}`,
129 }, {
130 `{"atomic":["a","b"]}`,
131 `{"atomic":["a","b","c"]}`,
132 `{"atomic":["a","b","c"]}`,
133 }, {
134 `{"atomic":["a","b","c"]}`,
135 `{"atomic":[]}`,
136 `{"atomic":[]}`,
137 }, {
138 `{"atomic":[]}`,
139 `{"atomic":["a","b","c"]}`,
140 `{"atomic":["a","b","c"]}`,
141 }, {
142 `{"":[true]}`,
143 `{"setBool":[false]}`,
144 `{"":[true],"setBool":[false]}`,
145 }, {
146 `{"atomic":[1,2,3.14159]}`,
147 `{"atomic":[1,2,3]}`,
148 `{"atomic":[1,2,3]}`,
149 }, {
150 `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
151 `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
152 `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
153 }, {
154 `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
155 `{"list":[{"key":"a","id":2,"value":{"a":"a"}}]}`,
156 `{"list":[{"key":"a","id":2,"value":{"a":"a"}}]}`,
157 }, {
158 `{"list":[{"key":"a","id":1},{"key":"b","id":1}]}`,
159 `{"list":[{"key":"a","id":1},{"key":"a","id":2}]}`,
160 `{"list":[{"key":"a","id":1},{"key":"a","id":2}]}`,
161 }, {
162 `{"atomicList":["a","a","a"]}`,
163 `{"atomicList":null}`,
164 `{"atomicList":null}`,
165 }, {
166 `{"atomicList":["a","b","c"]}`,
167 `{"atomicList":[]}`,
168 `{"atomicList":[]}`,
169 }, {
170 `{"atomicList":["a","a","a"]}`,
171 `{"atomicList":["a","a"]}`,
172 `{"atomicList":["a","a"]}`,
173 }, {
174 `{"a":1,"b":[null],"c":{"id":2,"list":["value"]}}`,
175 `{"a":2,"b":["value"],"c":{"name":"my_name"}}`,
176 `{"a":2,"b":["value"],"c":{"id":2,"list":["value"],"name":"my_name"}}`,
177 }}
178
179 for i, triplet := range triplets {
180 triplet := triplet
181 t.Run(fmt.Sprintf("triplet-%v", i), func(t *testing.T) {
182 t.Parallel()
183
184 pt := typed.DeducedParseableType
185 lhs, err := pt.FromYAML(triplet.lhs)
186 if err != nil {
187 t.Fatalf("unable to parser/validate lhs yaml: %v\n%v", err, triplet.lhs)
188 }
189
190 rhs, err := pt.FromYAML(triplet.rhs)
191 if err != nil {
192 t.Fatalf("unable to parser/validate rhs yaml: %v\n%v", err, triplet.rhs)
193 }
194
195 out, err := pt.FromYAML(triplet.out)
196 if err != nil {
197 t.Fatalf("unable to parser/validate out yaml: %v\n%v", err, triplet.out)
198 }
199
200 got, err := lhs.Merge(rhs)
201 if err != nil {
202 t.Errorf("got validation errors: %v", err)
203 } else {
204 if !value.Equals(got.AsValue(), out.AsValue()) {
205 t.Errorf("Expected\n%v\nbut got\n%v\n",
206 value.ToString(out.AsValue()), value.ToString(got.AsValue()),
207 )
208 }
209 }
210 })
211 }
212 }
213
214 func TestToSetDeduced(t *testing.T) {
215 tests := []objSetPair{
216 {`{"key":"foo","value":1}`, _NS(_P("key"), _P("value"))},
217 {`{"key":"foo","value":{"a": "b"}}`, _NS(_P("key"), _P("value"), _P("value", "a"))},
218 {`{"key":"foo","value":null}`, _NS(_P("key"), _P("value"))},
219 {`{"key":"foo"}`, _NS(_P("key"))},
220 {`{"key":"foo","value":true}`, _NS(_P("key"), _P("value"))},
221 {`{"numeric":1}`, _NS(_P("numeric"))},
222 {`{"numeric":3.14159}`, _NS(_P("numeric"))},
223 {`{"string":"aoeu"}`, _NS(_P("string"))},
224 {`{"bool":true}`, _NS(_P("bool"))},
225 {`{"bool":false}`, _NS(_P("bool"))},
226 {`{"list":["a","b","c"]}`, _NS(_P("list"))},
227 {`{"color":{}}`, _NS(_P("color"))},
228 {`{"color":null}`, _NS(_P("color"))},
229 {`{"color":{"R":255,"G":0,"B":0}}`, _NS(_P("color"), _P("color", "R"), _P("color", "G"), _P("color", "B"))},
230 {`{"arbitraryWavelengthColor":null}`, _NS(_P("arbitraryWavelengthColor"))},
231 {`{"arbitraryWavelengthColor":{"IR":255}}`, _NS(_P("arbitraryWavelengthColor"), _P("arbitraryWavelengthColor", "IR"))},
232 {`{"args":[]}`, _NS(_P("args"))},
233 {`{"args":null}`, _NS(_P("args"))},
234 {`{"args":[null]}`, _NS(_P("args"))},
235 {`{"args":[{"key":"a","value":"b"},{"key":"c","value":"d"}]}`, _NS(_P("args"))},
236 {`{"atomicList":["a","a","a"]}`, _NS(_P("atomicList"))},
237 }
238
239 for i, v := range tests {
240 v := v
241 t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
242 t.Parallel()
243
244 tv, err := typed.DeducedParseableType.FromYAML(v.object)
245 if err != nil {
246 t.Fatalf("failed to parse object: %v", err)
247 }
248 fs, err := tv.ToFieldSet()
249 if err != nil {
250 t.Fatalf("got validation errors: %v", err)
251 }
252 if !fs.Equals(v.set) {
253 t.Errorf("wanted\n%s\ngot\n%s\n", v.set, fs)
254 }
255 })
256 }
257 }
258
259 func TestSymdiffDeduced(t *testing.T) {
260 quints := []symdiffQuint{{
261 lhs: `{"key":"foo","value":1}`,
262 rhs: `{"key":"foo","value":1}`,
263 removed: _NS(),
264 modified: _NS(),
265 added: _NS(),
266 }, {
267 lhs: `{"key":"foo","value":{}}`,
268 rhs: `{"key":"foo","value":1}`,
269 removed: _NS(),
270 modified: _NS(_P("value")),
271 added: _NS(),
272 }, {
273 lhs: `{"key":"foo","value":1}`,
274 rhs: `{"key":"foo","value":{}}`,
275 removed: _NS(),
276 modified: _NS(_P("value")),
277 added: _NS(),
278 }, {
279 lhs: `{"key":"foo","value":1}`,
280 rhs: `{"key":"foo","value":{"deep":{"nested":1}}}`,
281 removed: _NS(),
282 modified: _NS(_P("value")),
283 added: _NS(_P("value", "deep"), _P("value", "deep", "nested")),
284 }, {
285 lhs: `{"key":"foo","value":null}`,
286 rhs: `{"key":"foo","value":{}}`,
287 removed: _NS(),
288 modified: _NS(_P("value")),
289 added: _NS(),
290 }, {
291 lhs: `{"key":"foo"}`,
292 rhs: `{"value":true}`,
293 removed: _NS(_P("key")),
294 modified: _NS(),
295 added: _NS(_P("value")),
296 }, {
297 lhs: `{"key":"foot"}`,
298 rhs: `{"key":"foo","value":true}`,
299 removed: _NS(),
300 modified: _NS(_P("key")),
301 added: _NS(_P("value")),
302 }, {
303 lhs: `{}`,
304 rhs: `{"inner":{}}`,
305 removed: _NS(),
306 modified: _NS(),
307 added: _NS(_P("inner")),
308 }, {
309 lhs: `{}`,
310 rhs: `{"inner":null}`,
311 removed: _NS(),
312 modified: _NS(),
313 added: _NS(_P("inner")),
314 }, {
315 lhs: `{"inner":null}`,
316 rhs: `{"inner":{}}`,
317 removed: _NS(),
318 modified: _NS(_P("inner")),
319 added: _NS(),
320 }, {
321 lhs: `{"inner":{}}`,
322 rhs: `{"inner":null}`,
323 removed: _NS(),
324 modified: _NS(_P("inner")),
325 added: _NS(),
326 }, {
327 lhs: `{"inner":{}}`,
328 rhs: `{"inner":{}}`,
329 removed: _NS(),
330 modified: _NS(),
331 added: _NS(),
332 }, {
333 lhs: `{}`,
334 rhs: `{"inner":[]}`,
335 removed: _NS(),
336 modified: _NS(),
337 added: _NS(_P("inner")),
338 }, {
339 lhs: `{}`,
340 rhs: `{"inner":null}`,
341 removed: _NS(),
342 modified: _NS(),
343 added: _NS(_P("inner")),
344 }, {
345 lhs: `{"inner":null}`,
346 rhs: `{"inner":[]}`,
347 removed: _NS(),
348 modified: _NS(_P("inner")),
349 added: _NS(),
350 }, {
351 lhs: `{"inner":[]}`,
352 rhs: `{"inner":null}`,
353 removed: _NS(),
354 modified: _NS(_P("inner")),
355 added: _NS(),
356 }, {
357 lhs: `{"inner":[]}`,
358 rhs: `{"inner":[]}`,
359 removed: _NS(),
360 modified: _NS(),
361 added: _NS(),
362 }, {
363 lhs: `{"a":{},"b":{}}`,
364 rhs: `{"a":{},"b":{}}`,
365 removed: _NS(),
366 modified: _NS(),
367 added: _NS(),
368 }, {
369 lhs: `{"a":{}}`,
370 rhs: `{"b":{}}`,
371 removed: _NS(_P("a")),
372 modified: _NS(),
373 added: _NS(_P("b")),
374 }, {
375 lhs: `{"a":{"b":{"c":{}}}}`,
376 rhs: `{"a":{"b":{}}}`,
377 removed: _NS(_P("a", "b", "c")),
378 modified: _NS(),
379 added: _NS(),
380 }, {
381 lhs: `{"a":{"b":{"c":[true]}}}`,
382 rhs: `{"a":{"b":[false]}}`,
383 removed: _NS(_P("a", "b", "c")),
384 modified: _NS(_P("a", "b")),
385 added: _NS(),
386 }, {
387 lhs: `{"a":{}}`,
388 rhs: `{"a":{"b":"true"}}`,
389 removed: _NS(),
390 modified: _NS(),
391 added: _NS(_P("a", "b")),
392 }, {
393 lhs: `{"numeric":1}`,
394 rhs: `{"numeric":3.14159}`,
395 removed: _NS(),
396 modified: _NS(_P("numeric")),
397 added: _NS(),
398 }, {
399 lhs: `{"numeric":3.14159}`,
400 rhs: `{"numeric":1}`,
401 removed: _NS(),
402 modified: _NS(_P("numeric")),
403 added: _NS(),
404 }, {
405 lhs: `{"string":"aoeu"}`,
406 rhs: `{"bool":true}`,
407 removed: _NS(_P("string")),
408 modified: _NS(),
409 added: _NS(_P("bool")),
410 }, {
411 lhs: `{"list":["a","b"]}`,
412 rhs: `{"list":["a","b","c"]}`,
413 removed: _NS(),
414 modified: _NS(_P("list")),
415 added: _NS(),
416 }, {
417 lhs: `{}`,
418 rhs: `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
419 removed: _NS(),
420 modified: _NS(),
421 added: _NS(_P("list")),
422 }, {
423 lhs: `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
424 rhs: `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
425 removed: _NS(),
426 modified: _NS(),
427 added: _NS(),
428 }, {
429 lhs: `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
430 rhs: `{"list":[{"key":"a","id":1,"value":{"a":"b"}}]}`,
431 removed: _NS(),
432 modified: _NS(_P("list")),
433 added: _NS(),
434 }, {
435 lhs: `{"atomicList":["a","a","a"]}`,
436 rhs: `{"atomicList":null}`,
437 removed: _NS(),
438 modified: _NS(_P("atomicList")),
439 added: _NS(),
440 }, {
441 lhs: `{"atomicList":["a","a","a"]}`,
442 rhs: `{"atomicList":["a","a"]}`,
443 removed: _NS(),
444 modified: _NS(_P("atomicList")),
445 added: _NS(),
446 }}
447
448 for i, quint := range quints {
449 quint := quint
450 t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
451
452 pt := typed.DeducedParseableType
453
454 tvLHS, err := pt.FromYAML(quint.lhs)
455 if err != nil {
456 t.Fatalf("failed to parse lhs: %v", err)
457 }
458 tvRHS, err := pt.FromYAML(quint.rhs)
459 if err != nil {
460 t.Fatalf("failed to parse rhs: %v", err)
461 }
462 got, err := tvLHS.Compare(tvRHS)
463 if err != nil {
464 t.Fatalf("got validation errors: %v", err)
465 }
466 t.Logf("got added:\n%s\n", got.Added)
467 if !got.Added.Equals(quint.added) {
468 t.Errorf("Expected added:\n%s\n", quint.added)
469 }
470 t.Logf("got modified:\n%s", got.Modified)
471 if !got.Modified.Equals(quint.modified) {
472 t.Errorf("Expected modified:\n%s\n", quint.modified)
473 }
474 t.Logf("got removed:\n%s", got.Removed)
475 if !got.Removed.Equals(quint.removed) {
476 t.Errorf("Expected removed:\n%s\n", quint.removed)
477 }
478
479
480 gotR, err := tvRHS.Compare(tvLHS)
481 if err != nil {
482 t.Fatalf("(reverse) got validation errors: %v", err)
483 }
484 if !gotR.Modified.Equals(got.Modified) {
485 t.Errorf("reverse operation gave different modified list:\n%s", gotR.Modified)
486 }
487 if !gotR.Removed.Equals(got.Added) {
488 t.Errorf("reverse removed gave different result than added:\n%s", gotR.Removed)
489 }
490 if !gotR.Added.Equals(got.Removed) {
491 t.Errorf("reverse added gave different result than removed:\n%s", gotR.Added)
492 }
493
494 })
495 }
496 }
497
View as plain text