github.com/lulzWill/go-agent@v2.1.2+incompatible/internal_test.go (about) 1 package newrelic 2 3 import ( 4 "errors" 5 "math" 6 "net/http" 7 "net/http/httptest" 8 "testing" 9 "time" 10 11 "github.com/lulzWill/go-agent/internal" 12 ) 13 14 var ( 15 singleCount = []float64{1, 0, 0, 0, 0, 0, 0} 16 webMetrics = []internal.WantMetric{ 17 {Name: "WebTransaction/Go/hello", Scope: "", Forced: true, Data: nil}, 18 {Name: "WebTransaction", Scope: "", Forced: true, Data: nil}, 19 {Name: "HttpDispatcher", Scope: "", Forced: true, Data: nil}, 20 {Name: "Apdex", Scope: "", Forced: true, Data: nil}, 21 {Name: "Apdex/Go/hello", Scope: "", Forced: false, Data: nil}, 22 } 23 webErrorMetrics = append([]internal.WantMetric{ 24 {Name: "Errors/all", Scope: "", Forced: true, Data: singleCount}, 25 {Name: "Errors/allWeb", Scope: "", Forced: true, Data: singleCount}, 26 {Name: "Errors/WebTransaction/Go/hello", Scope: "", Forced: true, Data: singleCount}, 27 }, webMetrics...) 28 backgroundMetrics = []internal.WantMetric{ 29 {Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil}, 30 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 31 } 32 backgroundErrorMetrics = append([]internal.WantMetric{ 33 {Name: "Errors/all", Scope: "", Forced: true, Data: singleCount}, 34 {Name: "Errors/allOther", Scope: "", Forced: true, Data: singleCount}, 35 {Name: "Errors/OtherTransaction/Go/hello", Scope: "", Forced: true, Data: singleCount}, 36 }, backgroundMetrics...) 37 ) 38 39 // compatibleResponseRecorder wraps ResponseRecorder to ensure consistent behavior 40 // between different versions of Go. 41 // 42 // Unfortunately, there was a behavior change in go1.6: 43 // 44 // "The net/http/httptest package's ResponseRecorder now initializes a default 45 // Content-Type header using the same content-sniffing algorithm as in 46 // http.Server." 47 type compatibleResponseRecorder struct { 48 *httptest.ResponseRecorder 49 wroteHeader bool 50 } 51 52 func newCompatibleResponseRecorder() *compatibleResponseRecorder { 53 return &compatibleResponseRecorder{ 54 ResponseRecorder: httptest.NewRecorder(), 55 } 56 } 57 58 func (rw *compatibleResponseRecorder) Header() http.Header { 59 return rw.ResponseRecorder.Header() 60 } 61 62 func (rw *compatibleResponseRecorder) Write(buf []byte) (int, error) { 63 if !rw.wroteHeader { 64 rw.WriteHeader(200) 65 rw.wroteHeader = true 66 } 67 return rw.ResponseRecorder.Write(buf) 68 } 69 70 func (rw *compatibleResponseRecorder) WriteHeader(code int) { 71 rw.wroteHeader = true 72 rw.ResponseRecorder.WriteHeader(code) 73 } 74 75 var ( 76 sampleLicense = "0123456789012345678901234567890123456789" 77 validParams = map[string]interface{}{"zip": 1, "zap": 2} 78 ) 79 80 var ( 81 helloResponse = []byte("hello") 82 helloPath = "/hello" 83 helloQueryParams = "?secret=hideme" 84 helloRequest = func() *http.Request { 85 r, err := http.NewRequest("GET", helloPath+helloQueryParams, nil) 86 if nil != err { 87 panic(err) 88 } 89 90 r.Header.Add(`Accept`, `text/plain`) 91 r.Header.Add(`Content-Type`, `text/html; charset=utf-8`) 92 r.Header.Add(`Content-Length`, `753`) 93 r.Header.Add(`Host`, `my_domain.com`) 94 r.Header.Add(`User-Agent`, `Mozilla/5.0`) 95 r.Header.Add(`Referer`, `http://en.wikipedia.org/zip?secret=password`) 96 97 return r 98 }() 99 ) 100 101 func TestNewApplicationNil(t *testing.T) { 102 cfg := NewConfig("appname", "wrong length") 103 cfg.Enabled = false 104 app, err := NewApplication(cfg) 105 if nil == err { 106 t.Error("error expected when license key is short") 107 } 108 if nil != app { 109 t.Error("app expected to be nil when error is returned") 110 } 111 } 112 113 func handler(w http.ResponseWriter, req *http.Request) { 114 w.Write(helloResponse) 115 } 116 117 func testApp(replyfn func(*internal.ConnectReply), cfgfn func(*Config), t testing.TB) expectApp { 118 cfg := NewConfig("my app", "0123456789012345678901234567890123456789") 119 120 if nil != cfgfn { 121 cfgfn(&cfg) 122 } 123 124 app, err := newTestApp(replyfn, cfg) 125 if nil != err { 126 t.Fatal(err) 127 } 128 return app 129 } 130 131 func TestRecordCustomEventSuccess(t *testing.T) { 132 app := testApp(nil, nil, t) 133 err := app.RecordCustomEvent("myType", validParams) 134 if nil != err { 135 t.Error(err) 136 } 137 app.ExpectCustomEvents(t, []internal.WantEvent{{ 138 Intrinsics: map[string]interface{}{ 139 "type": "myType", 140 "timestamp": internal.MatchAnything, 141 }, 142 UserAttributes: validParams, 143 }}) 144 } 145 146 func TestRecordCustomEventHighSecurityEnabled(t *testing.T) { 147 cfgfn := func(cfg *Config) { cfg.HighSecurity = true } 148 app := testApp(nil, cfgfn, t) 149 err := app.RecordCustomEvent("myType", validParams) 150 if err != errHighSecurityEnabled { 151 t.Error(err) 152 } 153 app.ExpectCustomEvents(t, []internal.WantEvent{}) 154 } 155 156 func TestRecordCustomEventSecurityPolicy(t *testing.T) { 157 replyfn := func(reply *internal.ConnectReply) { reply.SecurityPolicies.CustomEvents.SetEnabled(false) } 158 app := testApp(replyfn, nil, t) 159 err := app.RecordCustomEvent("myType", validParams) 160 if err != errSecurityPolicy { 161 t.Error(err) 162 } 163 app.ExpectCustomEvents(t, []internal.WantEvent{}) 164 } 165 166 func TestRecordCustomEventEventsDisabled(t *testing.T) { 167 cfgfn := func(cfg *Config) { cfg.CustomInsightsEvents.Enabled = false } 168 app := testApp(nil, cfgfn, t) 169 err := app.RecordCustomEvent("myType", validParams) 170 if err != errCustomEventsDisabled { 171 t.Error(err) 172 } 173 app.ExpectCustomEvents(t, []internal.WantEvent{}) 174 } 175 176 func TestRecordCustomEventBadInput(t *testing.T) { 177 app := testApp(nil, nil, t) 178 err := app.RecordCustomEvent("????", validParams) 179 if err != internal.ErrEventTypeRegex { 180 t.Error(err) 181 } 182 app.ExpectCustomEvents(t, []internal.WantEvent{}) 183 } 184 185 func TestRecordCustomEventRemoteDisable(t *testing.T) { 186 replyfn := func(reply *internal.ConnectReply) { reply.CollectCustomEvents = false } 187 app := testApp(replyfn, nil, t) 188 err := app.RecordCustomEvent("myType", validParams) 189 if err != errCustomEventsRemoteDisabled { 190 t.Error(err) 191 } 192 app.ExpectCustomEvents(t, []internal.WantEvent{}) 193 } 194 195 func TestRecordCustomMetricSuccess(t *testing.T) { 196 app := testApp(nil, nil, t) 197 err := app.RecordCustomMetric("myMetric", 123.0) 198 if nil != err { 199 t.Error(err) 200 } 201 expectData := []float64{1, 123.0, 123.0, 123.0, 123.0, 123.0 * 123.0} 202 app.ExpectMetrics(t, []internal.WantMetric{ 203 {Name: "Custom/myMetric", Scope: "", Forced: false, Data: expectData}, 204 }) 205 } 206 207 func TestRecordCustomMetricNameEmpty(t *testing.T) { 208 app := testApp(nil, nil, t) 209 err := app.RecordCustomMetric("", 123.0) 210 if err != errMetricNameEmpty { 211 t.Error(err) 212 } 213 } 214 215 func TestRecordCustomMetricNaN(t *testing.T) { 216 app := testApp(nil, nil, t) 217 err := app.RecordCustomMetric("myMetric", math.NaN()) 218 if err != errMetricNaN { 219 t.Error(err) 220 } 221 } 222 223 func TestRecordCustomMetricPositiveInf(t *testing.T) { 224 app := testApp(nil, nil, t) 225 err := app.RecordCustomMetric("myMetric", math.Inf(0)) 226 if err != errMetricInf { 227 t.Error(err) 228 } 229 } 230 231 func TestRecordCustomMetricNegativeInf(t *testing.T) { 232 app := testApp(nil, nil, t) 233 err := app.RecordCustomMetric("myMetric", math.Inf(-1)) 234 if err != errMetricInf { 235 t.Error(err) 236 } 237 } 238 239 type sampleResponseWriter struct { 240 code int 241 written int 242 header http.Header 243 } 244 245 func (w *sampleResponseWriter) Header() http.Header { return w.header } 246 func (w *sampleResponseWriter) Write([]byte) (int, error) { return w.written, nil } 247 func (w *sampleResponseWriter) WriteHeader(x int) { w.code = x } 248 249 func TestTxnResponseWriter(t *testing.T) { 250 // NOTE: Eventually when the ResponseWriter is instrumented, this test 251 // should be expanded to make sure that calling ResponseWriter methods 252 // after the transaction has ended is not problematic. 253 w := &sampleResponseWriter{ 254 header: make(http.Header), 255 } 256 app := testApp(nil, nil, t) 257 txn := app.StartTransaction("hello", w, nil) 258 w.header.Add("zip", "zap") 259 if out := txn.Header(); out.Get("zip") != "zap" { 260 t.Error(out.Get("zip")) 261 } 262 w.written = 123 263 if out, _ := txn.Write(nil); out != 123 { 264 t.Error(out) 265 } 266 if txn.WriteHeader(503); w.code != 503 { 267 t.Error(w.code) 268 } 269 } 270 271 func TestTransactionEventWeb(t *testing.T) { 272 app := testApp(nil, nil, t) 273 txn := app.StartTransaction("hello", nil, helloRequest) 274 err := txn.End() 275 if nil != err { 276 t.Error(err) 277 } 278 app.ExpectTxnEvents(t, []internal.WantEvent{{ 279 Intrinsics: map[string]interface{}{ 280 "name": "WebTransaction/Go/hello", 281 "nr.apdexPerfZone": "S", 282 }, 283 }}) 284 } 285 286 func TestTransactionEventBackground(t *testing.T) { 287 app := testApp(nil, nil, t) 288 txn := app.StartTransaction("hello", nil, nil) 289 err := txn.End() 290 if nil != err { 291 t.Error(err) 292 } 293 app.ExpectTxnEvents(t, []internal.WantEvent{{ 294 Intrinsics: map[string]interface{}{ 295 "name": "OtherTransaction/Go/hello", 296 }, 297 }}) 298 } 299 300 func TestTransactionEventLocallyDisabled(t *testing.T) { 301 cfgFn := func(cfg *Config) { cfg.TransactionEvents.Enabled = false } 302 app := testApp(nil, cfgFn, t) 303 txn := app.StartTransaction("hello", nil, helloRequest) 304 err := txn.End() 305 if nil != err { 306 t.Error(err) 307 } 308 app.ExpectTxnEvents(t, []internal.WantEvent{}) 309 } 310 311 func TestTransactionEventRemotelyDisabled(t *testing.T) { 312 replyfn := func(reply *internal.ConnectReply) { reply.CollectAnalyticsEvents = false } 313 app := testApp(replyfn, nil, t) 314 txn := app.StartTransaction("hello", nil, helloRequest) 315 err := txn.End() 316 if nil != err { 317 t.Error(err) 318 } 319 app.ExpectTxnEvents(t, []internal.WantEvent{}) 320 } 321 322 func myErrorHandler(w http.ResponseWriter, req *http.Request) { 323 w.Write([]byte("my response")) 324 if txn, ok := w.(Transaction); ok { 325 txn.NoticeError(myError{}) 326 } 327 } 328 329 func TestWrapHandleFunc(t *testing.T) { 330 app := testApp(nil, nil, t) 331 mux := http.NewServeMux() 332 mux.HandleFunc(WrapHandleFunc(app, helloPath, myErrorHandler)) 333 w := newCompatibleResponseRecorder() 334 mux.ServeHTTP(w, helloRequest) 335 336 out := w.Body.String() 337 if "my response" != out { 338 t.Error(out) 339 } 340 341 app.ExpectErrors(t, []internal.WantError{{ 342 TxnName: "WebTransaction/Go/hello", 343 Msg: "my msg", 344 Klass: "newrelic.myError", 345 Caller: "go-agent.myErrorHandler", 346 URL: "/hello", 347 }}) 348 app.ExpectErrorEvents(t, []internal.WantEvent{{ 349 Intrinsics: map[string]interface{}{ 350 "error.class": "newrelic.myError", 351 "error.message": "my msg", 352 "transactionName": "WebTransaction/Go/hello", 353 }, 354 }}) 355 app.ExpectMetrics(t, webErrorMetrics) 356 } 357 358 func TestWrapHandle(t *testing.T) { 359 app := testApp(nil, nil, t) 360 mux := http.NewServeMux() 361 mux.Handle(WrapHandle(app, helloPath, http.HandlerFunc(myErrorHandler))) 362 w := newCompatibleResponseRecorder() 363 mux.ServeHTTP(w, helloRequest) 364 365 out := w.Body.String() 366 if "my response" != out { 367 t.Error(out) 368 } 369 370 app.ExpectErrors(t, []internal.WantError{{ 371 TxnName: "WebTransaction/Go/hello", 372 Msg: "my msg", 373 Klass: "newrelic.myError", 374 Caller: "go-agent.myErrorHandler", 375 URL: "/hello", 376 }}) 377 app.ExpectErrorEvents(t, []internal.WantEvent{{ 378 Intrinsics: map[string]interface{}{ 379 "error.class": "newrelic.myError", 380 "error.message": "my msg", 381 "transactionName": "WebTransaction/Go/hello", 382 }, 383 }}) 384 app.ExpectMetrics(t, webErrorMetrics) 385 } 386 387 func TestSetName(t *testing.T) { 388 app := testApp(nil, nil, t) 389 txn := app.StartTransaction("one", nil, nil) 390 if err := txn.SetName("hello"); nil != err { 391 t.Error(err) 392 } 393 txn.End() 394 if err := txn.SetName("three"); err != errAlreadyEnded { 395 t.Error(err) 396 } 397 398 app.ExpectMetrics(t, backgroundMetrics) 399 } 400 401 func deferEndPanic(txn Transaction, panicMe interface{}) (r interface{}) { 402 defer func() { 403 r = recover() 404 }() 405 406 defer txn.End() 407 408 panic(panicMe) 409 } 410 411 func TestPanicError(t *testing.T) { 412 app := testApp(nil, nil, t) 413 txn := app.StartTransaction("hello", nil, nil) 414 415 e := myError{} 416 r := deferEndPanic(txn, e) 417 if r != e { 418 t.Error("panic not propagated", r) 419 } 420 421 app.ExpectErrors(t, []internal.WantError{{ 422 TxnName: "OtherTransaction/Go/hello", 423 Msg: "my msg", 424 Klass: internal.PanicErrorKlass, 425 Caller: "go-agent.(*txn).End", 426 URL: "", 427 }}) 428 app.ExpectErrorEvents(t, []internal.WantEvent{{ 429 Intrinsics: map[string]interface{}{ 430 "error.class": internal.PanicErrorKlass, 431 "error.message": "my msg", 432 "transactionName": "OtherTransaction/Go/hello", 433 }, 434 }}) 435 app.ExpectMetrics(t, backgroundErrorMetrics) 436 } 437 438 func TestPanicString(t *testing.T) { 439 app := testApp(nil, nil, t) 440 txn := app.StartTransaction("hello", nil, nil) 441 442 e := "my string" 443 r := deferEndPanic(txn, e) 444 if r != e { 445 t.Error("panic not propagated", r) 446 } 447 448 app.ExpectErrors(t, []internal.WantError{{ 449 TxnName: "OtherTransaction/Go/hello", 450 Msg: "my string", 451 Klass: internal.PanicErrorKlass, 452 Caller: "go-agent.(*txn).End", 453 URL: "", 454 }}) 455 app.ExpectErrorEvents(t, []internal.WantEvent{{ 456 Intrinsics: map[string]interface{}{ 457 "error.class": internal.PanicErrorKlass, 458 "error.message": "my string", 459 "transactionName": "OtherTransaction/Go/hello", 460 }, 461 }}) 462 app.ExpectMetrics(t, backgroundErrorMetrics) 463 } 464 465 func TestPanicInt(t *testing.T) { 466 app := testApp(nil, nil, t) 467 txn := app.StartTransaction("hello", nil, nil) 468 469 e := 22 470 r := deferEndPanic(txn, e) 471 if r != e { 472 t.Error("panic not propagated", r) 473 } 474 475 app.ExpectErrors(t, []internal.WantError{{ 476 TxnName: "OtherTransaction/Go/hello", 477 Msg: "22", 478 Klass: internal.PanicErrorKlass, 479 Caller: "go-agent.(*txn).End", 480 URL: "", 481 }}) 482 app.ExpectErrorEvents(t, []internal.WantEvent{{ 483 Intrinsics: map[string]interface{}{ 484 "error.class": internal.PanicErrorKlass, 485 "error.message": "22", 486 "transactionName": "OtherTransaction/Go/hello", 487 }, 488 }}) 489 app.ExpectMetrics(t, backgroundErrorMetrics) 490 } 491 492 func TestPanicNil(t *testing.T) { 493 app := testApp(nil, nil, t) 494 txn := app.StartTransaction("hello", nil, nil) 495 496 r := deferEndPanic(txn, nil) 497 if nil != r { 498 t.Error(r) 499 } 500 501 app.ExpectErrors(t, []internal.WantError{}) 502 app.ExpectErrorEvents(t, []internal.WantEvent{}) 503 app.ExpectMetrics(t, backgroundMetrics) 504 } 505 506 func TestResponseCodeError(t *testing.T) { 507 app := testApp(nil, nil, t) 508 w := newCompatibleResponseRecorder() 509 txn := app.StartTransaction("hello", w, helloRequest) 510 511 txn.WriteHeader(http.StatusBadRequest) // 400 512 txn.WriteHeader(http.StatusUnauthorized) // 401 513 514 txn.End() 515 516 if http.StatusBadRequest != w.Code { 517 t.Error(w.Code) 518 } 519 520 app.ExpectErrors(t, []internal.WantError{{ 521 TxnName: "WebTransaction/Go/hello", 522 Msg: "Bad Request", 523 Klass: "400", 524 Caller: "go-agent.(*txn).WriteHeader", 525 URL: "/hello", 526 }}) 527 app.ExpectErrorEvents(t, []internal.WantEvent{{ 528 Intrinsics: map[string]interface{}{ 529 "error.class": "400", 530 "error.message": "Bad Request", 531 "transactionName": "WebTransaction/Go/hello", 532 }, 533 }}) 534 app.ExpectMetrics(t, webErrorMetrics) 535 } 536 537 func TestResponseCode404Filtered(t *testing.T) { 538 app := testApp(nil, nil, t) 539 w := newCompatibleResponseRecorder() 540 txn := app.StartTransaction("hello", w, helloRequest) 541 542 txn.WriteHeader(http.StatusNotFound) 543 544 txn.End() 545 546 if http.StatusNotFound != w.Code { 547 t.Error(w.Code) 548 } 549 550 app.ExpectErrors(t, []internal.WantError{}) 551 app.ExpectErrorEvents(t, []internal.WantEvent{}) 552 app.ExpectMetrics(t, webMetrics) 553 } 554 555 func TestResponseCodeCustomFilter(t *testing.T) { 556 cfgFn := func(cfg *Config) { 557 cfg.ErrorCollector.IgnoreStatusCodes = 558 append(cfg.ErrorCollector.IgnoreStatusCodes, 559 http.StatusNotFound) 560 } 561 app := testApp(nil, cfgFn, t) 562 w := newCompatibleResponseRecorder() 563 txn := app.StartTransaction("hello", w, helloRequest) 564 565 txn.WriteHeader(http.StatusNotFound) 566 567 txn.End() 568 569 app.ExpectErrors(t, []internal.WantError{}) 570 app.ExpectErrorEvents(t, []internal.WantEvent{}) 571 app.ExpectMetrics(t, webMetrics) 572 } 573 574 func TestResponseCodeAfterEnd(t *testing.T) { 575 app := testApp(nil, nil, t) 576 w := newCompatibleResponseRecorder() 577 txn := app.StartTransaction("hello", w, helloRequest) 578 579 txn.End() 580 txn.WriteHeader(http.StatusBadRequest) 581 582 if http.StatusBadRequest != w.Code { 583 t.Error(w.Code) 584 } 585 586 app.ExpectErrors(t, []internal.WantError{}) 587 app.ExpectErrorEvents(t, []internal.WantEvent{}) 588 app.ExpectMetrics(t, webMetrics) 589 } 590 591 func TestResponseCodeAfterWrite(t *testing.T) { 592 app := testApp(nil, nil, t) 593 w := newCompatibleResponseRecorder() 594 txn := app.StartTransaction("hello", w, helloRequest) 595 596 txn.Write([]byte("zap")) 597 txn.WriteHeader(http.StatusBadRequest) 598 599 txn.End() 600 601 if out := w.Body.String(); "zap" != out { 602 t.Error(out) 603 } 604 605 if http.StatusOK != w.Code { 606 t.Error(w.Code) 607 } 608 609 app.ExpectErrors(t, []internal.WantError{}) 610 app.ExpectErrorEvents(t, []internal.WantEvent{}) 611 app.ExpectMetrics(t, webMetrics) 612 } 613 614 func TestQueueTime(t *testing.T) { 615 app := testApp(nil, nil, t) 616 req, err := http.NewRequest("GET", helloPath+helloQueryParams, nil) 617 req.Header.Add("X-Queue-Start", "1465793282.12345") 618 if nil != err { 619 t.Fatal(err) 620 } 621 txn := app.StartTransaction("hello", nil, req) 622 txn.NoticeError(myError{}) 623 txn.End() 624 625 app.ExpectErrors(t, []internal.WantError{{ 626 TxnName: "WebTransaction/Go/hello", 627 Msg: "my msg", 628 Klass: "newrelic.myError", 629 Caller: "go-agent.TestQueueTime", 630 URL: "/hello", 631 }}) 632 app.ExpectErrorEvents(t, []internal.WantEvent{{ 633 Intrinsics: map[string]interface{}{ 634 "error.class": "newrelic.myError", 635 "error.message": "my msg", 636 "transactionName": "WebTransaction/Go/hello", 637 "queueDuration": internal.MatchAnything, 638 }, 639 }}) 640 app.ExpectMetrics(t, append([]internal.WantMetric{ 641 {Name: "WebFrontend/QueueTime", Scope: "", Forced: true, Data: nil}, 642 }, webErrorMetrics...)) 643 app.ExpectTxnEvents(t, []internal.WantEvent{{ 644 Intrinsics: map[string]interface{}{ 645 "name": "WebTransaction/Go/hello", 646 "nr.apdexPerfZone": "F", 647 "queueDuration": internal.MatchAnything, 648 }, 649 AgentAttributes: nil, 650 }}) 651 } 652 653 func TestIgnore(t *testing.T) { 654 app := testApp(nil, nil, t) 655 txn := app.StartTransaction("hello", nil, nil) 656 txn.NoticeError(myError{}) 657 err := txn.Ignore() 658 if nil != err { 659 t.Error(err) 660 } 661 txn.End() 662 app.ExpectErrors(t, []internal.WantError{}) 663 app.ExpectErrorEvents(t, []internal.WantEvent{}) 664 app.ExpectMetrics(t, []internal.WantMetric{}) 665 app.ExpectTxnEvents(t, []internal.WantEvent{}) 666 } 667 668 func TestIgnoreAlreadyEnded(t *testing.T) { 669 app := testApp(nil, nil, t) 670 txn := app.StartTransaction("hello", nil, nil) 671 txn.NoticeError(myError{}) 672 txn.End() 673 err := txn.Ignore() 674 if err != errAlreadyEnded { 675 t.Error(err) 676 } 677 app.ExpectErrors(t, []internal.WantError{{ 678 TxnName: "OtherTransaction/Go/hello", 679 Msg: "my msg", 680 Klass: "newrelic.myError", 681 Caller: "go-agent.TestIgnoreAlreadyEnded", 682 URL: "", 683 }}) 684 app.ExpectErrorEvents(t, []internal.WantEvent{{ 685 Intrinsics: map[string]interface{}{ 686 "error.class": "newrelic.myError", 687 "error.message": "my msg", 688 "transactionName": "OtherTransaction/Go/hello", 689 }, 690 }}) 691 app.ExpectMetrics(t, backgroundErrorMetrics) 692 app.ExpectTxnEvents(t, []internal.WantEvent{{ 693 Intrinsics: map[string]interface{}{ 694 "name": "OtherTransaction/Go/hello", 695 }, 696 }}) 697 } 698 699 func TestResponseCodeIsError(t *testing.T) { 700 cfg := NewConfig("my app", "0123456789012345678901234567890123456789") 701 702 if is := responseCodeIsError(&cfg, 200); is { 703 t.Error(is) 704 } 705 if is := responseCodeIsError(&cfg, 400); !is { 706 t.Error(is) 707 } 708 if is := responseCodeIsError(&cfg, 404); is { 709 t.Error(is) 710 } 711 if is := responseCodeIsError(&cfg, 503); !is { 712 t.Error(is) 713 } 714 } 715 716 func TestExternalSegmentURL(t *testing.T) { 717 rawURL := "http://url.com" 718 req, err := http.NewRequest("GET", "http://request.com/", nil) 719 if err != nil { 720 t.Fatal(err) 721 } 722 responsereq, err := http.NewRequest("GET", "http://response.com/", nil) 723 if err != nil { 724 t.Fatal(err) 725 } 726 response := &http.Response{Request: responsereq} 727 728 // empty segment 729 u, err := externalSegmentURL(&ExternalSegment{}) 730 host := internal.HostFromURL(u) 731 if nil != err || nil != u || "" != host { 732 t.Error(u, err, internal.HostFromURL(u)) 733 } 734 // segment only containing url 735 u, err = externalSegmentURL(&ExternalSegment{URL: rawURL}) 736 host = internal.HostFromURL(u) 737 if nil != err || host != "url.com" { 738 t.Error(u, err, internal.HostFromURL(u)) 739 } 740 // segment only containing request 741 u, err = externalSegmentURL(&ExternalSegment{Request: req}) 742 host = internal.HostFromURL(u) 743 if nil != err || "request.com" != host { 744 t.Error(host) 745 } 746 // segment only containing response 747 u, err = externalSegmentURL(&ExternalSegment{Response: response}) 748 host = internal.HostFromURL(u) 749 if nil != err || "response.com" != host { 750 t.Error(host) 751 } 752 // segment containing request and response 753 u, err = externalSegmentURL(&ExternalSegment{ 754 Request: req, 755 Response: response, 756 }) 757 host = internal.HostFromURL(u) 758 if nil != err || "response.com" != host { 759 t.Error(host) 760 } 761 // segment containing url, request, and response 762 u, err = externalSegmentURL(&ExternalSegment{ 763 URL: rawURL, 764 Request: req, 765 Response: response, 766 }) 767 host = internal.HostFromURL(u) 768 if nil != err || "url.com" != host { 769 t.Error(err, host) 770 } 771 } 772 773 func TestZeroSegmentsSafe(t *testing.T) { 774 s := Segment{} 775 s.End() 776 777 StartSegmentNow(nil) 778 779 ds := DatastoreSegment{} 780 ds.End() 781 782 es := ExternalSegment{} 783 es.End() 784 785 StartSegment(nil, "").End() 786 787 StartExternalSegment(nil, nil).End() 788 } 789 790 func TestTraceSegmentDefer(t *testing.T) { 791 app := testApp(nil, nil, t) 792 txn := app.StartTransaction("hello", nil, helloRequest) 793 func() { 794 defer StartSegment(txn, "segment").End() 795 }() 796 txn.End() 797 scope := "WebTransaction/Go/hello" 798 app.ExpectMetrics(t, append([]internal.WantMetric{ 799 {Name: "Custom/segment", Scope: "", Forced: false, Data: nil}, 800 {Name: "Custom/segment", Scope: scope, Forced: false, Data: nil}, 801 }, webMetrics...)) 802 } 803 804 func TestTraceSegmentNilErr(t *testing.T) { 805 app := testApp(nil, nil, t) 806 txn := app.StartTransaction("hello", nil, helloRequest) 807 err := StartSegment(txn, "segment").End() 808 if nil != err { 809 t.Error(err) 810 } 811 txn.End() 812 scope := "WebTransaction/Go/hello" 813 app.ExpectMetrics(t, append([]internal.WantMetric{ 814 {Name: "Custom/segment", Scope: "", Forced: false, Data: nil}, 815 {Name: "Custom/segment", Scope: scope, Forced: false, Data: nil}, 816 }, webMetrics...)) 817 } 818 819 func TestTraceSegmentOutOfOrder(t *testing.T) { 820 app := testApp(nil, nil, t) 821 txn := app.StartTransaction("hello", nil, helloRequest) 822 s1 := StartSegment(txn, "s1") 823 s2 := StartSegment(txn, "s1") 824 err1 := s1.End() 825 err2 := s2.End() 826 if nil != err1 { 827 t.Error(err1) 828 } 829 if nil == err2 { 830 t.Error(err2) 831 } 832 txn.End() 833 scope := "WebTransaction/Go/hello" 834 app.ExpectMetrics(t, append([]internal.WantMetric{ 835 {Name: "Custom/s1", Scope: "", Forced: false, Data: nil}, 836 {Name: "Custom/s1", Scope: scope, Forced: false, Data: nil}, 837 }, webMetrics...)) 838 } 839 840 func TestTraceSegmentEndedBeforeStartSegment(t *testing.T) { 841 app := testApp(nil, nil, t) 842 txn := app.StartTransaction("hello", nil, helloRequest) 843 txn.End() 844 s := StartSegment(txn, "segment") 845 err := s.End() 846 if err != errAlreadyEnded { 847 t.Error(err) 848 } 849 app.ExpectMetrics(t, webMetrics) 850 } 851 852 func TestTraceSegmentEndedBeforeEndSegment(t *testing.T) { 853 app := testApp(nil, nil, t) 854 txn := app.StartTransaction("hello", nil, helloRequest) 855 s := StartSegment(txn, "segment") 856 txn.End() 857 err := s.End() 858 if err != errAlreadyEnded { 859 t.Error(err) 860 } 861 862 app.ExpectMetrics(t, webMetrics) 863 } 864 865 func TestTraceSegmentPanic(t *testing.T) { 866 app := testApp(nil, nil, t) 867 txn := app.StartTransaction("hello", nil, helloRequest) 868 func() { 869 defer func() { 870 recover() 871 }() 872 873 func() { 874 defer StartSegment(txn, "f1").End() 875 876 func() { 877 t := StartSegment(txn, "f2") 878 879 func() { 880 defer StartSegment(txn, "f3").End() 881 882 func() { 883 StartSegment(txn, "f4") 884 885 panic(nil) 886 }() 887 }() 888 889 t.End() 890 }() 891 }() 892 }() 893 894 txn.End() 895 scope := "WebTransaction/Go/hello" 896 app.ExpectMetrics(t, append([]internal.WantMetric{ 897 {Name: "Custom/f1", Scope: "", Forced: false, Data: nil}, 898 {Name: "Custom/f1", Scope: scope, Forced: false, Data: nil}, 899 {Name: "Custom/f3", Scope: "", Forced: false, Data: nil}, 900 {Name: "Custom/f3", Scope: scope, Forced: false, Data: nil}, 901 }, webMetrics...)) 902 } 903 904 func TestTraceSegmentNilTxn(t *testing.T) { 905 app := testApp(nil, nil, t) 906 txn := app.StartTransaction("hello", nil, helloRequest) 907 s := Segment{Name: "hello"} 908 err := s.End() 909 if err != nil { 910 t.Error(err) 911 } 912 txn.End() 913 app.ExpectMetrics(t, webMetrics) 914 } 915 916 func TestTraceDatastore(t *testing.T) { 917 app := testApp(nil, nil, t) 918 txn := app.StartTransaction("hello", nil, helloRequest) 919 s := DatastoreSegment{} 920 s.StartTime = txn.StartSegmentNow() 921 s.Product = DatastoreMySQL 922 s.Collection = "my_table" 923 s.Operation = "SELECT" 924 err := s.End() 925 if nil != err { 926 t.Error(err) 927 } 928 txn.NoticeError(myError{}) 929 txn.End() 930 scope := "WebTransaction/Go/hello" 931 app.ExpectMetrics(t, append([]internal.WantMetric{ 932 {Name: "Datastore/all", Scope: "", Forced: true, Data: nil}, 933 {Name: "Datastore/allWeb", Scope: "", Forced: true, Data: nil}, 934 {Name: "Datastore/MySQL/all", Scope: "", Forced: true, Data: nil}, 935 {Name: "Datastore/MySQL/allWeb", Scope: "", Forced: true, Data: nil}, 936 {Name: "Datastore/operation/MySQL/SELECT", Scope: "", Forced: false, Data: nil}, 937 {Name: "Datastore/statement/MySQL/my_table/SELECT", Scope: "", Forced: false, Data: nil}, 938 {Name: "Datastore/statement/MySQL/my_table/SELECT", Scope: scope, Forced: false, Data: nil}, 939 }, webErrorMetrics...)) 940 app.ExpectErrorEvents(t, []internal.WantEvent{{ 941 Intrinsics: map[string]interface{}{ 942 "error.class": "newrelic.myError", 943 "error.message": "my msg", 944 "transactionName": "WebTransaction/Go/hello", 945 "databaseCallCount": 1, 946 "databaseDuration": internal.MatchAnything, 947 }, 948 }}) 949 app.ExpectTxnEvents(t, []internal.WantEvent{{ 950 Intrinsics: map[string]interface{}{ 951 "name": "WebTransaction/Go/hello", 952 "nr.apdexPerfZone": "F", 953 "databaseCallCount": 1, 954 "databaseDuration": internal.MatchAnything, 955 }, 956 }}) 957 } 958 959 func TestTraceDatastoreBackground(t *testing.T) { 960 app := testApp(nil, nil, t) 961 txn := app.StartTransaction("hello", nil, nil) 962 s := DatastoreSegment{ 963 StartTime: txn.StartSegmentNow(), 964 Product: DatastoreMySQL, 965 Collection: "my_table", 966 Operation: "SELECT", 967 } 968 err := s.End() 969 if nil != err { 970 t.Error(err) 971 } 972 txn.NoticeError(myError{}) 973 txn.End() 974 scope := "OtherTransaction/Go/hello" 975 app.ExpectMetrics(t, append([]internal.WantMetric{ 976 {Name: "Datastore/all", Scope: "", Forced: true, Data: nil}, 977 {Name: "Datastore/allOther", Scope: "", Forced: true, Data: nil}, 978 {Name: "Datastore/MySQL/all", Scope: "", Forced: true, Data: nil}, 979 {Name: "Datastore/MySQL/allOther", Scope: "", Forced: true, Data: nil}, 980 {Name: "Datastore/operation/MySQL/SELECT", Scope: "", Forced: false, Data: nil}, 981 {Name: "Datastore/statement/MySQL/my_table/SELECT", Scope: "", Forced: false, Data: nil}, 982 {Name: "Datastore/statement/MySQL/my_table/SELECT", Scope: scope, Forced: false, Data: nil}, 983 }, backgroundErrorMetrics...)) 984 app.ExpectErrorEvents(t, []internal.WantEvent{{ 985 Intrinsics: map[string]interface{}{ 986 "error.class": "newrelic.myError", 987 "error.message": "my msg", 988 "transactionName": "OtherTransaction/Go/hello", 989 "databaseCallCount": 1, 990 "databaseDuration": internal.MatchAnything, 991 }, 992 }}) 993 app.ExpectTxnEvents(t, []internal.WantEvent{{ 994 Intrinsics: map[string]interface{}{ 995 "name": "OtherTransaction/Go/hello", 996 "databaseCallCount": 1, 997 "databaseDuration": internal.MatchAnything, 998 }, 999 }}) 1000 } 1001 1002 func TestTraceDatastoreMissingProductOperationCollection(t *testing.T) { 1003 app := testApp(nil, nil, t) 1004 txn := app.StartTransaction("hello", nil, helloRequest) 1005 s := DatastoreSegment{ 1006 StartTime: txn.StartSegmentNow(), 1007 } 1008 err := s.End() 1009 if nil != err { 1010 t.Error(err) 1011 } 1012 txn.NoticeError(myError{}) 1013 txn.End() 1014 scope := "WebTransaction/Go/hello" 1015 app.ExpectMetrics(t, append([]internal.WantMetric{ 1016 {Name: "Datastore/all", Scope: "", Forced: true, Data: nil}, 1017 {Name: "Datastore/allWeb", Scope: "", Forced: true, Data: nil}, 1018 {Name: "Datastore/Unknown/all", Scope: "", Forced: true, Data: nil}, 1019 {Name: "Datastore/Unknown/allWeb", Scope: "", Forced: true, Data: nil}, 1020 {Name: "Datastore/operation/Unknown/other", Scope: "", Forced: false, Data: nil}, 1021 {Name: "Datastore/operation/Unknown/other", Scope: scope, Forced: false, Data: nil}, 1022 }, webErrorMetrics...)) 1023 app.ExpectErrorEvents(t, []internal.WantEvent{{ 1024 Intrinsics: map[string]interface{}{ 1025 "error.class": "newrelic.myError", 1026 "error.message": "my msg", 1027 "transactionName": "WebTransaction/Go/hello", 1028 "databaseCallCount": 1, 1029 "databaseDuration": internal.MatchAnything, 1030 }, 1031 }}) 1032 app.ExpectTxnEvents(t, []internal.WantEvent{{ 1033 Intrinsics: map[string]interface{}{ 1034 "name": "WebTransaction/Go/hello", 1035 "nr.apdexPerfZone": "F", 1036 "databaseCallCount": 1, 1037 "databaseDuration": internal.MatchAnything, 1038 }, 1039 }}) 1040 } 1041 1042 func TestTraceDatastoreNilTxn(t *testing.T) { 1043 app := testApp(nil, nil, t) 1044 txn := app.StartTransaction("hello", nil, helloRequest) 1045 var s DatastoreSegment 1046 s.Product = DatastoreMySQL 1047 s.Collection = "my_table" 1048 s.Operation = "SELECT" 1049 err := s.End() 1050 if nil != err { 1051 t.Error(err) 1052 } 1053 txn.NoticeError(myError{}) 1054 txn.End() 1055 app.ExpectMetrics(t, webErrorMetrics) 1056 app.ExpectErrorEvents(t, []internal.WantEvent{{ 1057 Intrinsics: map[string]interface{}{ 1058 "error.class": "newrelic.myError", 1059 "error.message": "my msg", 1060 "transactionName": "WebTransaction/Go/hello", 1061 }, 1062 }}) 1063 app.ExpectTxnEvents(t, []internal.WantEvent{{ 1064 Intrinsics: map[string]interface{}{ 1065 "name": "WebTransaction/Go/hello", 1066 "nr.apdexPerfZone": "F", 1067 }, 1068 }}) 1069 } 1070 1071 func TestTraceDatastoreTxnEnded(t *testing.T) { 1072 app := testApp(nil, nil, t) 1073 txn := app.StartTransaction("hello", nil, helloRequest) 1074 txn.NoticeError(myError{}) 1075 s := DatastoreSegment{ 1076 StartTime: txn.StartSegmentNow(), 1077 Product: DatastoreMySQL, 1078 Collection: "my_table", 1079 Operation: "SELECT", 1080 } 1081 txn.End() 1082 err := s.End() 1083 if errAlreadyEnded != err { 1084 t.Error(err) 1085 } 1086 app.ExpectMetrics(t, webErrorMetrics) 1087 app.ExpectErrorEvents(t, []internal.WantEvent{{ 1088 Intrinsics: map[string]interface{}{ 1089 "error.class": "newrelic.myError", 1090 "error.message": "my msg", 1091 "transactionName": "WebTransaction/Go/hello", 1092 }, 1093 }}) 1094 app.ExpectTxnEvents(t, []internal.WantEvent{{ 1095 Intrinsics: map[string]interface{}{ 1096 "name": "WebTransaction/Go/hello", 1097 "nr.apdexPerfZone": "F", 1098 }, 1099 }}) 1100 } 1101 1102 func TestTraceExternal(t *testing.T) { 1103 app := testApp(nil, nil, t) 1104 txn := app.StartTransaction("hello", nil, helloRequest) 1105 s := ExternalSegment{ 1106 StartTime: txn.StartSegmentNow(), 1107 URL: "http://example.com/", 1108 } 1109 err := s.End() 1110 if nil != err { 1111 t.Error(err) 1112 } 1113 txn.NoticeError(myError{}) 1114 txn.End() 1115 scope := "WebTransaction/Go/hello" 1116 app.ExpectMetrics(t, append([]internal.WantMetric{ 1117 {Name: "External/all", Scope: "", Forced: true, Data: nil}, 1118 {Name: "External/allWeb", Scope: "", Forced: true, Data: nil}, 1119 {Name: "External/example.com/all", Scope: "", Forced: false, Data: nil}, 1120 {Name: "External/example.com/all", Scope: scope, Forced: false, Data: nil}, 1121 }, webErrorMetrics...)) 1122 app.ExpectErrorEvents(t, []internal.WantEvent{{ 1123 Intrinsics: map[string]interface{}{ 1124 "error.class": "newrelic.myError", 1125 "error.message": "my msg", 1126 "transactionName": "WebTransaction/Go/hello", 1127 "externalCallCount": 1, 1128 "externalDuration": internal.MatchAnything, 1129 }, 1130 }}) 1131 app.ExpectTxnEvents(t, []internal.WantEvent{{ 1132 Intrinsics: map[string]interface{}{ 1133 "name": "WebTransaction/Go/hello", 1134 "nr.apdexPerfZone": "F", 1135 "externalCallCount": 1, 1136 "externalDuration": internal.MatchAnything, 1137 }, 1138 }}) 1139 } 1140 1141 func TestTraceExternalBadURL(t *testing.T) { 1142 app := testApp(nil, nil, t) 1143 txn := app.StartTransaction("hello", nil, helloRequest) 1144 s := ExternalSegment{ 1145 StartTime: txn.StartSegmentNow(), 1146 URL: ":example.com/", 1147 } 1148 err := s.End() 1149 if nil == err { 1150 t.Error(err) 1151 } 1152 txn.NoticeError(myError{}) 1153 txn.End() 1154 app.ExpectMetrics(t, webErrorMetrics) 1155 app.ExpectErrorEvents(t, []internal.WantEvent{{ 1156 Intrinsics: map[string]interface{}{ 1157 "error.class": "newrelic.myError", 1158 "error.message": "my msg", 1159 "transactionName": "WebTransaction/Go/hello", 1160 }, 1161 }}) 1162 app.ExpectTxnEvents(t, []internal.WantEvent{{ 1163 Intrinsics: map[string]interface{}{ 1164 "name": "WebTransaction/Go/hello", 1165 "nr.apdexPerfZone": "F", 1166 }, 1167 }}) 1168 } 1169 1170 func TestTraceExternalBackground(t *testing.T) { 1171 app := testApp(nil, nil, t) 1172 txn := app.StartTransaction("hello", nil, nil) 1173 s := ExternalSegment{ 1174 StartTime: txn.StartSegmentNow(), 1175 URL: "http://example.com/", 1176 } 1177 err := s.End() 1178 if nil != err { 1179 t.Error(err) 1180 } 1181 txn.NoticeError(myError{}) 1182 txn.End() 1183 scope := "OtherTransaction/Go/hello" 1184 app.ExpectMetrics(t, append([]internal.WantMetric{ 1185 {Name: "External/all", Scope: "", Forced: true, Data: nil}, 1186 {Name: "External/allOther", Scope: "", Forced: true, Data: nil}, 1187 {Name: "External/example.com/all", Scope: "", Forced: false, Data: nil}, 1188 {Name: "External/example.com/all", Scope: scope, Forced: false, Data: nil}, 1189 }, backgroundErrorMetrics...)) 1190 app.ExpectErrorEvents(t, []internal.WantEvent{{ 1191 Intrinsics: map[string]interface{}{ 1192 "error.class": "newrelic.myError", 1193 "error.message": "my msg", 1194 "transactionName": "OtherTransaction/Go/hello", 1195 "externalCallCount": 1, 1196 "externalDuration": internal.MatchAnything, 1197 }, 1198 }}) 1199 app.ExpectTxnEvents(t, []internal.WantEvent{{ 1200 Intrinsics: map[string]interface{}{ 1201 "name": "OtherTransaction/Go/hello", 1202 "externalCallCount": 1, 1203 "externalDuration": internal.MatchAnything, 1204 }, 1205 }}) 1206 } 1207 1208 func TestTraceExternalMissingURL(t *testing.T) { 1209 app := testApp(nil, nil, t) 1210 txn := app.StartTransaction("hello", nil, helloRequest) 1211 s := ExternalSegment{ 1212 StartTime: txn.StartSegmentNow(), 1213 } 1214 err := s.End() 1215 if nil != err { 1216 t.Error(err) 1217 } 1218 txn.NoticeError(myError{}) 1219 txn.End() 1220 scope := "WebTransaction/Go/hello" 1221 app.ExpectMetrics(t, append([]internal.WantMetric{ 1222 {Name: "External/all", Scope: "", Forced: true, Data: nil}, 1223 {Name: "External/allWeb", Scope: "", Forced: true, Data: nil}, 1224 {Name: "External/unknown/all", Scope: "", Forced: false, Data: nil}, 1225 {Name: "External/unknown/all", Scope: scope, Forced: false, Data: nil}, 1226 }, webErrorMetrics...)) 1227 app.ExpectErrorEvents(t, []internal.WantEvent{{ 1228 Intrinsics: map[string]interface{}{ 1229 "error.class": "newrelic.myError", 1230 "error.message": "my msg", 1231 "transactionName": "WebTransaction/Go/hello", 1232 "externalCallCount": 1, 1233 "externalDuration": internal.MatchAnything, 1234 }, 1235 }}) 1236 app.ExpectTxnEvents(t, []internal.WantEvent{{ 1237 Intrinsics: map[string]interface{}{ 1238 "name": "WebTransaction/Go/hello", 1239 "nr.apdexPerfZone": "F", 1240 "externalCallCount": 1, 1241 "externalDuration": internal.MatchAnything, 1242 }, 1243 }}) 1244 } 1245 1246 func TestTraceExternalNilTxn(t *testing.T) { 1247 app := testApp(nil, nil, t) 1248 txn := app.StartTransaction("hello", nil, helloRequest) 1249 txn.NoticeError(myError{}) 1250 var s ExternalSegment 1251 err := s.End() 1252 if nil != err { 1253 t.Error(err) 1254 } 1255 txn.End() 1256 app.ExpectMetrics(t, webErrorMetrics) 1257 app.ExpectErrorEvents(t, []internal.WantEvent{{ 1258 Intrinsics: map[string]interface{}{ 1259 "error.class": "newrelic.myError", 1260 "error.message": "my msg", 1261 "transactionName": "WebTransaction/Go/hello", 1262 }, 1263 }}) 1264 app.ExpectTxnEvents(t, []internal.WantEvent{{ 1265 Intrinsics: map[string]interface{}{ 1266 "name": "WebTransaction/Go/hello", 1267 "nr.apdexPerfZone": "F", 1268 }, 1269 }}) 1270 } 1271 1272 func TestTraceExternalTxnEnded(t *testing.T) { 1273 app := testApp(nil, nil, t) 1274 txn := app.StartTransaction("hello", nil, helloRequest) 1275 txn.NoticeError(myError{}) 1276 s := ExternalSegment{ 1277 StartTime: txn.StartSegmentNow(), 1278 URL: "http://example.com/", 1279 } 1280 txn.End() 1281 err := s.End() 1282 if err != errAlreadyEnded { 1283 t.Error(err) 1284 } 1285 app.ExpectMetrics(t, webErrorMetrics) 1286 app.ExpectErrorEvents(t, []internal.WantEvent{{ 1287 Intrinsics: map[string]interface{}{ 1288 "error.class": "newrelic.myError", 1289 "error.message": "my msg", 1290 "transactionName": "WebTransaction/Go/hello", 1291 }, 1292 }}) 1293 app.ExpectTxnEvents(t, []internal.WantEvent{{ 1294 Intrinsics: map[string]interface{}{ 1295 "name": "WebTransaction/Go/hello", 1296 "nr.apdexPerfZone": "F", 1297 }, 1298 }}) 1299 } 1300 1301 func TestRoundTripper(t *testing.T) { 1302 app := testApp(nil, nil, t) 1303 txn := app.StartTransaction("hello", nil, nil) 1304 url := "http://example.com/" 1305 client := &http.Client{} 1306 inner := roundTripperFunc(func(r *http.Request) (*http.Response, error) { 1307 // TODO test that request headers have been set here. 1308 if r.URL.String() != url { 1309 t.Error(r.URL.String()) 1310 } 1311 return nil, errors.New("hello") 1312 }) 1313 client.Transport = NewRoundTripper(txn, inner) 1314 resp, err := client.Get(url) 1315 if resp != nil || err == nil { 1316 t.Error(resp, err.Error()) 1317 } 1318 txn.NoticeError(myError{}) 1319 txn.End() 1320 scope := "OtherTransaction/Go/hello" 1321 app.ExpectMetrics(t, append([]internal.WantMetric{ 1322 {Name: "External/all", Scope: "", Forced: true, Data: nil}, 1323 {Name: "External/allOther", Scope: "", Forced: true, Data: nil}, 1324 {Name: "External/example.com/all", Scope: "", Forced: false, Data: nil}, 1325 {Name: "External/example.com/all", Scope: scope, Forced: false, Data: nil}, 1326 }, backgroundErrorMetrics...)) 1327 app.ExpectErrorEvents(t, []internal.WantEvent{{ 1328 Intrinsics: map[string]interface{}{ 1329 "error.class": "newrelic.myError", 1330 "error.message": "my msg", 1331 "transactionName": "OtherTransaction/Go/hello", 1332 "externalCallCount": 1, 1333 "externalDuration": internal.MatchAnything, 1334 }, 1335 }}) 1336 app.ExpectTxnEvents(t, []internal.WantEvent{{ 1337 Intrinsics: map[string]interface{}{ 1338 "name": "OtherTransaction/Go/hello", 1339 "externalCallCount": 1, 1340 "externalDuration": internal.MatchAnything, 1341 "nr.guid": internal.MatchAnything, 1342 "nr.tripId": internal.MatchAnything, 1343 "nr.pathHash": internal.MatchAnything, 1344 }, 1345 }}) 1346 } 1347 1348 func TestTraceBelowThreshold(t *testing.T) { 1349 app := testApp(nil, nil, t) 1350 txn := app.StartTransaction("hello", nil, helloRequest) 1351 txn.End() 1352 app.ExpectTxnTraces(t, []internal.WantTxnTrace{}) 1353 } 1354 1355 func TestTraceBelowThresholdBackground(t *testing.T) { 1356 app := testApp(nil, nil, t) 1357 txn := app.StartTransaction("hello", nil, nil) 1358 txn.End() 1359 app.ExpectTxnTraces(t, []internal.WantTxnTrace{}) 1360 } 1361 1362 func TestTraceNoSegments(t *testing.T) { 1363 cfgfn := func(cfg *Config) { 1364 cfg.TransactionTracer.Threshold.IsApdexFailing = false 1365 cfg.TransactionTracer.Threshold.Duration = 0 1366 cfg.TransactionTracer.SegmentThreshold = 0 1367 } 1368 app := testApp(nil, cfgfn, t) 1369 txn := app.StartTransaction("hello", nil, helloRequest) 1370 txn.End() 1371 app.ExpectTxnTraces(t, []internal.WantTxnTrace{{ 1372 MetricName: "WebTransaction/Go/hello", 1373 CleanURL: "/hello", 1374 NumSegments: 0, 1375 }}) 1376 } 1377 1378 func TestTraceDisabledLocally(t *testing.T) { 1379 cfgfn := func(cfg *Config) { 1380 cfg.TransactionTracer.Threshold.IsApdexFailing = false 1381 cfg.TransactionTracer.Threshold.Duration = 0 1382 cfg.TransactionTracer.SegmentThreshold = 0 1383 cfg.TransactionTracer.Enabled = false 1384 } 1385 app := testApp(nil, cfgfn, t) 1386 txn := app.StartTransaction("hello", nil, helloRequest) 1387 txn.End() 1388 app.ExpectTxnTraces(t, []internal.WantTxnTrace{}) 1389 } 1390 1391 func TestTraceDisabledRemotely(t *testing.T) { 1392 cfgfn := func(cfg *Config) { 1393 cfg.TransactionTracer.Threshold.IsApdexFailing = false 1394 cfg.TransactionTracer.Threshold.Duration = 0 1395 cfg.TransactionTracer.SegmentThreshold = 0 1396 } 1397 replyfn := func(reply *internal.ConnectReply) { 1398 reply.CollectTraces = false 1399 } 1400 app := testApp(replyfn, cfgfn, t) 1401 txn := app.StartTransaction("hello", nil, helloRequest) 1402 txn.End() 1403 app.ExpectTxnTraces(t, []internal.WantTxnTrace{}) 1404 } 1405 1406 func TestTraceWithSegments(t *testing.T) { 1407 cfgfn := func(cfg *Config) { 1408 cfg.TransactionTracer.Threshold.IsApdexFailing = false 1409 cfg.TransactionTracer.Threshold.Duration = 0 1410 cfg.TransactionTracer.SegmentThreshold = 0 1411 } 1412 app := testApp(nil, cfgfn, t) 1413 txn := app.StartTransaction("hello", nil, helloRequest) 1414 s1 := StartSegment(txn, "s1") 1415 s1.End() 1416 s2 := ExternalSegment{ 1417 StartTime: StartSegmentNow(txn), 1418 URL: "http://example.com", 1419 } 1420 s2.End() 1421 s3 := DatastoreSegment{ 1422 StartTime: StartSegmentNow(txn), 1423 Product: DatastoreMySQL, 1424 Collection: "my_table", 1425 Operation: "SELECT", 1426 } 1427 s3.End() 1428 txn.End() 1429 app.ExpectTxnTraces(t, []internal.WantTxnTrace{{ 1430 MetricName: "WebTransaction/Go/hello", 1431 CleanURL: "/hello", 1432 NumSegments: 3, 1433 }}) 1434 } 1435 1436 func TestTraceSegmentsBelowThreshold(t *testing.T) { 1437 cfgfn := func(cfg *Config) { 1438 cfg.TransactionTracer.Threshold.IsApdexFailing = false 1439 cfg.TransactionTracer.Threshold.Duration = 0 1440 cfg.TransactionTracer.SegmentThreshold = 1 * time.Hour 1441 } 1442 app := testApp(nil, cfgfn, t) 1443 txn := app.StartTransaction("hello", nil, helloRequest) 1444 s1 := StartSegment(txn, "s1") 1445 s1.End() 1446 s2 := ExternalSegment{ 1447 StartTime: StartSegmentNow(txn), 1448 URL: "http://example.com", 1449 } 1450 s2.End() 1451 s3 := DatastoreSegment{ 1452 StartTime: StartSegmentNow(txn), 1453 Product: DatastoreMySQL, 1454 Collection: "my_table", 1455 Operation: "SELECT", 1456 } 1457 s3.End() 1458 txn.End() 1459 app.ExpectTxnTraces(t, []internal.WantTxnTrace{{ 1460 MetricName: "WebTransaction/Go/hello", 1461 CleanURL: "/hello", 1462 NumSegments: 0, 1463 }}) 1464 }