1/* 2Copyright 2019 The Kubernetes Authors. 3 Licensed under the Apache License, Version 2.0 (the "License"); 4you may not use this file except in compliance with the License. 5You may obtain a copy of the License at 6 http://www.apache.org/licenses/LICENSE-2.0 7 Unless required by applicable law or agreed to in writing, software 8distributed under the License is distributed on an "AS IS" BASIS, 9WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10See the License for the specific language governing permissions and 11limitations under the License. 12*/ 13 14package merge_test 15 16import ( 17 "testing" 18 19 "sigs.k8s.io/structured-merge-diff/v4/fieldpath" 20 . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" 21 "sigs.k8s.io/structured-merge-diff/v4/merge" 22 "sigs.k8s.io/structured-merge-diff/v4/typed" 23) 24 25// portListParser sets the default value of key "protocol" to "TCP" 26var portListParser = func() *typed.Parser { 27 parser, err := typed.NewParser(`types: 28- name: v1 29 map: 30 fields: 31 - name: containerPorts 32 type: 33 list: 34 elementType: 35 map: 36 fields: 37 - name: port 38 type: 39 scalar: numeric 40 - name: protocol 41 default: "TCP" 42 type: 43 scalar: string 44 - name: name 45 type: 46 scalar: string 47 elementRelationship: associative 48 keys: 49 - port 50 - protocol 51`) 52 if err != nil { 53 panic(err) 54 } 55 return parser 56}() 57 58func TestDefaultKeysFlat(t *testing.T) { 59 tests := map[string]TestCase{ 60 "apply_missing_defaulted_key_A": { 61 Ops: []Operation{ 62 Apply{ 63 Manager: "default", 64 APIVersion: "v1", 65 Object: ` 66 containerPorts: 67 - port: 80 68 `, 69 }, 70 }, 71 APIVersion: "v1", 72 Object: ` 73 containerPorts: 74 - port: 80 75 `, 76 Managed: fieldpath.ManagedFields{ 77 "default": fieldpath.NewVersionedSet( 78 _NS( 79 _P("containerPorts", _KBF("port", 80, "protocol", "TCP")), 80 _P("containerPorts", _KBF("port", 80, "protocol", "TCP"), "port"), 81 ), 82 "v1", 83 false, 84 ), 85 }, 86 }, 87 "apply_missing_defaulted_key_B": { 88 Ops: []Operation{ 89 Apply{ 90 Manager: "default", 91 APIVersion: "v1", 92 Object: ` 93 containerPorts: 94 - port: 80 95 - port: 80 96 protocol: UDP 97 `, 98 }, 99 }, 100 APIVersion: "v1", 101 Object: ` 102 containerPorts: 103 - port: 80 104 - port: 80 105 protocol: UDP 106 `, 107 Managed: fieldpath.ManagedFields{ 108 "default": fieldpath.NewVersionedSet( 109 _NS( 110 _P("containerPorts", _KBF("port", 80, "protocol", "TCP")), 111 _P("containerPorts", _KBF("port", 80, "protocol", "TCP"), "port"), 112 _P("containerPorts", _KBF("port", 80, "protocol", "UDP")), 113 _P("containerPorts", _KBF("port", 80, "protocol", "UDP"), "port"), 114 _P("containerPorts", _KBF("port", 80, "protocol", "UDP"), "protocol"), 115 ), 116 "v1", 117 false, 118 ), 119 }, 120 }, 121 "apply_missing_defaulted_key_with_conflict": { 122 Ops: []Operation{ 123 Apply{ 124 Manager: "apply-one", 125 APIVersion: "v1", 126 Object: ` 127 containerPorts: 128 - port: 80 129 protocol: TCP 130 name: foo 131 `, 132 }, 133 Apply{ 134 Manager: "apply-two", 135 APIVersion: "v1", 136 Object: ` 137 containerPorts: 138 - port: 80 139 name: bar 140 `, 141 Conflicts: merge.Conflicts{ 142 merge.Conflict{Manager: "apply-one", Path: _P("containerPorts", _KBF("port", 80, "protocol", "TCP"), "name")}, 143 }, 144 }, 145 }, 146 APIVersion: "v1", 147 Object: ` 148 containerPorts: 149 - port: 80 150 protocol: TCP 151 name: foo 152 `, 153 Managed: fieldpath.ManagedFields{ 154 "apply-one": fieldpath.NewVersionedSet( 155 _NS( 156 _P("containerPorts", _KBF("port", 80, "protocol", "TCP")), 157 _P("containerPorts", _KBF("port", 80, "protocol", "TCP"), "port"), 158 _P("containerPorts", _KBF("port", 80, "protocol", "TCP"), "protocol"), 159 _P("containerPorts", _KBF("port", 80, "protocol", "TCP"), "name"), 160 ), 161 "v1", 162 false, 163 ), 164 }, 165 }, 166 } 167 168 for name, test := range tests { 169 t.Run(name, func(t *testing.T) { 170 if err := test.Test(portListParser); err != nil { 171 t.Fatal(err) 172 } 173 }) 174 } 175} 176 177func TestDefaultKeysFlatErrors(t *testing.T) { 178 tests := map[string]TestCase{ 179 "apply_missing_undefaulted_defaulted_key": { 180 Ops: []Operation{ 181 Apply{ 182 Manager: "default", 183 APIVersion: "v1", 184 Object: ` 185 containerPorts: 186 - protocol: TCP 187 `, 188 }, 189 }, 190 }, 191 "apply_missing_defaulted_key_ambiguous_A": { 192 Ops: []Operation{ 193 Apply{ 194 Manager: "default", 195 APIVersion: "v1", 196 Object: ` 197 containerPorts: 198 - port: 80 199 - port: 80 200 `, 201 }, 202 }, 203 }, 204 "apply_missing_defaulted_key_ambiguous_B": { 205 Ops: []Operation{ 206 Apply{ 207 Manager: "default", 208 APIVersion: "v1", 209 Object: ` 210 containerPorts: 211 - port: 80 212 - port: 80 213 protocol: TCP 214 `, 215 }, 216 }, 217 }, 218 } 219 for name, test := range tests { 220 t.Run(name, func(t *testing.T) { 221 if test.Test(portListParser) == nil { 222 t.Fatal("Should fail") 223 } 224 }) 225 } 226} 227 228// bookParser sets the default value of key: 229// * "chapter" to 1 230// * "section" to "A" 231// * "page" to 2, 232// * "line" to 3, 233var bookParser = func() *typed.Parser { 234 parser, err := typed.NewParser(`types: 235- name: v1 236 map: 237 fields: 238 - name: book 239 type: 240 list: 241 elementType: 242 map: 243 fields: 244 - name: chapter 245 default: 1 246 type: 247 scalar: numeric 248 - name: section 249 default: "A" 250 type: 251 scalar: string 252 - name: sentences 253 type: 254 list: 255 elementType: 256 map: 257 fields: 258 - name: page 259 default: 2.0 260 type: 261 scalar: numeric 262 - name: line 263 default: 3 264 type: 265 scalar: numeric 266 - name: text 267 type: 268 scalar: string 269 elementRelationship: associative 270 keys: 271 - page 272 - line 273 elementRelationship: associative 274 keys: 275 - chapter 276 - section 277`) 278 if err != nil { 279 panic(err) 280 } 281 return parser 282}() 283 284func TestDefaultKeysNested(t *testing.T) { 285 tests := map[string]TestCase{ 286 "apply_missing_every_key_nested": { 287 Ops: []Operation{ 288 Apply{ 289 Manager: "default", 290 APIVersion: "v1", 291 Object: ` 292 book: 293 - sentences: 294 - text: blah 295 `, 296 }, 297 }, 298 APIVersion: "v1", 299 Object: ` 300 book: 301 - sentences: 302 - text: blah 303 `, 304 Managed: fieldpath.ManagedFields{ 305 "default": fieldpath.NewVersionedSet( 306 _NS( 307 _P( 308 "book", _KBF("chapter", 1, "section", "A"), 309 ), 310 _P( 311 "book", _KBF("chapter", 1, "section", "A"), 312 "sentences", _KBF("page", 2, "line", 3), 313 ), 314 _P( 315 "book", _KBF("chapter", 1, "section", "A"), 316 "sentences", _KBF("page", 2, "line", 3), 317 "text", 318 ), 319 ), 320 "v1", 321 false, 322 ), 323 }, 324 }, 325 "apply_integer_key_with_float_default": { 326 Ops: []Operation{ 327 Apply{ 328 Manager: "default", 329 APIVersion: "v1", 330 Object: ` 331 book: 332 - sentences: 333 - text: blah 334 `, 335 }, 336 Apply{ 337 Manager: "default", 338 APIVersion: "v1", 339 Object: ` 340 book: 341 - sentences: 342 - text: blah 343 page: 2 344 `, 345 }, 346 }, 347 APIVersion: "v1", 348 Object: ` 349 book: 350 - sentences: 351 - text: blah 352 page: 2 353 `, 354 Managed: fieldpath.ManagedFields{ 355 "default": fieldpath.NewVersionedSet( 356 _NS( 357 _P( 358 "book", _KBF("chapter", 1, "section", "A"), 359 ), 360 _P( 361 "book", _KBF("chapter", 1, "section", "A"), 362 "sentences", _KBF("page", 2, "line", 3), 363 ), 364 _P( 365 "book", _KBF("chapter", 1, "section", "A"), 366 "sentences", _KBF("page", 2, "line", 3), 367 "text", 368 ), 369 _P( 370 "book", _KBF("chapter", 1, "section", "A"), 371 "sentences", _KBF("page", 2, "line", 3), 372 "page", 373 ), 374 ), 375 "v1", 376 false, 377 ), 378 }, 379 }, 380 } 381 382 for name, test := range tests { 383 t.Run(name, func(t *testing.T) { 384 if err := test.Test(bookParser); err != nil { 385 t.Fatal(err) 386 } 387 }) 388 } 389} 390