github.com/newrelic/go-agent@v3.26.0+incompatible/internal_distributed_trace_test.go (about) 1 // Copyright 2020 New Relic Corporation. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package newrelic 5 6 import ( 7 "encoding/base64" 8 "encoding/json" 9 "errors" 10 "fmt" 11 "net/url" 12 "reflect" 13 "strings" 14 "testing" 15 "time" 16 17 "github.com/newrelic/go-agent/internal" 18 "github.com/newrelic/go-agent/internal/crossagent" 19 ) 20 21 type PayloadTest struct { 22 V *[2]int `json:"v,omitempty"` 23 D map[string]interface{} `json:"d,omitempty"` 24 } 25 26 func distributedTracingReplyFields(reply *internal.ConnectReply) { 27 reply.AccountID = "123" 28 reply.AppID = "456" 29 reply.PrimaryAppID = "456" 30 reply.TrustedAccounts = map[int]struct{}{ 31 123: {}, 32 } 33 reply.TrustedAccountKey = "123" 34 35 reply.AdaptiveSampler = internal.SampleEverything{} 36 } 37 38 func distributedTracingReplyFieldsNeedTrustKey(reply *internal.ConnectReply) { 39 reply.AccountID = "123" 40 reply.AppID = "456" 41 reply.PrimaryAppID = "456" 42 reply.TrustedAccounts = map[int]struct{}{ 43 123: {}, 44 } 45 reply.TrustedAccountKey = "789" 46 } 47 48 func makePayload(app Application, u *url.URL) DistributedTracePayload { 49 txn := app.StartTransaction("hello", nil, nil) 50 return txn.CreateDistributedTracePayload() 51 } 52 53 func enableOldCATDisableBetterCat(cfg *Config) { 54 cfg.CrossApplicationTracer.Enabled = true 55 cfg.DistributedTracer.Enabled = false 56 } 57 58 func disableCAT(cfg *Config) { 59 cfg.CrossApplicationTracer.Enabled = false 60 cfg.DistributedTracer.Enabled = false 61 } 62 63 func enableBetterCAT(cfg *Config) { 64 cfg.CrossApplicationTracer.Enabled = false 65 cfg.DistributedTracer.Enabled = true 66 } 67 68 func disableSpanEvents(cfg *Config) { 69 cfg.CrossApplicationTracer.Enabled = false 70 cfg.DistributedTracer.Enabled = true 71 cfg.SpanEvents.Enabled = false 72 } 73 74 func disableDistributedTracerEnableSpanEvents(cfg *Config) { 75 cfg.CrossApplicationTracer.Enabled = true 76 cfg.DistributedTracer.Enabled = false 77 cfg.SpanEvents.Enabled = true 78 } 79 80 var ( 81 distributedTracingSuccessMetrics = []internal.WantMetric{ 82 {Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil}, 83 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 84 {Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil}, 85 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 86 {Name: "DurationByCaller/App/123/456/HTTP/all", Scope: "", Forced: false, Data: nil}, 87 {Name: "DurationByCaller/App/123/456/HTTP/allOther", Scope: "", Forced: false, Data: nil}, 88 {Name: "TransportDuration/App/123/456/HTTP/all", Scope: "", Forced: false, Data: nil}, 89 {Name: "TransportDuration/App/123/456/HTTP/allOther", Scope: "", Forced: false, Data: nil}, 90 {Name: "Supportability/DistributedTrace/AcceptPayload/Success", Scope: "", Forced: true, Data: singleCount}, 91 } 92 ) 93 94 func TestPayloadConnection(t *testing.T) { 95 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 96 payload := makePayload(app, nil) 97 ip, ok := payload.(internal.Payload) 98 if !ok { 99 t.Fatal(payload) 100 } 101 txn := app.StartTransaction("hello", nil, nil) 102 err := txn.AcceptDistributedTracePayload(TransportHTTP, payload) 103 if nil != err { 104 t.Error(err) 105 } 106 err = txn.End() 107 if nil != err { 108 t.Error(err) 109 } 110 app.ExpectMetrics(t, distributedTracingSuccessMetrics) 111 app.ExpectTxnEvents(t, []internal.WantEvent{{ 112 Intrinsics: map[string]interface{}{ 113 "name": "OtherTransaction/Go/hello", 114 "parent.type": "App", 115 "parent.account": "123", 116 "parent.app": "456", 117 "parent.transportType": "HTTP", 118 "parent.transportDuration": internal.MatchAnything, 119 "parentId": ip.TransactionID, 120 "traceId": ip.TransactionID, 121 "parentSpanId": ip.ID, 122 "guid": internal.MatchAnything, 123 "sampled": internal.MatchAnything, 124 "priority": internal.MatchAnything, 125 }, 126 }}) 127 } 128 129 func TestAcceptMultiple(t *testing.T) { 130 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 131 payload := makePayload(app, nil) 132 ip, ok := payload.(internal.Payload) 133 if !ok { 134 t.Fatal(payload) 135 } 136 txn := app.StartTransaction("hello", nil, nil) 137 err := txn.AcceptDistributedTracePayload(TransportHTTP, payload) 138 if nil != err { 139 t.Error(err) 140 } 141 err = txn.AcceptDistributedTracePayload(TransportHTTP, payload) 142 if err != errAlreadyAccepted { 143 t.Error(err) 144 } 145 err = txn.End() 146 if nil != err { 147 t.Error(err) 148 } 149 app.ExpectMetrics(t, append([]internal.WantMetric{ 150 {Name: "Supportability/DistributedTrace/AcceptPayload/Ignored/Multiple", Scope: "", Forced: true, Data: singleCount}, 151 }, distributedTracingSuccessMetrics...)) 152 app.ExpectTxnEvents(t, []internal.WantEvent{{ 153 Intrinsics: map[string]interface{}{ 154 "name": "OtherTransaction/Go/hello", 155 "parent.type": "App", 156 "parent.account": "123", 157 "parent.app": "456", 158 "parent.transportType": "HTTP", 159 "parent.transportDuration": internal.MatchAnything, 160 "parentId": ip.TransactionID, 161 "traceId": ip.TransactionID, 162 "parentSpanId": ip.ID, 163 "guid": internal.MatchAnything, 164 "sampled": internal.MatchAnything, 165 "priority": internal.MatchAnything, 166 }, 167 }}) 168 } 169 170 func TestPayloadConnectionText(t *testing.T) { 171 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 172 payload := makePayload(app, nil) 173 ip, ok := payload.(internal.Payload) 174 if !ok { 175 t.Fatal(payload) 176 } 177 txn := app.StartTransaction("hello", nil, nil) 178 err := txn.AcceptDistributedTracePayload(TransportHTTP, payload.Text()) 179 if nil != err { 180 t.Error(err) 181 } 182 err = txn.End() 183 if nil != err { 184 t.Error(err) 185 } 186 app.ExpectMetrics(t, distributedTracingSuccessMetrics) 187 app.ExpectTxnEvents(t, []internal.WantEvent{{ 188 Intrinsics: map[string]interface{}{ 189 "name": "OtherTransaction/Go/hello", 190 "parent.type": "App", 191 "parent.account": "123", 192 "parent.app": "456", 193 "parent.transportType": "HTTP", 194 "parent.transportDuration": internal.MatchAnything, 195 "parentId": ip.TransactionID, 196 "traceId": ip.TransactionID, 197 "parentSpanId": ip.ID, 198 "guid": internal.MatchAnything, 199 "sampled": internal.MatchAnything, 200 "priority": internal.MatchAnything, 201 }, 202 }}) 203 } 204 205 func validBase64(s string) bool { 206 _, err := base64.StdEncoding.DecodeString(s) 207 return err == nil 208 } 209 210 func TestPayloadConnectionHTTPSafe(t *testing.T) { 211 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 212 payload := makePayload(app, nil) 213 ip, ok := payload.(internal.Payload) 214 if !ok { 215 t.Fatal(payload) 216 } 217 txn := app.StartTransaction("hello", nil, nil) 218 p := payload.HTTPSafe() 219 if !validBase64(p) { 220 t.Error(p) 221 } 222 err := txn.AcceptDistributedTracePayload(TransportHTTP, p) 223 if nil != err { 224 t.Error(err) 225 } 226 err = txn.End() 227 if nil != err { 228 t.Error(err) 229 } 230 app.ExpectMetrics(t, distributedTracingSuccessMetrics) 231 app.ExpectTxnEvents(t, []internal.WantEvent{{ 232 Intrinsics: map[string]interface{}{ 233 "name": "OtherTransaction/Go/hello", 234 "parent.type": "App", 235 "parent.account": "123", 236 "parent.app": "456", 237 "parent.transportType": "HTTP", 238 "parent.transportDuration": internal.MatchAnything, 239 "parentId": ip.TransactionID, 240 "traceId": ip.TransactionID, 241 "parentSpanId": ip.ID, 242 "guid": internal.MatchAnything, 243 "sampled": internal.MatchAnything, 244 "priority": internal.MatchAnything, 245 }, 246 }}) 247 } 248 249 func TestPayloadConnectionNotConnected(t *testing.T) { 250 app := testApp(nil, enableBetterCAT, t) 251 payload := makePayload(app, nil) 252 txn := app.StartTransaction("hello", nil, nil) 253 if nil == payload { 254 t.Fatal(payload) 255 } 256 if "" != payload.Text() { 257 t.Error(payload.Text()) 258 } 259 if "" != payload.HTTPSafe() { 260 t.Error(payload.HTTPSafe()) 261 } 262 err := txn.AcceptDistributedTracePayload(TransportHTTP, payload) 263 if nil != err { 264 t.Error(err) 265 } 266 err = txn.End() 267 if nil != err { 268 t.Error(err) 269 } 270 app.ExpectMetrics(t, backgroundMetricsUnknownCaller) 271 app.ExpectTxnEvents(t, []internal.WantEvent{{ 272 Intrinsics: map[string]interface{}{ 273 "name": "OtherTransaction/Go/hello", 274 "guid": internal.MatchAnything, 275 "traceId": internal.MatchAnything, 276 "priority": internal.MatchAnything, 277 "sampled": internal.MatchAnything, 278 }, 279 }}) 280 } 281 282 func TestPayloadConnectionBetterCatDisabled(t *testing.T) { 283 app := testApp(nil, disableCAT, t) 284 payload := makePayload(app, nil) 285 txn := app.StartTransaction("hello", nil, nil) 286 if nil == payload { 287 t.Fatal(payload) 288 } 289 if "" != payload.Text() { 290 t.Error(payload.Text()) 291 } 292 if "" != payload.HTTPSafe() { 293 t.Error(payload.HTTPSafe()) 294 } 295 err := txn.AcceptDistributedTracePayload(TransportHTTP, payload) 296 if err == nil { 297 t.Error("missing expected error") 298 } 299 if errInboundPayloadDTDisabled != err { 300 t.Error(err) 301 } 302 err = txn.End() 303 if nil != err { 304 t.Error(err) 305 } 306 } 307 308 func TestPayloadTransactionsDisabled(t *testing.T) { 309 cfgFn := func(cfg *Config) { 310 cfg.DistributedTracer.Enabled = true 311 cfg.SpanEvents.Enabled = true 312 cfg.TransactionEvents.Enabled = false 313 } 314 app := testApp(nil, cfgFn, t) 315 txn := app.StartTransaction("hello", nil, nil) 316 317 payload := txn.CreateDistributedTracePayload() 318 if nil == payload { 319 t.Fatal(payload) 320 } 321 if "" != payload.Text() { 322 t.Error(payload.Text()) 323 } 324 if "" != payload.HTTPSafe() { 325 t.Error(payload.HTTPSafe()) 326 } 327 err := txn.End() 328 if nil != err { 329 t.Error(err) 330 } 331 } 332 333 func TestPayloadConnectionEmptyString(t *testing.T) { 334 app := testApp(nil, enableBetterCAT, t) 335 txn := app.StartTransaction("hello", nil, nil) 336 err := txn.AcceptDistributedTracePayload(TransportHTTP, "") 337 if nil != err { 338 t.Error(err) 339 } 340 err = txn.End() 341 if nil != err { 342 t.Error(err) 343 } 344 app.ExpectMetrics(t, backgroundMetricsUnknownCaller) 345 app.ExpectTxnEvents(t, []internal.WantEvent{{ 346 Intrinsics: map[string]interface{}{ 347 "name": "OtherTransaction/Go/hello", 348 "guid": internal.MatchAnything, 349 "traceId": internal.MatchAnything, 350 "priority": internal.MatchAnything, 351 "sampled": internal.MatchAnything, 352 }, 353 }}) 354 } 355 356 func TestCreatePayloadFinished(t *testing.T) { 357 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 358 txn := app.StartTransaction("hello", nil, nil) 359 txn.End() 360 payload := txn.CreateDistributedTracePayload() 361 if nil == payload { 362 t.Fatal(payload) 363 } 364 if "" != payload.Text() { 365 t.Error(payload.Text()) 366 } 367 if "" != payload.HTTPSafe() { 368 t.Error(payload.HTTPSafe()) 369 } 370 } 371 372 func TestAcceptPayloadFinished(t *testing.T) { 373 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 374 payload := makePayload(app, nil) 375 txn := app.StartTransaction("hello", nil, nil) 376 err := txn.End() 377 if nil != err { 378 t.Error(err) 379 } 380 err = txn.AcceptDistributedTracePayload(TransportHTTP, payload) 381 if err != errAlreadyEnded { 382 t.Fatal(err) 383 } 384 app.ExpectMetrics(t, backgroundMetricsUnknownCaller) 385 app.ExpectTxnEvents(t, []internal.WantEvent{{ 386 Intrinsics: map[string]interface{}{ 387 "name": "OtherTransaction/Go/hello", 388 "guid": internal.MatchAnything, 389 "traceId": internal.MatchAnything, 390 "priority": internal.MatchAnything, 391 "sampled": internal.MatchAnything, 392 }, 393 }}) 394 } 395 396 func TestPayloadTypeUnknown(t *testing.T) { 397 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 398 txn := app.StartTransaction("hello", nil, nil) 399 invalidPayload := 22 400 err := txn.AcceptDistributedTracePayload(TransportHTTP, invalidPayload) 401 if nil != err { 402 t.Error(err) 403 } 404 err = txn.End() 405 if nil != err { 406 t.Error(err) 407 } 408 app.ExpectMetrics(t, backgroundMetricsUnknownCaller) 409 app.ExpectTxnEvents(t, []internal.WantEvent{{ 410 Intrinsics: map[string]interface{}{ 411 "name": "OtherTransaction/Go/hello", 412 "guid": internal.MatchAnything, 413 "traceId": internal.MatchAnything, 414 "priority": internal.MatchAnything, 415 "sampled": internal.MatchAnything, 416 }, 417 }}) 418 } 419 420 func TestPayloadAcceptAfterCreate(t *testing.T) { 421 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 422 payload := makePayload(app, nil) 423 txn := app.StartTransaction("hello", nil, nil) 424 txn.CreateDistributedTracePayload() 425 err := txn.AcceptDistributedTracePayload(TransportHTTP, payload) 426 if errOutboundPayloadCreated != err { 427 t.Error(err) 428 } 429 err = txn.End() 430 if nil != err { 431 t.Error(err) 432 } 433 app.ExpectMetrics(t, append([]internal.WantMetric{ 434 {Name: "Supportability/DistributedTrace/CreatePayload/Success", Scope: "", Forced: true, Data: singleCount}, 435 {Name: "Supportability/DistributedTrace/AcceptPayload/Ignored/CreateBeforeAccept", Scope: "", Forced: true, Data: singleCount}, 436 }, backgroundMetricsUnknownCaller...)) 437 app.ExpectTxnEvents(t, []internal.WantEvent{{ 438 Intrinsics: map[string]interface{}{ 439 "name": "OtherTransaction/Go/hello", 440 "guid": internal.MatchAnything, 441 "traceId": internal.MatchAnything, 442 "priority": internal.MatchAnything, 443 "sampled": internal.MatchAnything, 444 }, 445 }}) 446 } 447 448 func TestPayloadFromApplicationEmptyTransportType(t *testing.T) { 449 // A user has two options when it comes to TransportType. They can either use one of the 450 // defined vars, like TransportHTTP, or create their own empty variable. The name field inside of 451 // the TransportType struct is not exported outside of the package so users cannot modify its value. 452 // When they make the attempt, Go reports: 453 // 454 // implicit assignment of unexported field 'name' in newrelic.TransportType literal. 455 // 456 // This test makes sure an empty TransportType resolves to "Unknown" 457 var emptyTransport TransportType 458 459 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 460 txn := app.StartTransaction("hello", nil, nil) 461 err := txn.AcceptDistributedTracePayload(emptyTransport, 462 `{ 463 "v":[0,1], 464 "d":{ 465 "ty":"App", 466 "ap":"456", 467 "ac":"123", 468 "id":"id", 469 "tr":"traceID", 470 "ti":1488325987402 471 } 472 }`) 473 if nil != err { 474 t.Error(err) 475 } 476 err = txn.End() 477 if nil != err { 478 t.Error(err) 479 } 480 app.ExpectMetrics(t, []internal.WantMetric{ 481 {Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil}, 482 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 483 {Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil}, 484 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 485 {Name: "DurationByCaller/App/123/456/Unknown/all", Scope: "", Forced: false, Data: nil}, 486 {Name: "DurationByCaller/App/123/456/Unknown/allOther", Scope: "", Forced: false, Data: nil}, 487 {Name: "TransportDuration/App/123/456/Unknown/all", Scope: "", Forced: false, Data: nil}, 488 {Name: "TransportDuration/App/123/456/Unknown/allOther", Scope: "", Forced: false, Data: nil}, 489 {Name: "Supportability/DistributedTrace/AcceptPayload/Success", Scope: "", Forced: true, Data: singleCount}, 490 }) 491 app.ExpectTxnEvents(t, []internal.WantEvent{{ 492 Intrinsics: map[string]interface{}{ 493 "name": "OtherTransaction/Go/hello", 494 "parent.type": "App", 495 "parent.account": "123", 496 "parent.app": "456", 497 "parent.transportType": "Unknown", 498 "parent.transportDuration": internal.MatchAnything, 499 "sampled": internal.MatchAnything, 500 "priority": internal.MatchAnything, 501 "traceId": "traceID", 502 "parentSpanId": "id", 503 "guid": internal.MatchAnything, 504 }, 505 }}) 506 } 507 508 func TestPayloadFutureVersion(t *testing.T) { 509 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 510 txn := app.StartTransaction("hello", nil, nil) 511 err := txn.AcceptDistributedTracePayload(TransportHTTP, 512 `{ 513 "v":[100,0], 514 "d":{ 515 "ty":"App", 516 "ap":"456", 517 "ac":"123", 518 "ti":1488325987402 519 } 520 }`) 521 if nil == err { 522 t.Error("missing expected error here") 523 } 524 err = txn.End() 525 if nil != err { 526 t.Error(err) 527 } 528 app.ExpectMetrics(t, append([]internal.WantMetric{ 529 {Name: "Supportability/DistributedTrace/AcceptPayload/Ignored/MajorVersion", Scope: "", Forced: true, Data: singleCount}, 530 }, backgroundMetricsUnknownCaller...)) 531 app.ExpectTxnEvents(t, []internal.WantEvent{{ 532 Intrinsics: map[string]interface{}{ 533 "name": "OtherTransaction/Go/hello", 534 "sampled": internal.MatchAnything, 535 "priority": internal.MatchAnything, 536 "traceId": internal.MatchAnything, 537 "guid": internal.MatchAnything, 538 }, 539 }}) 540 } 541 542 func TestPayloadParsingError(t *testing.T) { 543 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 544 txn := app.StartTransaction("hello", nil, nil) 545 err := txn.AcceptDistributedTracePayload(TransportHTTP, 546 `{ 547 "v":[0,1], 548 "d":[] 549 }`) 550 if nil == err { 551 t.Error("missing expected parsing error") 552 } 553 err = txn.End() 554 if nil != err { 555 t.Error(err) 556 } 557 app.ExpectMetrics(t, append([]internal.WantMetric{ 558 {Name: "Supportability/DistributedTrace/AcceptPayload/ParseException", Scope: "", Forced: true, Data: singleCount}, 559 }, backgroundMetricsUnknownCaller...)) 560 app.ExpectTxnEvents(t, []internal.WantEvent{{ 561 Intrinsics: map[string]interface{}{ 562 "name": "OtherTransaction/Go/hello", 563 "sampled": internal.MatchAnything, 564 "priority": internal.MatchAnything, 565 "traceId": internal.MatchAnything, 566 "guid": internal.MatchAnything, 567 }, 568 }}) 569 } 570 571 func TestPayloadFromFuture(t *testing.T) { 572 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 573 payload := makePayload(app, nil) 574 ip, ok := payload.(internal.Payload) 575 if !ok { 576 t.Fatal(payload) 577 } 578 ip.Timestamp.Set(time.Now().Add(1 * time.Hour)) 579 txn := app.StartTransaction("hello", nil, nil) 580 err := txn.AcceptDistributedTracePayload(TransportHTTP, ip) 581 if nil != err { 582 t.Error(err) 583 } 584 err = txn.End() 585 if nil != err { 586 t.Error(err) 587 } 588 app.ExpectMetrics(t, distributedTracingSuccessMetrics) 589 app.ExpectTxnEvents(t, []internal.WantEvent{{ 590 Intrinsics: map[string]interface{}{ 591 "name": "OtherTransaction/Go/hello", 592 "parent.type": "App", 593 "parent.account": "123", 594 "parent.app": "456", 595 "parent.transportType": "HTTP", 596 "parent.transportDuration": 0, 597 "parentId": ip.TransactionID, 598 "traceId": ip.TransactionID, 599 "parentSpanId": ip.ID, 600 "guid": internal.MatchAnything, 601 "sampled": internal.MatchAnything, 602 "priority": internal.MatchAnything, 603 }, 604 }}) 605 } 606 607 func TestPayloadUntrustedAccount(t *testing.T) { 608 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 609 payload := makePayload(app, nil) 610 ip, ok := payload.(internal.Payload) 611 if !ok { 612 t.Fatal(payload) 613 } 614 ip.Account = "12345" 615 txn := app.StartTransaction("hello", nil, nil) 616 err := txn.AcceptDistributedTracePayload(TransportHTTP, ip) 617 618 if err != errTrustedAccountKey { 619 t.Error(err) 620 } 621 err = txn.End() 622 if nil != err { 623 t.Error(err) 624 } 625 app.ExpectMetrics(t, append([]internal.WantMetric{ 626 {Name: "Supportability/DistributedTrace/AcceptPayload/Ignored/UntrustedAccount", Scope: "", Forced: true, Data: singleCount}, 627 }, backgroundMetricsUnknownCaller...)) 628 app.ExpectTxnEvents(t, []internal.WantEvent{{ 629 Intrinsics: map[string]interface{}{ 630 "name": "OtherTransaction/Go/hello", 631 "guid": internal.MatchAnything, 632 "traceId": internal.MatchAnything, 633 "priority": internal.MatchAnything, 634 "sampled": internal.MatchAnything, 635 }, 636 }}) 637 } 638 639 func TestPayloadMissingVersion(t *testing.T) { 640 // ensures that a complete distributed trace payload without a version fails 641 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 642 txn := app.StartTransaction("hello", nil, nil) 643 err := txn.AcceptDistributedTracePayload(TransportHTTP, 644 `{ 645 "d":{ 646 "ty":"App", 647 "ap":"456", 648 "ac":"123", 649 "id":"id", 650 "tr":"traceID", 651 "ti":1488325987402 652 } 653 }`) 654 if nil == err { 655 t.Log("Expected error from missing Version (v)") 656 t.Fail() 657 } 658 err = txn.End() 659 if nil != err { 660 t.Error(err) 661 } 662 } 663 664 func TestTrustedAccountKeyPayloadHasKeyAndMatches(t *testing.T) { 665 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 666 667 // fixture has a "tk" of 123, which matches the trusted_account_key 668 // from distributedTracingReplyFields. 669 p := `{ 670 "v":[0,1], 671 "d":{ 672 "ty":"App", 673 "ap":"456", 674 "ac":"321", 675 "id":"id", 676 "tr":"traceID", 677 "ti":1488325987402, 678 "tk":"123" 679 } 680 }` 681 txn := app.StartTransaction("hello", nil, nil) 682 err := txn.AcceptDistributedTracePayload(TransportHTTP, p) 683 if nil != err { 684 t.Error(err) 685 } 686 err = txn.End() 687 if nil != err { 688 t.Error(err) 689 } 690 } 691 692 func TestTrustedAccountKeyPayloadHasKeyAndDoesNotMatch(t *testing.T) { 693 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 694 695 // fixture has a "tk" of 1234, which does not match the 696 // trusted_account_key from distributedTracingReplyFields. 697 p := `{ 698 "v":[0,1], 699 "d":{ 700 "ty":"App", 701 "ap":"456", 702 "ac":"321", 703 "id":"id", 704 "tr":"traceID", 705 "ti":1488325987402, 706 "tk":"1234" 707 } 708 }` 709 txn := app.StartTransaction("hello", nil, nil) 710 err := txn.AcceptDistributedTracePayload(TransportHTTP, p) 711 if err != errTrustedAccountKey { 712 t.Error("Expected ErrTrustedAccountKey from mismatched trustkeys", err) 713 } 714 err = txn.End() 715 if nil != err { 716 t.Error(err) 717 } 718 } 719 720 func TestTrustedAccountKeyPayloadMissingKeyAndAccountIdMatches(t *testing.T) { 721 722 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 723 724 // fixture has no trust key but its account id of 123 matches 725 // trusted_account_key from distributedTracingReplyFields. 726 p := `{ 727 "v":[0,1], 728 "d":{ 729 "ty":"App", 730 "ap":"456", 731 "ac":"123", 732 "id":"id", 733 "tr":"traceID", 734 "ti":1488325987402 735 } 736 }` 737 txn := app.StartTransaction("hello", nil, nil) 738 err := txn.AcceptDistributedTracePayload(TransportHTTP, p) 739 if nil != err { 740 t.Error(err) 741 } 742 err = txn.End() 743 if nil != err { 744 t.Error(err) 745 } 746 747 } 748 749 func TestTrustedAccountKeyPayloadMissingKeyAndAccountIdDoesNotMatch(t *testing.T) { 750 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 751 752 // fixture has no trust key and its account id of 1234 does not match the 753 // trusted_account_key from distributedTracingReplyFields. 754 p := `{ 755 "v":[0,1], 756 "d":{ 757 "ty":"App", 758 "ap":"456", 759 "ac":"1234", 760 "id":"id", 761 "tr":"traceID", 762 "ti":1488325987402 763 } 764 }` 765 txn := app.StartTransaction("hello", nil, nil) 766 err := txn.AcceptDistributedTracePayload(TransportHTTP, p) 767 if err != errTrustedAccountKey { 768 t.Error("Expected ErrTrustedAccountKey from mismatched trustkeys", err) 769 } 770 err = txn.End() 771 if nil != err { 772 t.Error(err) 773 } 774 } 775 776 var ( 777 backgroundUnknownCaller = []internal.WantMetric{ 778 {Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil}, 779 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 780 {Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil}, 781 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 782 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, 783 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil}, 784 } 785 ) 786 787 func TestNilPayload(t *testing.T) { 788 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 789 790 txn := app.StartTransaction("hello", nil, nil) 791 err := txn.AcceptDistributedTracePayload(TransportHTTP, nil) 792 793 if nil != err { 794 t.Error(err) 795 } 796 797 err = txn.End() 798 if nil != err { 799 t.Error(err) 800 } 801 802 app.ExpectMetrics(t, append([]internal.WantMetric{ 803 {Name: "Supportability/DistributedTrace/AcceptPayload/Ignored/Null", Scope: "", Forced: true, Data: singleCount}, 804 }, backgroundUnknownCaller...)) 805 } 806 807 func TestNoticeErrorPayload(t *testing.T) { 808 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 809 810 txn := app.StartTransaction("hello", nil, nil) 811 txn.NoticeError(errors.New("oh no")) 812 813 err := txn.End() 814 if nil != err { 815 t.Error(err) 816 } 817 818 app.ExpectMetrics(t, append([]internal.WantMetric{ 819 {Name: "Errors/all", Scope: "", Forced: true, Data: nil}, 820 {Name: "Errors/allOther", Scope: "", Forced: true, Data: nil}, 821 {Name: "Errors/OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil}, 822 {Name: "ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, 823 {Name: "ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil}, 824 }, backgroundUnknownCaller...)) 825 } 826 827 func TestMissingIDsForSupportabilityMetric(t *testing.T) { 828 p := `{ 829 "v":[0,1], 830 "d":{ 831 "ty":"App", 832 "ap":"456", 833 "ac":"123", 834 "tr":"traceID", 835 "ti":1488325987402 836 } 837 }` 838 839 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 840 841 txn := app.StartTransaction("hello", nil, nil) 842 err := txn.AcceptDistributedTracePayload(TransportHTTP, p) 843 844 if nil == err { 845 t.Log("Expected error from missing guid and transactionId") 846 t.Fail() 847 } 848 849 err = txn.End() 850 if nil != err { 851 t.Error(err) 852 } 853 854 app.ExpectMetrics(t, append([]internal.WantMetric{ 855 {Name: "Supportability/DistributedTrace/AcceptPayload/ParseException", Scope: "", Forced: true, Data: nil}, 856 }, backgroundUnknownCaller...)) 857 } 858 859 func TestMissingVersionForSupportabilityMetric(t *testing.T) { 860 p := `{ 861 "d":{ 862 "ty":"App", 863 "ap":"456", 864 "ac":"123", 865 "id":"id", 866 "tr":"traceID", 867 "ti":1488325987402 868 } 869 }` 870 871 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 872 873 txn := app.StartTransaction("hello", nil, nil) 874 err := txn.AcceptDistributedTracePayload(TransportHTTP, p) 875 876 if nil == err { 877 t.Log("Expected error from missing version") 878 t.Fail() 879 } 880 881 err = txn.End() 882 if nil != err { 883 t.Error(err) 884 } 885 886 app.ExpectMetrics(t, append([]internal.WantMetric{ 887 {Name: "Supportability/DistributedTrace/AcceptPayload/ParseException", Scope: "", Forced: true, Data: nil}, 888 }, backgroundUnknownCaller...)) 889 } 890 891 func TestMissingFieldForSupportabilityMetric(t *testing.T) { 892 p := `{ 893 "v":[0,1], 894 "d":{ 895 "ty":"App", 896 "ap":"456", 897 "id":"id", 898 "tr":"traceID", 899 "ti":1488325987402 900 } 901 }` 902 903 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 904 905 txn := app.StartTransaction("hello", nil, nil) 906 err := txn.AcceptDistributedTracePayload(TransportHTTP, p) 907 908 if nil == err { 909 t.Log("Expected error from missing ac field") 910 t.Fail() 911 } 912 913 err = txn.End() 914 if nil != err { 915 t.Error(err) 916 } 917 918 app.ExpectMetrics(t, append([]internal.WantMetric{ 919 {Name: "Supportability/DistributedTrace/AcceptPayload/ParseException", Scope: "", Forced: true, Data: nil}, 920 }, backgroundUnknownCaller...)) 921 } 922 923 func TestParseExceptionSupportabilityMetric(t *testing.T) { 924 p := `{ 925 "v":[0,1], 926 "d":{ 927 "ty":"App", 928 "ap":"456", 929 "id":"id", 930 "tr":"traceID", 931 "ti":1488325987402 932 } 933 ` 934 935 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 936 937 txn := app.StartTransaction("hello", nil, nil) 938 err := txn.AcceptDistributedTracePayload(TransportHTTP, p) 939 940 if nil == err { 941 t.Log("Expected error from invalid json") 942 t.Fail() 943 } 944 945 err = txn.End() 946 if nil != err { 947 t.Error(err) 948 } 949 950 app.ExpectMetrics(t, append([]internal.WantMetric{ 951 {Name: "Supportability/DistributedTrace/AcceptPayload/ParseException", Scope: "", Forced: true, Data: nil}, 952 }, backgroundUnknownCaller...)) 953 } 954 955 func TestErrorsByCaller(t *testing.T) { 956 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 957 958 txn := app.StartTransaction("hello", nil, nil) 959 payload := makePayload(app, nil) 960 err := txn.AcceptDistributedTracePayload(TransportHTTP, payload) 961 962 if nil != err { 963 t.Error(err) 964 } 965 966 txn.NoticeError(errors.New("oh no")) 967 968 err = txn.End() 969 if nil != err { 970 t.Error(err) 971 } 972 973 app.ExpectMetrics(t, []internal.WantMetric{ 974 {Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil}, 975 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 976 {Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil}, 977 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 978 979 {Name: "TransportDuration/App/123/456/HTTP/allOther", Scope: "", Forced: false, Data: nil}, 980 {Name: "Supportability/DistributedTrace/AcceptPayload/Success", Scope: "", Forced: true, Data: nil}, 981 {Name: "DurationByCaller/App/123/456/HTTP/all", Scope: "", Forced: false, Data: nil}, 982 {Name: "DurationByCaller/App/123/456/HTTP/allOther", Scope: "", Forced: false, Data: nil}, 983 {Name: "TransportDuration/App/123/456/HTTP/all", Scope: "", Forced: false, Data: nil}, 984 985 {Name: "ErrorsByCaller/App/123/456/HTTP/all", Scope: "", Forced: false, Data: nil}, 986 {Name: "ErrorsByCaller/App/123/456/HTTP/allOther", Scope: "", Forced: false, Data: nil}, 987 {Name: "Errors/all", Scope: "", Forced: true, Data: nil}, 988 {Name: "Errors/allOther", Scope: "", Forced: true, Data: nil}, 989 {Name: "Errors/OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil}, 990 }) 991 } 992 993 func TestCreateDistributedTraceCatDisabled(t *testing.T) { 994 995 // when distributed tracing is disabled, CreateDistributedTracePayload 996 // should return a value that indicates an empty payload. Examples of 997 // this depend on language but may be nil/null/None or an empty payload 998 // object. 999 1000 app := testApp(distributedTracingReplyFields, disableCAT, t) 1001 txn := app.StartTransaction("hello", nil, nil) 1002 1003 p := txn.CreateDistributedTracePayload() 1004 1005 // empty/shim payload objects return empty strings 1006 if "" != p.Text() { 1007 t.Log("Non empty string response for .Text() method") 1008 t.Fail() 1009 } 1010 1011 if "" != p.HTTPSafe() { 1012 t.Log("Non empty string response for .HTTPSafe() method") 1013 t.Fail() 1014 } 1015 1016 err := txn.End() 1017 if nil != err { 1018 t.Error(err) 1019 } 1020 1021 app.ExpectMetrics(t, []internal.WantMetric{ 1022 {Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil}, 1023 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 1024 {Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil}, 1025 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 1026 }) 1027 1028 } 1029 1030 func TestCreateDistributedTraceBetterCatDisabled(t *testing.T) { 1031 1032 // when distributed tracing is disabled, CreateDistributedTracePayload 1033 // should return a value that indicates an empty payload. Examples of 1034 // this depend on language but may be nil/null/None or an empty payload 1035 // object. 1036 1037 app := testApp(distributedTracingReplyFields, enableOldCATDisableBetterCat, t) 1038 txn := app.StartTransaction("hello", nil, nil) 1039 1040 p := txn.CreateDistributedTracePayload() 1041 1042 // empty/shim payload objects return empty strings 1043 if "" != p.Text() { 1044 t.Log("Non empty string response for .Text() method") 1045 t.Fail() 1046 } 1047 1048 if "" != p.HTTPSafe() { 1049 t.Log("Non empty string response for .HTTPSafe() method") 1050 t.Fail() 1051 } 1052 1053 err := txn.End() 1054 if nil != err { 1055 t.Error(err) 1056 } 1057 1058 app.ExpectMetrics(t, []internal.WantMetric{ 1059 {Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil}, 1060 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 1061 {Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil}, 1062 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 1063 }) 1064 1065 } 1066 1067 func TestCreateDistributedTraceBetterCatEnabled(t *testing.T) { 1068 1069 // When distributed tracing is enabled and the application is connected, 1070 // CreateDistributedTracePayload should return a valid payload object 1071 1072 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 1073 txn := app.StartTransaction("hello", nil, nil) 1074 1075 p := txn.CreateDistributedTracePayload() 1076 1077 // empty/shim payload objects return empty strings 1078 if "" == p.Text() { 1079 t.Log("Empty string response for .Text() method") 1080 t.Fail() 1081 } 1082 1083 if "" == p.HTTPSafe() { 1084 t.Log("Empty string response for .HTTPSafe() method") 1085 t.Fail() 1086 } 1087 1088 err := txn.End() 1089 if nil != err { 1090 t.Error(err) 1091 } 1092 1093 app.ExpectMetrics(t, append([]internal.WantMetric{ 1094 {Name: "Supportability/DistributedTrace/CreatePayload/Success", Scope: "", Forced: true, Data: nil}, 1095 }, backgroundUnknownCaller...)) 1096 } 1097 1098 func isZeroValue(x interface{}) bool { 1099 // https://stackoverflow.com/questions/13901819/quick-way-to-detect-empty-values-via-reflection-in-go 1100 return nil == x || x == reflect.Zero(reflect.TypeOf(x)).Interface() 1101 } 1102 1103 func testPayloadFieldsPresent(t *testing.T, p DistributedTracePayload, keys ...string) { 1104 out := struct { 1105 Version []int `json:"v"` 1106 Data map[string]interface{} `json:"d"` 1107 }{} 1108 if err := json.Unmarshal([]byte(p.Text()), &out); nil != err { 1109 t.Fatal("unable to unmarshal payload Text", err) 1110 } 1111 for _, key := range keys { 1112 val, ok := out.Data[key] 1113 if !ok { 1114 t.Fatal("required key missing", key) 1115 } 1116 if isZeroValue(val) { 1117 t.Fatal("value has default value", key, val) 1118 } 1119 } 1120 } 1121 1122 func TestCreateDistributedTraceRequiredFields(t *testing.T) { 1123 1124 // creates a distributed trace payload and then checks 1125 // to ensure the required fields are in place 1126 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 1127 txn := app.StartTransaction("hello", nil, nil) 1128 1129 p := txn.CreateDistributedTracePayload() 1130 1131 testPayloadFieldsPresent(t, p, "ty", "ac", "ap", "tr", "ti") 1132 1133 err := txn.End() 1134 if nil != err { 1135 t.Error(err) 1136 } 1137 1138 app.ExpectMetrics(t, append([]internal.WantMetric{ 1139 {Name: "Supportability/DistributedTrace/CreatePayload/Success", Scope: "", Forced: true, Data: nil}, 1140 }, backgroundUnknownCaller...)) 1141 } 1142 1143 func TestCreateDistributedTraceTrustKeyAbsent(t *testing.T) { 1144 1145 // creates a distributed trace payload and then checks 1146 // to ensure the required fields are in place 1147 var payloadData PayloadTest 1148 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 1149 txn := app.StartTransaction("hello", nil, nil) 1150 1151 p := txn.CreateDistributedTracePayload() 1152 1153 if err := json.Unmarshal([]byte(p.Text()), &payloadData); nil != err { 1154 t.Log("Could not marshall payload into test struct") 1155 t.Error(err) 1156 } 1157 1158 if nil != payloadData.D["tk"] { 1159 t.Log("Did not expect trust key (tk) to be there") 1160 t.Log(p.Text()) 1161 t.Fail() 1162 } 1163 1164 err := txn.End() 1165 if nil != err { 1166 t.Error(err) 1167 } 1168 1169 app.ExpectMetrics(t, append([]internal.WantMetric{ 1170 {Name: "Supportability/DistributedTrace/CreatePayload/Success", Scope: "", Forced: true, Data: nil}, 1171 }, backgroundUnknownCaller...)) 1172 } 1173 1174 func TestCreateDistributedTraceTrustKeyNeeded(t *testing.T) { 1175 1176 // creates a distributed trace payload and then checks 1177 // to ensure the required fields are in place 1178 var payloadData PayloadTest 1179 app := testApp(distributedTracingReplyFieldsNeedTrustKey, enableBetterCAT, t) 1180 txn := app.StartTransaction("hello", nil, nil) 1181 1182 p := txn.CreateDistributedTracePayload() 1183 1184 if err := json.Unmarshal([]byte(p.Text()), &payloadData); nil != err { 1185 t.Log("Could not marshall payload into test struct") 1186 t.Error(err) 1187 } 1188 1189 testPayloadFieldsPresent(t, p, "tk") 1190 1191 err := txn.End() 1192 if nil != err { 1193 t.Error(err) 1194 } 1195 1196 app.ExpectMetrics(t, append([]internal.WantMetric{ 1197 {Name: "Supportability/DistributedTrace/CreatePayload/Success", Scope: "", Forced: true, Data: nil}, 1198 }, backgroundUnknownCaller...)) 1199 } 1200 1201 func TestCreateDistributedTraceAfterAcceptSampledTrue(t *testing.T) { 1202 1203 // simulates 1. reading distributed trace payload from non-header external storage 1204 // (for queues, other customer integrations); 2. Accpeting that Payload; 3. Creating 1205 // a new payload 1206 1207 // tests that the required fields, plus priority and sampled are set 1208 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 1209 1210 // fixture has a "tk" of 123, which matches the trusted_account_key 1211 // from distributedTracingReplyFields. 1212 p := `{ 1213 "v":[0,1], 1214 "d":{ 1215 "ty":"App", 1216 "ap":"456", 1217 "ac":"321", 1218 "id":"id", 1219 "tr":"traceID", 1220 "ti":1488325987402, 1221 "tk":"123", 1222 "sa":true 1223 } 1224 }` 1225 txn := app.StartTransaction("hello", nil, nil) 1226 err := txn.AcceptDistributedTracePayload(TransportHTTP, p) 1227 if nil != err { 1228 t.Error(err) 1229 } 1230 1231 payload := txn.CreateDistributedTracePayload() 1232 1233 testPayloadFieldsPresent(t, payload, 1234 "ty", "ac", "ap", "tr", "ti", "pr", "sa") 1235 1236 err = txn.End() 1237 if nil != err { 1238 t.Error(err) 1239 } 1240 } 1241 1242 func TestCreateDistributedTraceAfterAcceptSampledNotSet(t *testing.T) { 1243 1244 // simulates 1. reading distributed trace payload from non-header external storage 1245 // (for queues, other customer integrations); 2. Accpeting that Payload; 3. Creating 1246 // a new payload 1247 1248 // tests that the required fields, plus priority and sampled are set. When "sa" 1249 // is not set, the payload should pickup on sampled value of the transaction 1250 app := testApp(distributedTracingReplyFields, enableBetterCAT, t) 1251 1252 // fixture has a "tk" of 123, which matches the trusted_account_key 1253 // from distributedTracingReplyFields. 1254 p := `{ 1255 "v":[0,1], 1256 "d":{ 1257 "ty":"App", 1258 "ap":"456", 1259 "ac":"321", 1260 "id":"id", 1261 "tr":"traceID", 1262 "ti":1488325987402, 1263 "tk":"123", 1264 "pr":0.54343 1265 } 1266 }` 1267 txn := app.StartTransaction("hello", nil, nil) 1268 err := txn.AcceptDistributedTracePayload(TransportHTTP, p) 1269 if nil != err { 1270 t.Error(err) 1271 } 1272 1273 payload := txn.CreateDistributedTracePayload() 1274 testPayloadFieldsPresent(t, payload, 1275 "ty", "ac", "ap", "id", "tr", "ti", "pr", "sa") 1276 1277 err = txn.End() 1278 if nil != err { 1279 t.Error(err) 1280 } 1281 } 1282 1283 type fieldExpectations struct { 1284 Exact map[string]interface{} `json:"exact,omitempty"` 1285 Expected []string `json:"expected,omitempty"` 1286 Unexpected []string `json:"unexpected,omitempty"` 1287 } 1288 1289 type distributedTraceTestcase struct { 1290 TestName string `json:"test_name"` 1291 Comment string `json:"comment,omitempty"` 1292 TrustedAccountKey string `json:"trusted_account_key"` 1293 AccountID string `json:"account_id"` 1294 WebTransaction bool `json:"web_transaction"` 1295 RaisesException bool `json:"raises_exception"` 1296 ForceSampledTrue bool `json:"force_sampled_true"` 1297 SpanEventsEnabled bool `json:"span_events_enabled"` 1298 MajorVersion int `json:"major_version"` 1299 MinorVersion int `json:"minor_version"` 1300 TransportType string `json:"transport_type"` 1301 InboundPayloads []json.RawMessage `json:"inbound_payloads"` 1302 1303 OutboundPayloads []fieldExpectations `json:"outbound_payloads,omitempty"` 1304 1305 Intrinsics struct { 1306 TargetEvents []string `json:"target_events"` 1307 Common *fieldExpectations `json:"common,omitempty"` 1308 Transaction *fieldExpectations `json:"Transaction,omitempty"` 1309 Span *fieldExpectations `json:"Span,omitempty"` 1310 TransactionError *fieldExpectations `json:"TransactionError,omitempty"` 1311 } `json:"intrinsics"` 1312 1313 ExpectedMetrics [][2]interface{} `json:"expected_metrics"` 1314 } 1315 1316 func (fe *fieldExpectations) add(intrinsics map[string]interface{}) { 1317 if nil != fe { 1318 for k, v := range fe.Exact { 1319 intrinsics[k] = v 1320 } 1321 for _, v := range fe.Expected { 1322 intrinsics[v] = internal.MatchAnything 1323 } 1324 } 1325 } 1326 1327 func (fe *fieldExpectations) unexpected() []string { 1328 if nil != fe { 1329 return fe.Unexpected 1330 } 1331 return nil 1332 } 1333 1334 // getTransport ensures that our transport names match cross agent test values. 1335 func getTransport(transport string) TransportType { 1336 switch transport { 1337 case TransportHTTP.name: 1338 return TransportHTTP 1339 case TransportHTTPS.name: 1340 return TransportHTTPS 1341 case TransportKafka.name: 1342 return TransportKafka 1343 case TransportJMS.name: 1344 return TransportJMS 1345 case TransportIronMQ.name: 1346 return TransportIronMQ 1347 case TransportAMQP.name: 1348 return TransportAMQP 1349 case TransportQueue.name: 1350 return TransportQueue 1351 case TransportOther.name: 1352 return TransportOther 1353 default: 1354 return TransportUnknown 1355 } 1356 } 1357 1358 func runDistributedTraceCrossAgentTestcase(tst *testing.T, tc distributedTraceTestcase, extraAsserts func(expectApp, internal.Validator)) { 1359 t := internal.ExtendValidator(tst, "test="+tc.TestName) 1360 configCallback := enableBetterCAT 1361 if false == tc.SpanEventsEnabled { 1362 configCallback = disableSpanEvents 1363 } 1364 1365 app := testApp(func(reply *internal.ConnectReply) { 1366 reply.AccountID = tc.AccountID 1367 reply.AppID = "456" 1368 reply.PrimaryAppID = "456" 1369 reply.TrustedAccountKey = tc.TrustedAccountKey 1370 1371 // if cross agent tests ever include logic for sampling 1372 // we'll need to revisit this testing sampler 1373 reply.AdaptiveSampler = internal.SampleEverything{} 1374 1375 }, configCallback, tst) 1376 1377 txn := app.StartTransaction("hello", nil, nil) 1378 if tc.WebTransaction { 1379 txn.SetWebRequest(nil) 1380 } 1381 1382 // If the tests wants us to have an error, give 'em an error 1383 if tc.RaisesException { 1384 txn.NoticeError(errors.New("my error message")) 1385 } 1386 1387 // If there are no inbound payloads, invoke Accept on an empty inbound payload. 1388 if nil == tc.InboundPayloads { 1389 txn.AcceptDistributedTracePayload(getTransport(tc.TransportType), nil) 1390 } 1391 1392 for _, value := range tc.InboundPayloads { 1393 // Note that the error return value is not tested here because 1394 // some of the tests are intentionally errors. 1395 txn.AcceptDistributedTracePayload(getTransport(tc.TransportType), string(value)) 1396 } 1397 1398 //call create each time an outbound payload appears in the testcase 1399 for _, expect := range tc.OutboundPayloads { 1400 actual := txn.CreateDistributedTracePayload().Text() 1401 assertTestCaseOutboundPayload(expect, t, actual) 1402 } 1403 1404 err := txn.End() 1405 if nil != err { 1406 t.Error(err) 1407 } 1408 1409 // create WantMetrics and assert 1410 wantMetrics := []internal.WantMetric{} 1411 for _, metric := range tc.ExpectedMetrics { 1412 wantMetrics = append(wantMetrics, 1413 internal.WantMetric{Name: metric[0].(string), Scope: "", Forced: nil, Data: nil}) 1414 } 1415 app.ExpectMetricsPresent(t, wantMetrics) 1416 1417 // Add extra fields that are not listed in the JSON file so that we can 1418 // always do exact intrinsic set match. 1419 1420 extraTxnFields := &fieldExpectations{Expected: []string{"name"}} 1421 if tc.WebTransaction { 1422 extraTxnFields.Expected = append(extraTxnFields.Expected, "nr.apdexPerfZone") 1423 } 1424 1425 extraSpanFields := &fieldExpectations{ 1426 Expected: []string{"name", "category", "nr.entryPoint"}, 1427 } 1428 1429 // There is a single test with an error (named "exception"), so these 1430 // error expectations can be hard coded. TODO: Move some of these. 1431 // fields into the cross agent tests. 1432 extraErrorFields := &fieldExpectations{ 1433 Expected: []string{"parent.type", "parent.account", "parent.app", 1434 "parent.transportType", "error.message", "transactionName", 1435 "parent.transportDuration", "error.class"}, 1436 } 1437 1438 for _, value := range tc.Intrinsics.TargetEvents { 1439 switch value { 1440 case "Transaction": 1441 assertTestCaseIntrinsics(t, 1442 app.ExpectTxnEvents, 1443 tc.Intrinsics.Common, 1444 tc.Intrinsics.Transaction, 1445 extraTxnFields) 1446 case "Span": 1447 assertTestCaseIntrinsics(t, 1448 app.ExpectSpanEvents, 1449 tc.Intrinsics.Common, 1450 tc.Intrinsics.Span, 1451 extraSpanFields) 1452 1453 case "TransactionError": 1454 assertTestCaseIntrinsics(t, 1455 app.ExpectErrorEvents, 1456 tc.Intrinsics.Common, 1457 tc.Intrinsics.TransactionError, 1458 extraErrorFields) 1459 } 1460 } 1461 1462 extraAsserts(app, t) 1463 } 1464 1465 func assertTestCaseOutboundPayload(expect fieldExpectations, t internal.Validator, actual string) { 1466 type outboundTestcase struct { 1467 Version [2]uint `json:"v"` 1468 Data map[string]interface{} `json:"d"` 1469 } 1470 var actualPayload outboundTestcase 1471 err := json.Unmarshal([]byte(actual), &actualPayload) 1472 if nil != err { 1473 t.Error(err) 1474 } 1475 // Affirm that the exact values are in the payload. 1476 for k, v := range expect.Exact { 1477 if k != "v" { 1478 field := strings.Split(k, ".")[1] 1479 if v != actualPayload.Data[field] { 1480 t.Error(fmt.Sprintf("exact outbound payload field mismatch key=%s wanted=%v got=%v", 1481 k, v, actualPayload.Data[field])) 1482 } 1483 } 1484 } 1485 // Affirm that the expected values are in the actual payload. 1486 for _, e := range expect.Expected { 1487 field := strings.Split(e, ".")[1] 1488 if nil == actualPayload.Data[field] { 1489 t.Error(fmt.Sprintf("expected outbound payload field missing key=%s", e)) 1490 } 1491 } 1492 // Affirm that the unexpected values are not in the actual payload. 1493 for _, u := range expect.Unexpected { 1494 field := strings.Split(u, ".")[1] 1495 if nil != actualPayload.Data[field] { 1496 t.Error(fmt.Sprintf("unexpected outbound payload field present key=%s", u)) 1497 } 1498 } 1499 } 1500 1501 func assertTestCaseIntrinsics(t internal.Validator, 1502 expect func(internal.Validator, []internal.WantEvent), 1503 fields ...*fieldExpectations) { 1504 1505 intrinsics := map[string]interface{}{} 1506 for _, f := range fields { 1507 f.add(intrinsics) 1508 } 1509 expect(t, []internal.WantEvent{{Intrinsics: intrinsics}}) 1510 } 1511 1512 func TestDistributedTraceCrossAgent(t *testing.T) { 1513 var tcs []distributedTraceTestcase 1514 data, err := crossagent.ReadFile(`distributed_tracing/distributed_tracing.json`) 1515 if nil != err { 1516 t.Fatal(err) 1517 } 1518 if err := json.Unmarshal(data, &tcs); nil != err { 1519 t.Fatal(err) 1520 } 1521 // Test that we are correctly parsing all of the testcase fields by 1522 // comparing an opaque object from original JSON to an object from JSON 1523 // created by our testcases. 1524 backToJSON, err := json.Marshal(tcs) 1525 if nil != err { 1526 t.Fatal(err) 1527 } 1528 var fromFile []map[string]interface{} 1529 var fromMarshalled []map[string]interface{} 1530 if err := json.Unmarshal(data, &fromFile); nil != err { 1531 t.Fatal(err) 1532 } 1533 if err := json.Unmarshal(backToJSON, &fromMarshalled); nil != err { 1534 t.Fatal(err) 1535 } 1536 if !reflect.DeepEqual(fromFile, fromMarshalled) { 1537 t.Error(internal.CompactJSONString(string(data)), "\n", 1538 internal.CompactJSONString(string(backToJSON))) 1539 } 1540 1541 // Iterate over all cross-agent tests 1542 for _, tc := range tcs { 1543 extraAsserts := func(app expectApp, t internal.Validator) {} 1544 if "spans_disabled_in_child" == tc.TestName { 1545 // if span events are disabled but distributed tracing is enabled, then 1546 // we expect there are zero span events 1547 extraAsserts = func(app expectApp, t internal.Validator) { 1548 app.ExpectSpanEvents(t, nil) 1549 } 1550 } 1551 runDistributedTraceCrossAgentTestcase(t, tc, extraAsserts) 1552 } 1553 } 1554 1555 func TestDistributedTraceDisabledSpanEventsEnabled(t *testing.T) { 1556 app := testApp(distributedTracingReplyFields, disableDistributedTracerEnableSpanEvents, t) 1557 payload := makePayload(app, nil) 1558 txn := app.StartTransaction("hello", nil, nil) 1559 err := txn.AcceptDistributedTracePayload(TransportHTTP, payload) 1560 if err != errInboundPayloadDTDisabled { 1561 t.Fatal("we expected an error with DT disabled", err) 1562 } 1563 err = txn.End() 1564 if nil != err { 1565 t.Error(err) 1566 } 1567 1568 // ensure no span events created 1569 app.ExpectSpanEvents(t, nil) 1570 } 1571 1572 func TestCreatePayloadAppNotConnected(t *testing.T) { 1573 // Test that an app which isn't connected does not create distributed 1574 // trace payloads. 1575 app := testApp(nil, enableBetterCAT, t) 1576 txn := app.StartTransaction("hello", nil, nil) 1577 payload := txn.CreateDistributedTracePayload() 1578 if payload.Text() != "" || payload.HTTPSafe() != "" { 1579 t.Error(payload.Text(), payload.HTTPSafe()) 1580 } 1581 } 1582 func TestCreatePayloadReplyMissingTrustKey(t *testing.T) { 1583 // Test that an app whose reply is missing the trust key does not create 1584 // distributed trace payloads. 1585 app := testApp(func(reply *internal.ConnectReply) { 1586 distributedTracingReplyFields(reply) 1587 reply.TrustedAccountKey = "" 1588 }, enableBetterCAT, t) 1589 txn := app.StartTransaction("hello", nil, nil) 1590 payload := txn.CreateDistributedTracePayload() 1591 if payload.Text() != "" || payload.HTTPSafe() != "" { 1592 t.Error(payload.Text(), payload.HTTPSafe()) 1593 } 1594 } 1595 1596 func TestAcceptPayloadAppNotConnected(t *testing.T) { 1597 // Test that an app which isn't connected does not accept distributed 1598 // trace payloads. 1599 app := testApp(nil, enableBetterCAT, t) 1600 payload := testApp(distributedTracingReplyFields, enableBetterCAT, t). 1601 StartTransaction("name", nil, nil). 1602 CreateDistributedTracePayload() 1603 if payload.Text() == "" { 1604 t.Fatal(payload) 1605 } 1606 txn := app.StartTransaction("hello", nil, nil) 1607 err := txn.AcceptDistributedTracePayload(TransportHTTP, payload) 1608 if nil != err { 1609 t.Error(err) 1610 } 1611 txn.End() 1612 app.ExpectMetrics(t, backgroundUnknownCaller) 1613 } 1614 1615 func TestAcceptPayloadReplyMissingTrustKey(t *testing.T) { 1616 // Test that an app whose reply is missing a trust key does not accept 1617 // distributed trace payloads. 1618 app := testApp(func(reply *internal.ConnectReply) { 1619 distributedTracingReplyFields(reply) 1620 reply.TrustedAccountKey = "" 1621 }, enableBetterCAT, t) 1622 payload := testApp(distributedTracingReplyFields, enableBetterCAT, t). 1623 StartTransaction("name", nil, nil). 1624 CreateDistributedTracePayload() 1625 if payload.Text() == "" { 1626 t.Fatal(payload) 1627 } 1628 txn := app.StartTransaction("hello", nil, nil) 1629 err := txn.AcceptDistributedTracePayload(TransportHTTP, payload) 1630 if nil != err { 1631 t.Error(err) 1632 } 1633 txn.End() 1634 app.ExpectMetrics(t, backgroundUnknownCaller) 1635 }