1
16
17 package typed_test
18
19 import (
20 "fmt"
21 "testing"
22
23 "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
24 "sigs.k8s.io/structured-merge-diff/v4/typed"
25 )
26
27 type symdiffTestCase struct {
28 name string
29 rootTypeName string
30 schema typed.YAMLObject
31 quints []symdiffQuint
32 }
33
34 type symdiffQuint struct {
35 lhs typed.YAMLObject
36 rhs typed.YAMLObject
37
38
39
40
41 removed *fieldpath.Set
42 modified *fieldpath.Set
43 added *fieldpath.Set
44 }
45
46 var symdiffCases = []symdiffTestCase{{
47 name: "simple pair",
48 rootTypeName: "stringPair",
49 schema: `types:
50 - name: stringPair
51 map:
52 fields:
53 - name: key
54 type:
55 scalar: string
56 - name: value
57 type:
58 namedType: __untyped_atomic_
59 - name: __untyped_atomic_
60 scalar: untyped
61 list:
62 elementType:
63 namedType: __untyped_atomic_
64 elementRelationship: atomic
65 map:
66 elementType:
67 namedType: __untyped_atomic_
68 elementRelationship: atomic
69 `,
70 quints: []symdiffQuint{{
71 lhs: `{"key":"foo","value":1}`,
72 rhs: `{"key":"foo","value":1}`,
73 removed: _NS(),
74 modified: _NS(),
75 added: _NS(),
76 }, {
77 lhs: `{"key":"foo","value":{}}`,
78 rhs: `{"key":"foo","value":1}`,
79 removed: _NS(),
80 modified: _NS(_P("value")),
81 added: _NS(),
82 }, {
83 lhs: `{"key":"foo","value":1}`,
84 rhs: `{"key":"foo","value":{}}`,
85 removed: _NS(),
86 modified: _NS(_P("value")),
87 added: _NS(),
88 }, {
89 lhs: `{"key":"foo","value":1}`,
90 rhs: `{"key":"foo","value":{"doesn't matter":"what's here","or":{"how":"nested"}}}`,
91 removed: _NS(),
92 modified: _NS(_P("value")),
93 added: _NS(),
94 }, {
95 lhs: `{"key":"foo","value":null}`,
96 rhs: `{"key":"foo","value":{}}`,
97 removed: _NS(),
98 modified: _NS(_P("value")),
99 added: _NS(),
100 }, {
101 lhs: `{"key":"foo"}`,
102 rhs: `{"value":true}`,
103 removed: _NS(_P("key")),
104 modified: _NS(),
105 added: _NS(_P("value")),
106 }, {
107 lhs: `{"key":"foot"}`,
108 rhs: `{"key":"foo","value":true}`,
109 removed: _NS(),
110 modified: _NS(_P("key")),
111 added: _NS(_P("value")),
112 }},
113 }, {
114 name: "null/empty map",
115 rootTypeName: "nestedMap",
116 schema: `types:
117 - name: nestedMap
118 map:
119 fields:
120 - name: inner
121 type:
122 map:
123 elementType:
124 namedType: __untyped_atomic_
125 - name: __untyped_atomic_
126 scalar: untyped
127 list:
128 elementType:
129 namedType: __untyped_atomic_
130 elementRelationship: atomic
131 map:
132 elementType:
133 namedType: __untyped_atomic_
134 elementRelationship: atomic
135 `,
136 quints: []symdiffQuint{{
137 lhs: `{}`,
138 rhs: `{"inner":{}}`,
139 removed: _NS(),
140 modified: _NS(),
141 added: _NS(_P("inner")),
142 }, {
143 lhs: `{}`,
144 rhs: `{"inner":null}`,
145 removed: _NS(),
146 modified: _NS(),
147 added: _NS(_P("inner")),
148 }, {
149 lhs: `{"inner":null}`,
150 rhs: `{"inner":{}}`,
151 removed: _NS(),
152 modified: _NS(_P("inner")),
153 added: _NS(),
154 }, {
155 lhs: `{"inner":{}}`,
156 rhs: `{"inner":null}`,
157 removed: _NS(),
158 modified: _NS(_P("inner")),
159 added: _NS(),
160 }, {
161 lhs: `{"inner":{}}`,
162 rhs: `{"inner":{}}`,
163 removed: _NS(),
164 modified: _NS(),
165 added: _NS(),
166 }},
167 }, {
168 name: "null/empty struct",
169 rootTypeName: "nestedStruct",
170 schema: `types:
171 - name: nestedStruct
172 map:
173 fields:
174 - name: inner
175 type:
176 map:
177 fields:
178 - name: value
179 type:
180 namedType: __untyped_atomic_
181 `,
182 quints: []symdiffQuint{{
183 lhs: `{}`,
184 rhs: `{"inner":{}}`,
185 removed: _NS(),
186 modified: _NS(),
187 added: _NS(_P("inner")),
188 }, {
189 lhs: `{}`,
190 rhs: `{"inner":null}`,
191 removed: _NS(),
192 modified: _NS(),
193 added: _NS(_P("inner")),
194 }, {
195 lhs: `{"inner":null}`,
196 rhs: `{"inner":{}}`,
197 removed: _NS(),
198 modified: _NS(_P("inner")),
199 added: _NS(),
200 }, {
201 lhs: `{"inner":{}}`,
202 rhs: `{"inner":null}`,
203 removed: _NS(),
204 modified: _NS(_P("inner")),
205 added: _NS(),
206 }, {
207 lhs: `{"inner":{}}`,
208 rhs: `{"inner":{}}`,
209 removed: _NS(),
210 modified: _NS(),
211 added: _NS(),
212 }},
213 }, {
214 name: "null/empty list",
215 rootTypeName: "nestedList",
216 schema: `types:
217 - name: nestedList
218 map:
219 fields:
220 - name: inner
221 type:
222 list:
223 elementType:
224 namedType: __untyped_atomic_
225 elementRelationship: atomic
226 - name: __untyped_atomic_
227 scalar: untyped
228 list:
229 elementType:
230 namedType: __untyped_atomic_
231 elementRelationship: atomic
232 map:
233 elementType:
234 namedType: __untyped_atomic_
235 elementRelationship: atomic
236 `,
237 quints: []symdiffQuint{{
238 lhs: `{}`,
239 rhs: `{"inner":[]}`,
240 removed: _NS(),
241 modified: _NS(),
242 added: _NS(_P("inner")),
243 }, {
244 lhs: `{}`,
245 rhs: `{"inner":null}`,
246 removed: _NS(),
247 modified: _NS(),
248 added: _NS(_P("inner")),
249 }, {
250 lhs: `{"inner":null}`,
251 rhs: `{"inner":[]}`,
252 removed: _NS(),
253 modified: _NS(_P("inner")),
254 added: _NS(),
255 }, {
256 lhs: `{"inner":[]}`,
257 rhs: `{"inner":null}`,
258 removed: _NS(),
259 modified: _NS(_P("inner")),
260 added: _NS(),
261 }, {
262 lhs: `{"inner":[]}`,
263 rhs: `{"inner":[]}`,
264 removed: _NS(),
265 modified: _NS(),
266 added: _NS(),
267 }},
268 }, {
269 name: "map merge",
270 rootTypeName: "nestedMap",
271 schema: `types:
272 - name: nestedMap
273 map:
274 elementType:
275 namedType: nestedMap
276 `,
277 quints: []symdiffQuint{{
278 lhs: `{"a":{},"b":{}}`,
279 rhs: `{"a":{},"b":{}}`,
280 removed: _NS(),
281 modified: _NS(),
282 added: _NS(),
283 }, {
284 lhs: `{"a":{}}`,
285 rhs: `{"b":{}}`,
286 removed: _NS(_P("a")),
287 modified: _NS(),
288 added: _NS(_P("b")),
289 }, {
290 lhs: `{"a":{"b":{"c":{}}}}`,
291 rhs: `{"a":{"b":{}}}`,
292 removed: _NS(_P("a", "b", "c")),
293 modified: _NS(),
294 added: _NS(),
295 }, {
296 lhs: `{"a":{}}`,
297 rhs: `{"a":{"b":{}}}`,
298 removed: _NS(),
299 modified: _NS(),
300 added: _NS(_P("a", "b")),
301 }},
302 }, {
303 name: "untyped deduced",
304 rootTypeName: "__untyped_deduced_",
305 schema: `types:
306 - name: __untyped_atomic_
307 scalar: untyped
308 list:
309 elementType:
310 namedType: __untyped_atomic_
311 elementRelationship: atomic
312 map:
313 elementType:
314 namedType: __untyped_atomic_
315 elementRelationship: atomic
316 - name: __untyped_deduced_
317 scalar: untyped
318 list:
319 elementType:
320 namedType: __untyped_atomic_
321 elementRelationship: atomic
322 map:
323 elementType:
324 namedType: __untyped_deduced_
325 elementRelationship: separable
326 `,
327 quints: []symdiffQuint{{
328 lhs: `{"a":{}}}`,
329 rhs: `{"a":{"b":{}}}`,
330 removed: _NS(),
331 modified: _NS(),
332 added: _NS(_P("a", "b")),
333 }, {
334 lhs: `{"a":null}`,
335 rhs: `{"a":{"b":{}}}`,
336 removed: _NS(),
337 modified: _NS(),
338 added: _NS(_P("a", "b")),
339 }, {
340 lhs: `{"a":{"b":{}}}`,
341 rhs: `{"a":{}}}`,
342 removed: _NS(_P("a", "b")),
343 modified: _NS(),
344 added: _NS(),
345 }, {
346 lhs: `{"a":{"b":{}}}`,
347 rhs: `{"a":null}`,
348 removed: _NS(_P("a", "b")),
349 modified: _NS(),
350 added: _NS(),
351 }, {
352 lhs: `{"a":[]}`,
353 rhs: `{"a":["b"]}`,
354 removed: _NS(),
355 modified: _NS(_P("a")),
356 added: _NS(),
357 }, {
358 lhs: `{"a":null}`,
359 rhs: `{"a":["b"]}`,
360 removed: _NS(),
361 modified: _NS(_P("a")),
362 added: _NS(),
363 }, {
364 lhs: `{"a":["b"]}`,
365 rhs: `{"a":[]}`,
366 removed: _NS(),
367 modified: _NS(_P("a")),
368 added: _NS(),
369 }, {
370 lhs: `{"a":["b"]}`,
371 rhs: `{"a":null}`,
372 removed: _NS(),
373 modified: _NS(_P("a")),
374 added: _NS(),
375 }, {
376 lhs: `{"a":null}`,
377 rhs: `{"a":"b"}`,
378 removed: _NS(),
379 modified: _NS(_P("a")),
380 added: _NS(),
381 }, {
382 lhs: `{"a":"b"}`,
383 rhs: `{"a":null}`,
384 removed: _NS(),
385 modified: _NS(_P("a")),
386 added: _NS(),
387 }, {
388 lhs: `{"a":{"b":{}}}`,
389 rhs: `{"a":["b"]}}`,
390 removed: _NS(_P("a", "b")),
391 modified: _NS(_P("a")),
392 added: _NS(),
393 }, {
394 lhs: `{"a":["b"]}}`,
395 rhs: `{"a":{"b":{}}}`,
396 removed: _NS(),
397 modified: _NS(_P("a")),
398 added: _NS(_P("a", "b")),
399 }, {
400 lhs: `{"a":{"b":{}}}`,
401 rhs: `{"a":"b"}`,
402 removed: _NS(_P("a", "b")),
403 modified: _NS(_P("a")),
404 added: _NS(),
405 }, {
406 lhs: `{"a":"b"}`,
407 rhs: `{"a":{"b":{}}}`,
408 removed: _NS(),
409 modified: _NS(_P("a")),
410 added: _NS(_P("a", "b")),
411 }, {
412 lhs: `{"a":["b"]}}`,
413 rhs: `{"a":"b"}`,
414 removed: _NS(),
415 modified: _NS(_P("a")),
416 added: _NS(),
417 }, {
418 lhs: `{"a":"b"}`,
419 rhs: `{"a":["b"]}}`,
420 removed: _NS(),
421 modified: _NS(_P("a")),
422 added: _NS(),
423 }},
424 }, {
425 name: "untyped separable",
426 rootTypeName: "__untyped_separable_",
427 schema: `types:
428 - name: __untyped_separable_
429 scalar: untyped
430 list:
431 elementType:
432 namedType: __untyped_separable_
433 elementRelationship: associative
434 map:
435 elementType:
436 namedType: __untyped_separable_
437 elementRelationship: separable
438 `,
439 quints: []symdiffQuint{{
440 lhs: `{"a":{}}}`,
441 rhs: `{"a":{"b":{}}}`,
442 removed: _NS(),
443 modified: _NS(),
444 added: _NS(_P("a", "b")),
445 }, {
446 lhs: `{"a":null}`,
447 rhs: `{"a":{"b":{}}}`,
448 removed: _NS(),
449 modified: _NS(),
450 added: _NS(_P("a", "b")),
451 }, {
452 lhs: `{"a":{"b":{}}}`,
453 rhs: `{"a":{}}}`,
454 removed: _NS(_P("a", "b")),
455 modified: _NS(),
456 added: _NS(),
457 }, {
458 lhs: `{"a":{"b":{}}}`,
459 rhs: `{"a":null}`,
460 removed: _NS(_P("a", "b")),
461 modified: _NS(),
462 added: _NS(),
463 }, {
464 lhs: `{"a":[]}`,
465 rhs: `{"a":["b"]}`,
466 removed: _NS(),
467 modified: _NS(),
468 added: _NS(_P("a", _V("b"))),
469 }, {
470 lhs: `{"a":null}`,
471 rhs: `{"a":["b"]}`,
472 removed: _NS(),
473
474
475 modified: _NS(_P("a")),
476 added: _NS(_P("a", _V("b"))),
477 }, {
478 lhs: `{"a":["b"]}`,
479 rhs: `{"a":[]}`,
480 removed: _NS(_P("a", _V("b"))),
481 modified: _NS(),
482 added: _NS(),
483 }, {
484 lhs: `{"a":["b"]}`,
485 rhs: `{"a":null}`,
486 removed: _NS(_P("a", _V("b"))),
487
488
489 modified: _NS(_P("a")),
490 added: _NS(),
491 }, {
492 lhs: `{"a":null}`,
493 rhs: `{"a":"b"}`,
494 removed: _NS(),
495 modified: _NS(_P("a")),
496 added: _NS(),
497 }, {
498 lhs: `{"a":"b"}`,
499 rhs: `{"a":null}`,
500 removed: _NS(),
501 modified: _NS(_P("a")),
502 added: _NS(),
503 }, {
504 lhs: `{"a":{"b":{}}}`,
505 rhs: `{"a":["b"]}}`,
506 removed: _NS(_P("a", "b")),
507 modified: _NS(),
508 added: _NS(_P("a", _V("b"))),
509 }, {
510 lhs: `{"a":["b"]}}`,
511 rhs: `{"a":{"b":{}}}`,
512 removed: _NS(_P("a", _V("b"))),
513 modified: _NS(),
514 added: _NS(_P("a", "b")),
515 }, {
516 lhs: `{"a":{"b":{}}}`,
517 rhs: `{"a":"b"}`,
518 removed: _NS(_P("a", "b")),
519 modified: _NS(_P("a")),
520 added: _NS(),
521 }, {
522 lhs: `{"a":"b"}`,
523 rhs: `{"a":{"b":{}}}`,
524 removed: _NS(),
525 modified: _NS(_P("a")),
526 added: _NS(_P("a", "b")),
527 }, {
528 lhs: `{"a":["b"]}}`,
529 rhs: `{"a":"b"}`,
530 removed: _NS(_P("a", _V("b"))),
531 modified: _NS(_P("a")),
532 added: _NS(),
533 }, {
534 lhs: `{"a":"b"}`,
535 rhs: `{"a":["b"]}}`,
536 removed: _NS(),
537 modified: _NS(_P("a")),
538 added: _NS(_P("a", _V("b"))),
539 }},
540 }, {
541 name: "struct grab bag",
542 rootTypeName: "myStruct",
543 schema: `types:
544 - name: myStruct
545 map:
546 fields:
547 - name: numeric
548 type:
549 scalar: numeric
550 - name: string
551 type:
552 scalar: string
553 - name: bool
554 type:
555 scalar: boolean
556 - name: setStr
557 type:
558 list:
559 elementType:
560 scalar: string
561 elementRelationship: associative
562 - name: setBool
563 type:
564 list:
565 elementType:
566 scalar: boolean
567 elementRelationship: associative
568 - name: setNumeric
569 type:
570 list:
571 elementType:
572 scalar: numeric
573 elementRelationship: associative
574 `,
575 quints: []symdiffQuint{{
576 lhs: `{"numeric":1}`,
577 rhs: `{"numeric":3.14159}`,
578 removed: _NS(),
579 modified: _NS(_P("numeric")),
580 added: _NS(),
581 }, {
582 lhs: `{"numeric":3.14159}`,
583 rhs: `{"numeric":1}`,
584 removed: _NS(),
585 modified: _NS(_P("numeric")),
586 added: _NS(),
587 }, {
588 lhs: `{"string":"aoeu"}`,
589 rhs: `{"bool":true}`,
590 removed: _NS(_P("string")),
591 modified: _NS(),
592 added: _NS(_P("bool")),
593 }, {
594 lhs: `{"setStr":["a","b"]}`,
595 rhs: `{"setStr":["a","b","c"]}`,
596 removed: _NS(),
597 modified: _NS(),
598 added: _NS(_P("setStr", _V("c"))),
599 }, {
600 lhs: `{"setStr":["a"]}`,
601 rhs: `{"setStr":["a","b","b"]}`,
602 removed: _NS(),
603 modified: _NS(),
604 added: _NS(_P("setStr", _V("b"))),
605 }, {
606 lhs: `{"setStr":["a","b"]}`,
607 rhs: `{"setStr":["a","b","b"]}`,
608 removed: _NS(_P("setStr", _V("b"))),
609 modified: _NS(),
610 added: _NS(_P("setStr", _V("b"))),
611 }, {
612 lhs: `{"setStr":["b","b"]}`,
613 rhs: `{"setStr":["a","b","b"]}`,
614 removed: _NS(),
615 modified: _NS(),
616 added: _NS(_P("setStr", _V("a"))),
617 }, {
618 lhs: `{"setStr":["a","b","c"]}`,
619 rhs: `{"setStr":[]}`,
620 removed: _NS(
621 _P("setStr", _V("a")),
622 _P("setStr", _V("b")),
623 _P("setStr", _V("c")),
624 ),
625 modified: _NS(),
626 added: _NS(),
627 }, {
628 lhs: `{"setBool":[true]}`,
629 rhs: `{"setBool":[false]}`,
630 removed: _NS(_P("setBool", _V(true))),
631 modified: _NS(),
632 added: _NS(_P("setBool", _V(false))),
633 }, {
634 lhs: `{"setNumeric":[1,2,3.14159]}`,
635 rhs: `{"setNumeric":[1,2,3]}`,
636 removed: _NS(_P("setNumeric", _V(3.14159))),
637 modified: _NS(),
638 added: _NS(_P("setNumeric", _V(3))),
639 }, {
640 lhs: `{"setStr":["a","b","b","c","a"]}`,
641 rhs: `{"setStr":[]}`,
642 removed: _NS(
643 _P("setStr", _V("a")),
644 _P("setStr", _V("b")),
645 _P("setStr", _V("c")),
646 ),
647 modified: _NS(),
648 added: _NS(),
649 }, {
650 lhs: `{"setBool":[true,true]}`,
651 rhs: `{"setBool":[false]}`,
652 removed: _NS(_P("setBool", _V(true))),
653 modified: _NS(),
654 added: _NS(_P("setBool", _V(false))),
655 }, {
656 lhs: `{"setNumeric":[1,2,2,3.14159,1]}`,
657 rhs: `{"setNumeric":[1,2,3]}`,
658 removed: _NS(_P("setNumeric", _V(1)), _P("setNumeric", _V(2)), _P("setNumeric", _V(3.14159))),
659 modified: _NS(),
660 added: _NS(_P("setNumeric", _V(1)), _P("setNumeric", _V(2)), _P("setNumeric", _V(3))),
661 }},
662 }, {
663 name: "associative list",
664 rootTypeName: "myRoot",
665 schema: `types:
666 - name: myRoot
667 map:
668 fields:
669 - name: list
670 type:
671 namedType: myList
672 - name: atomicList
673 type:
674 namedType: mySequence
675 - name: myList
676 list:
677 elementType:
678 namedType: myElement
679 elementRelationship: associative
680 keys:
681 - key
682 - id
683 - name: mySequence
684 list:
685 elementType:
686 scalar: string
687 elementRelationship: atomic
688 - name: myElement
689 map:
690 fields:
691 - name: key
692 type:
693 scalar: string
694 - name: id
695 type:
696 scalar: numeric
697 - name: value
698 type:
699 namedType: myValue
700 - name: bv
701 type:
702 scalar: boolean
703 - name: nv
704 type:
705 scalar: numeric
706 - name: myValue
707 map:
708 elementType:
709 scalar: string
710 `,
711 quints: []symdiffQuint{{
712 lhs: `{}`,
713 rhs: `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
714 removed: _NS(),
715 modified: _NS(),
716 added: _NS(
717 _P("list"),
718 _P("list", _KBF("key", "a", "id", 1)),
719 _P("list", _KBF("key", "a", "id", 1), "key"),
720 _P("list", _KBF("key", "a", "id", 1), "id"),
721 _P("list", _KBF("key", "a", "id", 1), "value"),
722 _P("list", _KBF("key", "a", "id", 1), "value", "a"),
723 ),
724 }, {
725 lhs: `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
726 rhs: `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
727 removed: _NS(),
728 modified: _NS(),
729 added: _NS(),
730 }, {
731 lhs: `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
732 rhs: `{"list":[{"key":"a","id":1,"value":{"a":"b"}}]}`,
733 removed: _NS(),
734 modified: _NS(_P("list", _KBF("key", "a", "id", 1), "value", "a")),
735 added: _NS(),
736 }, {
737 lhs: `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
738 rhs: `{"list":[{"key":"a","id":2,"value":{"a":"a"}}]}`,
739 removed: _NS(
740 _P("list", _KBF("key", "a", "id", 1)),
741 _P("list", _KBF("key", "a", "id", 1), "key"),
742 _P("list", _KBF("key", "a", "id", 1), "id"),
743 _P("list", _KBF("key", "a", "id", 1), "value"),
744 _P("list", _KBF("key", "a", "id", 1), "value", "a"),
745 ),
746 modified: _NS(),
747 added: _NS(
748 _P("list", _KBF("key", "a", "id", 2)),
749 _P("list", _KBF("key", "a", "id", 2), "key"),
750 _P("list", _KBF("key", "a", "id", 2), "id"),
751 _P("list", _KBF("key", "a", "id", 2), "value"),
752 _P("list", _KBF("key", "a", "id", 2), "value", "a"),
753 ),
754 }, {
755 lhs: `{"list":[{"key":"a","id":1},{"key":"b","id":1}]}`,
756 rhs: `{"list":[{"key":"a","id":1},{"key":"a","id":2}]}`,
757 removed: _NS(
758 _P("list", _KBF("key", "b", "id", 1)),
759 _P("list", _KBF("key", "b", "id", 1), "key"),
760 _P("list", _KBF("key", "b", "id", 1), "id"),
761 ),
762 modified: _NS(),
763 added: _NS(
764 _P("list", _KBF("key", "a", "id", 2)),
765 _P("list", _KBF("key", "a", "id", 2), "key"),
766 _P("list", _KBF("key", "a", "id", 2), "id"),
767 ),
768 }, {
769 lhs: `{"atomicList":["a","a","a"]}`,
770 rhs: `{"atomicList":null}`,
771 removed: _NS(),
772 modified: _NS(_P("atomicList")),
773 added: _NS(),
774 }, {
775 lhs: `{"atomicList":["a","b","c"]}`,
776 rhs: `{"atomicList":[]}`,
777 removed: _NS(),
778 modified: _NS(_P("atomicList")),
779 added: _NS(),
780 }, {
781 lhs: `{"atomicList":["a","a","a"]}`,
782 rhs: `{"atomicList":["a","a"]}`,
783 removed: _NS(),
784 modified: _NS(_P("atomicList")),
785 added: _NS(),
786 }, {
787 lhs: `{"list":[{"key":"a","id":1,"nv":2},{"key":"b","id":1},{"key":"a","id":1,"bv":true}]}`,
788 rhs: `{"list":[{"key":"a","id":1},{"key":"a","id":2}]}`,
789 removed: _NS(
790 _P("list", _KBF("key", "b", "id", 1)),
791 _P("list", _KBF("key", "b", "id", 1), "key"),
792 _P("list", _KBF("key", "b", "id", 1), "id"),
793 _P("list", _KBF("key", "a", "id", 1)),
794 ),
795 modified: _NS(),
796 added: _NS(
797 _P("list", _KBF("key", "a", "id", 1)),
798 _P("list", _KBF("key", "a", "id", 1), "key"),
799 _P("list", _KBF("key", "a", "id", 1), "id"),
800 _P("list", _KBF("key", "a", "id", 2)),
801 _P("list", _KBF("key", "a", "id", 2), "key"),
802 _P("list", _KBF("key", "a", "id", 2), "id"),
803 ),
804 }, {
805 lhs: `{"list":[{"key":"a","id":1},{"key":"b","id":1},{"key":"a","id":1,"bv":true}]}`,
806 rhs: `{"list":[{"key":"a","id":1},{"key":"b","id":1},{"key":"a","id":1,"bv":true,"nv":1}]}`,
807 removed: _NS(),
808 modified: _NS(_P("list", _KBF("key", "a", "id", 1))),
809 added: _NS(),
810 }, {
811 lhs: `{"list":[{"key":"a","id":1},{"key":"b","id":1},{"key":"a","id":1,"bv":true}]}`,
812 rhs: `{"list":[{"key":"a","id":1},{"key":"b","id":1},{"key":"a","id":1,"bv":true}]}`,
813 removed: _NS(),
814 modified: _NS(),
815 added: _NS(),
816 }, {
817 lhs: `{"list":[{"key":"a","id":1},{"key":"b","id":1},{"key":"a","id":1,"bv":true}]}`,
818 rhs: `{"list":[{"key":"a","id":1},{"key":"b","id":1,"bv":true},{"key":"a","id":1,"bv":true}]}`,
819 removed: _NS(),
820 modified: _NS(),
821 added: _NS(_P("list", _KBF("key", "b", "id", 1), "bv")),
822 }, {
823 lhs: `{"list":[{"key":"a","id":1},{"key":"b","id":1},{"key":"a","id":1,"bv":true}]}`,
824 rhs: `{"list":[{"key":"a","id":1},{"key":"b","id":1},{"key":"a","id":1,"bv":true}],"atomicList":["unrelated"]}`,
825 removed: _NS(),
826 modified: _NS(),
827 added: _NS(_P("atomicList")),
828 }},
829 }}
830
831 func (tt symdiffTestCase) test(t *testing.T) {
832 parser, err := typed.NewParser(tt.schema)
833 if err != nil {
834 t.Fatalf("failed to create schema: %v", err)
835 }
836 for i, quint := range tt.quints {
837 quint := quint
838 t.Run(fmt.Sprintf("%v-valid-%v", tt.name, i), func(t *testing.T) {
839 t.Parallel()
840 pt := parser.Type(tt.rootTypeName)
841
842 tvLHS, err := pt.FromYAML(quint.lhs, typed.AllowDuplicates)
843 if err != nil {
844 t.Fatalf("failed to parse lhs: %v", err)
845 }
846 tvRHS, err := pt.FromYAML(quint.rhs, typed.AllowDuplicates)
847 if err != nil {
848 t.Fatalf("failed to parse rhs: %v", err)
849 }
850 got, err := tvLHS.Compare(tvRHS)
851 if err != nil {
852 t.Fatalf("got validation errors: %v", err)
853 }
854 t.Logf("got added:\n%s\n", got.Added)
855 if !got.Added.Equals(quint.added) {
856 t.Errorf("Expected added:\n%s\n", quint.added)
857 }
858 t.Logf("got modified:\n%s", got.Modified)
859 if !got.Modified.Equals(quint.modified) {
860 t.Errorf("Expected modified:\n%s\n", quint.modified)
861 }
862 t.Logf("got removed:\n%s", got.Removed)
863 if !got.Removed.Equals(quint.removed) {
864 t.Errorf("Expected removed:\n%s\n", quint.removed)
865 }
866
867
868 gotR, err := tvRHS.Compare(tvLHS)
869 if err != nil {
870 t.Fatalf("(reverse) got validation errors: %v", err)
871 }
872 if !gotR.Modified.Equals(got.Modified) {
873 t.Errorf("reverse operation gave different modified list:\n%s", gotR.Modified)
874 }
875 if !gotR.Removed.Equals(got.Added) {
876 t.Errorf("reverse removed gave different result than added:\n%s", gotR.Removed)
877 }
878 if !gotR.Added.Equals(got.Removed) {
879 t.Errorf("reverse added gave different result than removed:\n%s", gotR.Added)
880 }
881
882 })
883 }
884 }
885
886 func TestSymdiff(t *testing.T) {
887 for _, tt := range symdiffCases {
888 tt := tt
889 t.Run(tt.name, func(t *testing.T) {
890 t.Parallel()
891 tt.test(t)
892 })
893 }
894 }
895
View as plain text