github.com/newrelic/go-agent@v3.26.0+incompatible/internal/txn_cross_process_test.go (about) 1 // Copyright 2020 New Relic Corporation. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package internal 5 6 import ( 7 "fmt" 8 "net/http" 9 "reflect" 10 "testing" 11 "time" 12 13 "github.com/newrelic/go-agent/internal/cat" 14 ) 15 16 var ( 17 replyAccountOne = &ConnectReply{ 18 CrossProcessID: "1#1", 19 EncodingKey: "foo", 20 TrustedAccounts: map[int]struct{}{1: {}}, 21 } 22 23 replyAccountTwo = &ConnectReply{ 24 CrossProcessID: "2#2", 25 EncodingKey: "foo", 26 TrustedAccounts: map[int]struct{}{2: {}}, 27 } 28 29 requestEmpty = newRequest().Request 30 requestCATOne = newRequest().withCAT(newTxnCrossProcessFromConnectReply(replyAccountOne), "txn", "app").Request 31 requestSyntheticsOne = newRequest().withSynthetics(1, "foo").Request 32 requestCATSyntheticsOne = newRequest().withCAT(newTxnCrossProcessFromConnectReply(replyAccountOne), "txn", "app").withSynthetics(1, "foo").Request 33 ) 34 35 func mustObfuscate(input, encodingKey string) string { 36 output, err := Obfuscate([]byte(input), []byte(encodingKey)) 37 if err != nil { 38 panic(err) 39 } 40 41 return string(output) 42 } 43 44 func newTxnCrossProcessFromConnectReply(reply *ConnectReply) *TxnCrossProcess { 45 txp := &TxnCrossProcess{GUID: "abcdefgh"} 46 txp.Init(true, false, reply) 47 48 return txp 49 } 50 51 type request struct { 52 *http.Request 53 } 54 55 func newRequest() *request { 56 req, err := http.NewRequest("GET", "http://foo.bar", nil) 57 if err != nil { 58 panic(err) 59 } 60 61 return &request{Request: req} 62 } 63 64 func (req *request) withCAT(txp *TxnCrossProcess, txnName, appName string) *request { 65 metadata, err := txp.CreateCrossProcessMetadata(txnName, appName) 66 if err != nil { 67 panic(err) 68 } 69 70 for k, values := range MetadataToHTTPHeader(metadata) { 71 for _, v := range values { 72 req.Header.Add(k, v) 73 } 74 } 75 76 return req 77 } 78 79 func (req *request) withSynthetics(account int, encodingKey string) *request { 80 header := fmt.Sprintf(`[1,%d,"resource","job","monitor"]`, account) 81 obfuscated, err := Obfuscate([]byte(header), []byte(encodingKey)) 82 if err != nil { 83 panic(err) 84 } 85 86 req.Header.Add(cat.NewRelicSyntheticsName, string(obfuscated)) 87 return req 88 } 89 90 func TestTxnCrossProcessInit(t *testing.T) { 91 for _, tc := range []struct { 92 name string 93 enabled bool 94 reply *ConnectReply 95 req *http.Request 96 expected *TxnCrossProcess 97 expectedError bool 98 }{ 99 { 100 name: "disabled", 101 enabled: false, 102 reply: replyAccountOne, 103 req: nil, 104 expected: &TxnCrossProcess{ 105 CrossProcessID: []byte("1#1"), 106 EncodingKey: []byte("foo"), 107 Enabled: false, 108 TrustedAccounts: map[int]struct{}{1: {}}, 109 }, 110 expectedError: false, 111 }, 112 { 113 name: "normal connect reply without a request", 114 enabled: true, 115 reply: replyAccountOne, 116 req: nil, 117 expected: &TxnCrossProcess{ 118 CrossProcessID: []byte("1#1"), 119 EncodingKey: []byte("foo"), 120 Enabled: true, 121 TrustedAccounts: map[int]struct{}{1: {}}, 122 }, 123 expectedError: false, 124 }, 125 { 126 name: "normal connect reply with a request without headers", 127 enabled: true, 128 reply: replyAccountOne, 129 req: requestEmpty, 130 expected: &TxnCrossProcess{ 131 CrossProcessID: []byte("1#1"), 132 EncodingKey: []byte("foo"), 133 Enabled: true, 134 TrustedAccounts: map[int]struct{}{1: {}}, 135 }, 136 expectedError: false, 137 }, 138 { 139 name: "normal connect reply with a request with untrusted headers", 140 enabled: true, 141 reply: replyAccountTwo, 142 req: requestCATOne, 143 expected: &TxnCrossProcess{ 144 CrossProcessID: []byte("2#2"), 145 EncodingKey: []byte("foo"), 146 Enabled: true, 147 TrustedAccounts: map[int]struct{}{2: {}}, 148 }, 149 expectedError: true, 150 }, 151 { 152 name: "normal connect reply with a request with trusted headers", 153 enabled: true, 154 reply: replyAccountOne, 155 req: requestCATOne, 156 expected: &TxnCrossProcess{ 157 CrossProcessID: []byte("1#1"), 158 EncodingKey: []byte("foo"), 159 Enabled: true, 160 TrustedAccounts: map[int]struct{}{1: {}}, 161 }, 162 expectedError: false, 163 }, 164 } { 165 actual := &TxnCrossProcess{} 166 167 id := "" 168 txnData := "" 169 synthetics := "" 170 if tc.req != nil { 171 id = tc.req.Header.Get(cat.NewRelicIDName) 172 txnData = tc.req.Header.Get(cat.NewRelicTxnName) 173 synthetics = tc.req.Header.Get(cat.NewRelicSyntheticsName) 174 } 175 176 actual.Init(tc.enabled, false, tc.reply) 177 err := actual.handleInboundRequestHeaders(CrossProcessMetadata{id, txnData, synthetics}) 178 179 if tc.expectedError == false && err != nil { 180 t.Errorf("%s: unexpected error returned from Init: %v", tc.name, err) 181 } else if tc.expectedError && err == nil { 182 t.Errorf("%s: no error returned from Init when one was expected", tc.name) 183 } 184 185 if !reflect.DeepEqual(actual.EncodingKey, tc.expected.EncodingKey) { 186 t.Errorf("%s: EncodingKey mismatch: expected=%v; got=%v", tc.name, tc.expected.EncodingKey, actual.EncodingKey) 187 } 188 189 if !reflect.DeepEqual(actual.CrossProcessID, tc.expected.CrossProcessID) { 190 t.Errorf("%s: CrossProcessID mismatch: expected=%v; got=%v", tc.name, tc.expected.CrossProcessID, actual.CrossProcessID) 191 } 192 193 if !reflect.DeepEqual(actual.TrustedAccounts, tc.expected.TrustedAccounts) { 194 t.Errorf("%s: TrustedAccounts mismatch: expected=%v; got=%v", tc.name, tc.expected.TrustedAccounts, actual.TrustedAccounts) 195 } 196 197 if actual.Enabled != tc.expected.Enabled { 198 t.Errorf("%s: Enabled mismatch: expected=%v; got=%v", tc.name, tc.expected.Enabled, actual.Enabled) 199 } 200 } 201 } 202 203 func TestTxnCrossProcessCreateCrossProcessMetadata(t *testing.T) { 204 for _, tc := range []struct { 205 name string 206 enabled bool 207 reply *ConnectReply 208 req *http.Request 209 txnName string 210 appName string 211 expectedError bool 212 expectedMetadata CrossProcessMetadata 213 }{ 214 { 215 name: "disabled, no header", 216 enabled: false, 217 reply: replyAccountOne, 218 req: nil, 219 txnName: "txn", 220 appName: "app", 221 expectedError: false, 222 expectedMetadata: CrossProcessMetadata{}, 223 }, 224 { 225 name: "disabled, header", 226 enabled: false, 227 reply: replyAccountOne, 228 req: requestCATOne, 229 txnName: "txn", 230 appName: "app", 231 expectedError: false, 232 expectedMetadata: CrossProcessMetadata{}, 233 }, 234 { 235 name: "disabled, synthetics", 236 enabled: false, 237 reply: replyAccountOne, 238 req: requestSyntheticsOne, 239 txnName: "txn", 240 appName: "app", 241 expectedError: false, 242 expectedMetadata: CrossProcessMetadata{ 243 Synthetics: mustObfuscate(`[1,1,"resource","job","monitor"]`, "foo"), 244 }, 245 }, 246 { 247 name: "disabled, header, synthetics", 248 enabled: false, 249 reply: replyAccountOne, 250 req: requestCATSyntheticsOne, 251 txnName: "txn", 252 appName: "app", 253 expectedError: false, 254 expectedMetadata: CrossProcessMetadata{ 255 Synthetics: mustObfuscate(`[1,1,"resource","job","monitor"]`, "foo"), 256 }, 257 }, 258 { 259 name: "enabled, no header, no synthetics", 260 enabled: true, 261 reply: replyAccountOne, 262 req: requestEmpty, 263 txnName: "txn", 264 appName: "app", 265 expectedError: false, 266 expectedMetadata: CrossProcessMetadata{ 267 ID: mustObfuscate(`1#1`, "foo"), 268 TxnData: mustObfuscate(`["00000000",false,"00000000","b95be233"]`, "foo"), 269 }, 270 }, 271 { 272 name: "enabled, no header, synthetics", 273 enabled: true, 274 reply: replyAccountOne, 275 req: requestSyntheticsOne, 276 txnName: "txn", 277 appName: "app", 278 expectedError: false, 279 expectedMetadata: CrossProcessMetadata{ 280 ID: mustObfuscate(`1#1`, "foo"), 281 TxnData: mustObfuscate(`["00000000",false,"00000000","b95be233"]`, "foo"), 282 Synthetics: mustObfuscate(`[1,1,"resource","job","monitor"]`, "foo"), 283 }, 284 }, 285 { 286 name: "enabled, header, no synthetics", 287 enabled: true, 288 reply: replyAccountOne, 289 req: requestCATOne, 290 txnName: "txn", 291 appName: "app", 292 expectedError: false, 293 expectedMetadata: CrossProcessMetadata{ 294 ID: mustObfuscate(`1#1`, "foo"), 295 TxnData: mustObfuscate(`["00000000",false,"abcdefgh","cbec2654"]`, "foo"), 296 }, 297 }, 298 { 299 name: "enabled, header, synthetics", 300 enabled: true, 301 reply: replyAccountOne, 302 req: requestCATSyntheticsOne, 303 txnName: "txn", 304 appName: "app", 305 expectedError: false, 306 expectedMetadata: CrossProcessMetadata{ 307 ID: mustObfuscate(`1#1`, "foo"), 308 TxnData: mustObfuscate(`["00000000",false,"abcdefgh","cbec2654"]`, "foo"), 309 Synthetics: mustObfuscate(`[1,1,"resource","job","monitor"]`, "foo"), 310 }, 311 }, 312 } { 313 txp := &TxnCrossProcess{GUID: "00000000"} 314 txp.Init(tc.enabled, false, tc.reply) 315 if nil != tc.req { 316 txp.InboundHTTPRequest(tc.req.Header) 317 } 318 metadata, err := txp.CreateCrossProcessMetadata(tc.txnName, tc.appName) 319 320 if tc.expectedError == false && err != nil { 321 t.Errorf("%s: unexpected error returned from CreateCrossProcessMetadata: %v", tc.name, err) 322 } else if tc.expectedError && err == nil { 323 t.Errorf("%s: no error returned from CreateCrossProcessMetadata when one was expected", tc.name) 324 } 325 326 if !reflect.DeepEqual(tc.expectedMetadata, metadata) { 327 t.Errorf("%s: metadata mismatch: expected=%v; got=%v", tc.name, tc.expectedMetadata, metadata) 328 } 329 330 // Ensure that a path hash was generated if TxnData was created. 331 if metadata.TxnData != "" && txp.PathHash == "" { 332 t.Errorf("%s: no path hash generated", tc.name) 333 } 334 } 335 } 336 337 func TestTxnCrossProcessCreateCrossProcessMetadataError(t *testing.T) { 338 // Ensure errors bubble back up from deeper within our obfuscation code. 339 // It's likely impossible to get outboundTxnData() to fail, but we can get 340 // outboundID() to fail by having an empty encoding key. 341 txp := &TxnCrossProcess{Enabled: true} 342 metadata, err := txp.CreateCrossProcessMetadata("txn", "app") 343 if metadata.ID != "" || metadata.TxnData != "" || metadata.Synthetics != "" { 344 t.Errorf("one or more metadata fields were set unexpectedly; got %v", metadata) 345 } 346 if err == nil { 347 t.Errorf("did not get expected error with an empty encoding key") 348 } 349 350 // Test the above with Synthetics support to ensure that the Synthetics 351 // payload is still set. 352 txp = &TxnCrossProcess{ 353 Enabled: true, 354 Type: txnCrossProcessSynthetics, 355 SyntheticsHeader: "foo", 356 // This won't be actually examined, but can't be nil for the IsSynthetics() 357 // check to pass. 358 Synthetics: &cat.SyntheticsHeader{}, 359 } 360 metadata, err = txp.CreateCrossProcessMetadata("txn", "app") 361 if metadata.ID != "" || metadata.TxnData != "" { 362 t.Errorf("one or more metadata fields were set unexpectedly; got %v", metadata) 363 } 364 if metadata.Synthetics != "foo" { 365 t.Errorf("unexpected synthetics metadata: expected %s; got %s", "foo", metadata.Synthetics) 366 } 367 if err == nil { 368 t.Errorf("did not get expected error with an empty encoding key") 369 } 370 } 371 372 func TestTxnCrossProcessFinalise(t *testing.T) { 373 // No CAT. 374 txp := &TxnCrossProcess{} 375 txp.Init(true, false, replyAccountOne) 376 if err := txp.Finalise("txn", "app"); err != nil { 377 t.Errorf("unexpected error: %v", err) 378 } 379 if txp.PathHash != "" { 380 t.Errorf("unexpected path hash: %s", txp.PathHash) 381 } 382 383 // CAT, but no path hash. 384 txp = &TxnCrossProcess{} 385 txp.Init(true, false, replyAccountOne) 386 txp.InboundHTTPRequest(requestCATOne.Header) 387 if txp.PathHash != "" { 388 t.Errorf("unexpected path hash: %s", txp.PathHash) 389 } 390 if err := txp.Finalise("txn", "app"); err != nil { 391 t.Errorf("unexpected error: %v", err) 392 } 393 if txp.PathHash == "" { 394 t.Error("unexpected lack of path hash") 395 } 396 397 // CAT, with a path hash. 398 txp = &TxnCrossProcess{} 399 txp.Init(true, false, replyAccountOne) 400 txp.InboundHTTPRequest(requestCATOne.Header) 401 txp.CreateCrossProcessMetadata("txn", "app") 402 if txp.PathHash == "" { 403 t.Error("unexpected lack of path hash") 404 } 405 if err := txp.Finalise("txn", "app"); err != nil { 406 t.Errorf("unexpected error: %v", err) 407 } 408 if txp.PathHash == "" { 409 t.Error("unexpected lack of path hash") 410 } 411 } 412 413 func TestTxnCrossProcessIsInbound(t *testing.T) { 414 for _, tc := range []struct { 415 txpType uint8 416 expected bool 417 }{ 418 {0, false}, 419 {txnCrossProcessSynthetics, false}, 420 {txnCrossProcessInbound, true}, 421 {txnCrossProcessOutbound, false}, 422 {txnCrossProcessSynthetics | txnCrossProcessInbound, true}, 423 {txnCrossProcessSynthetics | txnCrossProcessOutbound, false}, 424 {txnCrossProcessInbound | txnCrossProcessOutbound, true}, 425 {txnCrossProcessSynthetics | txnCrossProcessInbound | txnCrossProcessOutbound, true}, 426 } { 427 txp := &TxnCrossProcess{Type: tc.txpType} 428 actual := txp.IsInbound() 429 if actual != tc.expected { 430 t.Errorf("unexpected IsInbound result for input %d: expected=%v; got=%v", tc.txpType, tc.expected, actual) 431 } 432 } 433 } 434 435 func TestTxnCrossProcessIsOutbound(t *testing.T) { 436 for _, tc := range []struct { 437 txpType uint8 438 expected bool 439 }{ 440 {0, false}, 441 {txnCrossProcessSynthetics, false}, 442 {txnCrossProcessInbound, false}, 443 {txnCrossProcessOutbound, true}, 444 {txnCrossProcessSynthetics | txnCrossProcessInbound, false}, 445 {txnCrossProcessSynthetics | txnCrossProcessOutbound, true}, 446 {txnCrossProcessInbound | txnCrossProcessOutbound, true}, 447 {txnCrossProcessSynthetics | txnCrossProcessInbound | txnCrossProcessOutbound, true}, 448 } { 449 txp := &TxnCrossProcess{Type: tc.txpType} 450 actual := txp.IsOutbound() 451 if actual != tc.expected { 452 t.Errorf("unexpected IsOutbound result for input %d: expected=%v; got=%v", tc.txpType, tc.expected, actual) 453 } 454 } 455 } 456 457 func TestTxnCrossProcessIsSynthetics(t *testing.T) { 458 for _, tc := range []struct { 459 txpType uint8 460 synthetics *cat.SyntheticsHeader 461 expected bool 462 }{ 463 {0, nil, false}, 464 {txnCrossProcessSynthetics, nil, false}, 465 {txnCrossProcessInbound, nil, false}, 466 {txnCrossProcessOutbound, nil, false}, 467 {txnCrossProcessSynthetics | txnCrossProcessInbound, nil, false}, 468 {txnCrossProcessSynthetics | txnCrossProcessOutbound, nil, false}, 469 {txnCrossProcessInbound | txnCrossProcessOutbound, nil, false}, 470 {txnCrossProcessSynthetics | txnCrossProcessInbound | txnCrossProcessOutbound, nil, false}, 471 {0, &cat.SyntheticsHeader{}, false}, 472 {txnCrossProcessSynthetics, &cat.SyntheticsHeader{}, true}, 473 {txnCrossProcessInbound, &cat.SyntheticsHeader{}, false}, 474 {txnCrossProcessOutbound, &cat.SyntheticsHeader{}, false}, 475 {txnCrossProcessSynthetics | txnCrossProcessInbound, &cat.SyntheticsHeader{}, true}, 476 {txnCrossProcessSynthetics | txnCrossProcessOutbound, &cat.SyntheticsHeader{}, true}, 477 {txnCrossProcessInbound | txnCrossProcessOutbound, &cat.SyntheticsHeader{}, false}, 478 {txnCrossProcessSynthetics | txnCrossProcessInbound | txnCrossProcessOutbound, &cat.SyntheticsHeader{}, true}, 479 } { 480 txp := &TxnCrossProcess{Type: tc.txpType, Synthetics: tc.synthetics} 481 actual := txp.IsSynthetics() 482 if actual != tc.expected { 483 t.Errorf("unexpected IsSynthetics result for input %d and %p: expected=%v; got=%v", tc.txpType, tc.synthetics, tc.expected, actual) 484 } 485 } 486 } 487 488 func TestTxnCrossProcessUsed(t *testing.T) { 489 for _, tc := range []struct { 490 txpType uint8 491 expected bool 492 }{ 493 {0, false}, 494 {txnCrossProcessSynthetics, true}, 495 {txnCrossProcessInbound, true}, 496 {txnCrossProcessOutbound, true}, 497 {txnCrossProcessSynthetics | txnCrossProcessInbound, true}, 498 {txnCrossProcessSynthetics | txnCrossProcessOutbound, true}, 499 {txnCrossProcessInbound | txnCrossProcessOutbound, true}, 500 {txnCrossProcessSynthetics | txnCrossProcessInbound | txnCrossProcessOutbound, true}, 501 } { 502 txp := &TxnCrossProcess{Type: tc.txpType} 503 actual := txp.Used() 504 if actual != tc.expected { 505 t.Errorf("unexpected Used result for input %d: expected=%v; got=%v", tc.txpType, tc.expected, actual) 506 } 507 } 508 } 509 510 func TestTxnCrossProcessSetInbound(t *testing.T) { 511 txp := &TxnCrossProcess{Type: 0} 512 513 txp.SetInbound(false) 514 if txp.IsInbound() != false { 515 t.Error("Inbound is not false after being set to false from false") 516 } 517 518 txp.SetInbound(true) 519 if txp.IsInbound() != true { 520 t.Error("Inbound is not true after being set to true from false") 521 } 522 523 txp.SetInbound(true) 524 if txp.IsInbound() != true { 525 t.Error("Inbound is not true after being set to true from true") 526 } 527 528 txp.SetInbound(false) 529 if txp.IsInbound() != false { 530 t.Error("Inbound is not false after being set to false from true") 531 } 532 } 533 534 func TestTxnCrossProcessSetOutbound(t *testing.T) { 535 txp := &TxnCrossProcess{Type: 0} 536 537 txp.SetOutbound(false) 538 if txp.IsOutbound() != false { 539 t.Error("Outbound is not false after being set to false from false") 540 } 541 542 txp.SetOutbound(true) 543 if txp.IsOutbound() != true { 544 t.Error("Outbound is not true after being set to true from false") 545 } 546 547 txp.SetOutbound(true) 548 if txp.IsOutbound() != true { 549 t.Error("Outbound is not true after being set to true from true") 550 } 551 552 txp.SetOutbound(false) 553 if txp.IsOutbound() != false { 554 t.Error("Outbound is not false after being set to false from true") 555 } 556 } 557 558 func TestTxnCrossProcessSetSynthetics(t *testing.T) { 559 // We'll always set SyntheticsHeader, since we're not really testing the full 560 // behaviour of IsSynthetics() here. 561 txp := &TxnCrossProcess{ 562 Type: 0, 563 Synthetics: &cat.SyntheticsHeader{}, 564 } 565 566 txp.SetSynthetics(false) 567 if txp.IsSynthetics() != false { 568 t.Error("Synthetics is not false after being set to false from false") 569 } 570 571 txp.SetSynthetics(true) 572 if txp.IsSynthetics() != true { 573 t.Error("Synthetics is not true after being set to true from false") 574 } 575 576 txp.SetSynthetics(true) 577 if txp.IsSynthetics() != true { 578 t.Error("Synthetics is not true after being set to true from true") 579 } 580 581 txp.SetSynthetics(false) 582 if txp.IsSynthetics() != false { 583 t.Error("Synthetics is not false after being set to false from true") 584 } 585 } 586 587 func TestTxnCrossProcessParseAppData(t *testing.T) { 588 for _, tc := range []struct { 589 name string 590 encodingKey string 591 input string 592 expectedAppData *cat.AppDataHeader 593 expectedError bool 594 }{ 595 { 596 name: "empty string", 597 encodingKey: "foo", 598 input: "", 599 expectedAppData: nil, 600 expectedError: false, 601 }, 602 { 603 name: "invalidly encoded string", 604 encodingKey: "foo", 605 input: "xxx", 606 expectedAppData: nil, 607 expectedError: true, 608 }, 609 { 610 name: "invalid JSON", 611 encodingKey: "foo", 612 input: mustObfuscate("xxx", "foo"), 613 expectedAppData: nil, 614 expectedError: true, 615 }, 616 { 617 name: "invalid encoding key", 618 encodingKey: "foo", 619 input: mustObfuscate(`["xp","txn",1,2,3,"guid",false]`, "bar"), 620 expectedAppData: nil, 621 expectedError: true, 622 }, 623 { 624 name: "success", 625 encodingKey: "foo", 626 input: mustObfuscate(`["xp","txn",1,2,3,"guid",false]`, "foo"), 627 expectedAppData: &cat.AppDataHeader{ 628 CrossProcessID: "xp", 629 TransactionName: "txn", 630 QueueTimeInSeconds: 1, 631 ResponseTimeInSeconds: 2, 632 ContentLength: 3, 633 TransactionGUID: "guid", 634 }, 635 expectedError: false, 636 }, 637 } { 638 txp := &TxnCrossProcess{ 639 Enabled: true, 640 EncodingKey: []byte(tc.encodingKey), 641 } 642 643 actualAppData, actualErr := txp.ParseAppData(tc.input) 644 645 if tc.expectedError && actualErr == nil { 646 t.Errorf("%s: expected an error, but didn't get one", tc.name) 647 } else if tc.expectedError == false && actualErr != nil { 648 t.Errorf("%s: expected no error, but got %v", tc.name, actualErr) 649 } 650 651 if !reflect.DeepEqual(actualAppData, tc.expectedAppData) { 652 t.Errorf("%s: app data mismatched: expected=%v; got=%v", tc.name, tc.expectedAppData, actualAppData) 653 } 654 } 655 } 656 657 func TestTxnCrossProcessCreateAppData(t *testing.T) { 658 for _, tc := range []struct { 659 name string 660 enabled bool 661 crossProcessID string 662 encodingKey string 663 txnName string 664 queueTime time.Duration 665 responseTime time.Duration 666 contentLength int64 667 guid string 668 expectedAppData string 669 expectedError bool 670 }{ 671 { 672 name: "cat disabled", 673 enabled: false, 674 crossProcessID: "1#1", 675 encodingKey: "foo", 676 txnName: "txn", 677 queueTime: 1 * time.Second, 678 responseTime: 2 * time.Second, 679 contentLength: 4096, 680 guid: "", 681 expectedAppData: "", 682 expectedError: false, 683 }, 684 { 685 name: "invalid encoding key", 686 enabled: true, 687 crossProcessID: "1#1", 688 encodingKey: "", 689 txnName: "txn", 690 queueTime: 1 * time.Second, 691 responseTime: 2 * time.Second, 692 contentLength: 4096, 693 guid: "", 694 expectedAppData: "", 695 expectedError: true, 696 }, 697 { 698 name: "success", 699 enabled: true, 700 crossProcessID: "1#1", 701 encodingKey: "foo", 702 txnName: "txn", 703 queueTime: 1 * time.Second, 704 responseTime: 2 * time.Second, 705 contentLength: 4096, 706 guid: "guid", 707 expectedAppData: mustObfuscate(`["1#1","txn",1,2,4096,"guid",false]`, "foo"), 708 expectedError: false, 709 }, 710 } { 711 txp := &TxnCrossProcess{ 712 Enabled: tc.enabled, 713 EncodingKey: []byte(tc.encodingKey), 714 CrossProcessID: []byte(tc.crossProcessID), 715 GUID: tc.guid, 716 } 717 718 actualAppData, actualErr := txp.CreateAppData(tc.txnName, tc.queueTime, tc.responseTime, tc.contentLength) 719 720 if tc.expectedError && actualErr == nil { 721 t.Errorf("%s: expected an error, but didn't get one", tc.name) 722 } else if tc.expectedError == false && actualErr != nil { 723 t.Errorf("%s: expected no error, but got %v", tc.name, actualErr) 724 } 725 726 if !reflect.DeepEqual(actualAppData, tc.expectedAppData) { 727 t.Errorf("%s: app data mismatched: expected=%v; got=%v", tc.name, tc.expectedAppData, actualAppData) 728 } 729 } 730 } 731 732 func TestTxnCrossProcessHandleInboundRequestHeaders(t *testing.T) { 733 for _, tc := range []struct { 734 name string 735 enabled bool 736 reply *ConnectReply 737 metadata CrossProcessMetadata 738 expectedError bool 739 }{ 740 { 741 name: "disabled, invalid encoding key, invalid synthetics", 742 enabled: false, 743 reply: &ConnectReply{ 744 EncodingKey: "", 745 }, 746 metadata: CrossProcessMetadata{ 747 Synthetics: "foo", 748 }, 749 expectedError: true, 750 }, 751 { 752 name: "disabled, valid encoding key, invalid synthetics", 753 enabled: false, 754 reply: replyAccountOne, 755 metadata: CrossProcessMetadata{ 756 Synthetics: "foo", 757 }, 758 expectedError: true, 759 }, 760 { 761 name: "disabled, valid encoding key, valid synthetics", 762 enabled: false, 763 reply: replyAccountOne, 764 metadata: CrossProcessMetadata{ 765 Synthetics: mustObfuscate(`[1,1,"resource","job","monitor"]`, "foo"), 766 }, 767 expectedError: false, 768 }, 769 { 770 name: "enabled, invalid encoding key, valid input", 771 enabled: true, 772 reply: &ConnectReply{ 773 EncodingKey: "", 774 }, 775 metadata: CrossProcessMetadata{ 776 ID: mustObfuscate(`1#1`, "foo"), 777 TxnData: mustObfuscate(`["00000000",false,"00000000","b95be233"]`, "foo"), 778 }, 779 expectedError: true, 780 }, 781 { 782 name: "enabled, valid encoding key, invalid id", 783 enabled: true, 784 reply: replyAccountOne, 785 metadata: CrossProcessMetadata{ 786 ID: mustObfuscate(`1`, "foo"), 787 TxnData: mustObfuscate(`["00000000",false,"00000000","b95be233"]`, "foo"), 788 }, 789 expectedError: true, 790 }, 791 { 792 name: "enabled, valid encoding key, invalid txndata", 793 enabled: true, 794 reply: replyAccountOne, 795 metadata: CrossProcessMetadata{ 796 ID: mustObfuscate(`1#1`, "foo"), 797 TxnData: mustObfuscate(`["00000000",invalid,"00000000","b95be233"]`, "foo"), 798 }, 799 expectedError: true, 800 }, 801 { 802 name: "enabled, valid encoding key, valid input", 803 enabled: true, 804 reply: replyAccountOne, 805 metadata: CrossProcessMetadata{ 806 ID: mustObfuscate(`1#1`, "foo"), 807 TxnData: mustObfuscate(`["00000000",false,"00000000","b95be233"]`, "foo"), 808 }, 809 expectedError: false, 810 }, 811 } { 812 txp := &TxnCrossProcess{Enabled: tc.enabled} 813 txp.Init(tc.enabled, false, tc.reply) 814 815 err := txp.handleInboundRequestHeaders(tc.metadata) 816 if tc.expectedError && err == nil { 817 t.Errorf("%s: expected error, but didn't get one", tc.name) 818 } else if tc.expectedError == false && err != nil { 819 t.Errorf("%s: expected no error, but got %v", tc.name, err) 820 } 821 } 822 }