1// Copyright The OpenTelemetry Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package processorhelper 16 17import ( 18 "crypto/sha1" // #nosec 19 "encoding/binary" 20 "errors" 21 "fmt" 22 "math" 23 "regexp" 24 "testing" 25 26 "github.com/stretchr/testify/assert" 27 "github.com/stretchr/testify/require" 28 29 "go.opentelemetry.io/collector/model/pdata" 30) 31 32// Common structure for all the Tests 33type testCase struct { 34 name string 35 inputAttributes map[string]pdata.AttributeValue 36 expectedAttributes map[string]pdata.AttributeValue 37} 38 39// runIndividualTestCase is the common logic of passing trace data through a configured attributes processor. 40func runIndividualTestCase(t *testing.T, tt testCase, ap *AttrProc) { 41 t.Run(tt.name, func(t *testing.T) { 42 attrMap := pdata.NewAttributeMap().InitFromMap(tt.inputAttributes) 43 ap.Process(attrMap) 44 attrMap.Sort() 45 require.Equal(t, pdata.NewAttributeMap().InitFromMap(tt.expectedAttributes).Sort(), attrMap) 46 }) 47} 48 49func TestAttributes_InsertValue(t *testing.T) { 50 testCases := []testCase{ 51 // Ensure `attribute1` is set for spans with no attributes. 52 { 53 name: "InsertEmptyAttributes", 54 inputAttributes: map[string]pdata.AttributeValue{}, 55 expectedAttributes: map[string]pdata.AttributeValue{ 56 "attribute1": pdata.NewAttributeValueInt(123), 57 }, 58 }, 59 // Ensure `attribute1` is set. 60 { 61 name: "InsertKeyNoExists", 62 inputAttributes: map[string]pdata.AttributeValue{ 63 "anotherkey": pdata.NewAttributeValueString("bob"), 64 }, 65 expectedAttributes: map[string]pdata.AttributeValue{ 66 "anotherkey": pdata.NewAttributeValueString("bob"), 67 "attribute1": pdata.NewAttributeValueInt(123), 68 }, 69 }, 70 // Ensures no insert is performed because the keys `attribute1` already exists. 71 { 72 name: "InsertKeyExists", 73 inputAttributes: map[string]pdata.AttributeValue{ 74 "attribute1": pdata.NewAttributeValueString("bob"), 75 }, 76 expectedAttributes: map[string]pdata.AttributeValue{ 77 "attribute1": pdata.NewAttributeValueString("bob"), 78 }, 79 }, 80 } 81 82 cfg := &Settings{ 83 Actions: []ActionKeyValue{ 84 {Key: "attribute1", Action: INSERT, Value: 123}, 85 }, 86 } 87 88 ap, err := NewAttrProc(cfg) 89 require.Nil(t, err) 90 require.NotNil(t, ap) 91 92 for _, tt := range testCases { 93 runIndividualTestCase(t, tt, ap) 94 } 95} 96 97func TestAttributes_InsertFromAttribute(t *testing.T) { 98 99 testCases := []testCase{ 100 // Ensure no attribute is inserted because because attributes do not exist. 101 { 102 name: "InsertEmptyAttributes", 103 inputAttributes: map[string]pdata.AttributeValue{}, 104 expectedAttributes: map[string]pdata.AttributeValue{}, 105 }, 106 // Ensure no attribute is inserted because because from_attribute `string_key` does not exist. 107 { 108 name: "InsertMissingFromAttribute", 109 inputAttributes: map[string]pdata.AttributeValue{ 110 "bob": pdata.NewAttributeValueInt(1), 111 }, 112 expectedAttributes: map[string]pdata.AttributeValue{ 113 "bob": pdata.NewAttributeValueInt(1), 114 }, 115 }, 116 // Ensure `string key` is set. 117 { 118 name: "InsertAttributeExists", 119 inputAttributes: map[string]pdata.AttributeValue{ 120 "anotherkey": pdata.NewAttributeValueInt(8892342), 121 }, 122 expectedAttributes: map[string]pdata.AttributeValue{ 123 "anotherkey": pdata.NewAttributeValueInt(8892342), 124 "string key": pdata.NewAttributeValueInt(8892342), 125 }, 126 }, 127 // Ensures no insert is performed because the keys `string key` already exist. 128 { 129 name: "InsertKeysExists", 130 inputAttributes: map[string]pdata.AttributeValue{ 131 "anotherkey": pdata.NewAttributeValueInt(8892342), 132 "string key": pdata.NewAttributeValueString("here"), 133 }, 134 expectedAttributes: map[string]pdata.AttributeValue{ 135 "anotherkey": pdata.NewAttributeValueInt(8892342), 136 "string key": pdata.NewAttributeValueString("here"), 137 }, 138 }, 139 } 140 cfg := &Settings{ 141 Actions: []ActionKeyValue{ 142 {Key: "string key", Action: INSERT, FromAttribute: "anotherkey"}, 143 }, 144 } 145 146 ap, err := NewAttrProc(cfg) 147 require.Nil(t, err) 148 require.NotNil(t, ap) 149 150 for _, tt := range testCases { 151 runIndividualTestCase(t, tt, ap) 152 } 153} 154 155func TestAttributes_UpdateValue(t *testing.T) { 156 157 testCases := []testCase{ 158 // Ensure no changes to the span as there is no attributes map. 159 { 160 name: "UpdateNoAttributes", 161 inputAttributes: map[string]pdata.AttributeValue{}, 162 expectedAttributes: map[string]pdata.AttributeValue{}, 163 }, 164 // Ensure no changes to the span as the key does not exist. 165 { 166 name: "UpdateKeyNoExist", 167 inputAttributes: map[string]pdata.AttributeValue{ 168 "boo": pdata.NewAttributeValueString("foo"), 169 }, 170 expectedAttributes: map[string]pdata.AttributeValue{ 171 "boo": pdata.NewAttributeValueString("foo"), 172 }, 173 }, 174 // Ensure the attribute `db.secret` is updated. 175 { 176 name: "UpdateAttributes", 177 inputAttributes: map[string]pdata.AttributeValue{ 178 "db.secret": pdata.NewAttributeValueString("password1234"), 179 }, 180 expectedAttributes: map[string]pdata.AttributeValue{ 181 "db.secret": pdata.NewAttributeValueString("redacted"), 182 }, 183 }, 184 } 185 186 cfg := &Settings{ 187 Actions: []ActionKeyValue{ 188 {Key: "db.secret", Action: UPDATE, Value: "redacted"}, 189 }, 190 } 191 192 ap, err := NewAttrProc(cfg) 193 require.Nil(t, err) 194 require.NotNil(t, ap) 195 196 for _, tt := range testCases { 197 runIndividualTestCase(t, tt, ap) 198 } 199} 200 201func TestAttributes_UpdateFromAttribute(t *testing.T) { 202 203 testCases := []testCase{ 204 // Ensure no changes to the span as there is no attributes map. 205 { 206 name: "UpdateNoAttributes", 207 inputAttributes: map[string]pdata.AttributeValue{}, 208 expectedAttributes: map[string]pdata.AttributeValue{}, 209 }, 210 // Ensure the attribute `boo` isn't updated because attribute `foo` isn't present in the span. 211 { 212 name: "UpdateKeyNoExistFromAttribute", 213 inputAttributes: map[string]pdata.AttributeValue{ 214 "boo": pdata.NewAttributeValueString("bob"), 215 }, 216 expectedAttributes: map[string]pdata.AttributeValue{ 217 "boo": pdata.NewAttributeValueString("bob"), 218 }, 219 }, 220 // Ensure no updates as the target key `boo` doesn't exists. 221 { 222 name: "UpdateKeyNoExistMainAttributed", 223 inputAttributes: map[string]pdata.AttributeValue{ 224 "foo": pdata.NewAttributeValueString("over there"), 225 }, 226 expectedAttributes: map[string]pdata.AttributeValue{ 227 "foo": pdata.NewAttributeValueString("over there"), 228 }, 229 }, 230 // Ensure no updates as the target key `boo` doesn't exists. 231 { 232 name: "UpdateKeyFromExistingAttribute", 233 inputAttributes: map[string]pdata.AttributeValue{ 234 "foo": pdata.NewAttributeValueString("there is a party over here"), 235 "boo": pdata.NewAttributeValueString("not here"), 236 }, 237 expectedAttributes: map[string]pdata.AttributeValue{ 238 "foo": pdata.NewAttributeValueString("there is a party over here"), 239 "boo": pdata.NewAttributeValueString("there is a party over here"), 240 }, 241 }, 242 } 243 244 cfg := &Settings{ 245 Actions: []ActionKeyValue{ 246 {Key: "boo", Action: UPDATE, FromAttribute: "foo"}, 247 }, 248 } 249 250 ap, err := NewAttrProc(cfg) 251 require.Nil(t, err) 252 require.NotNil(t, ap) 253 254 for _, tt := range testCases { 255 runIndividualTestCase(t, tt, ap) 256 } 257} 258 259func TestAttributes_UpsertValue(t *testing.T) { 260 testCases := []testCase{ 261 // Ensure `region` is set for spans with no attributes. 262 { 263 name: "UpsertNoAttributes", 264 inputAttributes: map[string]pdata.AttributeValue{}, 265 expectedAttributes: map[string]pdata.AttributeValue{ 266 "region": pdata.NewAttributeValueString("planet-earth"), 267 }, 268 }, 269 // Ensure `region` is inserted for spans with some attributes(the key doesn't exist). 270 { 271 name: "UpsertAttributeNoExist", 272 inputAttributes: map[string]pdata.AttributeValue{ 273 "mission": pdata.NewAttributeValueString("to mars"), 274 }, 275 expectedAttributes: map[string]pdata.AttributeValue{ 276 "mission": pdata.NewAttributeValueString("to mars"), 277 "region": pdata.NewAttributeValueString("planet-earth"), 278 }, 279 }, 280 // Ensure `region` is updated for spans with the attribute key `region`. 281 { 282 name: "UpsertAttributeExists", 283 inputAttributes: map[string]pdata.AttributeValue{ 284 "mission": pdata.NewAttributeValueString("to mars"), 285 "region": pdata.NewAttributeValueString("solar system"), 286 }, 287 expectedAttributes: map[string]pdata.AttributeValue{ 288 "mission": pdata.NewAttributeValueString("to mars"), 289 "region": pdata.NewAttributeValueString("planet-earth"), 290 }, 291 }, 292 } 293 294 cfg := &Settings{ 295 Actions: []ActionKeyValue{ 296 {Key: "region", Action: UPSERT, Value: "planet-earth"}, 297 }, 298 } 299 300 ap, err := NewAttrProc(cfg) 301 require.Nil(t, err) 302 require.NotNil(t, ap) 303 304 for _, tt := range testCases { 305 runIndividualTestCase(t, tt, ap) 306 } 307} 308 309func TestAttributes_Extract(t *testing.T) { 310 testCases := []testCase{ 311 // Ensure `new_user_key` is not set for spans with no attributes. 312 { 313 name: "UpsertEmptyAttributes", 314 inputAttributes: map[string]pdata.AttributeValue{}, 315 expectedAttributes: map[string]pdata.AttributeValue{}, 316 }, 317 // Ensure `new_user_key` is not inserted for spans with missing attribute `user_key`. 318 { 319 name: "No extract with no target key", 320 inputAttributes: map[string]pdata.AttributeValue{ 321 "boo": pdata.NewAttributeValueString("ghosts are scary"), 322 }, 323 expectedAttributes: map[string]pdata.AttributeValue{ 324 "boo": pdata.NewAttributeValueString("ghosts are scary"), 325 }, 326 }, 327 // Ensure `new_user_key` is not inserted for spans with missing attribute `user_key`. 328 { 329 name: "No extract with non string target key", 330 inputAttributes: map[string]pdata.AttributeValue{ 331 "boo": pdata.NewAttributeValueString("ghosts are scary"), 332 "user_key": pdata.NewAttributeValueInt(1234), 333 }, 334 expectedAttributes: map[string]pdata.AttributeValue{ 335 "boo": pdata.NewAttributeValueString("ghosts are scary"), 336 "user_key": pdata.NewAttributeValueInt(1234), 337 }, 338 }, 339 // Ensure `new_user_key` is not updated for spans with attribute 340 // `user_key` because `user_key` does not match the regular expression. 341 { 342 name: "No extract with no pattern matching", 343 inputAttributes: map[string]pdata.AttributeValue{ 344 "user_key": pdata.NewAttributeValueString("does not match"), 345 "boo": pdata.NewAttributeValueString("ghosts are scary"), 346 }, 347 expectedAttributes: map[string]pdata.AttributeValue{ 348 "user_key": pdata.NewAttributeValueString("does not match"), 349 "boo": pdata.NewAttributeValueString("ghosts are scary"), 350 }, 351 }, 352 // Ensure `new_user_key` is not updated for spans with attribute 353 // `user_key` because `user_key` does not match all of the regular 354 // expression. 355 { 356 name: "No extract with no pattern matching", 357 inputAttributes: map[string]pdata.AttributeValue{ 358 "user_key": pdata.NewAttributeValueString("/api/v1/document/12345678/update"), 359 "boo": pdata.NewAttributeValueString("ghosts are scary"), 360 }, 361 expectedAttributes: map[string]pdata.AttributeValue{ 362 "user_key": pdata.NewAttributeValueString("/api/v1/document/12345678/update"), 363 "boo": pdata.NewAttributeValueString("ghosts are scary"), 364 }, 365 }, 366 // Ensure `new_user_key` and `version` is inserted for spans with attribute `user_key`. 367 { 368 name: "Extract insert new values.", 369 inputAttributes: map[string]pdata.AttributeValue{ 370 "user_key": pdata.NewAttributeValueString("/api/v1/document/12345678/update/v1"), 371 "foo": pdata.NewAttributeValueString("casper the friendly ghost"), 372 }, 373 expectedAttributes: map[string]pdata.AttributeValue{ 374 "user_key": pdata.NewAttributeValueString("/api/v1/document/12345678/update/v1"), 375 "new_user_key": pdata.NewAttributeValueString("12345678"), 376 "version": pdata.NewAttributeValueString("v1"), 377 "foo": pdata.NewAttributeValueString("casper the friendly ghost"), 378 }, 379 }, 380 // Ensure `new_user_key` and `version` is updated for spans with attribute `user_key`. 381 { 382 name: "Extract updates existing values ", 383 inputAttributes: map[string]pdata.AttributeValue{ 384 "user_key": pdata.NewAttributeValueString("/api/v1/document/12345678/update/v1"), 385 "new_user_key": pdata.NewAttributeValueString("2321"), 386 "version": pdata.NewAttributeValueString("na"), 387 "foo": pdata.NewAttributeValueString("casper the friendly ghost"), 388 }, 389 expectedAttributes: map[string]pdata.AttributeValue{ 390 "user_key": pdata.NewAttributeValueString("/api/v1/document/12345678/update/v1"), 391 "new_user_key": pdata.NewAttributeValueString("12345678"), 392 "version": pdata.NewAttributeValueString("v1"), 393 "foo": pdata.NewAttributeValueString("casper the friendly ghost"), 394 }, 395 }, 396 // Ensure `new_user_key` is updated and `version` is inserted for spans with attribute `user_key`. 397 { 398 name: "Extract upserts values", 399 inputAttributes: map[string]pdata.AttributeValue{ 400 "user_key": pdata.NewAttributeValueString("/api/v1/document/12345678/update/v1"), 401 "new_user_key": pdata.NewAttributeValueString("2321"), 402 "foo": pdata.NewAttributeValueString("casper the friendly ghost"), 403 }, 404 expectedAttributes: map[string]pdata.AttributeValue{ 405 "user_key": pdata.NewAttributeValueString("/api/v1/document/12345678/update/v1"), 406 "new_user_key": pdata.NewAttributeValueString("12345678"), 407 "version": pdata.NewAttributeValueString("v1"), 408 "foo": pdata.NewAttributeValueString("casper the friendly ghost"), 409 }, 410 }, 411 } 412 413 cfg := &Settings{ 414 Actions: []ActionKeyValue{ 415 416 {Key: "user_key", RegexPattern: "^\\/api\\/v1\\/document\\/(?P<new_user_key>.*)\\/update\\/(?P<version>.*)$", Action: EXTRACT}, 417 }, 418 } 419 420 ap, err := NewAttrProc(cfg) 421 require.Nil(t, err) 422 require.NotNil(t, ap) 423 424 for _, tt := range testCases { 425 runIndividualTestCase(t, tt, ap) 426 } 427} 428 429func TestAttributes_UpsertFromAttribute(t *testing.T) { 430 431 testCases := []testCase{ 432 // Ensure `new_user_key` is not set for spans with no attributes. 433 { 434 name: "UpsertEmptyAttributes", 435 inputAttributes: map[string]pdata.AttributeValue{}, 436 expectedAttributes: map[string]pdata.AttributeValue{}, 437 }, 438 // Ensure `new_user_key` is not inserted for spans with missing attribute `user_key`. 439 { 440 name: "UpsertFromAttributeNoExist", 441 inputAttributes: map[string]pdata.AttributeValue{ 442 "boo": pdata.NewAttributeValueString("ghosts are scary"), 443 }, 444 expectedAttributes: map[string]pdata.AttributeValue{ 445 "boo": pdata.NewAttributeValueString("ghosts are scary"), 446 }, 447 }, 448 // Ensure `new_user_key` is inserted for spans with attribute `user_key`. 449 { 450 name: "UpsertFromAttributeExistsInsert", 451 inputAttributes: map[string]pdata.AttributeValue{ 452 "user_key": pdata.NewAttributeValueInt(2245), 453 "foo": pdata.NewAttributeValueString("casper the friendly ghost"), 454 }, 455 expectedAttributes: map[string]pdata.AttributeValue{ 456 "user_key": pdata.NewAttributeValueInt(2245), 457 "new_user_key": pdata.NewAttributeValueInt(2245), 458 "foo": pdata.NewAttributeValueString("casper the friendly ghost"), 459 }, 460 }, 461 // Ensure `new_user_key` is updated for spans with attribute `user_key`. 462 { 463 name: "UpsertFromAttributeExistsUpdate", 464 inputAttributes: map[string]pdata.AttributeValue{ 465 "user_key": pdata.NewAttributeValueInt(2245), 466 "new_user_key": pdata.NewAttributeValueInt(5422), 467 "foo": pdata.NewAttributeValueString("casper the friendly ghost"), 468 }, 469 expectedAttributes: map[string]pdata.AttributeValue{ 470 "user_key": pdata.NewAttributeValueInt(2245), 471 "new_user_key": pdata.NewAttributeValueInt(2245), 472 "foo": pdata.NewAttributeValueString("casper the friendly ghost"), 473 }, 474 }, 475 } 476 477 cfg := &Settings{ 478 Actions: []ActionKeyValue{ 479 {Key: "new_user_key", Action: UPSERT, FromAttribute: "user_key"}, 480 }, 481 } 482 483 ap, err := NewAttrProc(cfg) 484 require.Nil(t, err) 485 require.NotNil(t, ap) 486 487 for _, tt := range testCases { 488 runIndividualTestCase(t, tt, ap) 489 } 490} 491 492func TestAttributes_Delete(t *testing.T) { 493 testCases := []testCase{ 494 // Ensure the span contains no changes. 495 { 496 name: "DeleteEmptyAttributes", 497 inputAttributes: map[string]pdata.AttributeValue{}, 498 expectedAttributes: map[string]pdata.AttributeValue{}, 499 }, 500 // Ensure the span contains no changes because the key doesn't exist. 501 { 502 name: "DeleteAttributeNoExist", 503 inputAttributes: map[string]pdata.AttributeValue{ 504 "boo": pdata.NewAttributeValueString("ghosts are scary"), 505 }, 506 expectedAttributes: map[string]pdata.AttributeValue{ 507 "boo": pdata.NewAttributeValueString("ghosts are scary"), 508 }, 509 }, 510 // Ensure `duplicate_key` is deleted for spans with the attribute set. 511 { 512 name: "DeleteAttributeExists", 513 inputAttributes: map[string]pdata.AttributeValue{ 514 "duplicate_key": pdata.NewAttributeValueDouble(3245.6), 515 "original_key": pdata.NewAttributeValueDouble(3245.6), 516 }, 517 expectedAttributes: map[string]pdata.AttributeValue{ 518 "original_key": pdata.NewAttributeValueDouble(3245.6), 519 }, 520 }, 521 } 522 523 cfg := &Settings{ 524 Actions: []ActionKeyValue{ 525 {Key: "duplicate_key", Action: DELETE}, 526 }, 527 } 528 529 ap, err := NewAttrProc(cfg) 530 require.Nil(t, err) 531 require.NotNil(t, ap) 532 533 for _, tt := range testCases { 534 runIndividualTestCase(t, tt, ap) 535 } 536} 537 538func TestAttributes_HashValue(t *testing.T) { 539 540 intVal := int64(24) 541 intBytes := make([]byte, int64ByteSize) 542 binary.LittleEndian.PutUint64(intBytes, uint64(intVal)) 543 544 doubleVal := 2.4 545 doubleBytes := make([]byte, float64ByteSize) 546 binary.LittleEndian.PutUint64(doubleBytes, math.Float64bits(doubleVal)) 547 548 testCases := []testCase{ 549 // Ensure no changes to the span as there is no attributes map. 550 { 551 name: "HashNoAttributes", 552 inputAttributes: map[string]pdata.AttributeValue{}, 553 expectedAttributes: map[string]pdata.AttributeValue{}, 554 }, 555 // Ensure no changes to the span as the key does not exist. 556 { 557 name: "HashKeyNoExist", 558 inputAttributes: map[string]pdata.AttributeValue{ 559 "boo": pdata.NewAttributeValueString("foo"), 560 }, 561 expectedAttributes: map[string]pdata.AttributeValue{ 562 "boo": pdata.NewAttributeValueString("foo"), 563 }, 564 }, 565 // Ensure string data types are hashed correctly 566 { 567 name: "HashString", 568 inputAttributes: map[string]pdata.AttributeValue{ 569 "updateme": pdata.NewAttributeValueString("foo"), 570 }, 571 expectedAttributes: map[string]pdata.AttributeValue{ 572 "updateme": pdata.NewAttributeValueString(sha1Hash([]byte("foo"))), 573 }, 574 }, 575 // Ensure int data types are hashed correctly 576 { 577 name: "HashInt", 578 inputAttributes: map[string]pdata.AttributeValue{ 579 "updateme": pdata.NewAttributeValueInt(intVal), 580 }, 581 expectedAttributes: map[string]pdata.AttributeValue{ 582 "updateme": pdata.NewAttributeValueString(sha1Hash(intBytes)), 583 }, 584 }, 585 // Ensure double data types are hashed correctly 586 { 587 name: "HashDouble", 588 inputAttributes: map[string]pdata.AttributeValue{ 589 "updateme": pdata.NewAttributeValueDouble(doubleVal), 590 }, 591 expectedAttributes: map[string]pdata.AttributeValue{ 592 "updateme": pdata.NewAttributeValueString(sha1Hash(doubleBytes)), 593 }, 594 }, 595 // Ensure bool data types are hashed correctly 596 { 597 name: "HashBoolTrue", 598 inputAttributes: map[string]pdata.AttributeValue{ 599 "updateme": pdata.NewAttributeValueBool(true), 600 }, 601 expectedAttributes: map[string]pdata.AttributeValue{ 602 "updateme": pdata.NewAttributeValueString(sha1Hash([]byte{1})), 603 }, 604 }, 605 // Ensure bool data types are hashed correctly 606 { 607 name: "HashBoolFalse", 608 inputAttributes: map[string]pdata.AttributeValue{ 609 "updateme": pdata.NewAttributeValueBool(false), 610 }, 611 expectedAttributes: map[string]pdata.AttributeValue{ 612 "updateme": pdata.NewAttributeValueString(sha1Hash([]byte{0})), 613 }, 614 }, 615 } 616 617 cfg := &Settings{ 618 Actions: []ActionKeyValue{ 619 {Key: "updateme", Action: HASH}, 620 }, 621 } 622 623 ap, err := NewAttrProc(cfg) 624 require.Nil(t, err) 625 require.NotNil(t, ap) 626 627 for _, tt := range testCases { 628 runIndividualTestCase(t, tt, ap) 629 } 630} 631 632func TestAttributes_FromAttributeNoChange(t *testing.T) { 633 tc := testCase{ 634 name: "FromAttributeNoChange", 635 inputAttributes: map[string]pdata.AttributeValue{ 636 "boo": pdata.NewAttributeValueString("ghosts are scary"), 637 }, 638 expectedAttributes: map[string]pdata.AttributeValue{ 639 "boo": pdata.NewAttributeValueString("ghosts are scary"), 640 }, 641 } 642 643 cfg := &Settings{ 644 Actions: []ActionKeyValue{ 645 {Key: "boo", Action: INSERT, FromAttribute: "boo"}, 646 {Key: "boo", Action: UPDATE, FromAttribute: "boo"}, 647 {Key: "boo", Action: UPSERT, FromAttribute: "boo"}, 648 }, 649 } 650 651 ap, err := NewAttrProc(cfg) 652 require.Nil(t, err) 653 require.NotNil(t, ap) 654 655 runIndividualTestCase(t, tc, ap) 656} 657 658func TestAttributes_Ordering(t *testing.T) { 659 testCases := []testCase{ 660 // For this example, the operations performed are 661 // 1. insert `operation`: `default` 662 // 2. insert `svc.operation`: `default` 663 // 3. delete `operation`. 664 { 665 name: "OrderingApplyAllSteps", 666 inputAttributes: map[string]pdata.AttributeValue{ 667 "foo": pdata.NewAttributeValueString("casper the friendly ghost"), 668 }, 669 expectedAttributes: map[string]pdata.AttributeValue{ 670 "foo": pdata.NewAttributeValueString("casper the friendly ghost"), 671 "svc.operation": pdata.NewAttributeValueString("default"), 672 }, 673 }, 674 // For this example, the operations performed are 675 // 1. do nothing for the first action of insert `operation`: `default` 676 // 2. insert `svc.operation`: `arithmetic` 677 // 3. delete `operation`. 678 { 679 name: "OrderingOperationExists", 680 inputAttributes: map[string]pdata.AttributeValue{ 681 "foo": pdata.NewAttributeValueString("casper the friendly ghost"), 682 "operation": pdata.NewAttributeValueString("arithmetic"), 683 }, 684 expectedAttributes: map[string]pdata.AttributeValue{ 685 "foo": pdata.NewAttributeValueString("casper the friendly ghost"), 686 "svc.operation": pdata.NewAttributeValueString("arithmetic"), 687 }, 688 }, 689 690 // For this example, the operations performed are 691 // 1. insert `operation`: `default` 692 // 2. update `svc.operation` to `default` 693 // 3. delete `operation`. 694 { 695 name: "OrderingSvcOperationExists", 696 inputAttributes: map[string]pdata.AttributeValue{ 697 "foo": pdata.NewAttributeValueString("casper the friendly ghost"), 698 "svc.operation": pdata.NewAttributeValueString("some value"), 699 }, 700 expectedAttributes: map[string]pdata.AttributeValue{ 701 "foo": pdata.NewAttributeValueString("casper the friendly ghost"), 702 "svc.operation": pdata.NewAttributeValueString("default"), 703 }, 704 }, 705 706 // For this example, the operations performed are 707 // 1. do nothing for the first action of insert `operation`: `default` 708 // 2. update `svc.operation` to `arithmetic` 709 // 3. delete `operation`. 710 { 711 name: "OrderingBothAttributesExist", 712 inputAttributes: map[string]pdata.AttributeValue{ 713 "foo": pdata.NewAttributeValueString("casper the friendly ghost"), 714 "operation": pdata.NewAttributeValueString("arithmetic"), 715 "svc.operation": pdata.NewAttributeValueString("add"), 716 }, 717 expectedAttributes: map[string]pdata.AttributeValue{ 718 "foo": pdata.NewAttributeValueString("casper the friendly ghost"), 719 "svc.operation": pdata.NewAttributeValueString("arithmetic"), 720 }, 721 }, 722 } 723 724 cfg := &Settings{ 725 Actions: []ActionKeyValue{ 726 {Key: "operation", Action: INSERT, Value: "default"}, 727 {Key: "svc.operation", Action: UPSERT, FromAttribute: "operation"}, 728 {Key: "operation", Action: DELETE}, 729 }, 730 } 731 732 ap, err := NewAttrProc(cfg) 733 require.Nil(t, err) 734 require.NotNil(t, ap) 735 736 for _, tt := range testCases { 737 runIndividualTestCase(t, tt, ap) 738 } 739} 740 741func TestInvalidConfig(t *testing.T) { 742 testcase := []struct { 743 name string 744 actionLists []ActionKeyValue 745 errorString string 746 }{ 747 { 748 name: "missing key", 749 actionLists: []ActionKeyValue{ 750 {Key: "one", Action: DELETE}, 751 {Key: "", Value: 123, Action: UPSERT}, 752 }, 753 errorString: "error creating AttrProc due to missing required field \"key\" at the 1-th actions", 754 }, 755 { 756 name: "invalid action", 757 actionLists: []ActionKeyValue{ 758 {Key: "invalid", Action: "invalid"}, 759 }, 760 errorString: "error creating AttrProc due to unsupported action \"invalid\" at the 0-th actions", 761 }, 762 { 763 name: "unsupported value", 764 actionLists: []ActionKeyValue{ 765 {Key: "UnsupportedValue", Value: []int{}, Action: UPSERT}, 766 }, 767 errorString: "error unsupported value type \"[]int\"", 768 }, 769 { 770 name: "missing value or from attribute", 771 actionLists: []ActionKeyValue{ 772 {Key: "MissingValueFromAttributes", Action: INSERT}, 773 }, 774 errorString: "error creating AttrProc. Either field \"value\" or \"from_attribute\" setting must be specified for 0-th action", 775 }, 776 { 777 name: "both set value and from attribute", 778 actionLists: []ActionKeyValue{ 779 {Key: "BothSet", Value: 123, FromAttribute: "aa", Action: UPSERT}, 780 }, 781 errorString: "error creating AttrProc due to both fields \"value\" and \"from_attribute\" being set at the 0-th actions", 782 }, 783 { 784 name: "pattern shouldn't be specified", 785 actionLists: []ActionKeyValue{ 786 {Key: "key", RegexPattern: "(?P<operation_website>.*?)$", FromAttribute: "aa", Action: INSERT}, 787 }, 788 errorString: "error creating AttrProc. Action \"insert\" does not use the \"pattern\" field. This must not be specified for 0-th action", 789 }, 790 { 791 name: "missing rule for extract", 792 actionLists: []ActionKeyValue{ 793 {Key: "aa", Action: EXTRACT}, 794 }, 795 errorString: "error creating AttrProc due to missing required field \"pattern\" for action \"extract\" at the 0-th action", 796 }, 797 {name: "set value for extract", 798 actionLists: []ActionKeyValue{ 799 {Key: "Key", RegexPattern: "(?P<operation_website>.*?)$", Value: "value", Action: EXTRACT}, 800 }, 801 errorString: "error creating AttrProc. Action \"extract\" does not use \"value\" or \"from_attribute\" field. These must not be specified for 0-th action", 802 }, 803 { 804 name: "set from attribute for extract", 805 actionLists: []ActionKeyValue{ 806 {Key: "key", RegexPattern: "(?P<operation_website>.*?)$", FromAttribute: "aa", Action: EXTRACT}, 807 }, 808 errorString: "error creating AttrProc. Action \"extract\" does not use \"value\" or \"from_attribute\" field. These must not be specified for 0-th action", 809 }, 810 { 811 name: "invalid regex", 812 actionLists: []ActionKeyValue{ 813 {Key: "aa", RegexPattern: "(?P<invalid.regex>.*?)$", Action: EXTRACT}, 814 }, 815 errorString: "error creating AttrProc. Field \"pattern\" has invalid pattern: \"(?P<invalid.regex>.*?)$\" to be set at the 0-th actions", 816 }, 817 { 818 name: "delete with regex", 819 actionLists: []ActionKeyValue{ 820 {RegexPattern: "(?P<operation_website>.*?)$", Key: "ab", Action: DELETE}, 821 }, 822 errorString: "error creating AttrProc. Action \"delete\" does not use \"value\", \"pattern\" or \"from_attribute\" field. These must not be specified for 0-th action", 823 }, 824 { 825 name: "regex with unnamed capture group", 826 actionLists: []ActionKeyValue{ 827 {Key: "aa", RegexPattern: ".*$", Action: EXTRACT}, 828 }, 829 errorString: "error creating AttrProc. Field \"pattern\" contains no named matcher groups at the 0-th actions", 830 }, 831 { 832 name: "regex with one unnamed capture groups", 833 actionLists: []ActionKeyValue{ 834 {Key: "aa", RegexPattern: "^\\/api\\/v1\\/document\\/(?P<new_user_key>.*)\\/update\\/(.*)$", Action: EXTRACT}, 835 }, 836 errorString: "error creating AttrProc. Field \"pattern\" contains at least one unnamed matcher group at the 0-th actions", 837 }, 838 } 839 840 for _, tc := range testcase { 841 t.Run(tc.name, func(t *testing.T) { 842 ap, err := NewAttrProc(&Settings{Actions: tc.actionLists}) 843 assert.Nil(t, ap) 844 assert.EqualValues(t, errors.New(tc.errorString), err) 845 }) 846 } 847} 848 849func TestValidConfiguration(t *testing.T) { 850 cfg := &Settings{ 851 Actions: []ActionKeyValue{ 852 {Key: "one", Action: "Delete"}, 853 {Key: "two", Value: 123, Action: "INSERT"}, 854 {Key: "three", FromAttribute: "two", Action: "upDaTE"}, 855 {Key: "five", FromAttribute: "two", Action: "upsert"}, 856 {Key: "two", RegexPattern: "^\\/api\\/v1\\/document\\/(?P<documentId>.*)\\/update$", Action: "EXTRact"}, 857 }, 858 } 859 ap, err := NewAttrProc(cfg) 860 require.NoError(t, err) 861 862 av := pdata.NewAttributeValueInt(123) 863 compiledRegex := regexp.MustCompile(`^\/api\/v1\/document\/(?P<documentId>.*)\/update$`) 864 assert.Equal(t, []attributeAction{ 865 {Key: "one", Action: DELETE}, 866 {Key: "two", Action: INSERT, 867 AttributeValue: &av, 868 }, 869 {Key: "three", FromAttribute: "two", Action: UPDATE}, 870 {Key: "five", FromAttribute: "two", Action: UPSERT}, 871 {Key: "two", Regex: compiledRegex, AttrNames: []string{"", "documentId"}, Action: EXTRACT}, 872 }, ap.actions) 873 874} 875 876func sha1Hash(b []byte) string { 877 // #nosec 878 h := sha1.New() 879 h.Write(b) 880 return fmt.Sprintf("%x", h.Sum(nil)) 881} 882