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 type mergeTestCase struct {
28 name string
29 rootTypeName string
30 schema typed.YAMLObject
31 triplets []mergeTriplet
32 }
33
34 type mergeTriplet struct {
35 lhs typed.YAMLObject
36 rhs typed.YAMLObject
37 out typed.YAMLObject
38 }
39
40 var mergeCases = []mergeTestCase{{
41 name: "simple pair",
42 rootTypeName: "stringPair",
43 schema: `types:
44 - name: stringPair
45 map:
46 fields:
47 - name: key
48 type:
49 scalar: string
50 - name: value
51 type:
52 namedType: __untyped_atomic_
53 - name: __untyped_atomic_
54 scalar: untyped
55 list:
56 elementType:
57 namedType: __untyped_atomic_
58 elementRelationship: atomic
59 map:
60 elementType:
61 namedType: __untyped_atomic_
62 elementRelationship: atomic
63 `,
64 triplets: []mergeTriplet{{
65 `{"key":"foo","value":{}}`,
66 `{"key":"foo","value":1}`,
67 `{"key":"foo","value":1}`,
68 }, {
69 `{"key":"foo","value":{}}`,
70 `{"key":"foo","value":1}`,
71 `{"key":"foo","value":1}`,
72 }, {
73 `{"key":"foo","value":1}`,
74 `{"key":"foo","value":{}}`,
75 `{"key":"foo","value":{}}`,
76 }, {
77 `{"key":"foo","value":null}`,
78 `{"key":"foo","value":{}}`,
79 `{"key":"foo","value":{}}`,
80 }, {
81 `{"key":"foo"}`,
82 `{"value":true}`,
83 `{"key":"foo","value":true}`,
84 }},
85 }, {
86 name: "null/empty map",
87 rootTypeName: "nestedMap",
88 schema: `types:
89 - name: nestedMap
90 map:
91 fields:
92 - name: inner
93 type:
94 map:
95 elementType:
96 namedType: __untyped_atomic_
97 - name: __untyped_atomic_
98 scalar: untyped
99 list:
100 elementType:
101 namedType: __untyped_atomic_
102 elementRelationship: atomic
103 map:
104 elementType:
105 namedType: __untyped_atomic_
106 elementRelationship: atomic
107 `,
108 triplets: []mergeTriplet{{
109 `{}`,
110 `{"inner":{}}`,
111 `{"inner":{}}`,
112 }, {
113 `{}`,
114 `{"inner":null}`,
115 `{"inner":null}`,
116 }, {
117 `{"inner":null}`,
118 `{"inner":{}}`,
119 `{"inner":{}}`,
120 }, {
121 `{"inner":{}}`,
122 `{"inner":null}`,
123 `{"inner":null}`,
124 }, {
125 `{"inner":{}}`,
126 `{"inner":{}}`,
127 `{"inner":{}}`,
128 }},
129 }, {
130 name: "null/empty struct",
131 rootTypeName: "nestedStruct",
132 schema: `types:
133 - name: nestedStruct
134 map:
135 fields:
136 - name: inner
137 type:
138 map:
139 fields:
140 - name: value
141 type:
142 namedType: __untyped_atomic_
143 - name: __untyped_atomic_
144 scalar: untyped
145 list:
146 elementType:
147 namedType: __untyped_atomic_
148 elementRelationship: atomic
149 map:
150 elementType:
151 namedType: __untyped_atomic_
152 elementRelationship: atomic
153 `,
154 triplets: []mergeTriplet{{
155 `{}`,
156 `{"inner":{}}`,
157 `{"inner":{}}`,
158 }, {
159 `{}`,
160 `{"inner":null}`,
161 `{"inner":null}`,
162 }, {
163 `{"inner":null}`,
164 `{"inner":{}}`,
165 `{"inner":{}}`,
166 }, {
167 `{"inner":{}}`,
168 `{"inner":null}`,
169 `{"inner":null}`,
170 }, {
171 `{"inner":{}}`,
172 `{"inner":{}}`,
173 `{"inner":{}}`,
174 }},
175 }, {
176 name: "null/empty list",
177 rootTypeName: "nestedList",
178 schema: `types:
179 - name: nestedList
180 map:
181 fields:
182 - name: inner
183 type:
184 list:
185 elementType:
186 namedType: __untyped_atomic_
187 elementRelationship: atomic
188 - name: __untyped_atomic_
189 scalar: untyped
190 list:
191 elementType:
192 namedType: __untyped_atomic_
193 elementRelationship: atomic
194 map:
195 elementType:
196 namedType: __untyped_atomic_
197 elementRelationship: atomic
198 `,
199 triplets: []mergeTriplet{{
200 `{}`,
201 `{"inner":[]}`,
202 `{"inner":[]}`,
203 }, {
204 `{}`,
205 `{"inner":null}`,
206 `{"inner":null}`,
207 }, {
208 `{"inner":null}`,
209 `{"inner":[]}`,
210 `{"inner":[]}`,
211 }, {
212 `{"inner":[]}`,
213 `{"inner":null}`,
214 `{"inner":null}`,
215 }, {
216 `{"inner":[]}`,
217 `{"inner":[]}`,
218 `{"inner":[]}`,
219 }},
220 }, {
221 name: "struct grab bag",
222 rootTypeName: "myStruct",
223 schema: `types:
224 - name: myStruct
225 map:
226 fields:
227 - name: numeric
228 type:
229 scalar: numeric
230 - name: string
231 type:
232 scalar: string
233 - name: bool
234 type:
235 scalar: boolean
236 - name: setStr
237 type:
238 list:
239 elementType:
240 scalar: string
241 elementRelationship: associative
242 - name: setBool
243 type:
244 list:
245 elementType:
246 scalar: boolean
247 elementRelationship: associative
248 - name: setNumeric
249 type:
250 list:
251 elementType:
252 scalar: numeric
253 elementRelationship: associative
254 `,
255 triplets: []mergeTriplet{{
256 `{"numeric":1}`,
257 `{"numeric":3.14159}`,
258 `{"numeric":3.14159}`,
259 }, {
260 `{"numeric":3.14159}`,
261 `{"numeric":1}`,
262 `{"numeric":1}`,
263 }, {
264 `{"string":"aoeu"}`,
265 `{"bool":true}`,
266 `{"string":"aoeu","bool":true}`,
267 }, {
268 `{"setStr":["a","b","c"]}`,
269 `{"setStr":["a","b"]}`,
270 `{"setStr":["a","b","c"]}`,
271 }, {
272 `{"setStr":["a","b"]}`,
273 `{"setStr":["a","b","c"]}`,
274 `{"setStr":["a","b","c"]}`,
275 }, {
276 `{"setStr":["a","b","c"]}`,
277 `{"setStr":[]}`,
278 `{"setStr":["a","b","c"]}`,
279 }, {
280 `{"setStr":[]}`,
281 `{"setStr":["a","b","c"]}`,
282 `{"setStr":["a","b","c"]}`,
283 }, {
284 `{"setStr":["a","b"]}`,
285 `{"setStr":["b","a"]}`,
286 `{"setStr":["b","a"]}`,
287 }, {
288 `{"setStr":["a","b","c"]}`,
289 `{"setStr":["d","e","f"]}`,
290 `{"setStr":["a","b","c","d","e","f"]}`,
291 }, {
292 `{"setStr":["a","b","c"]}`,
293 `{"setStr":["c","d","e","f"]}`,
294 `{"setStr":["a","b","c","d","e","f"]}`,
295 }, {
296 `{"setStr":["a","b","c","g","f"]}`,
297 `{"setStr":["c","d","e","f"]}`,
298 `{"setStr":["a","b","c","g","d","e","f"]}`,
299 }, {
300 `{"setStr":["a","b","c"]}`,
301 `{"setStr":["d","e","f","x","y","z"]}`,
302 `{"setStr":["a","b","c","d","e","f","x","y","z"]}`,
303 }, {
304 `{"setStr":["c","d","e","f"]}`,
305 `{"setStr":["a","c","e"]}`,
306 `{"setStr":["a","c","d","e","f"]}`,
307 }, {
308 `{"setStr":["a","b","c","x","y","z"]}`,
309 `{"setStr":["d","e","f"]}`,
310 `{"setStr":["a","b","c","x","y","z","d","e","f"]}`,
311 }, {
312 `{"setStr":["a","b","c","x","y","z"]}`,
313 `{"setStr":["d","e","f","x","y","z"]}`,
314 `{"setStr":["a","b","c","d","e","f","x","y","z"]}`,
315 }, {
316 `{"setStr":["c","a","g","f"]}`,
317 `{"setStr":["c","f","a","g"]}`,
318 `{"setStr":["c","f","a","g"]}`,
319 }, {
320 `{"setStr":["a","b","c","d"]}`,
321 `{"setStr":["d","e","f","a"]}`,
322 `{"setStr":["b","c","d","e","f","a"]}`,
323 }, {
324 `{"setStr":["c","d","e","f","g","h","i","j"]}`,
325 `{"setStr":["2","h","3","e","4","k","l"]}`,
326 `{"setStr":["c","d","f","g","2","h","i","j","3","e","4","k","l"]}`,
327 }, {
328 `{"setStr":["a","b","c","d","e","f","g","h","i","j"]}`,
329 `{"setStr":["1","b","2","h","3","e","4","k","l"]}`,
330 `{"setStr":["a","1","b","c","d","f","g","2","h","i","j","3","e","4","k","l"]}`,
331 }, {
332 `{"setStr":["a","b","b"]}`,
333 `{"setStr":["c"]}`,
334 `{"setStr":["a","b","b","c"]}`,
335 }, {
336 `{"setStr":["a","b","b"]}`,
337 `{"setStr":["b"]}`,
338 `{"setStr":["a","b"]}`,
339 }, {
340 `{"setStr":["a","b","b"]}`,
341 `{"setStr":["a"]}`,
342 `{"setStr":["a","b","b"]}`,
343 }, {
344 `{"setStr":["a","b","c","d","e","c"]}`,
345 `{"setStr":["1","b","2","e","d"]}`,
346 `{"setStr":["a","1","b","c","2","e","c","d"]}`,
347 }, {
348 `{"setStr":["a","b","c","d","e","c"]}`,
349 `{"setStr":["1","b","2","c","e","d"]}`,
350 `{"setStr":["a","1","b","2","c","e","d"]}`,
351 }, {
352 `{"setStr":["a","a","b","b"]}`,
353 `{"setStr":["b","c","d"]}`,
354 `{"setStr":["a","a","b","c","d"]}`,
355 }, {
356 `{"setStr":["a","a","b","b"]}`,
357 `{"setStr":[]}`,
358 `{"setStr":["a","a","b","b"]}`,
359 }, {
360 `{"setBool":[true]}`,
361 `{"setBool":[false]}`,
362 `{"setBool":[true, false]}`,
363 }, {
364 `{"setNumeric":[1,2,3.14159]}`,
365 `{"setNumeric":[1,2,3]}`,
366 `{"setNumeric":[1,2,3.14159,3]}`,
367 }, {
368 `{"setStr":["c","a","g","f","c","a"]}`,
369 `{"setStr":["c","f","a","g"]}`,
370 `{"setStr":["c","f","a","g"]}`,
371 }, {
372 `{"setNumeric":[1,2,3.14159,1,2]}`,
373 `{"setNumeric":[1,2,3]}`,
374 `{"setNumeric":[1,2,3.14159,3]}`,
375 }, {
376 `{"setBool":[true,false,true]}`,
377 `{"setBool":[false]}`,
378 `{"setBool":[true,false,true]}`,
379 }, {
380 `{"setBool":[true,false,true]}`,
381 `{"setBool":[true]}`,
382 `{"setBool":[true, false]}`,
383 },
384 },
385 }, {
386 name: "associative list",
387 rootTypeName: "myRoot",
388 schema: `types:
389 - name: myRoot
390 map:
391 fields:
392 - name: list
393 type:
394 namedType: myList
395 - name: atomicList
396 type:
397 namedType: mySequence
398 - name: myList
399 list:
400 elementType:
401 namedType: myElement
402 elementRelationship: associative
403 keys:
404 - key
405 - id
406 - name: mySequence
407 list:
408 elementType:
409 scalar: string
410 elementRelationship: atomic
411 - name: myElement
412 map:
413 fields:
414 - name: key
415 type:
416 scalar: string
417 - name: id
418 type:
419 scalar: numeric
420 - name: value
421 type:
422 namedType: myValue
423 - name: bv
424 type:
425 scalar: boolean
426 - name: nv
427 type:
428 scalar: numeric
429 - name: myValue
430 map:
431 elementType:
432 scalar: string
433 `,
434 triplets: []mergeTriplet{{
435 `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
436 `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
437 `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
438 }, {
439 `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
440 `{"list":[{"key":"a","id":2,"value":{"a":"a"}}]}`,
441 `{"list":[{"key":"a","id":1,"value":{"a":"a"}},{"key":"a","id":2,"value":{"a":"a"}}]}`,
442 }, {
443 `{"list":[{"key":"a","id":1},{"key":"b","id":1}]}`,
444 `{"list":[{"key":"a","id":1},{"key":"a","id":2}]}`,
445 `{"list":[{"key":"a","id":1},{"key":"b","id":1},{"key":"a","id":2}]}`,
446 }, {
447 `{"list":[{"key":"b","id":2}]}`,
448 `{"list":[{"key":"a","id":1},{"key":"b","id":2},{"key":"c","id":3}]}`,
449 `{"list":[{"key":"a","id":1},{"key":"b","id":2},{"key":"c","id":3}]}`,
450 }, {
451 `{"list":[{"key":"a","id":1},{"key":"b","id":2},{"key":"c","id":3}]}`,
452 `{"list":[{"key":"c","id":3},{"key":"b","id":2}]}`,
453 `{"list":[{"key":"a","id":1},{"key":"c","id":3},{"key":"b","id":2}]}`,
454 }, {
455 `{"list":[{"key":"a","id":1},{"key":"b","id":2},{"key":"c","id":3}]}`,
456 `{"list":[{"key":"c","id":3},{"key":"a","id":1}]}`,
457 `{"list":[{"key":"b","id":2},{"key":"c","id":3},{"key":"a","id":1}]}`,
458 }, {
459 `{"atomicList":["a","a","a"]}`,
460 `{"atomicList":null}`,
461 `{"atomicList":null}`,
462 }, {
463 `{"atomicList":["a","b","c"]}`,
464 `{"atomicList":[]}`,
465 `{"atomicList":[]}`,
466 }, {
467 `{"atomicList":["a","a","a"]}`,
468 `{"atomicList":["a","a"]}`,
469 `{"atomicList":["a","a"]}`,
470 }, {
471 `{"list":[{"key":"a","id":1,"bv":true},{"key":"b","id":2},{"key":"a","id":1,"bv":false,"nv":2}]}`,
472 `{"list":[{"key":"a","id":1,"nv":3},{"key":"c","id":3},{"key":"b","id":2}]}`,
473 `{"list":[{"key":"a","id":1,"nv":3},{"key":"c","id":3},{"key":"b","id":2}]}`,
474 }, {
475 `{"list":[{"key":"a","id":1,"nv":1},{"key":"a","id":1,"nv":2}]}`,
476 `{"list":[]}`,
477 `{"list":[{"key":"a","id":1,"nv":1},{"key":"a","id":1,"nv":2}]}`,
478 }, {
479 `{"list":[{"key":"a","id":1,"nv":1},{"key":"a","id":1,"nv":2}]}`,
480 `{}`,
481 `{"list":[{"key":"a","id":1,"nv":1},{"key":"a","id":1,"nv":2}]}`,
482 }},
483 }}
484
485 func (tt mergeTestCase) test(t *testing.T) {
486 parser, err := typed.NewParser(tt.schema)
487 if err != nil {
488 t.Fatalf("failed to create schema: %v", err)
489 }
490
491 for i, triplet := range tt.triplets {
492 triplet := triplet
493 t.Run(fmt.Sprintf("%v-valid-%v", tt.name, i), func(t *testing.T) {
494 t.Parallel()
495 pt := parser.Type(tt.rootTypeName)
496
497 lhs, err := pt.FromYAML(triplet.lhs, typed.AllowDuplicates)
498 if err != nil {
499 t.Fatalf("unable to parser/validate lhs yaml: %v\n%v", err, triplet.lhs)
500 }
501 rhs, err := pt.FromYAML(triplet.rhs)
502 if err != nil {
503 t.Fatalf("unable to parser/validate rhs yaml: %v\n%v", err, triplet.rhs)
504 }
505 out, err := pt.FromYAML(triplet.out, typed.AllowDuplicates)
506 if err != nil {
507 t.Fatalf("unable to parser/validate out yaml: %v\n%v", err, triplet.out)
508 }
509
510 got, err := lhs.Merge(rhs)
511 if err != nil {
512 t.Errorf("got validation errors: %v", err)
513 } else {
514 if !value.Equals(got.AsValue(), out.AsValue()) {
515 t.Errorf("Expected\n%v\nbut got\n%v\n",
516 value.ToString(out.AsValue()), value.ToString(got.AsValue()),
517 )
518 }
519 }
520 })
521 }
522 }
523
524 func TestMerge(t *testing.T) {
525 for _, tt := range mergeCases {
526 tt := tt
527 t.Run(tt.name, func(t *testing.T) {
528 t.Parallel()
529 tt.test(t)
530 })
531 }
532 }
533
View as plain text