github.com/avenga/couper@v1.12.2/eval/lib/merge_test.go (about) 1 package lib_test 2 3 import ( 4 "testing" 5 6 "github.com/zclconf/go-cty/cty" 7 8 "github.com/avenga/couper/config/configload" 9 "github.com/avenga/couper/config/request" 10 "github.com/avenga/couper/eval" 11 "github.com/avenga/couper/internal/test" 12 ) 13 14 func TestMerge(t *testing.T) { 15 helper := test.New(t) 16 17 cf, err := configload.LoadBytes([]byte(`server "test" {}`), "couper.hcl") 18 helper.Must(err) 19 20 hclContext := cf.Context.Value(request.ContextType).(*eval.Context).HCLContext() 21 mergeFn := hclContext.Functions["merge"] 22 23 tests := []struct { 24 name string 25 args []cty.Value 26 want cty.Value 27 }{ 28 { 29 /* 30 merge( 31 {"k1": 1}, 32 {"k2": {"k2.2": 2}}, 33 {"k3": 3}, 34 null, 35 {"k2": {"k4.2": 4}}, 36 {"k3": 5}, 37 {"k6": [6]}, 38 null, 39 {"k6": ["7", true]}, 40 {"k6": [null]}, 41 {"k6": [8]}, 42 {"k9": [9]}, 43 {"k9": 10} 44 ) 45 */ 46 "merge objects ignoring null arguments", 47 []cty.Value{ 48 cty.ObjectVal(map[string]cty.Value{ 49 "k1": cty.NumberIntVal(1), 50 }), 51 cty.ObjectVal(map[string]cty.Value{ 52 "k2": cty.ObjectVal(map[string]cty.Value{ 53 "k2.2": cty.NumberIntVal(2), 54 }), 55 }), 56 cty.ObjectVal(map[string]cty.Value{ 57 "k3": cty.NumberIntVal(3), 58 }), 59 cty.NullVal(cty.Bool), 60 cty.ObjectVal(map[string]cty.Value{ 61 "k2": cty.MapVal(map[string]cty.Value{ // ähnlicher Typ -> mergen 62 "k4.2": cty.NumberIntVal(4), 63 }), 64 }), 65 cty.ObjectVal(map[string]cty.Value{ 66 "k3": cty.NumberIntVal(5), // gleicher Typ, primitive -> überschreiben 67 }), 68 cty.ObjectVal(map[string]cty.Value{ 69 "k6": cty.TupleVal([]cty.Value{ 70 cty.NumberIntVal(6), 71 }), 72 }), 73 cty.NullVal(cty.Bool), 74 cty.ObjectVal(map[string]cty.Value{ 75 "k6": cty.TupleVal([]cty.Value{ // gleicher Typ -> mergen 76 cty.StringVal("7"), 77 cty.BoolVal(true), 78 }), 79 }), 80 cty.ObjectVal(map[string]cty.Value{ 81 "k6": cty.ListVal([]cty.Value{ // ähnlicher Typ -> mergen 82 cty.NullVal(cty.Bool), 83 }), 84 }), 85 cty.ObjectVal(map[string]cty.Value{ 86 "k6": cty.ListVal([]cty.Value{ // ähnlicher Typ -> mergen 87 cty.NumberIntVal(8), 88 }), 89 }), 90 cty.ObjectVal(map[string]cty.Value{ 91 "k9": cty.ListVal([]cty.Value{ 92 cty.NumberIntVal(9), 93 }), 94 }), 95 cty.ObjectVal(map[string]cty.Value{ 96 "k9": cty.NumberIntVal(10), // unterschiedlicher Typ -> überschreiben 97 }), 98 }, 99 /* 100 { 101 "k1": 1, 102 "k2": {"k2.2": 2, "k4.2": 4}, 103 "k3": 5, 104 "k6": [6, "7", true, null, 8], 105 "k9": 10 106 } 107 */ 108 cty.ObjectVal(map[string]cty.Value{ 109 "k1": cty.NumberIntVal(1), 110 "k2": cty.ObjectVal(map[string]cty.Value{ 111 "k2.2": cty.NumberIntVal(2), 112 "k4.2": cty.NumberIntVal(4), 113 }), 114 "k3": cty.NumberIntVal(5), 115 "k6": cty.TupleVal([]cty.Value{ 116 cty.NumberIntVal(6), 117 cty.StringVal("7"), 118 cty.BoolVal(true), 119 cty.NullVal(cty.Bool), 120 cty.NumberIntVal(8), 121 }), 122 "k9": cty.NumberIntVal(10), 123 }), 124 }, 125 { 126 /* 127 merge( 128 {"k1": 1} 129 ) 130 */ 131 "merge with only one object", 132 []cty.Value{ 133 cty.ObjectVal(map[string]cty.Value{ 134 "k1": cty.NumberIntVal(1), 135 }), 136 }, 137 /* 138 { 139 "k1": 1 140 } 141 */ 142 cty.ObjectVal(map[string]cty.Value{ 143 "k1": cty.NumberIntVal(1), 144 }), 145 }, 146 { 147 /* 148 merge( 149 {"k2": {"k2.2": 2}}, 150 {"k0": null}, 151 {"k2": {"k4.2": 4}}, 152 {"k2": {"k4.0": null}} 153 ) 154 */ 155 "merge objects with null", 156 []cty.Value{ 157 cty.ObjectVal(map[string]cty.Value{ 158 "k2": cty.ObjectVal(map[string]cty.Value{ 159 "k2.2": cty.NumberIntVal(2), 160 }), 161 }), 162 cty.ObjectVal(map[string]cty.Value{ 163 "k0": cty.NullVal(cty.Bool), 164 }), 165 cty.ObjectVal(map[string]cty.Value{ 166 "k2": cty.MapVal(map[string]cty.Value{ 167 "k4.2": cty.NumberIntVal(4), 168 }), 169 }), 170 cty.ObjectVal(map[string]cty.Value{ 171 "k2": cty.MapVal(map[string]cty.Value{ 172 "k4.0": cty.NullVal(cty.Bool), 173 }), 174 }), 175 }, 176 /* 177 { 178 "k0": null, 179 "k2": {"k4.2": 4} 180 } 181 */ 182 cty.ObjectVal(map[string]cty.Value{ 183 "k0": cty.NullVal(cty.Bool), 184 "k2": cty.ObjectVal(map[string]cty.Value{ 185 "k2.2": cty.NumberIntVal(2), 186 "k4.2": cty.NumberIntVal(4), 187 "k4.0": cty.NullVal(cty.Bool), 188 }), 189 }), 190 }, 191 { 192 /* 193 merge( 194 [1], 195 null, 196 ["2"], 197 [true], 198 null, 199 [{"k": 4}], 200 [[5]] 201 ) 202 */ 203 "merge tuples ignoring null arguments", 204 []cty.Value{ 205 cty.TupleVal([]cty.Value{ 206 cty.NumberIntVal(1), 207 }), 208 cty.NullVal(cty.Bool), 209 cty.TupleVal([]cty.Value{ 210 cty.StringVal("2"), 211 }), 212 cty.ListVal([]cty.Value{ 213 cty.BoolVal(true), 214 }), 215 cty.NullVal(cty.Bool), 216 cty.TupleVal([]cty.Value{ 217 cty.ObjectVal(map[string]cty.Value{ 218 "k": cty.NumberIntVal(4), 219 }), 220 }), 221 cty.TupleVal([]cty.Value{ 222 cty.TupleVal([]cty.Value{ 223 cty.NumberIntVal(5), 224 }), 225 }), 226 }, 227 /* 228 [ 229 1, 230 "2", 231 true, 232 {"k": 4}, 233 [5] 234 ] 235 */ 236 cty.TupleVal([]cty.Value{ 237 cty.NumberIntVal(1), 238 cty.StringVal("2"), 239 cty.BoolVal(true), 240 cty.ObjectVal(map[string]cty.Value{ 241 "k": cty.NumberIntVal(4), 242 }), 243 cty.TupleVal([]cty.Value{ 244 cty.NumberIntVal(5), 245 }), 246 }), 247 }, 248 { 249 /* 250 merge( 251 [1] 252 ) 253 */ 254 "merge with only one tuple", 255 []cty.Value{ 256 cty.TupleVal([]cty.Value{ 257 cty.NumberIntVal(1), 258 }), 259 }, 260 /* 261 [ 262 1 263 ] 264 */ 265 cty.TupleVal([]cty.Value{ 266 cty.NumberIntVal(1), 267 }), 268 }, 269 { 270 /* 271 merge() 272 */ 273 "merge without parameters", 274 []cty.Value{}, 275 /* null */ 276 cty.NullVal(cty.Bool), 277 }, 278 } 279 280 for _, tt := range tests { 281 t.Run(tt.name, func(subT *testing.T) { 282 h := test.New(subT) 283 284 mergedV, merr := mergeFn.Call(tt.args) 285 h.Must(merr) 286 if !mergedV.RawEquals(tt.want) { 287 subT.Errorf("Wrong return value:\nwant:\t%#v\ngot:\t%#v\n", tt.want, mergedV) 288 } 289 }) 290 } 291 } 292 293 func TestMergeErrors(t *testing.T) { 294 helper := test.New(t) 295 296 cf, err := configload.LoadBytes([]byte(`server "test" {}`), "couper.hcl") 297 helper.Must(err) 298 299 tests := []struct { 300 name string 301 args []cty.Value 302 wantErr string 303 }{ 304 { 305 /* 306 merge( 307 {"k1": 1}, 308 true, 309 {"k3": 3}, 310 ) 311 */ 312 "mix objects with bool", 313 []cty.Value{ 314 cty.ObjectVal(map[string]cty.Value{ 315 "k1": cty.NumberIntVal(1), 316 }), 317 cty.BoolVal(true), 318 cty.ObjectVal(map[string]cty.Value{ 319 "k3": cty.NumberIntVal(3), 320 }), 321 }, 322 "cannot merge primitive value", 323 }, 324 { 325 /* 326 merge( 327 {"k1": 1}, 328 2, 329 {"k3": 3}, 330 ) 331 */ 332 "mix objects with integer", 333 []cty.Value{ 334 cty.ObjectVal(map[string]cty.Value{ 335 "k1": cty.NumberIntVal(1), 336 }), 337 cty.NumberIntVal(2), 338 cty.ObjectVal(map[string]cty.Value{ 339 "k3": cty.NumberIntVal(3), 340 }), 341 }, 342 "cannot merge primitive value", 343 }, 344 { 345 /* 346 merge( 347 {"k1": 1}, 348 "2", 349 {"k3": 3}, 350 ) 351 */ 352 "mix objects with string", 353 []cty.Value{ 354 cty.ObjectVal(map[string]cty.Value{ 355 "k1": cty.NumberIntVal(1), 356 }), 357 cty.StringVal("2"), 358 cty.ObjectVal(map[string]cty.Value{ 359 "k3": cty.NumberIntVal(3), 360 }), 361 }, 362 "cannot merge primitive value", 363 }, 364 { 365 /* 366 merge( 367 ["1"], 368 true, 369 ["3"], 370 ) 371 */ 372 "mix tuples with bool", 373 []cty.Value{ 374 cty.TupleVal([]cty.Value{ 375 cty.StringVal("1"), 376 }), 377 cty.BoolVal(true), 378 cty.TupleVal([]cty.Value{ 379 cty.StringVal("3"), 380 }), 381 }, 382 "cannot merge primitive value", 383 }, 384 { 385 /* 386 merge( 387 ["1"], 388 2, 389 ["3"], 390 ) 391 */ 392 "mix tuples with integer", 393 []cty.Value{ 394 cty.TupleVal([]cty.Value{ 395 cty.StringVal("1"), 396 }), 397 cty.NumberIntVal(2), 398 cty.TupleVal([]cty.Value{ 399 cty.StringVal("3"), 400 }), 401 }, 402 "cannot merge primitive value", 403 }, 404 { 405 /* 406 merge( 407 ["1"], 408 "2", 409 ["3"], 410 ) 411 */ 412 "mix tuples with string", 413 []cty.Value{ 414 cty.TupleVal([]cty.Value{ 415 cty.StringVal("1"), 416 }), 417 cty.StringVal("2"), 418 cty.TupleVal([]cty.Value{ 419 cty.StringVal("3"), 420 }), 421 }, 422 "cannot merge primitive value", 423 }, 424 { 425 /* 426 merge( 427 {"k1": 1}, 428 ["2"], 429 {"k3": 3}, 430 ) 431 */ 432 "mix objects with tuple", 433 []cty.Value{ 434 cty.ObjectVal(map[string]cty.Value{ 435 "k1": cty.NumberIntVal(1), 436 }), 437 cty.TupleVal([]cty.Value{ 438 cty.StringVal("2"), 439 }), 440 cty.ObjectVal(map[string]cty.Value{ 441 "k3": cty.NumberIntVal(3), 442 }), 443 }, 444 "type mismatch", 445 }, 446 { 447 /* 448 merge( 449 ["1"], 450 {"k2": 2}, 451 ["3"], 452 ) 453 */ 454 "mix tuples with object", 455 []cty.Value{ 456 cty.TupleVal([]cty.Value{ 457 cty.StringVal("1"), 458 }), 459 cty.ObjectVal(map[string]cty.Value{ 460 "k2": cty.NumberIntVal(2), 461 }), 462 cty.TupleVal([]cty.Value{ 463 cty.StringVal("3"), 464 }), 465 }, 466 "type mismatch", 467 }, 468 } 469 470 hclContext := cf.Context.Value(request.ContextType).(*eval.Context).HCLContext() 471 472 for _, tt := range tests { 473 t.Run(tt.name, func(subT *testing.T) { 474 _, err := hclContext.Functions["merge"].Call(tt.args) 475 if err == nil { 476 subT.Error("Error expected") 477 } 478 if err != nil && err.Error() != tt.wantErr { 479 subT.Errorf("Wrong error message; expected %#v, got: %#v", tt.wantErr, err.Error()) 480 } 481 }) 482 } 483 }