github.com/Jeffail/benthos/v3@v3.65.0/lib/processor/json_test.go (about) 1 package processor 2 3 import ( 4 "testing" 5 6 "github.com/Jeffail/benthos/v3/lib/log" 7 "github.com/Jeffail/benthos/v3/lib/message" 8 "github.com/Jeffail/benthos/v3/lib/metrics" 9 "github.com/Jeffail/benthos/v3/lib/util/config" 10 yaml "gopkg.in/yaml.v3" 11 ) 12 13 func TestJSONValidation(t *testing.T) { 14 conf := NewConfig() 15 conf.JSON.Operator = "dfjjkdsgjkdfhgjfh" 16 conf.JSON.Parts = []int{0} 17 conf.JSON.Path = "foo.bar" 18 conf.JSON.Value = []byte(`this isnt valid json`) 19 20 testLog := log.Noop() 21 22 if _, err := NewJSON(conf, nil, testLog, metrics.Noop()); err == nil { 23 t.Error("Expected error from bad operator") 24 } 25 26 conf = NewConfig() 27 conf.JSON.Operator = "move" 28 conf.JSON.Parts = []int{0} 29 conf.JSON.Path = "foo.bar" 30 conf.JSON.Value = []byte(`#%#@$his isnt valid json`) 31 32 if _, err := NewJSON(conf, nil, testLog, metrics.Noop()); err == nil { 33 t.Error("Expected error from bad value") 34 } 35 36 conf = NewConfig() 37 conf.JSON.Operator = "move" 38 conf.JSON.Parts = []int{0} 39 conf.JSON.Path = "" 40 conf.JSON.Value = []byte(`""`) 41 42 if _, err := NewJSON(conf, nil, testLog, metrics.Noop()); err == nil { 43 t.Error("Expected error from empty move paths") 44 } 45 46 conf = NewConfig() 47 conf.JSON.Operator = "copy" 48 conf.JSON.Parts = []int{0} 49 conf.JSON.Path = "" 50 conf.JSON.Value = []byte(`"foo.bar"`) 51 52 if _, err := NewJSON(conf, nil, testLog, metrics.Noop()); err == nil { 53 t.Error("Expected error from empty copy path") 54 } 55 56 conf = NewConfig() 57 conf.JSON.Operator = "copy" 58 conf.JSON.Parts = []int{0} 59 conf.JSON.Path = "foo.bar" 60 conf.JSON.Value = []byte(`""`) 61 62 if _, err := NewJSON(conf, nil, testLog, metrics.Noop()); err == nil { 63 t.Error("Expected error from empty copy destination") 64 } 65 66 conf = NewConfig() 67 conf.JSON.Operator = "set" 68 conf.JSON.Parts = []int{0} 69 conf.JSON.Path = "foo.bar" 70 conf.JSON.Value = []byte(`this isnt valid json`) 71 72 jSet, err := NewJSON(conf, nil, testLog, metrics.Noop()) 73 if err != nil { 74 t.Fatal(err) 75 } 76 77 msgIn := message.New([][]byte{[]byte("this is bad json")}) 78 msgs, res := jSet.ProcessMessage(msgIn) 79 if len(msgs) != 1 { 80 t.Fatal("No passthrough for bad input data") 81 } 82 if res != nil { 83 t.Fatal("Non-nil result") 84 } 85 if exp, act := "this is bad json", string(message.GetAllBytes(msgs[0])[0]); exp != act { 86 t.Errorf("Wrong output from bad json: %v != %v", act, exp) 87 } 88 89 conf.JSON.Parts = []int{5} 90 91 jSet, err = NewJSON(conf, nil, testLog, metrics.Noop()) 92 if err != nil { 93 t.Fatal(err) 94 } 95 96 msgIn = message.New([][]byte{[]byte("{}")}) 97 msgs, res = jSet.ProcessMessage(msgIn) 98 if len(msgs) != 1 { 99 t.Fatal("No passthrough for bad index") 100 } 101 if res != nil { 102 t.Fatal("Non-nil result") 103 } 104 if exp, act := "{}", string(message.GetAllBytes(msgs[0])[0]); exp != act { 105 t.Errorf("Wrong output from bad index: %v != %v", act, exp) 106 } 107 } 108 109 func TestJSONPartBounds(t *testing.T) { 110 tLog := log.Noop() 111 tStats := metrics.Noop() 112 113 conf := NewConfig() 114 conf.JSON.Operator = "set" 115 conf.JSON.Path = "foo.bar" 116 conf.JSON.Value = []byte(`{"baz":1}`) 117 118 exp := `{"foo":{"bar":{"baz":1}}}` 119 120 tests := map[int]int{ 121 -3: 0, 122 -2: 1, 123 -1: 2, 124 0: 0, 125 1: 1, 126 2: 2, 127 } 128 129 for i, j := range tests { 130 input := [][]byte{ 131 []byte(`{"foo":{"bar":2}}`), 132 []byte(`{"foo":{"bar":2}}`), 133 []byte(`{"foo":{"bar":2}}`), 134 } 135 136 conf.JSON.Parts = []int{i} 137 proc, err := NewJSON(conf, nil, tLog, tStats) 138 if err != nil { 139 t.Fatal(err) 140 } 141 142 msgs, res := proc.ProcessMessage(message.New(input)) 143 if len(msgs) != 1 { 144 t.Errorf("Select Parts failed on index: %v", i) 145 } else if res != nil { 146 t.Errorf("Expected nil response: %v", res) 147 } 148 if act := string(message.GetAllBytes(msgs[0])[j]); exp != act { 149 t.Errorf("Unexpected output for index %v: %v != %v", i, act, exp) 150 } 151 if act := string(message.GetAllBytes(msgs[0])[(j+1)%3]); exp == act { 152 t.Errorf("Processor was applied to wrong index %v: %v != %v", j+1%3, act, exp) 153 } 154 } 155 } 156 157 func TestJSONFlattenArray(t *testing.T) { 158 type jTest struct { 159 name string 160 path string 161 value string 162 input string 163 output string 164 } 165 166 tests := []jTest{ 167 { 168 name: "flatten ints 1", 169 path: "foo.bar", 170 input: `{"foo":{"bar":[0,[1,2],3,4]}}`, 171 output: `{"foo":{"bar":[0,1,2,3,4]}}`, 172 }, 173 { 174 name: "flatten numbers 1", 175 path: "foo.bar", 176 input: `{"foo":{"bar":[[0],[],1.5,[2,3,4]]}}`, 177 output: `{"foo":{"bar":[0,1.5,2,3,4]}}`, 178 }, 179 { 180 name: "flatten root numbers 1", 181 path: ".", 182 input: `[0,[1.5],2,[3],4]`, 183 output: `[0,1.5,2,3,4]`, 184 }, 185 { 186 name: "flatten strings 1", 187 path: "foo.bar", 188 input: `{"foo":{"bar":[["foo"],["bar","baz"]]}}`, 189 output: `{"foo":{"bar":["foo","bar","baz"]}}`, 190 }, 191 { 192 name: "flatten mixed 1", 193 path: "foo.bar", 194 input: `{"foo":{"bar":[["foo","bar"],[5],6,null]}}`, 195 output: `{"foo":{"bar":["foo","bar",5,6,null]}}`, 196 }, 197 { 198 name: "flatten empty", 199 path: "foo.bar", 200 input: `{"foo":{"bar":[]}}`, 201 output: `{"foo":{"bar":[]}}`, 202 }, 203 } 204 205 for _, test := range tests { 206 conf := NewConfig() 207 conf.JSON.Operator = "flatten_array" 208 conf.JSON.Parts = []int{0} 209 conf.JSON.Path = test.path 210 conf.JSON.Value = []byte(test.value) 211 212 jSet, err := NewJSON(conf, nil, log.Noop(), metrics.Noop()) 213 if err != nil { 214 t.Fatalf("Error for test '%v': %v", test.name, err) 215 } 216 217 inMsg := message.New( 218 [][]byte{ 219 []byte(test.input), 220 }, 221 ) 222 msgs, _ := jSet.ProcessMessage(inMsg) 223 if len(msgs) != 1 { 224 t.Fatalf("Test '%v' did not succeed", test.name) 225 } 226 227 if exp, act := test.output, string(message.GetAllBytes(msgs[0])[0]); exp != act { 228 t.Errorf("Wrong result '%v': %v != %v", test.name, act, exp) 229 } 230 } 231 } 232 233 func TestJSONFoldStringArray(t *testing.T) { 234 type jTest struct { 235 name string 236 path string 237 value string 238 input string 239 output string 240 } 241 242 tests := []jTest{ 243 { 244 name: "fold strings 1", 245 path: "foo.bar", 246 input: `{"foo":{"bar":["foo","bar","baz"]}}`, 247 output: `{"foo":{"bar":"foobarbaz"}}`, 248 }, 249 { 250 name: "fold strings 2", 251 path: "foo.bar", 252 value: `" "`, 253 input: `{"foo":{"bar":["foo","bar","baz"]}}`, 254 output: `{"foo":{"bar":"foo bar baz"}}`, 255 }, 256 { 257 name: "fold empty", 258 path: "foo.bar", 259 input: `{"foo":{"bar":[]}}`, 260 output: `{"foo":{"bar":""}}`, 261 }, 262 } 263 264 for _, test := range tests { 265 conf := NewConfig() 266 conf.JSON.Operator = "fold_string_array" 267 conf.JSON.Parts = []int{0} 268 conf.JSON.Path = test.path 269 conf.JSON.Value = []byte(test.value) 270 271 jSet, err := NewJSON(conf, nil, log.Noop(), metrics.Noop()) 272 if err != nil { 273 t.Fatalf("Error for test '%v': %v", test.name, err) 274 } 275 276 inMsg := message.New( 277 [][]byte{ 278 []byte(test.input), 279 }, 280 ) 281 msgs, _ := jSet.ProcessMessage(inMsg) 282 if len(msgs) != 1 { 283 t.Fatalf("Test '%v' did not succeed", test.name) 284 } 285 286 if exp, act := test.output, string(message.GetAllBytes(msgs[0])[0]); exp != act { 287 t.Errorf("Wrong result '%v': %v != %v", test.name, act, exp) 288 } 289 } 290 } 291 292 func TestJSONNumberArray(t *testing.T) { 293 type jTest struct { 294 name string 295 path string 296 value string 297 input string 298 output string 299 } 300 301 tests := []jTest{ 302 { 303 name: "fold ints 1", 304 path: "foo.bar", 305 input: `{"foo":{"bar":[0,1,2,3,4]}}`, 306 output: `{"foo":{"bar":10}}`, 307 }, 308 { 309 name: "fold numbers 1", 310 path: "foo.bar", 311 input: `{"foo":{"bar":[0,1.5,2,3,4]}}`, 312 output: `{"foo":{"bar":10.5}}`, 313 }, 314 { 315 name: "fold root numbers 1", 316 path: ".", 317 input: `[0,1.5,2,3,4]`, 318 output: `10.5`, 319 }, 320 { 321 name: "fold numbers empty", 322 path: "foo.bar", 323 input: `{"foo":{"bar":[]}}`, 324 output: `{"foo":{"bar":0}}`, 325 }, 326 } 327 328 for _, test := range tests { 329 conf := NewConfig() 330 conf.JSON.Operator = "fold_number_array" 331 conf.JSON.Parts = []int{0} 332 conf.JSON.Path = test.path 333 conf.JSON.Value = []byte(test.value) 334 335 jSet, err := NewJSON(conf, nil, log.Noop(), metrics.Noop()) 336 if err != nil { 337 t.Fatalf("Error for test '%v': %v", test.name, err) 338 } 339 340 inMsg := message.New( 341 [][]byte{ 342 []byte(test.input), 343 }, 344 ) 345 msgs, _ := jSet.ProcessMessage(inMsg) 346 if len(msgs) != 1 { 347 t.Fatalf("Test '%v' did not succeed", test.name) 348 } 349 350 if exp, act := test.output, string(message.GetAllBytes(msgs[0])[0]); exp != act { 351 t.Errorf("Wrong result '%v': %v != %v", test.name, act, exp) 352 } 353 } 354 } 355 356 func TestJSONFlatten(t *testing.T) { 357 type jTest struct { 358 name string 359 path string 360 input string 361 output string 362 } 363 364 tests := []jTest{ 365 { 366 name: "flatten 1", 367 path: ".", 368 input: `{"foo":{"bar":"baz"}}`, 369 output: `{"foo.bar":"baz"}`, 370 }, 371 { 372 name: "flatten 2", 373 path: ".", 374 input: `{"foo":[{"bar":"1"},{"bar":"2"}]}`, 375 output: `{"foo.0.bar":"1","foo.1.bar":"2"}`, 376 }, 377 { 378 name: "flatten 3", 379 path: "", 380 input: `[{"bar":"1"},{"bar":"2"}]`, 381 output: `{"0.bar":"1","1.bar":"2"}`, 382 }, 383 { 384 name: "flatten 4", 385 path: "", 386 input: `[["1"],["2","3"]]`, 387 output: `{"0.0":"1","1.0":"2","1.1":"3"}`, 388 }, 389 { 390 name: "flatten nested 1", 391 path: "inner", 392 input: `{"inner":{"foo":{"bar":"baz"}}}`, 393 output: `{"inner":{"foo.bar":"baz"}}`, 394 }, 395 { 396 name: "flatten nested 2", 397 path: "inner", 398 input: `{"also":"this","inner":{"foo":[{"bar":"1"},{"bar":"2"}]}}`, 399 output: `{"also":"this","inner":{"foo.0.bar":"1","foo.1.bar":"2"}}`, 400 }, 401 } 402 403 for _, test := range tests { 404 conf := NewConfig() 405 conf.JSON.Operator = "flatten" 406 conf.JSON.Parts = []int{0} 407 conf.JSON.Path = test.path 408 409 jSet, err := NewJSON(conf, nil, log.Noop(), metrics.Noop()) 410 if err != nil { 411 t.Fatalf("Error for test '%v': %v", test.name, err) 412 } 413 414 inMsg := message.New( 415 [][]byte{ 416 []byte(test.input), 417 }, 418 ) 419 msgs, _ := jSet.ProcessMessage(inMsg) 420 if len(msgs) != 1 { 421 t.Fatalf("Test '%v' did not succeed", test.name) 422 } 423 424 if exp, act := test.output, string(message.GetAllBytes(msgs[0])[0]); exp != act { 425 t.Errorf("Wrong result '%v': %v != %v", test.name, act, exp) 426 } 427 } 428 } 429 430 func TestJSONAppend(t *testing.T) { 431 tLog := log.Noop() 432 tStats := metrics.Noop() 433 434 type jTest struct { 435 name string 436 path string 437 value string 438 input string 439 output string 440 } 441 442 tests := []jTest{ 443 { 444 name: "append 1", 445 path: "foo.bar", 446 value: `{"baz":1}`, 447 input: `{"foo":{"bar":5}}`, 448 output: `{"foo":{"bar":[5,{"baz":1}]}}`, 449 }, 450 { 451 name: "append in array 1", 452 path: "foo.1.bar", 453 value: `{"baz":1}`, 454 input: `{"foo":[{"ignored":true},{"bar":5}]}`, 455 output: `{"foo":[{"ignored":true},{"bar":[5,{"baz":1}]}]}`, 456 }, 457 { 458 name: "append nil 1", 459 path: "foo.bar", 460 value: `{"baz":1}`, 461 input: `{"foo":{"bar":null}}`, 462 output: `{"foo":{"bar":[null,{"baz":1}]}}`, 463 }, 464 { 465 name: "append nil 2", 466 path: "foo.bar", 467 value: `{"baz":1}`, 468 input: `{"foo":{"bar":[null]}}`, 469 output: `{"foo":{"bar":[null,{"baz":1}]}}`, 470 }, 471 { 472 name: "append empty 1", 473 path: "foo.bar", 474 value: `{"baz":1}`, 475 input: `{"foo":{}}`, 476 output: `{"foo":{"bar":[{"baz":1}]}}`, 477 }, 478 { 479 name: "append collision 1", 480 path: "foo.bar", 481 value: `{"baz":1}`, 482 input: `{"foo":0}`, 483 output: `{"foo":0}`, 484 }, 485 { 486 name: "append array 1", 487 path: "foo.bar", 488 value: `[1,2,3]`, 489 input: `{"foo":{"bar":[0]}}`, 490 output: `{"foo":{"bar":[0,1,2,3]}}`, 491 }, 492 { 493 name: "append array 2", 494 path: "foo.bar", 495 value: `[1,2,3]`, 496 input: `{"foo":{"bar":0}}`, 497 output: `{"foo":{"bar":[0,1,2,3]}}`, 498 }, 499 } 500 501 for _, test := range tests { 502 conf := NewConfig() 503 conf.JSON.Operator = "append" 504 conf.JSON.Parts = []int{0} 505 conf.JSON.Path = test.path 506 conf.JSON.Value = []byte(test.value) 507 508 jSet, err := NewJSON(conf, nil, tLog, tStats) 509 if err != nil { 510 t.Fatalf("Error for test '%v': %v", test.name, err) 511 } 512 513 inMsg := message.New( 514 [][]byte{ 515 []byte(test.input), 516 }, 517 ) 518 msgs, _ := jSet.ProcessMessage(inMsg) 519 if len(msgs) != 1 { 520 t.Fatalf("Test '%v' did not succeed", test.name) 521 } 522 523 if exp, act := test.output, string(message.GetAllBytes(msgs[0])[0]); exp != act { 524 t.Errorf("Wrong result '%v': %v != %v", test.name, act, exp) 525 } 526 } 527 } 528 529 func TestJSONSplit(t *testing.T) { 530 type jTest struct { 531 name string 532 path string 533 value string 534 input string 535 output string 536 } 537 538 tests := []jTest{ 539 { 540 name: "split 1", 541 path: "foo.bar", 542 value: `","`, 543 input: `{"foo":{"bar":"1,2,3"}}`, 544 output: `{"foo":{"bar":["1","2","3"]}}`, 545 }, 546 { 547 name: "split 2", 548 path: "foo.bar", 549 value: `"-"`, 550 input: `{"foo":{"bar":"1-2-3"}}`, 551 output: `{"foo":{"bar":["1","2","3"]}}`, 552 }, 553 { 554 name: "split 3", 555 path: "foo.bar", 556 value: `"-"`, 557 input: `{"foo":{"bar":20}}`, 558 output: `{"foo":{"bar":20}}`, 559 }, 560 { 561 name: "split 4", 562 path: "foo.bar", 563 value: `","`, 564 input: `{"foo":{"bar":"1"}}`, 565 output: `{"foo":{"bar":["1"]}}`, 566 }, 567 { 568 name: "split 5", 569 path: "foo.bar", 570 value: `","`, 571 input: `{"foo":{"bar":","}}`, 572 output: `{"foo":{"bar":["",""]}}`, 573 }, 574 } 575 576 for _, test := range tests { 577 conf := NewConfig() 578 conf.JSON.Operator = "split" 579 conf.JSON.Parts = []int{0} 580 conf.JSON.Path = test.path 581 conf.JSON.Value = []byte(test.value) 582 583 jSet, err := NewJSON(conf, nil, log.Noop(), metrics.Noop()) 584 if err != nil { 585 t.Fatalf("Error for test '%v': %v", test.name, err) 586 } 587 588 inMsg := message.New( 589 [][]byte{ 590 []byte(test.input), 591 }, 592 ) 593 msgs, _ := jSet.ProcessMessage(inMsg) 594 if len(msgs) != 1 { 595 t.Fatalf("Test '%v' did not succeed", test.name) 596 } 597 598 if exp, act := test.output, string(message.GetAllBytes(msgs[0])[0]); exp != act { 599 t.Errorf("Wrong result '%v': %v != %v", test.name, act, exp) 600 } 601 } 602 } 603 604 func TestJSONMove(t *testing.T) { 605 type jTest struct { 606 name string 607 path string 608 value string 609 input string 610 output string 611 } 612 613 tests := []jTest{ 614 { 615 name: "move 1", 616 path: "foo.bar", 617 value: `"bar.baz"`, 618 input: `{"foo":{"bar":5}}`, 619 output: `{"bar":{"baz":5},"foo":{}}`, 620 }, 621 { 622 name: "move 2", 623 path: "foo.bar", 624 value: `"bar.baz"`, 625 input: `{"foo":{"bar":5},"bar":{"qux":6}}`, 626 output: `{"bar":{"baz":5,"qux":6},"foo":{}}`, 627 }, 628 { 629 name: "move to same path 1", 630 path: "foo.bar", 631 value: `"foo.bar"`, 632 input: `{"foo":{"bar":5},"bar":{"qux":6}}`, 633 output: `{"bar":{"qux":6},"foo":{"bar":5}}`, 634 }, 635 { 636 name: "move from root 1", 637 path: ".", 638 value: `"bar.baz"`, 639 input: `{"foo":{"bar":5}}`, 640 output: `{"bar":{"baz":{"foo":{"bar":5}}}}`, 641 }, 642 { 643 name: "move to root 1", 644 path: "foo", 645 value: `""`, 646 input: `{"foo":{"bar":5}}`, 647 output: `{"bar":5}`, 648 }, 649 } 650 651 for _, test := range tests { 652 conf := NewConfig() 653 conf.JSON.Operator = "move" 654 conf.JSON.Parts = []int{0} 655 conf.JSON.Path = test.path 656 conf.JSON.Value = []byte(test.value) 657 658 jSet, err := NewJSON(conf, nil, log.Noop(), metrics.Noop()) 659 if err != nil { 660 t.Fatalf("Error for test '%v': %v", test.name, err) 661 } 662 663 inMsg := message.New( 664 [][]byte{ 665 []byte(test.input), 666 }, 667 ) 668 msgs, _ := jSet.ProcessMessage(inMsg) 669 if len(msgs) != 1 { 670 t.Fatalf("Test '%v' did not succeed", test.name) 671 } 672 673 if exp, act := test.output, string(message.GetAllBytes(msgs[0])[0]); exp != act { 674 t.Errorf("Wrong result '%v': %v != %v", test.name, act, exp) 675 } 676 } 677 } 678 679 func TestJSONExplode(t *testing.T) { 680 type jTest struct { 681 name string 682 path string 683 input string 684 output string 685 } 686 687 tests := []jTest{ 688 { 689 name: "explode 1", 690 path: "foo", 691 input: `{"foo":[1,2,3],"id":"bar"}`, 692 output: `[{"foo":1,"id":"bar"},{"foo":2,"id":"bar"},{"foo":3,"id":"bar"}]`, 693 }, 694 { 695 name: "explode 2", 696 path: "foo.bar", 697 input: `{"foo":{"also":"this","bar":[{"key":"value1"},{"key":"value2"},{"key":"value3"}]},"id":"baz"}`, 698 output: `[{"foo":{"also":"this","bar":{"key":"value1"}},"id":"baz"},{"foo":{"also":"this","bar":{"key":"value2"}},"id":"baz"},{"foo":{"also":"this","bar":{"key":"value3"}},"id":"baz"}]`, 699 }, 700 { 701 name: "explode 3", 702 path: "foo", 703 input: `{"foo":{"a":1,"b":2,"c":3},"id":"bar"}`, 704 output: `{"a":{"foo":1,"id":"bar"},"b":{"foo":2,"id":"bar"},"c":{"foo":3,"id":"bar"}}`, 705 }, 706 { 707 name: "explode 4", 708 path: "foo.bar", 709 input: `{"foo":{"also":"this","bar":{"key1":["a","b"],"key2":{"c":3,"d":4}}},"id":"baz"}`, 710 output: `{"key1":{"foo":{"also":"this","bar":["a","b"]},"id":"baz"},"key2":{"foo":{"also":"this","bar":{"c":3,"d":4}},"id":"baz"}}`, 711 }, 712 } 713 714 for _, test := range tests { 715 conf := NewConfig() 716 conf.JSON.Operator = "explode" 717 conf.JSON.Parts = []int{0} 718 conf.JSON.Path = test.path 719 720 jSet, err := NewJSON(conf, nil, log.Noop(), metrics.Noop()) 721 if err != nil { 722 t.Fatalf("Error for test '%v': %v", test.name, err) 723 } 724 725 inMsg := message.New( 726 [][]byte{ 727 []byte(test.input), 728 }, 729 ) 730 msgs, _ := jSet.ProcessMessage(inMsg) 731 if len(msgs) != 1 { 732 t.Fatalf("Test '%v' did not succeed", test.name) 733 } 734 735 if exp, act := test.output, string(message.GetAllBytes(msgs[0])[0]); exp != act { 736 t.Errorf("Wrong result '%v': %v != %v", test.name, act, exp) 737 } 738 } 739 } 740 741 func TestJSONCopy(t *testing.T) { 742 tLog := log.Noop() 743 tStats := metrics.Noop() 744 745 type jTest struct { 746 name string 747 path string 748 value string 749 input string 750 output string 751 } 752 753 tests := []jTest{ 754 { 755 name: "copy 1", 756 path: "foo.bar", 757 value: `"bar.baz"`, 758 input: `{"foo":{"bar":5}}`, 759 output: `{"bar":{"baz":5},"foo":{"bar":5}}`, 760 }, 761 { 762 name: "copy 2", 763 path: "foo.bar", 764 value: `"bar.baz"`, 765 input: `{"foo":{"bar":5},"bar":{"qux":6}}`, 766 output: `{"bar":{"baz":5,"qux":6},"foo":{"bar":5}}`, 767 }, 768 } 769 770 for _, test := range tests { 771 conf := NewConfig() 772 conf.JSON.Operator = "copy" 773 conf.JSON.Parts = []int{0} 774 conf.JSON.Path = test.path 775 conf.JSON.Value = []byte(test.value) 776 777 jSet, err := NewJSON(conf, nil, tLog, tStats) 778 if err != nil { 779 t.Fatalf("Error for test '%v': %v", test.name, err) 780 } 781 782 inMsg := message.New( 783 [][]byte{ 784 []byte(test.input), 785 }, 786 ) 787 msgs, _ := jSet.ProcessMessage(inMsg) 788 if len(msgs) != 1 { 789 t.Fatalf("Test '%v' did not succeed", test.name) 790 } 791 792 if exp, act := test.output, string(message.GetAllBytes(msgs[0])[0]); exp != act { 793 t.Errorf("Wrong result '%v': %v != %v", test.name, act, exp) 794 } 795 } 796 } 797 798 func TestJSONClean(t *testing.T) { 799 tLog := log.Noop() 800 tStats := metrics.Noop() 801 802 type jTest struct { 803 name string 804 path string 805 input string 806 output string 807 } 808 809 tests := []jTest{ 810 { 811 name: "clean nothing", 812 path: "foo.bar", 813 input: `{"foo":{"bar":5}}`, 814 output: `{"foo":{"bar":5}}`, 815 }, 816 { 817 name: "clean array", 818 path: "foo.bar", 819 input: `{"foo":{"bar":[]}}`, 820 output: `{"foo":{}}`, 821 }, 822 { 823 name: "clean array 2", 824 path: "foo.bar", 825 input: `{"foo":{"b":[1],"bar":[]}}`, 826 output: `{"foo":{"b":[1]}}`, 827 }, 828 { 829 name: "clean array 3", 830 path: "foo", 831 input: `{"foo":{"b":[1],"bar":[]}}`, 832 output: `{"foo":{"b":[1]}}`, 833 }, 834 { 835 name: "clean object", 836 path: "foo.bar", 837 input: `{"foo":{"bar":{}}}`, 838 output: `{"foo":{}}`, 839 }, 840 { 841 name: "clean object 2", 842 path: "foo.bar", 843 input: `{"foo":{"b":{"1":1},"bar":{}}}`, 844 output: `{"foo":{"b":{"1":1}}}`, 845 }, 846 { 847 name: "clean object 3", 848 path: "foo", 849 input: `{"foo":{"b":{"1":1},"bar":{}}}`, 850 output: `{"foo":{"b":{"1":1}}}`, 851 }, 852 { 853 name: "clean array from root", 854 path: "", 855 input: `{"foo":{"b":"b","bar":[]}}`, 856 output: `{"foo":{"b":"b"}}`, 857 }, 858 { 859 name: "clean object from root", 860 path: "", 861 input: `{"foo":{"b":"b","bar":{}}}`, 862 output: `{"foo":{"b":"b"}}`, 863 }, 864 { 865 name: "clean everything object", 866 path: "", 867 input: `{"foo":{"bar":{}}}`, 868 output: `{}`, 869 }, 870 { 871 name: "clean everything array", 872 path: "", 873 input: `[{"foo":{"bar":{}}},[]]`, 874 output: `[]`, 875 }, 876 { 877 name: "clean everything string", 878 path: "", 879 input: `""`, 880 output: `null`, 881 }, 882 { 883 name: "clean arrays", 884 path: "", 885 input: `[[],1,"",2,{},"test",{"foo":{}}]`, 886 output: `[1,2,"test"]`, 887 }, 888 } 889 890 for _, test := range tests { 891 conf := NewConfig() 892 conf.JSON.Operator = "clean" 893 conf.JSON.Parts = []int{0} 894 conf.JSON.Path = test.path 895 896 jSet, err := NewJSON(conf, nil, tLog, tStats) 897 if err != nil { 898 t.Fatalf("Error for test '%v': %v", test.name, err) 899 } 900 901 inMsg := message.New( 902 [][]byte{ 903 []byte(test.input), 904 }, 905 ) 906 msgs, _ := jSet.ProcessMessage(inMsg) 907 if len(msgs) != 1 { 908 t.Fatalf("Test '%v' did not succeed", test.name) 909 } 910 911 if exp, act := test.output, string(message.GetAllBytes(msgs[0])[0]); exp != act { 912 t.Errorf("Wrong result '%v': %v != %v", test.name, act, exp) 913 } 914 } 915 } 916 917 func TestJSONSet(t *testing.T) { 918 type jTest struct { 919 name string 920 path string 921 value string 922 input string 923 output string 924 } 925 926 tests := []jTest{ 927 { 928 name: "set 1", 929 path: "foo.bar", 930 value: `{"baz":1}`, 931 input: `{"foo":{"bar":5}}`, 932 output: `{"foo":{"bar":{"baz":1}}}`, 933 }, 934 { 935 name: "set 2", 936 path: "foo", 937 value: `5`, 938 input: `{"foo":{"bar":5}}`, 939 output: `{"foo":5}`, 940 }, 941 { 942 name: "set 3", 943 path: "foo", 944 value: `"5"`, 945 input: `{"foo":{"bar":5}}`, 946 output: `{"foo":"5"}`, 947 }, 948 { 949 name: "set 4", 950 path: "foo.bar", 951 value: `{ 952 "baz": 1 953 }`, 954 input: `{"foo":{"bar":5}}`, 955 output: `{"foo":{"bar":{"baz":1}}}`, 956 }, 957 { 958 name: "set 5", 959 path: "foo.bar", 960 value: `{"baz":"${!echo:foo}"}`, 961 input: `{"foo":{"bar":5}}`, 962 output: `{"foo":{"bar":{"baz":"foo"}}}`, 963 }, 964 { 965 name: "set 6", 966 path: "foo.bar", 967 value: `${!echo:10}`, 968 input: `{"foo":{"bar":5}}`, 969 output: `{"foo":{"bar":10}}`, 970 }, 971 { 972 name: "set root 1", 973 path: "", 974 value: `{"baz":1}`, 975 input: `"hello world"`, 976 output: `{"baz":1}`, 977 }, 978 { 979 name: "set root 2", 980 path: ".", 981 value: `{"baz":1}`, 982 input: `{"foo":2}`, 983 output: `{"baz":1}`, 984 }, 985 { 986 name: "set interpolate 1", 987 path: "foo", 988 value: `{"baz":"${!json("bar")}"}`, 989 input: `{"foo":2,"bar":"hello world this is a string"}`, 990 output: `{"bar":"hello world this is a string","foo":{"baz":"hello world this is a string"}}`, 991 }, 992 { 993 name: "set interpolate 2", 994 path: ".", 995 value: `{"${!json("key")}":{"value":"${!json("value")}"}}`, 996 input: `{"key":"dynamic","value":{"foo":"bar"}}`, 997 output: `{"dynamic":{"value":"{\"foo\":\"bar\"}"}}`, 998 }, 999 { 1000 name: "set null 1", 1001 path: "foo.bar", 1002 value: `null`, 1003 input: `{"foo":{"bar":5}}`, 1004 output: `{"foo":{"bar":null}}`, 1005 }, 1006 { 1007 name: "set null 2", 1008 path: "foo.bar", 1009 value: `null`, 1010 input: `{"foo":{"bar":{"baz":"yelp"}}}`, 1011 output: `{"foo":{"bar":null}}`, 1012 }, 1013 { 1014 name: "set unicode 1", 1015 path: "foo.bar", 1016 value: `"contains 🦄 emoji"`, 1017 input: `{"foo":{"bar":{"baz":"yelp"}}}`, 1018 output: `{"foo":{"bar":"contains 🦄 emoji"}}`, 1019 }, 1020 { 1021 name: "set unicode 2", 1022 path: "foo.bar", 1023 value: `{"value":{"unicode":"contains 🦄 emoji"}}`, 1024 input: `{"foo":{"bar":{"baz":"yelp"}}}`, 1025 output: `{"foo":{"bar":{"value":{"unicode":"contains 🦄 emoji"}}}}`, 1026 }, 1027 { 1028 name: "set unicode 3", 1029 path: "foo.bar", 1030 value: `{"value":"${!json("foo.bar.baz")}"}`, 1031 input: `{"foo":{"bar":{"baz":"foo 🦄 bar"}}}`, 1032 output: `{"foo":{"bar":{"value":"foo 🦄 bar"}}}`, 1033 }, 1034 } 1035 1036 for _, test := range tests { 1037 conf := NewConfig() 1038 conf.JSON.Operator = "set" 1039 conf.JSON.Parts = []int{0} 1040 conf.JSON.Path = test.path 1041 conf.JSON.Value = []byte(test.value) 1042 1043 jSet, err := NewJSON(conf, nil, log.Noop(), metrics.Noop()) 1044 if err != nil { 1045 t.Fatalf("Error for test '%v': %v", test.name, err) 1046 } 1047 1048 inMsg := message.New( 1049 [][]byte{ 1050 []byte(test.input), 1051 }, 1052 ) 1053 msgs, _ := jSet.ProcessMessage(inMsg) 1054 if len(msgs) != 1 { 1055 t.Fatalf("Test '%v' did not succeed", test.name) 1056 } 1057 1058 if exp, act := test.output, string(message.GetAllBytes(msgs[0])[0]); exp != act { 1059 t.Errorf("Wrong result '%v': %v != %v", test.name, act, exp) 1060 } 1061 } 1062 } 1063 1064 func TestJSONSetEdge(t *testing.T) { 1065 conf := NewConfig() 1066 conf.JSON.Operator = "set" 1067 conf.JSON.Path = "foo" 1068 conf.JSON.Value = []byte(`"bar"`) 1069 1070 jSet, err := NewJSON(conf, nil, log.Noop(), metrics.Noop()) 1071 if err != nil { 1072 t.Fatal(err) 1073 } 1074 1075 inMsg := message.New([][]byte{[]byte(`{}`)}) 1076 msgs, _ := jSet.ProcessMessage(inMsg) 1077 if len(msgs) != 1 { 1078 t.Fatalf("Wrong count of result messages: %v", len(msgs)) 1079 } 1080 if exp, act := `{"foo":"bar"}`, string(msgs[0].Get(0).Get()); exp != act { 1081 t.Errorf("Wrong result: %v != %v", act, exp) 1082 } 1083 1084 msgs, _ = jSet.ProcessMessage(msgs[0]) 1085 if len(msgs) != 1 { 1086 t.Fatalf("Wrong count of result messages: %v", len(msgs)) 1087 } 1088 if exp, act := `{"foo":"bar"}`, string(msgs[0].Get(0).Get()); exp != act { 1089 t.Errorf("Wrong result: %v != %v", act, exp) 1090 } 1091 } 1092 1093 func TestJSONConfigYAML(t *testing.T) { 1094 tLog := log.Noop() 1095 tStats := metrics.Noop() 1096 1097 input := `{"foo":{"bar":5}}` 1098 1099 tests := map[string]string{ 1100 `value: 10`: `{"foo":{"bar":10}}`, 1101 `value: "hello world"`: `{"foo":{"bar":"hello world"}}`, 1102 `value: hello world`: `{"foo":{"bar":"hello world"}}`, 1103 ` 1104 value: 1105 baz: 10`: `{"foo":{"bar":{"baz":10}}}`, 1106 ` 1107 value: 1108 baz: 1109 - first 1110 - 2 1111 - third`: `{"foo":{"bar":{"baz":["first",2,"third"]}}}`, 1112 ` 1113 value: 1114 baz: 1115 deeper: look at me 1116 here: 11`: `{"foo":{"bar":{"baz":{"deeper":"look at me"},"here":11}}}`, 1117 } 1118 1119 for config, exp := range tests { 1120 conf := NewConfig() 1121 conf.JSON.Operator = "set" 1122 conf.JSON.Parts = []int{} 1123 conf.JSON.Path = "foo.bar" 1124 1125 if err := yaml.Unmarshal([]byte(config), &conf.JSON); err != nil { 1126 t.Fatal(err) 1127 } 1128 1129 jSet, err := NewJSON(conf, nil, tLog, tStats) 1130 if err != nil { 1131 t.Fatalf("Error creating proc '%v': %v", config, err) 1132 } 1133 1134 inMsg := message.New( 1135 [][]byte{ 1136 []byte(input), 1137 }, 1138 ) 1139 msgs, _ := jSet.ProcessMessage(inMsg) 1140 if len(msgs) != 1 { 1141 t.Fatalf("Test did not succeed with config: %v", config) 1142 } 1143 1144 if act := string(message.GetAllBytes(msgs[0])[0]); exp != act { 1145 t.Errorf("Wrong result '%v': %v != %v", config, act, exp) 1146 } 1147 } 1148 } 1149 1150 func TestJSONConfigYAMLMarshal(t *testing.T) { 1151 tLog := log.Noop() 1152 tStats := metrics.Noop() 1153 1154 tests := []string{ 1155 `parts: 1156 - 0 1157 operator: set 1158 path: foo.bar 1159 value: 1160 baz: 1161 deeper: look at me 1162 here: 11 1163 `, 1164 `parts: 1165 - 0 1166 operator: set 1167 path: foo.bar 1168 value: null 1169 `, 1170 `parts: 1171 - 0 1172 operator: set 1173 path: foo.bar 1174 value: 1175 foo: null 1176 `, 1177 `parts: 1178 - 0 1179 operator: set 1180 path: foo.bar 1181 value: 1182 baz: 1183 deeper: 1184 - first 1185 - second 1186 - third 1187 here: 11 1188 `, 1189 `parts: 1190 - 5 1191 operator: set 1192 path: foo.bar.baz 1193 value: 5 1194 `, 1195 `parts: 1196 - 0 1197 operator: set 1198 path: foo.bar 1199 value: hello world 1200 `, 1201 `parts: 1202 - 0 1203 operator: set 1204 path: foo.bar 1205 value: 1206 root: 1207 - values: 1208 - nested: true 1209 with: array 1210 `, 1211 `parts: 1212 - 0 1213 operator: set 1214 path: foo.bar 1215 value: 1216 foo: 1217 bar: 1218 baz: 1219 value: true 1220 `, 1221 } 1222 1223 for _, testconfig := range tests { 1224 conf := NewConfig() 1225 if err := yaml.Unmarshal([]byte(testconfig), &conf.JSON); err != nil { 1226 t.Error(err) 1227 continue 1228 } 1229 1230 if act, err := config.MarshalYAML(conf.JSON); err != nil { 1231 t.Error(err) 1232 } else if string(act) != testconfig { 1233 t.Errorf("Marshalled config does not match: %v != %v", string(act), testconfig) 1234 } 1235 1236 if _, err := NewJSON(conf, nil, tLog, tStats); err != nil { 1237 t.Errorf("Error creating proc '%v': %v", testconfig, err) 1238 } 1239 } 1240 } 1241 1242 func TestJSONSelect(t *testing.T) { 1243 tLog := log.Noop() 1244 tStats := metrics.Noop() 1245 1246 type jTest struct { 1247 name string 1248 path string 1249 input string 1250 output string 1251 } 1252 1253 tests := []jTest{ 1254 { 1255 name: "select obj", 1256 path: "foo.bar", 1257 input: `{"foo":{"bar":{"baz":1}}}`, 1258 output: `{"baz":1}`, 1259 }, 1260 { 1261 name: "select array", 1262 path: "foo.bar", 1263 input: `{"foo":{"bar":["baz","qux"]}}`, 1264 output: `["baz","qux"]`, 1265 }, 1266 { 1267 name: "select obj as str", 1268 path: "foo.bar", 1269 input: `{"foo":{"bar":"{\"baz\":1}"}}`, 1270 output: `{"baz":1}`, 1271 }, 1272 { 1273 name: "select str", 1274 path: "foo.bar", 1275 input: `{"foo":{"bar":"hello world"}}`, 1276 output: `hello world`, 1277 }, 1278 { 1279 name: "select float", 1280 path: "foo.bar", 1281 input: `{"foo":{"bar":0.123}}`, 1282 output: `0.123`, 1283 }, 1284 { 1285 name: "select int", 1286 path: "foo.bar", 1287 input: `{"foo":{"bar":123}}`, 1288 output: `123`, 1289 }, 1290 { 1291 name: "select bool", 1292 path: "foo.bar", 1293 input: `{"foo":{"bar":true}}`, 1294 output: `true`, 1295 }, 1296 } 1297 1298 for _, test := range tests { 1299 conf := NewConfig() 1300 conf.JSON.Operator = "select" 1301 conf.JSON.Parts = []int{0} 1302 conf.JSON.Path = test.path 1303 1304 jSet, err := NewJSON(conf, nil, tLog, tStats) 1305 if err != nil { 1306 t.Fatalf("Error for test '%v': %v", test.name, err) 1307 } 1308 1309 inMsg := message.New( 1310 [][]byte{ 1311 []byte(test.input), 1312 }, 1313 ) 1314 msgs, _ := jSet.ProcessMessage(inMsg) 1315 if len(msgs) != 1 { 1316 t.Fatalf("Test '%v' did not succeed", test.name) 1317 } 1318 1319 if exp, act := test.output, string(message.GetAllBytes(msgs[0])[0]); exp != act { 1320 t.Errorf("Wrong result '%v': %v != %v", test.name, act, exp) 1321 } 1322 } 1323 } 1324 1325 func TestJSONDeletePartBounds(t *testing.T) { 1326 tLog := log.Noop() 1327 tStats := metrics.Noop() 1328 1329 conf := NewConfig() 1330 conf.JSON.Path = "foo.bar" 1331 conf.JSON.Operator = "delete" 1332 1333 exp := `{"foo":{}}` 1334 1335 tests := map[int]int{ 1336 -3: 0, 1337 -2: 1, 1338 -1: 2, 1339 0: 0, 1340 1: 1, 1341 2: 2, 1342 } 1343 1344 for i, j := range tests { 1345 input := [][]byte{ 1346 []byte(`{"foo":{"bar":2}}`), 1347 []byte(`{"foo":{"bar":2}}`), 1348 []byte(`{"foo":{"bar":2}}`), 1349 } 1350 1351 conf.JSON.Parts = []int{i} 1352 proc, err := NewJSON(conf, nil, tLog, tStats) 1353 if err != nil { 1354 t.Fatal(err) 1355 } 1356 1357 msgs, res := proc.ProcessMessage(message.New(input)) 1358 if len(msgs) != 1 { 1359 t.Errorf("Select Parts failed on index: %v", i) 1360 } else if res != nil { 1361 t.Errorf("Expected nil response: %v", res) 1362 } 1363 if act := string(message.GetAllBytes(msgs[0])[j]); exp != act { 1364 t.Errorf("Unexpected output for index %v: %v != %v", i, act, exp) 1365 } 1366 } 1367 } 1368 1369 func TestJSONDelete(t *testing.T) { 1370 tLog := log.Noop() 1371 tStats := metrics.Noop() 1372 1373 type jTest struct { 1374 name string 1375 path string 1376 input string 1377 output string 1378 } 1379 1380 tests := []jTest{ 1381 { 1382 name: "del field 1", 1383 path: "foo.bar", 1384 input: `{"foo":{"bar":5}}`, 1385 output: `{"foo":{}}`, 1386 }, 1387 { 1388 name: "del obj field 1", 1389 path: "foo.bar", 1390 input: `{"foo":{"bar":{"baz":5}}}`, 1391 output: `{"foo":{}}`, 1392 }, 1393 { 1394 name: "del array field 1", 1395 path: "foo.bar", 1396 input: `{"foo":{"bar":[5]}}`, 1397 output: `{"foo":{}}`, 1398 }, 1399 } 1400 1401 for _, test := range tests { 1402 conf := NewConfig() 1403 conf.JSON.Parts = []int{0} 1404 conf.JSON.Operator = "delete" 1405 conf.JSON.Path = test.path 1406 1407 jSet, err := NewJSON(conf, nil, tLog, tStats) 1408 if err != nil { 1409 t.Fatalf("Error for test '%v': %v", test.name, err) 1410 } 1411 1412 inMsg := message.New( 1413 [][]byte{ 1414 []byte(test.input), 1415 }, 1416 ) 1417 msgs, _ := jSet.ProcessMessage(inMsg) 1418 if len(msgs) != 1 { 1419 t.Fatalf("Test '%v' did not succeed", test.name) 1420 } 1421 1422 if exp, act := test.output, string(message.GetAllBytes(msgs[0])[0]); exp != act { 1423 t.Errorf("Wrong result '%v': %v != %v", test.name, act, exp) 1424 } 1425 } 1426 }