github.com/newrelic/go-agent@v3.26.0+incompatible/internal_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/json"
     8  	"errors"
     9  	"math"
    10  	"net/http"
    11  	"net/http/httptest"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/newrelic/go-agent/internal"
    16  )
    17  
    18  var (
    19  	singleCount = []float64{1, 0, 0, 0, 0, 0, 0}
    20  	webMetrics  = []internal.WantMetric{
    21  		{Name: "WebTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
    22  		{Name: "WebTransaction", Scope: "", Forced: true, Data: nil},
    23  		{Name: "WebTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
    24  		{Name: "WebTransactionTotalTime", Scope: "", Forced: true, Data: nil},
    25  		{Name: "HttpDispatcher", Scope: "", Forced: true, Data: nil},
    26  		{Name: "Apdex", Scope: "", Forced: true, Data: nil},
    27  		{Name: "Apdex/Go/hello", Scope: "", Forced: false, Data: nil},
    28  	}
    29  	webErrorMetrics = append([]internal.WantMetric{
    30  		{Name: "Errors/all", Scope: "", Forced: true, Data: singleCount},
    31  		{Name: "Errors/allWeb", Scope: "", Forced: true, Data: singleCount},
    32  		{Name: "Errors/WebTransaction/Go/hello", Scope: "", Forced: true, Data: singleCount},
    33  	}, webMetrics...)
    34  	backgroundMetrics = []internal.WantMetric{
    35  		{Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
    36  		{Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil},
    37  		{Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
    38  		{Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil},
    39  	}
    40  	backgroundMetricsUnknownCaller = append([]internal.WantMetric{
    41  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil},
    42  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil},
    43  	}, backgroundMetrics...)
    44  	backgroundErrorMetrics = append([]internal.WantMetric{
    45  		{Name: "Errors/all", Scope: "", Forced: true, Data: singleCount},
    46  		{Name: "Errors/allOther", Scope: "", Forced: true, Data: singleCount},
    47  		{Name: "Errors/OtherTransaction/Go/hello", Scope: "", Forced: true, Data: singleCount},
    48  	}, backgroundMetrics...)
    49  )
    50  
    51  // compatibleResponseRecorder wraps ResponseRecorder to ensure consistent behavior
    52  // between different versions of Go.
    53  //
    54  // Unfortunately, there was a behavior change in go1.6:
    55  //
    56  // "The net/http/httptest package's ResponseRecorder now initializes a default
    57  // Content-Type header using the same content-sniffing algorithm as in
    58  // http.Server."
    59  type compatibleResponseRecorder struct {
    60  	*httptest.ResponseRecorder
    61  	wroteHeader bool
    62  }
    63  
    64  func newCompatibleResponseRecorder() *compatibleResponseRecorder {
    65  	return &compatibleResponseRecorder{
    66  		ResponseRecorder: httptest.NewRecorder(),
    67  	}
    68  }
    69  
    70  func (rw *compatibleResponseRecorder) Header() http.Header {
    71  	return rw.ResponseRecorder.Header()
    72  }
    73  
    74  func (rw *compatibleResponseRecorder) Write(buf []byte) (int, error) {
    75  	if !rw.wroteHeader {
    76  		rw.WriteHeader(200)
    77  		rw.wroteHeader = true
    78  	}
    79  	return rw.ResponseRecorder.Write(buf)
    80  }
    81  
    82  func (rw *compatibleResponseRecorder) WriteHeader(code int) {
    83  	rw.wroteHeader = true
    84  	rw.ResponseRecorder.WriteHeader(code)
    85  }
    86  
    87  var (
    88  	validParams = map[string]interface{}{"zip": 1, "zap": 2}
    89  )
    90  
    91  var (
    92  	helloResponse    = []byte("hello")
    93  	helloPath        = "/hello"
    94  	helloQueryParams = "?secret=hideme"
    95  	helloRequest     = func() *http.Request {
    96  		r, err := http.NewRequest("GET", helloPath+helloQueryParams, nil)
    97  		if nil != err {
    98  			panic(err)
    99  		}
   100  
   101  		r.Header.Add(`Accept`, `text/plain`)
   102  		r.Header.Add(`Content-Type`, `text/html; charset=utf-8`)
   103  		r.Header.Add(`Content-Length`, `753`)
   104  		r.Header.Add(`Host`, `my_domain.com`)
   105  		r.Header.Add(`User-Agent`, `Mozilla/5.0`)
   106  		r.Header.Add(`Referer`, `http://en.wikipedia.org/zip?secret=password`)
   107  
   108  		return r
   109  	}()
   110  	helloRequestAttributes = map[string]interface{}{
   111  		"request.uri":                   "/hello",
   112  		"request.headers.host":          "my_domain.com",
   113  		"request.headers.referer":       "http://en.wikipedia.org/zip",
   114  		"request.headers.contentLength": 753,
   115  		"request.method":                "GET",
   116  		"request.headers.accept":        "text/plain",
   117  		"request.headers.User-Agent":    "Mozilla/5.0",
   118  		"request.headers.contentType":   "text/html; charset=utf-8",
   119  	}
   120  )
   121  
   122  func TestNewApplicationNil(t *testing.T) {
   123  	cfg := NewConfig("appname", "wrong length")
   124  	cfg.Enabled = false
   125  	app, err := NewApplication(cfg)
   126  	if nil == err {
   127  		t.Error("error expected when license key is short")
   128  	}
   129  	if nil != app {
   130  		t.Error("app expected to be nil when error is returned")
   131  	}
   132  }
   133  
   134  func handler(w http.ResponseWriter, req *http.Request) {
   135  	w.Write(helloResponse)
   136  }
   137  
   138  const (
   139  	testLicenseKey = "0123456789012345678901234567890123456789"
   140  )
   141  
   142  type expectApp interface {
   143  	internal.Expect
   144  	Application
   145  }
   146  
   147  func testApp(replyfn func(*internal.ConnectReply), cfgfn func(*Config), t testing.TB) expectApp {
   148  	cfg := NewConfig("my app", testLicenseKey)
   149  
   150  	if nil != cfgfn {
   151  		cfgfn(&cfg)
   152  	}
   153  
   154  	// Prevent spawning app goroutines in tests.
   155  	if !cfg.ServerlessMode.Enabled {
   156  		cfg.Enabled = false
   157  	}
   158  
   159  	app, err := newApp(cfg)
   160  	if nil != err {
   161  		t.Fatal(err)
   162  	}
   163  
   164  	internal.HarvestTesting(app, replyfn)
   165  
   166  	return app.(expectApp)
   167  }
   168  
   169  func TestRecordCustomEventSuccess(t *testing.T) {
   170  	app := testApp(nil, nil, t)
   171  	err := app.RecordCustomEvent("myType", validParams)
   172  	if nil != err {
   173  		t.Error(err)
   174  	}
   175  	app.ExpectCustomEvents(t, []internal.WantEvent{{
   176  		Intrinsics: map[string]interface{}{
   177  			"type":      "myType",
   178  			"timestamp": internal.MatchAnything,
   179  		},
   180  		UserAttributes: validParams,
   181  	}})
   182  }
   183  
   184  func TestRecordCustomEventHighSecurityEnabled(t *testing.T) {
   185  	cfgfn := func(cfg *Config) { cfg.HighSecurity = true }
   186  	app := testApp(nil, cfgfn, t)
   187  	err := app.RecordCustomEvent("myType", validParams)
   188  	if err != errHighSecurityEnabled {
   189  		t.Error(err)
   190  	}
   191  	app.ExpectCustomEvents(t, []internal.WantEvent{})
   192  }
   193  
   194  func TestRecordCustomEventSecurityPolicy(t *testing.T) {
   195  	replyfn := func(reply *internal.ConnectReply) { reply.SecurityPolicies.CustomEvents.SetEnabled(false) }
   196  	app := testApp(replyfn, nil, t)
   197  	err := app.RecordCustomEvent("myType", validParams)
   198  	if err != errSecurityPolicy {
   199  		t.Error(err)
   200  	}
   201  	app.ExpectCustomEvents(t, []internal.WantEvent{})
   202  }
   203  
   204  func TestRecordCustomEventEventsDisabled(t *testing.T) {
   205  	cfgfn := func(cfg *Config) { cfg.CustomInsightsEvents.Enabled = false }
   206  	app := testApp(nil, cfgfn, t)
   207  	err := app.RecordCustomEvent("myType", validParams)
   208  	if err != errCustomEventsDisabled {
   209  		t.Error(err)
   210  	}
   211  	app.ExpectCustomEvents(t, []internal.WantEvent{})
   212  }
   213  
   214  func TestRecordCustomEventBadInput(t *testing.T) {
   215  	app := testApp(nil, nil, t)
   216  	err := app.RecordCustomEvent("????", validParams)
   217  	if err != internal.ErrEventTypeRegex {
   218  		t.Error(err)
   219  	}
   220  	app.ExpectCustomEvents(t, []internal.WantEvent{})
   221  }
   222  
   223  func TestRecordCustomEventRemoteDisable(t *testing.T) {
   224  	replyfn := func(reply *internal.ConnectReply) { reply.CollectCustomEvents = false }
   225  	app := testApp(replyfn, nil, t)
   226  	err := app.RecordCustomEvent("myType", validParams)
   227  	if err != errCustomEventsRemoteDisabled {
   228  		t.Error(err)
   229  	}
   230  	app.ExpectCustomEvents(t, []internal.WantEvent{})
   231  }
   232  
   233  func TestRecordCustomMetricSuccess(t *testing.T) {
   234  	app := testApp(nil, nil, t)
   235  	err := app.RecordCustomMetric("myMetric", 123.0)
   236  	if nil != err {
   237  		t.Error(err)
   238  	}
   239  	expectData := []float64{1, 123.0, 123.0, 123.0, 123.0, 123.0 * 123.0}
   240  	app.ExpectMetrics(t, []internal.WantMetric{
   241  		{Name: "Custom/myMetric", Scope: "", Forced: false, Data: expectData},
   242  	})
   243  }
   244  
   245  func TestRecordCustomMetricNameEmpty(t *testing.T) {
   246  	app := testApp(nil, nil, t)
   247  	err := app.RecordCustomMetric("", 123.0)
   248  	if err != errMetricNameEmpty {
   249  		t.Error(err)
   250  	}
   251  }
   252  
   253  func TestRecordCustomMetricNaN(t *testing.T) {
   254  	app := testApp(nil, nil, t)
   255  	err := app.RecordCustomMetric("myMetric", math.NaN())
   256  	if err != errMetricNaN {
   257  		t.Error(err)
   258  	}
   259  }
   260  
   261  func TestRecordCustomMetricPositiveInf(t *testing.T) {
   262  	app := testApp(nil, nil, t)
   263  	err := app.RecordCustomMetric("myMetric", math.Inf(0))
   264  	if err != errMetricInf {
   265  		t.Error(err)
   266  	}
   267  }
   268  
   269  func TestRecordCustomMetricNegativeInf(t *testing.T) {
   270  	app := testApp(nil, nil, t)
   271  	err := app.RecordCustomMetric("myMetric", math.Inf(-1))
   272  	if err != errMetricInf {
   273  		t.Error(err)
   274  	}
   275  }
   276  
   277  type sampleResponseWriter struct {
   278  	code    int
   279  	written int
   280  	header  http.Header
   281  }
   282  
   283  func (w *sampleResponseWriter) Header() http.Header       { return w.header }
   284  func (w *sampleResponseWriter) Write([]byte) (int, error) { return w.written, nil }
   285  func (w *sampleResponseWriter) WriteHeader(x int)         { w.code = x }
   286  
   287  func TestTxnResponseWriter(t *testing.T) {
   288  	// NOTE: Eventually when the ResponseWriter is instrumented, this test
   289  	// should be expanded to make sure that calling ResponseWriter methods
   290  	// after the transaction has ended is not problematic.
   291  	w := &sampleResponseWriter{
   292  		header: make(http.Header),
   293  	}
   294  	app := testApp(nil, nil, t)
   295  	txn := app.StartTransaction("hello", w, nil)
   296  	w.header.Add("zip", "zap")
   297  	if out := txn.Header(); out.Get("zip") != "zap" {
   298  		t.Error(out.Get("zip"))
   299  	}
   300  	w.written = 123
   301  	if out, _ := txn.Write(nil); out != 123 {
   302  		t.Error(out)
   303  	}
   304  	if txn.WriteHeader(503); w.code != 503 {
   305  		t.Error(w.code)
   306  	}
   307  }
   308  
   309  func TestTransactionEventWeb(t *testing.T) {
   310  	app := testApp(nil, nil, t)
   311  	txn := app.StartTransaction("hello", nil, helloRequest)
   312  	err := txn.End()
   313  	if nil != err {
   314  		t.Error(err)
   315  	}
   316  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   317  		Intrinsics: map[string]interface{}{
   318  			"name":             "WebTransaction/Go/hello",
   319  			"nr.apdexPerfZone": "S",
   320  		},
   321  	}})
   322  }
   323  
   324  func TestTransactionEventBackground(t *testing.T) {
   325  	app := testApp(nil, nil, t)
   326  	txn := app.StartTransaction("hello", nil, nil)
   327  	err := txn.End()
   328  	if nil != err {
   329  		t.Error(err)
   330  	}
   331  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   332  		Intrinsics: map[string]interface{}{
   333  			"name": "OtherTransaction/Go/hello",
   334  		},
   335  	}})
   336  }
   337  
   338  func TestTransactionEventLocallyDisabled(t *testing.T) {
   339  	cfgFn := func(cfg *Config) { cfg.TransactionEvents.Enabled = false }
   340  	app := testApp(nil, cfgFn, t)
   341  	txn := app.StartTransaction("hello", nil, helloRequest)
   342  	err := txn.End()
   343  	if nil != err {
   344  		t.Error(err)
   345  	}
   346  	app.ExpectTxnEvents(t, []internal.WantEvent{})
   347  }
   348  
   349  func TestTransactionEventRemotelyDisabled(t *testing.T) {
   350  	replyfn := func(reply *internal.ConnectReply) { reply.CollectAnalyticsEvents = false }
   351  	app := testApp(replyfn, nil, t)
   352  	txn := app.StartTransaction("hello", nil, helloRequest)
   353  	err := txn.End()
   354  	if nil != err {
   355  		t.Error(err)
   356  	}
   357  	app.ExpectTxnEvents(t, []internal.WantEvent{})
   358  }
   359  
   360  func myErrorHandler(w http.ResponseWriter, req *http.Request) {
   361  	w.Write([]byte("my response"))
   362  	if txn, ok := w.(Transaction); ok {
   363  		txn.NoticeError(myError{})
   364  	}
   365  }
   366  
   367  func TestWrapHandleFunc(t *testing.T) {
   368  	app := testApp(nil, nil, t)
   369  	mux := http.NewServeMux()
   370  	mux.HandleFunc(WrapHandleFunc(app, helloPath, myErrorHandler))
   371  	w := newCompatibleResponseRecorder()
   372  	mux.ServeHTTP(w, helloRequest)
   373  
   374  	out := w.Body.String()
   375  	if "my response" != out {
   376  		t.Error(out)
   377  	}
   378  
   379  	app.ExpectErrors(t, []internal.WantError{{
   380  		TxnName: "WebTransaction/Go/hello",
   381  		Msg:     "my msg",
   382  		Klass:   "newrelic.myError",
   383  	}})
   384  	app.ExpectErrorEvents(t, []internal.WantEvent{{
   385  		Intrinsics: map[string]interface{}{
   386  			"error.class":     "newrelic.myError",
   387  			"error.message":   "my msg",
   388  			"transactionName": "WebTransaction/Go/hello",
   389  		},
   390  		AgentAttributes: mergeAttributes(helloRequestAttributes, map[string]interface{}{
   391  			"httpResponseCode": "200",
   392  		}),
   393  	}})
   394  	app.ExpectMetrics(t, webErrorMetrics)
   395  }
   396  
   397  func TestWrapHandle(t *testing.T) {
   398  	app := testApp(nil, nil, t)
   399  	mux := http.NewServeMux()
   400  	mux.Handle(WrapHandle(app, helloPath, http.HandlerFunc(myErrorHandler)))
   401  	w := newCompatibleResponseRecorder()
   402  	mux.ServeHTTP(w, helloRequest)
   403  
   404  	out := w.Body.String()
   405  	if "my response" != out {
   406  		t.Error(out)
   407  	}
   408  
   409  	app.ExpectErrors(t, []internal.WantError{{
   410  		TxnName: "WebTransaction/Go/hello",
   411  		Msg:     "my msg",
   412  		Klass:   "newrelic.myError",
   413  	}})
   414  	app.ExpectErrorEvents(t, []internal.WantEvent{{
   415  		Intrinsics: map[string]interface{}{
   416  			"error.class":     "newrelic.myError",
   417  			"error.message":   "my msg",
   418  			"transactionName": "WebTransaction/Go/hello",
   419  		},
   420  		AgentAttributes: mergeAttributes(helloRequestAttributes, map[string]interface{}{
   421  			"httpResponseCode": "200",
   422  		}),
   423  	}})
   424  	app.ExpectMetrics(t, webErrorMetrics)
   425  }
   426  
   427  func TestWrapHandleNilApp(t *testing.T) {
   428  	var app Application
   429  	mux := http.NewServeMux()
   430  	mux.Handle(WrapHandle(app, helloPath, http.HandlerFunc(myErrorHandler)))
   431  	w := newCompatibleResponseRecorder()
   432  	mux.ServeHTTP(w, helloRequest)
   433  
   434  	out := w.Body.String()
   435  	if "my response" != out {
   436  		t.Error(out)
   437  	}
   438  }
   439  
   440  func TestSetName(t *testing.T) {
   441  	app := testApp(nil, nil, t)
   442  	txn := app.StartTransaction("one", nil, nil)
   443  	if err := txn.SetName("hello"); nil != err {
   444  		t.Error(err)
   445  	}
   446  	txn.End()
   447  	if err := txn.SetName("three"); err != errAlreadyEnded {
   448  		t.Error(err)
   449  	}
   450  
   451  	app.ExpectMetrics(t, backgroundMetrics)
   452  }
   453  
   454  func deferEndPanic(txn Transaction, panicMe interface{}) (r interface{}) {
   455  	defer func() {
   456  		r = recover()
   457  	}()
   458  
   459  	defer txn.End()
   460  
   461  	panic(panicMe)
   462  }
   463  
   464  func TestPanicError(t *testing.T) {
   465  	app := testApp(nil, nil, t)
   466  	txn := app.StartTransaction("hello", nil, nil)
   467  
   468  	e := myError{}
   469  	r := deferEndPanic(txn, e)
   470  	if r != e {
   471  		t.Error("panic not propagated", r)
   472  	}
   473  
   474  	app.ExpectErrors(t, []internal.WantError{{
   475  		TxnName: "OtherTransaction/Go/hello",
   476  		Msg:     "my msg",
   477  		Klass:   internal.PanicErrorKlass,
   478  	}})
   479  	app.ExpectErrorEvents(t, []internal.WantEvent{{
   480  		Intrinsics: map[string]interface{}{
   481  			"error.class":     internal.PanicErrorKlass,
   482  			"error.message":   "my msg",
   483  			"transactionName": "OtherTransaction/Go/hello",
   484  		},
   485  	}})
   486  	app.ExpectMetrics(t, backgroundErrorMetrics)
   487  }
   488  
   489  func TestPanicString(t *testing.T) {
   490  	app := testApp(nil, nil, t)
   491  	txn := app.StartTransaction("hello", nil, nil)
   492  
   493  	e := "my string"
   494  	r := deferEndPanic(txn, e)
   495  	if r != e {
   496  		t.Error("panic not propagated", r)
   497  	}
   498  
   499  	app.ExpectErrors(t, []internal.WantError{{
   500  		TxnName: "OtherTransaction/Go/hello",
   501  		Msg:     "my string",
   502  		Klass:   internal.PanicErrorKlass,
   503  	}})
   504  	app.ExpectErrorEvents(t, []internal.WantEvent{{
   505  		Intrinsics: map[string]interface{}{
   506  			"error.class":     internal.PanicErrorKlass,
   507  			"error.message":   "my string",
   508  			"transactionName": "OtherTransaction/Go/hello",
   509  		},
   510  	}})
   511  	app.ExpectMetrics(t, backgroundErrorMetrics)
   512  }
   513  
   514  func TestPanicInt(t *testing.T) {
   515  	app := testApp(nil, nil, t)
   516  	txn := app.StartTransaction("hello", nil, nil)
   517  
   518  	e := 22
   519  	r := deferEndPanic(txn, e)
   520  	if r != e {
   521  		t.Error("panic not propagated", r)
   522  	}
   523  
   524  	app.ExpectErrors(t, []internal.WantError{{
   525  		TxnName: "OtherTransaction/Go/hello",
   526  		Msg:     "22",
   527  		Klass:   internal.PanicErrorKlass,
   528  	}})
   529  	app.ExpectErrorEvents(t, []internal.WantEvent{{
   530  		Intrinsics: map[string]interface{}{
   531  			"error.class":     internal.PanicErrorKlass,
   532  			"error.message":   "22",
   533  			"transactionName": "OtherTransaction/Go/hello",
   534  		},
   535  	}})
   536  	app.ExpectMetrics(t, backgroundErrorMetrics)
   537  }
   538  
   539  func TestPanicNil(t *testing.T) {
   540  	app := testApp(nil, nil, t)
   541  	txn := app.StartTransaction("hello", nil, nil)
   542  
   543  	r := deferEndPanic(txn, nil)
   544  	if nil != r {
   545  		t.Error(r)
   546  	}
   547  
   548  	app.ExpectErrors(t, []internal.WantError{})
   549  	app.ExpectErrorEvents(t, []internal.WantEvent{})
   550  	app.ExpectMetrics(t, backgroundMetrics)
   551  }
   552  
   553  func TestResponseCodeError(t *testing.T) {
   554  	app := testApp(nil, nil, t)
   555  	w := newCompatibleResponseRecorder()
   556  	txn := app.StartTransaction("hello", w, helloRequest)
   557  
   558  	txn.WriteHeader(http.StatusBadRequest)   // 400
   559  	txn.WriteHeader(http.StatusUnauthorized) // 401
   560  
   561  	txn.End()
   562  
   563  	if http.StatusBadRequest != w.Code {
   564  		t.Error(w.Code)
   565  	}
   566  
   567  	app.ExpectErrors(t, []internal.WantError{{
   568  		TxnName: "WebTransaction/Go/hello",
   569  		Msg:     "Bad Request",
   570  		Klass:   "400",
   571  	}})
   572  	app.ExpectErrorEvents(t, []internal.WantEvent{{
   573  		Intrinsics: map[string]interface{}{
   574  			"error.class":     "400",
   575  			"error.message":   "Bad Request",
   576  			"transactionName": "WebTransaction/Go/hello",
   577  		},
   578  		AgentAttributes: mergeAttributes(helloRequestAttributes, map[string]interface{}{
   579  			"httpResponseCode": "400",
   580  		}),
   581  	}})
   582  	app.ExpectMetrics(t, webErrorMetrics)
   583  }
   584  
   585  func TestResponseCode404Filtered(t *testing.T) {
   586  	app := testApp(nil, nil, t)
   587  	w := newCompatibleResponseRecorder()
   588  	txn := app.StartTransaction("hello", w, helloRequest)
   589  
   590  	txn.WriteHeader(http.StatusNotFound)
   591  
   592  	txn.End()
   593  
   594  	if http.StatusNotFound != w.Code {
   595  		t.Error(w.Code)
   596  	}
   597  
   598  	app.ExpectErrors(t, []internal.WantError{})
   599  	app.ExpectErrorEvents(t, []internal.WantEvent{})
   600  	app.ExpectMetrics(t, webMetrics)
   601  }
   602  
   603  func TestResponseCodeCustomFilter(t *testing.T) {
   604  	cfgFn := func(cfg *Config) {
   605  		cfg.ErrorCollector.IgnoreStatusCodes =
   606  			append(cfg.ErrorCollector.IgnoreStatusCodes, 405)
   607  	}
   608  	app := testApp(nil, cfgFn, t)
   609  	w := newCompatibleResponseRecorder()
   610  	txn := app.StartTransaction("hello", w, helloRequest)
   611  
   612  	txn.WriteHeader(405)
   613  
   614  	txn.End()
   615  
   616  	app.ExpectErrors(t, []internal.WantError{})
   617  	app.ExpectErrorEvents(t, []internal.WantEvent{})
   618  	app.ExpectMetrics(t, webMetrics)
   619  }
   620  
   621  func TestResponseCodeServerSideFilterObserved(t *testing.T) {
   622  	// Test that server-side ignore_status_codes are observed.
   623  	cfgFn := func(cfg *Config) {
   624  		cfg.ErrorCollector.IgnoreStatusCodes = nil
   625  	}
   626  	replyfn := func(reply *internal.ConnectReply) {
   627  		json.Unmarshal([]byte(`{"agent_config":{"error_collector.ignore_status_codes":[405]}}`), reply)
   628  	}
   629  	app := testApp(replyfn, cfgFn, t)
   630  	w := newCompatibleResponseRecorder()
   631  	txn := app.StartTransaction("hello", w, helloRequest)
   632  
   633  	txn.WriteHeader(405)
   634  
   635  	txn.End()
   636  
   637  	app.ExpectErrors(t, []internal.WantError{})
   638  	app.ExpectErrorEvents(t, []internal.WantEvent{})
   639  	app.ExpectMetrics(t, webMetrics)
   640  }
   641  
   642  func TestResponseCodeServerSideOverwriteLocal(t *testing.T) {
   643  	// Test that server-side ignore_status_codes are used in place of local
   644  	// Config.ErrorCollector.IgnoreStatusCodes.
   645  	cfgFn := func(cfg *Config) {
   646  	}
   647  	replyfn := func(reply *internal.ConnectReply) {
   648  		json.Unmarshal([]byte(`{"agent_config":{"error_collector.ignore_status_codes":[402]}}`), reply)
   649  	}
   650  	app := testApp(replyfn, cfgFn, t)
   651  	w := newCompatibleResponseRecorder()
   652  	txn := app.StartTransaction("hello", w, helloRequest)
   653  
   654  	txn.WriteHeader(404)
   655  
   656  	txn.End()
   657  
   658  	app.ExpectErrors(t, []internal.WantError{{
   659  		TxnName: "WebTransaction/Go/hello",
   660  		Msg:     "Not Found",
   661  		Klass:   "404",
   662  	}})
   663  	app.ExpectErrorEvents(t, []internal.WantEvent{{
   664  		Intrinsics: map[string]interface{}{
   665  			"error.class":     "404",
   666  			"error.message":   "Not Found",
   667  			"transactionName": "WebTransaction/Go/hello",
   668  		},
   669  		AgentAttributes: mergeAttributes(helloRequestAttributes, map[string]interface{}{
   670  			"httpResponseCode": "404",
   671  		}),
   672  	}})
   673  	app.ExpectMetrics(t, webErrorMetrics)
   674  }
   675  
   676  func TestResponseCodeAfterEnd(t *testing.T) {
   677  	app := testApp(nil, nil, t)
   678  	w := newCompatibleResponseRecorder()
   679  	txn := app.StartTransaction("hello", w, helloRequest)
   680  
   681  	txn.End()
   682  	txn.WriteHeader(http.StatusBadRequest)
   683  
   684  	if http.StatusBadRequest != w.Code {
   685  		t.Error(w.Code)
   686  	}
   687  
   688  	app.ExpectErrors(t, []internal.WantError{})
   689  	app.ExpectErrorEvents(t, []internal.WantEvent{})
   690  	app.ExpectMetrics(t, webMetrics)
   691  }
   692  
   693  func TestResponseCodeAfterWrite(t *testing.T) {
   694  	app := testApp(nil, nil, t)
   695  	w := newCompatibleResponseRecorder()
   696  	txn := app.StartTransaction("hello", w, helloRequest)
   697  
   698  	txn.Write([]byte("zap"))
   699  	txn.WriteHeader(http.StatusBadRequest)
   700  
   701  	txn.End()
   702  
   703  	if out := w.Body.String(); "zap" != out {
   704  		t.Error(out)
   705  	}
   706  
   707  	if http.StatusOK != w.Code {
   708  		t.Error(w.Code)
   709  	}
   710  
   711  	app.ExpectErrors(t, []internal.WantError{})
   712  	app.ExpectErrorEvents(t, []internal.WantEvent{})
   713  	app.ExpectMetrics(t, webMetrics)
   714  }
   715  
   716  func TestQueueTime(t *testing.T) {
   717  	app := testApp(nil, nil, t)
   718  	req, err := http.NewRequest("GET", helloPath+helloQueryParams, nil)
   719  	req.Header.Add("X-Queue-Start", "1465793282.12345")
   720  	if nil != err {
   721  		t.Fatal(err)
   722  	}
   723  	txn := app.StartTransaction("hello", nil, req)
   724  	txn.NoticeError(myError{})
   725  	txn.End()
   726  
   727  	app.ExpectErrors(t, []internal.WantError{{
   728  		TxnName: "WebTransaction/Go/hello",
   729  		Msg:     "my msg",
   730  		Klass:   "newrelic.myError",
   731  	}})
   732  	app.ExpectErrorEvents(t, []internal.WantEvent{{
   733  		Intrinsics: map[string]interface{}{
   734  			"error.class":     "newrelic.myError",
   735  			"error.message":   "my msg",
   736  			"transactionName": "WebTransaction/Go/hello",
   737  			"queueDuration":   internal.MatchAnything,
   738  		},
   739  		AgentAttributes: map[string]interface{}{
   740  			"request.uri":    "/hello",
   741  			"request.method": "GET",
   742  		},
   743  	}})
   744  	app.ExpectMetrics(t, append([]internal.WantMetric{
   745  		{Name: "WebFrontend/QueueTime", Scope: "", Forced: true, Data: nil},
   746  	}, webErrorMetrics...))
   747  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   748  		Intrinsics: map[string]interface{}{
   749  			"name":             "WebTransaction/Go/hello",
   750  			"nr.apdexPerfZone": "F",
   751  			"queueDuration":    internal.MatchAnything,
   752  		},
   753  		AgentAttributes: nil,
   754  	}})
   755  }
   756  
   757  func TestIgnore(t *testing.T) {
   758  	app := testApp(nil, nil, t)
   759  	txn := app.StartTransaction("hello", nil, nil)
   760  	txn.NoticeError(myError{})
   761  	err := txn.Ignore()
   762  	if nil != err {
   763  		t.Error(err)
   764  	}
   765  	txn.End()
   766  	app.ExpectErrors(t, []internal.WantError{})
   767  	app.ExpectErrorEvents(t, []internal.WantEvent{})
   768  	app.ExpectMetrics(t, []internal.WantMetric{})
   769  	app.ExpectTxnEvents(t, []internal.WantEvent{})
   770  }
   771  
   772  func TestIgnoreAlreadyEnded(t *testing.T) {
   773  	app := testApp(nil, nil, t)
   774  	txn := app.StartTransaction("hello", nil, nil)
   775  	txn.NoticeError(myError{})
   776  	txn.End()
   777  	err := txn.Ignore()
   778  	if err != errAlreadyEnded {
   779  		t.Error(err)
   780  	}
   781  	app.ExpectErrors(t, []internal.WantError{{
   782  		TxnName: "OtherTransaction/Go/hello",
   783  		Msg:     "my msg",
   784  		Klass:   "newrelic.myError",
   785  	}})
   786  	app.ExpectErrorEvents(t, []internal.WantEvent{{
   787  		Intrinsics: map[string]interface{}{
   788  			"error.class":     "newrelic.myError",
   789  			"error.message":   "my msg",
   790  			"transactionName": "OtherTransaction/Go/hello",
   791  		},
   792  	}})
   793  	app.ExpectMetrics(t, backgroundErrorMetrics)
   794  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   795  		Intrinsics: map[string]interface{}{
   796  			"name": "OtherTransaction/Go/hello",
   797  		},
   798  	}})
   799  }
   800  
   801  func TestExternalSegmentMethod(t *testing.T) {
   802  	req, err := http.NewRequest("POST", "http://request.com/", nil)
   803  	if err != nil {
   804  		t.Fatal(err)
   805  	}
   806  	responsereq, err := http.NewRequest("POST", "http://response.com/", nil)
   807  	if err != nil {
   808  		t.Fatal(err)
   809  	}
   810  	response := &http.Response{Request: responsereq}
   811  
   812  	// empty segment
   813  	m := externalSegmentMethod(&ExternalSegment{})
   814  	if "" != m {
   815  		t.Error(m)
   816  	}
   817  
   818  	// empty request
   819  	m = externalSegmentMethod(&ExternalSegment{
   820  		Request: nil,
   821  	})
   822  	if "" != m {
   823  		t.Error(m)
   824  	}
   825  
   826  	// segment containing request and response
   827  	m = externalSegmentMethod(&ExternalSegment{
   828  		Request:  req,
   829  		Response: response,
   830  	})
   831  	if "POST" != m {
   832  		t.Error(m)
   833  	}
   834  
   835  	// Procedure field overrides request and response.
   836  	m = externalSegmentMethod(&ExternalSegment{
   837  		Procedure: "GET",
   838  		Request:   req,
   839  		Response:  response,
   840  	})
   841  	if "GET" != m {
   842  		t.Error(m)
   843  	}
   844  
   845  	req, err = http.NewRequest("", "http://request.com/", nil)
   846  	if err != nil {
   847  		t.Fatal(err)
   848  	}
   849  	responsereq, err = http.NewRequest("", "http://response.com/", nil)
   850  	if err != nil {
   851  		t.Fatal(err)
   852  	}
   853  	response = &http.Response{Request: responsereq}
   854  
   855  	// empty string method means a client GET request
   856  	m = externalSegmentMethod(&ExternalSegment{
   857  		Request:  req,
   858  		Response: response,
   859  	})
   860  	if "GET" != m {
   861  		t.Error(m)
   862  	}
   863  
   864  }
   865  
   866  func TestExternalSegmentURL(t *testing.T) {
   867  	rawURL := "http://url.com"
   868  	req, err := http.NewRequest("GET", "http://request.com/", nil)
   869  	if err != nil {
   870  		t.Fatal(err)
   871  	}
   872  	responsereq, err := http.NewRequest("GET", "http://response.com/", nil)
   873  	if err != nil {
   874  		t.Fatal(err)
   875  	}
   876  	response := &http.Response{Request: responsereq}
   877  
   878  	// empty segment
   879  	u, err := externalSegmentURL(&ExternalSegment{})
   880  	host := internal.HostFromURL(u)
   881  	if nil != err || nil != u || "" != host {
   882  		t.Error(u, err, internal.HostFromURL(u))
   883  	}
   884  	// segment only containing url
   885  	u, err = externalSegmentURL(&ExternalSegment{URL: rawURL})
   886  	host = internal.HostFromURL(u)
   887  	if nil != err || host != "url.com" {
   888  		t.Error(u, err, internal.HostFromURL(u))
   889  	}
   890  	// segment only containing request
   891  	u, err = externalSegmentURL(&ExternalSegment{Request: req})
   892  	host = internal.HostFromURL(u)
   893  	if nil != err || "request.com" != host {
   894  		t.Error(host)
   895  	}
   896  	// segment only containing response
   897  	u, err = externalSegmentURL(&ExternalSegment{Response: response})
   898  	host = internal.HostFromURL(u)
   899  	if nil != err || "response.com" != host {
   900  		t.Error(host)
   901  	}
   902  	// segment containing request and response
   903  	u, err = externalSegmentURL(&ExternalSegment{
   904  		Request:  req,
   905  		Response: response,
   906  	})
   907  	host = internal.HostFromURL(u)
   908  	if nil != err || "response.com" != host {
   909  		t.Error(host)
   910  	}
   911  	// segment containing url, request, and response
   912  	u, err = externalSegmentURL(&ExternalSegment{
   913  		URL:      rawURL,
   914  		Request:  req,
   915  		Response: response,
   916  	})
   917  	host = internal.HostFromURL(u)
   918  	if nil != err || "url.com" != host {
   919  		t.Error(err, host)
   920  	}
   921  }
   922  
   923  func TestZeroSegmentsSafe(t *testing.T) {
   924  	s := Segment{}
   925  	s.End()
   926  
   927  	StartSegmentNow(nil)
   928  
   929  	ds := DatastoreSegment{}
   930  	ds.End()
   931  
   932  	es := ExternalSegment{}
   933  	es.End()
   934  
   935  	StartSegment(nil, "").End()
   936  
   937  	StartExternalSegment(nil, nil).End()
   938  }
   939  
   940  func TestTraceSegmentDefer(t *testing.T) {
   941  	app := testApp(nil, nil, t)
   942  	txn := app.StartTransaction("hello", nil, helloRequest)
   943  	func() {
   944  		defer StartSegment(txn, "segment").End()
   945  	}()
   946  	txn.End()
   947  	scope := "WebTransaction/Go/hello"
   948  	app.ExpectMetrics(t, append([]internal.WantMetric{
   949  		{Name: "Custom/segment", Scope: "", Forced: false, Data: nil},
   950  		{Name: "Custom/segment", Scope: scope, Forced: false, Data: nil},
   951  	}, webMetrics...))
   952  }
   953  
   954  func TestTraceSegmentNilErr(t *testing.T) {
   955  	app := testApp(nil, nil, t)
   956  	txn := app.StartTransaction("hello", nil, helloRequest)
   957  	err := StartSegment(txn, "segment").End()
   958  	if nil != err {
   959  		t.Error(err)
   960  	}
   961  	txn.End()
   962  	scope := "WebTransaction/Go/hello"
   963  	app.ExpectMetrics(t, append([]internal.WantMetric{
   964  		{Name: "Custom/segment", Scope: "", Forced: false, Data: nil},
   965  		{Name: "Custom/segment", Scope: scope, Forced: false, Data: nil},
   966  	}, webMetrics...))
   967  }
   968  
   969  func TestTraceSegmentOutOfOrder(t *testing.T) {
   970  	app := testApp(nil, nil, t)
   971  	txn := app.StartTransaction("hello", nil, helloRequest)
   972  	s1 := StartSegment(txn, "s1")
   973  	s2 := StartSegment(txn, "s1")
   974  	err1 := s1.End()
   975  	err2 := s2.End()
   976  	if nil != err1 {
   977  		t.Error(err1)
   978  	}
   979  	if nil == err2 {
   980  		t.Error(err2)
   981  	}
   982  	txn.End()
   983  	scope := "WebTransaction/Go/hello"
   984  	app.ExpectMetrics(t, append([]internal.WantMetric{
   985  		{Name: "Custom/s1", Scope: "", Forced: false, Data: nil},
   986  		{Name: "Custom/s1", Scope: scope, Forced: false, Data: nil},
   987  	}, webMetrics...))
   988  }
   989  
   990  func TestTraceSegmentEndedBeforeStartSegment(t *testing.T) {
   991  	app := testApp(nil, nil, t)
   992  	txn := app.StartTransaction("hello", nil, helloRequest)
   993  	txn.End()
   994  	s := StartSegment(txn, "segment")
   995  	err := s.End()
   996  	if err != errAlreadyEnded {
   997  		t.Error(err)
   998  	}
   999  	app.ExpectMetrics(t, webMetrics)
  1000  }
  1001  
  1002  func TestTraceSegmentEndedBeforeEndSegment(t *testing.T) {
  1003  	app := testApp(nil, nil, t)
  1004  	txn := app.StartTransaction("hello", nil, helloRequest)
  1005  	s := StartSegment(txn, "segment")
  1006  	txn.End()
  1007  	err := s.End()
  1008  	if err != errAlreadyEnded {
  1009  		t.Error(err)
  1010  	}
  1011  
  1012  	app.ExpectMetrics(t, webMetrics)
  1013  }
  1014  
  1015  func TestTraceSegmentPanic(t *testing.T) {
  1016  	app := testApp(nil, nil, t)
  1017  	txn := app.StartTransaction("hello", nil, helloRequest)
  1018  	func() {
  1019  		defer func() {
  1020  			recover()
  1021  		}()
  1022  
  1023  		func() {
  1024  			defer StartSegment(txn, "f1").End()
  1025  
  1026  			func() {
  1027  				t := StartSegment(txn, "f2")
  1028  
  1029  				func() {
  1030  					defer StartSegment(txn, "f3").End()
  1031  
  1032  					func() {
  1033  						StartSegment(txn, "f4")
  1034  
  1035  						panic(nil)
  1036  					}()
  1037  				}()
  1038  
  1039  				t.End()
  1040  			}()
  1041  		}()
  1042  	}()
  1043  
  1044  	txn.End()
  1045  	scope := "WebTransaction/Go/hello"
  1046  	app.ExpectMetrics(t, append([]internal.WantMetric{
  1047  		{Name: "Custom/f1", Scope: "", Forced: false, Data: nil},
  1048  		{Name: "Custom/f1", Scope: scope, Forced: false, Data: nil},
  1049  		{Name: "Custom/f3", Scope: "", Forced: false, Data: nil},
  1050  		{Name: "Custom/f3", Scope: scope, Forced: false, Data: nil},
  1051  	}, webMetrics...))
  1052  }
  1053  
  1054  func TestTraceSegmentNilTxn(t *testing.T) {
  1055  	app := testApp(nil, nil, t)
  1056  	txn := app.StartTransaction("hello", nil, helloRequest)
  1057  	s := Segment{Name: "hello"}
  1058  	err := s.End()
  1059  	if err != nil {
  1060  		t.Error(err)
  1061  	}
  1062  	txn.End()
  1063  	app.ExpectMetrics(t, webMetrics)
  1064  }
  1065  
  1066  func TestTraceDatastore(t *testing.T) {
  1067  	app := testApp(nil, nil, t)
  1068  	txn := app.StartTransaction("hello", nil, helloRequest)
  1069  	s := DatastoreSegment{}
  1070  	s.StartTime = txn.StartSegmentNow()
  1071  	s.Product = DatastoreMySQL
  1072  	s.Collection = "my_table"
  1073  	s.Operation = "SELECT"
  1074  	err := s.End()
  1075  	if nil != err {
  1076  		t.Error(err)
  1077  	}
  1078  	txn.NoticeError(myError{})
  1079  	txn.End()
  1080  	scope := "WebTransaction/Go/hello"
  1081  	app.ExpectMetrics(t, append([]internal.WantMetric{
  1082  		{Name: "Datastore/all", Scope: "", Forced: true, Data: nil},
  1083  		{Name: "Datastore/allWeb", Scope: "", Forced: true, Data: nil},
  1084  		{Name: "Datastore/MySQL/all", Scope: "", Forced: true, Data: nil},
  1085  		{Name: "Datastore/MySQL/allWeb", Scope: "", Forced: true, Data: nil},
  1086  		{Name: "Datastore/operation/MySQL/SELECT", Scope: "", Forced: false, Data: nil},
  1087  		{Name: "Datastore/statement/MySQL/my_table/SELECT", Scope: "", Forced: false, Data: nil},
  1088  		{Name: "Datastore/statement/MySQL/my_table/SELECT", Scope: scope, Forced: false, Data: nil},
  1089  	}, webErrorMetrics...))
  1090  	app.ExpectErrorEvents(t, []internal.WantEvent{{
  1091  		Intrinsics: map[string]interface{}{
  1092  			"error.class":       "newrelic.myError",
  1093  			"error.message":     "my msg",
  1094  			"transactionName":   "WebTransaction/Go/hello",
  1095  			"databaseCallCount": 1,
  1096  			"databaseDuration":  internal.MatchAnything,
  1097  		},
  1098  	}})
  1099  	app.ExpectTxnEvents(t, []internal.WantEvent{{
  1100  		Intrinsics: map[string]interface{}{
  1101  			"name":              "WebTransaction/Go/hello",
  1102  			"nr.apdexPerfZone":  "F",
  1103  			"databaseCallCount": 1,
  1104  			"databaseDuration":  internal.MatchAnything,
  1105  		},
  1106  	}})
  1107  }
  1108  
  1109  func TestTraceDatastoreBackground(t *testing.T) {
  1110  	app := testApp(nil, nil, t)
  1111  	txn := app.StartTransaction("hello", nil, nil)
  1112  	s := DatastoreSegment{
  1113  		StartTime:  txn.StartSegmentNow(),
  1114  		Product:    DatastoreMySQL,
  1115  		Collection: "my_table",
  1116  		Operation:  "SELECT",
  1117  	}
  1118  	err := s.End()
  1119  	if nil != err {
  1120  		t.Error(err)
  1121  	}
  1122  	txn.NoticeError(myError{})
  1123  	txn.End()
  1124  	scope := "OtherTransaction/Go/hello"
  1125  	app.ExpectMetrics(t, append([]internal.WantMetric{
  1126  		{Name: "Datastore/all", Scope: "", Forced: true, Data: nil},
  1127  		{Name: "Datastore/allOther", Scope: "", Forced: true, Data: nil},
  1128  		{Name: "Datastore/MySQL/all", Scope: "", Forced: true, Data: nil},
  1129  		{Name: "Datastore/MySQL/allOther", Scope: "", Forced: true, Data: nil},
  1130  		{Name: "Datastore/operation/MySQL/SELECT", Scope: "", Forced: false, Data: nil},
  1131  		{Name: "Datastore/statement/MySQL/my_table/SELECT", Scope: "", Forced: false, Data: nil},
  1132  		{Name: "Datastore/statement/MySQL/my_table/SELECT", Scope: scope, Forced: false, Data: nil},
  1133  	}, backgroundErrorMetrics...))
  1134  	app.ExpectErrorEvents(t, []internal.WantEvent{{
  1135  		Intrinsics: map[string]interface{}{
  1136  			"error.class":       "newrelic.myError",
  1137  			"error.message":     "my msg",
  1138  			"transactionName":   "OtherTransaction/Go/hello",
  1139  			"databaseCallCount": 1,
  1140  			"databaseDuration":  internal.MatchAnything,
  1141  		},
  1142  	}})
  1143  	app.ExpectTxnEvents(t, []internal.WantEvent{{
  1144  		Intrinsics: map[string]interface{}{
  1145  			"name":              "OtherTransaction/Go/hello",
  1146  			"databaseCallCount": 1,
  1147  			"databaseDuration":  internal.MatchAnything,
  1148  		},
  1149  	}})
  1150  }
  1151  
  1152  func TestTraceDatastoreMissingProductOperationCollection(t *testing.T) {
  1153  	app := testApp(nil, nil, t)
  1154  	txn := app.StartTransaction("hello", nil, helloRequest)
  1155  	s := DatastoreSegment{
  1156  		StartTime: txn.StartSegmentNow(),
  1157  	}
  1158  	err := s.End()
  1159  	if nil != err {
  1160  		t.Error(err)
  1161  	}
  1162  	txn.NoticeError(myError{})
  1163  	txn.End()
  1164  	scope := "WebTransaction/Go/hello"
  1165  	app.ExpectMetrics(t, append([]internal.WantMetric{
  1166  		{Name: "Datastore/all", Scope: "", Forced: true, Data: nil},
  1167  		{Name: "Datastore/allWeb", Scope: "", Forced: true, Data: nil},
  1168  		{Name: "Datastore/Unknown/all", Scope: "", Forced: true, Data: nil},
  1169  		{Name: "Datastore/Unknown/allWeb", Scope: "", Forced: true, Data: nil},
  1170  		{Name: "Datastore/operation/Unknown/other", Scope: "", Forced: false, Data: nil},
  1171  		{Name: "Datastore/operation/Unknown/other", Scope: scope, Forced: false, Data: nil},
  1172  	}, webErrorMetrics...))
  1173  	app.ExpectErrorEvents(t, []internal.WantEvent{{
  1174  		Intrinsics: map[string]interface{}{
  1175  			"error.class":       "newrelic.myError",
  1176  			"error.message":     "my msg",
  1177  			"transactionName":   "WebTransaction/Go/hello",
  1178  			"databaseCallCount": 1,
  1179  			"databaseDuration":  internal.MatchAnything,
  1180  		},
  1181  	}})
  1182  	app.ExpectTxnEvents(t, []internal.WantEvent{{
  1183  		Intrinsics: map[string]interface{}{
  1184  			"name":              "WebTransaction/Go/hello",
  1185  			"nr.apdexPerfZone":  "F",
  1186  			"databaseCallCount": 1,
  1187  			"databaseDuration":  internal.MatchAnything,
  1188  		},
  1189  	}})
  1190  }
  1191  
  1192  func TestTraceDatastoreNilTxn(t *testing.T) {
  1193  	app := testApp(nil, nil, t)
  1194  	txn := app.StartTransaction("hello", nil, helloRequest)
  1195  	var s DatastoreSegment
  1196  	s.Product = DatastoreMySQL
  1197  	s.Collection = "my_table"
  1198  	s.Operation = "SELECT"
  1199  	err := s.End()
  1200  	if nil != err {
  1201  		t.Error(err)
  1202  	}
  1203  	txn.NoticeError(myError{})
  1204  	txn.End()
  1205  	app.ExpectMetrics(t, webErrorMetrics)
  1206  	app.ExpectErrorEvents(t, []internal.WantEvent{{
  1207  		Intrinsics: map[string]interface{}{
  1208  			"error.class":     "newrelic.myError",
  1209  			"error.message":   "my msg",
  1210  			"transactionName": "WebTransaction/Go/hello",
  1211  		},
  1212  	}})
  1213  	app.ExpectTxnEvents(t, []internal.WantEvent{{
  1214  		Intrinsics: map[string]interface{}{
  1215  			"name":             "WebTransaction/Go/hello",
  1216  			"nr.apdexPerfZone": "F",
  1217  		},
  1218  	}})
  1219  }
  1220  
  1221  func TestTraceDatastoreTxnEnded(t *testing.T) {
  1222  	app := testApp(nil, nil, t)
  1223  	txn := app.StartTransaction("hello", nil, helloRequest)
  1224  	txn.NoticeError(myError{})
  1225  	s := DatastoreSegment{
  1226  		StartTime:  txn.StartSegmentNow(),
  1227  		Product:    DatastoreMySQL,
  1228  		Collection: "my_table",
  1229  		Operation:  "SELECT",
  1230  	}
  1231  	txn.End()
  1232  	err := s.End()
  1233  	if errAlreadyEnded != err {
  1234  		t.Error(err)
  1235  	}
  1236  	app.ExpectMetrics(t, webErrorMetrics)
  1237  	app.ExpectErrorEvents(t, []internal.WantEvent{{
  1238  		Intrinsics: map[string]interface{}{
  1239  			"error.class":     "newrelic.myError",
  1240  			"error.message":   "my msg",
  1241  			"transactionName": "WebTransaction/Go/hello",
  1242  		},
  1243  	}})
  1244  	app.ExpectTxnEvents(t, []internal.WantEvent{{
  1245  		Intrinsics: map[string]interface{}{
  1246  			"name":             "WebTransaction/Go/hello",
  1247  			"nr.apdexPerfZone": "F",
  1248  		},
  1249  	}})
  1250  }
  1251  
  1252  func TestTraceExternal(t *testing.T) {
  1253  	app := testApp(nil, nil, t)
  1254  	txn := app.StartTransaction("hello", nil, helloRequest)
  1255  	s := ExternalSegment{
  1256  		StartTime: txn.StartSegmentNow(),
  1257  		URL:       "http://example.com/",
  1258  	}
  1259  	err := s.End()
  1260  	if nil != err {
  1261  		t.Error(err)
  1262  	}
  1263  	txn.NoticeError(myError{})
  1264  	txn.End()
  1265  	scope := "WebTransaction/Go/hello"
  1266  	app.ExpectMetrics(t, append([]internal.WantMetric{
  1267  		{Name: "External/all", Scope: "", Forced: true, Data: nil},
  1268  		{Name: "External/allWeb", Scope: "", Forced: true, Data: nil},
  1269  		{Name: "External/example.com/all", Scope: "", Forced: false, Data: nil},
  1270  		{Name: "External/example.com/http", Scope: scope, Forced: false, Data: nil},
  1271  	}, webErrorMetrics...))
  1272  	app.ExpectErrorEvents(t, []internal.WantEvent{{
  1273  		Intrinsics: map[string]interface{}{
  1274  			"error.class":       "newrelic.myError",
  1275  			"error.message":     "my msg",
  1276  			"transactionName":   "WebTransaction/Go/hello",
  1277  			"externalCallCount": 1,
  1278  			"externalDuration":  internal.MatchAnything,
  1279  		},
  1280  	}})
  1281  	app.ExpectTxnEvents(t, []internal.WantEvent{{
  1282  		Intrinsics: map[string]interface{}{
  1283  			"name":              "WebTransaction/Go/hello",
  1284  			"nr.apdexPerfZone":  "F",
  1285  			"externalCallCount": 1,
  1286  			"externalDuration":  internal.MatchAnything,
  1287  		},
  1288  	}})
  1289  }
  1290  
  1291  func TestExternalSegmentCustomFieldsWithURL(t *testing.T) {
  1292  	replyfn := func(reply *internal.ConnectReply) {
  1293  		reply.AdaptiveSampler = internal.SampleEverything{}
  1294  	}
  1295  	cfgfn := func(cfg *Config) {
  1296  		cfg.DistributedTracer.Enabled = true
  1297  		cfg.CrossApplicationTracer.Enabled = false
  1298  	}
  1299  	app := testApp(replyfn, cfgfn, t)
  1300  	txn := app.StartTransaction("hello", nil, helloRequest)
  1301  	s := ExternalSegment{
  1302  		StartTime: txn.StartSegmentNow(),
  1303  		URL:       "https://otherhost.com/path/zip/zap?secret=ssshhh",
  1304  		Host:      "bufnet",
  1305  		Procedure: "TestApplication/DoUnaryUnary",
  1306  		Library:   "grpc",
  1307  	}
  1308  	err := s.End()
  1309  	if nil != err {
  1310  		t.Error(err)
  1311  	}
  1312  	txn.End()
  1313  	scope := "WebTransaction/Go/hello"
  1314  	app.ExpectMetrics(t, append([]internal.WantMetric{
  1315  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil},
  1316  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", Scope: "", Forced: false, Data: nil},
  1317  		{Name: "External/all", Scope: "", Forced: true, Data: nil},
  1318  		{Name: "External/allWeb", Scope: "", Forced: true, Data: nil},
  1319  		{Name: "External/bufnet/all", Scope: "", Forced: false, Data: nil},
  1320  		{Name: "External/bufnet/grpc/TestApplication/DoUnaryUnary", Scope: scope, Forced: false, Data: nil},
  1321  	}, webMetrics...))
  1322  	app.ExpectSpanEvents(t, []internal.WantEvent{
  1323  		{
  1324  			Intrinsics: map[string]interface{}{
  1325  				"name":          "WebTransaction/Go/hello",
  1326  				"sampled":       true,
  1327  				"category":      "generic",
  1328  				"nr.entryPoint": true,
  1329  			},
  1330  			UserAttributes:  map[string]interface{}{},
  1331  			AgentAttributes: map[string]interface{}{},
  1332  		},
  1333  		{
  1334  			Intrinsics: map[string]interface{}{
  1335  				"parentId":  internal.MatchAnything,
  1336  				"name":      "External/bufnet/grpc/TestApplication/DoUnaryUnary",
  1337  				"category":  "http",
  1338  				"component": "grpc",
  1339  				"span.kind": "client",
  1340  			},
  1341  			UserAttributes:  map[string]interface{}{},
  1342  			AgentAttributes: map[string]interface{}{
  1343  				// "http.url" and "http.method" are not saved if
  1344  				// library is not "http".
  1345  			},
  1346  		},
  1347  	})
  1348  }
  1349  
  1350  func TestExternalSegmentCustomFieldsWithRequest(t *testing.T) {
  1351  	replyfn := func(reply *internal.ConnectReply) {
  1352  		reply.AdaptiveSampler = internal.SampleEverything{}
  1353  	}
  1354  	cfgfn := func(cfg *Config) {
  1355  		cfg.DistributedTracer.Enabled = true
  1356  		cfg.CrossApplicationTracer.Enabled = false
  1357  	}
  1358  	app := testApp(replyfn, cfgfn, t)
  1359  	txn := app.StartTransaction("hello", nil, helloRequest)
  1360  	req, _ := http.NewRequest("GET", "https://www.something.com/path/zip/zap?secret=ssshhh", nil)
  1361  	s := StartExternalSegment(txn, req)
  1362  	s.Host = "bufnet"
  1363  	s.Procedure = "TestApplication/DoUnaryUnary"
  1364  	s.Library = "grpc"
  1365  	err := s.End()
  1366  	if nil != err {
  1367  		t.Error(err)
  1368  	}
  1369  	txn.End()
  1370  	scope := "WebTransaction/Go/hello"
  1371  	app.ExpectMetrics(t, append([]internal.WantMetric{
  1372  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil},
  1373  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", Scope: "", Forced: false, Data: nil},
  1374  		{Name: "External/all", Scope: "", Forced: true, Data: nil},
  1375  		{Name: "External/allWeb", Scope: "", Forced: true, Data: nil},
  1376  		{Name: "External/bufnet/all", Scope: "", Forced: false, Data: nil},
  1377  		{Name: "External/bufnet/grpc/TestApplication/DoUnaryUnary", Scope: scope, Forced: false, Data: nil},
  1378  	}, webMetrics...))
  1379  	app.ExpectSpanEvents(t, []internal.WantEvent{
  1380  		{
  1381  			Intrinsics: map[string]interface{}{
  1382  				"name":          "WebTransaction/Go/hello",
  1383  				"sampled":       true,
  1384  				"category":      "generic",
  1385  				"nr.entryPoint": true,
  1386  			},
  1387  			UserAttributes:  map[string]interface{}{},
  1388  			AgentAttributes: map[string]interface{}{},
  1389  		},
  1390  		{
  1391  			Intrinsics: map[string]interface{}{
  1392  				"parentId":  internal.MatchAnything,
  1393  				"name":      "External/bufnet/grpc/TestApplication/DoUnaryUnary",
  1394  				"category":  "http",
  1395  				"component": "grpc",
  1396  				"span.kind": "client",
  1397  			},
  1398  			UserAttributes:  map[string]interface{}{},
  1399  			AgentAttributes: map[string]interface{}{
  1400  				// "http.url" and "http.method" are not saved if
  1401  				// library is not "http".
  1402  			},
  1403  		},
  1404  	})
  1405  }
  1406  
  1407  func TestExternalSegmentCustomFieldsWithResponse(t *testing.T) {
  1408  	replyfn := func(reply *internal.ConnectReply) {
  1409  		reply.AdaptiveSampler = internal.SampleEverything{}
  1410  	}
  1411  	cfgfn := func(cfg *Config) {
  1412  		cfg.DistributedTracer.Enabled = true
  1413  		cfg.CrossApplicationTracer.Enabled = false
  1414  	}
  1415  	app := testApp(replyfn, cfgfn, t)
  1416  	txn := app.StartTransaction("hello", nil, helloRequest)
  1417  	req, _ := http.NewRequest("GET", "https://www.something.com/path/zip/zap?secret=ssshhh", nil)
  1418  	resp := &http.Response{Request: req}
  1419  	s := ExternalSegment{
  1420  		StartTime: txn.StartSegmentNow(),
  1421  		Response:  resp,
  1422  		Host:      "bufnet",
  1423  		Procedure: "TestApplication/DoUnaryUnary",
  1424  		Library:   "grpc",
  1425  	}
  1426  	err := s.End()
  1427  	if nil != err {
  1428  		t.Error(err)
  1429  	}
  1430  	txn.End()
  1431  	scope := "WebTransaction/Go/hello"
  1432  	app.ExpectMetrics(t, append([]internal.WantMetric{
  1433  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil},
  1434  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", Scope: "", Forced: false, Data: nil},
  1435  		{Name: "External/all", Scope: "", Forced: true, Data: nil},
  1436  		{Name: "External/allWeb", Scope: "", Forced: true, Data: nil},
  1437  		{Name: "External/bufnet/all", Scope: "", Forced: false, Data: nil},
  1438  		{Name: "External/bufnet/grpc/TestApplication/DoUnaryUnary", Scope: scope, Forced: false, Data: nil},
  1439  	}, webMetrics...))
  1440  	app.ExpectSpanEvents(t, []internal.WantEvent{
  1441  		{
  1442  			Intrinsics: map[string]interface{}{
  1443  				"name":          "WebTransaction/Go/hello",
  1444  				"sampled":       true,
  1445  				"category":      "generic",
  1446  				"nr.entryPoint": true,
  1447  			},
  1448  			UserAttributes:  map[string]interface{}{},
  1449  			AgentAttributes: map[string]interface{}{},
  1450  		},
  1451  		{
  1452  			Intrinsics: map[string]interface{}{
  1453  				"parentId":  internal.MatchAnything,
  1454  				"name":      "External/bufnet/grpc/TestApplication/DoUnaryUnary",
  1455  				"category":  "http",
  1456  				"component": "grpc",
  1457  				"span.kind": "client",
  1458  			},
  1459  			UserAttributes:  map[string]interface{}{},
  1460  			AgentAttributes: map[string]interface{}{
  1461  				// "http.url" and "http.method" are not saved if
  1462  				// library is not "http".
  1463  			},
  1464  		},
  1465  	})
  1466  }
  1467  
  1468  func TestTraceExternalBadURL(t *testing.T) {
  1469  	app := testApp(nil, nil, t)
  1470  	txn := app.StartTransaction("hello", nil, helloRequest)
  1471  	s := ExternalSegment{
  1472  		StartTime: txn.StartSegmentNow(),
  1473  		URL:       ":example.com/",
  1474  	}
  1475  	err := s.End()
  1476  	if nil == err {
  1477  		t.Error(err)
  1478  	}
  1479  	txn.NoticeError(myError{})
  1480  	txn.End()
  1481  	app.ExpectMetrics(t, webErrorMetrics)
  1482  	app.ExpectErrorEvents(t, []internal.WantEvent{{
  1483  		Intrinsics: map[string]interface{}{
  1484  			"error.class":     "newrelic.myError",
  1485  			"error.message":   "my msg",
  1486  			"transactionName": "WebTransaction/Go/hello",
  1487  		},
  1488  	}})
  1489  	app.ExpectTxnEvents(t, []internal.WantEvent{{
  1490  		Intrinsics: map[string]interface{}{
  1491  			"name":             "WebTransaction/Go/hello",
  1492  			"nr.apdexPerfZone": "F",
  1493  		},
  1494  	}})
  1495  }
  1496  
  1497  func TestTraceExternalBackground(t *testing.T) {
  1498  	app := testApp(nil, nil, t)
  1499  	txn := app.StartTransaction("hello", nil, nil)
  1500  	s := ExternalSegment{
  1501  		StartTime: txn.StartSegmentNow(),
  1502  		URL:       "http://example.com/",
  1503  	}
  1504  	err := s.End()
  1505  	if nil != err {
  1506  		t.Error(err)
  1507  	}
  1508  	txn.NoticeError(myError{})
  1509  	txn.End()
  1510  	scope := "OtherTransaction/Go/hello"
  1511  	app.ExpectMetrics(t, append([]internal.WantMetric{
  1512  		{Name: "External/all", Scope: "", Forced: true, Data: nil},
  1513  		{Name: "External/allOther", Scope: "", Forced: true, Data: nil},
  1514  		{Name: "External/example.com/all", Scope: "", Forced: false, Data: nil},
  1515  		{Name: "External/example.com/http", Scope: scope, Forced: false, Data: nil},
  1516  	}, backgroundErrorMetrics...))
  1517  	app.ExpectErrorEvents(t, []internal.WantEvent{{
  1518  		Intrinsics: map[string]interface{}{
  1519  			"error.class":       "newrelic.myError",
  1520  			"error.message":     "my msg",
  1521  			"transactionName":   "OtherTransaction/Go/hello",
  1522  			"externalCallCount": 1,
  1523  			"externalDuration":  internal.MatchAnything,
  1524  		},
  1525  	}})
  1526  	app.ExpectTxnEvents(t, []internal.WantEvent{{
  1527  		Intrinsics: map[string]interface{}{
  1528  			"name":              "OtherTransaction/Go/hello",
  1529  			"externalCallCount": 1,
  1530  			"externalDuration":  internal.MatchAnything,
  1531  		},
  1532  	}})
  1533  }
  1534  
  1535  func TestTraceExternalMissingURL(t *testing.T) {
  1536  	app := testApp(nil, nil, t)
  1537  	txn := app.StartTransaction("hello", nil, helloRequest)
  1538  	s := ExternalSegment{
  1539  		StartTime: txn.StartSegmentNow(),
  1540  	}
  1541  	err := s.End()
  1542  	if nil != err {
  1543  		t.Error(err)
  1544  	}
  1545  	txn.NoticeError(myError{})
  1546  	txn.End()
  1547  	scope := "WebTransaction/Go/hello"
  1548  	app.ExpectMetrics(t, append([]internal.WantMetric{
  1549  		{Name: "External/all", Scope: "", Forced: true, Data: nil},
  1550  		{Name: "External/allWeb", Scope: "", Forced: true, Data: nil},
  1551  		{Name: "External/unknown/all", Scope: "", Forced: false, Data: nil},
  1552  		{Name: "External/unknown/http", Scope: scope, Forced: false, Data: nil},
  1553  	}, webErrorMetrics...))
  1554  	app.ExpectErrorEvents(t, []internal.WantEvent{{
  1555  		Intrinsics: map[string]interface{}{
  1556  			"error.class":       "newrelic.myError",
  1557  			"error.message":     "my msg",
  1558  			"transactionName":   "WebTransaction/Go/hello",
  1559  			"externalCallCount": 1,
  1560  			"externalDuration":  internal.MatchAnything,
  1561  		},
  1562  	}})
  1563  	app.ExpectTxnEvents(t, []internal.WantEvent{{
  1564  		Intrinsics: map[string]interface{}{
  1565  			"name":              "WebTransaction/Go/hello",
  1566  			"nr.apdexPerfZone":  "F",
  1567  			"externalCallCount": 1,
  1568  			"externalDuration":  internal.MatchAnything,
  1569  		},
  1570  	}})
  1571  }
  1572  
  1573  func TestTraceExternalNilTxn(t *testing.T) {
  1574  	app := testApp(nil, nil, t)
  1575  	txn := app.StartTransaction("hello", nil, helloRequest)
  1576  	txn.NoticeError(myError{})
  1577  	var s ExternalSegment
  1578  	err := s.End()
  1579  	if nil != err {
  1580  		t.Error(err)
  1581  	}
  1582  	txn.End()
  1583  	app.ExpectMetrics(t, webErrorMetrics)
  1584  	app.ExpectErrorEvents(t, []internal.WantEvent{{
  1585  		Intrinsics: map[string]interface{}{
  1586  			"error.class":     "newrelic.myError",
  1587  			"error.message":   "my msg",
  1588  			"transactionName": "WebTransaction/Go/hello",
  1589  		},
  1590  	}})
  1591  	app.ExpectTxnEvents(t, []internal.WantEvent{{
  1592  		Intrinsics: map[string]interface{}{
  1593  			"name":             "WebTransaction/Go/hello",
  1594  			"nr.apdexPerfZone": "F",
  1595  		},
  1596  	}})
  1597  }
  1598  
  1599  func TestTraceExternalTxnEnded(t *testing.T) {
  1600  	app := testApp(nil, nil, t)
  1601  	txn := app.StartTransaction("hello", nil, helloRequest)
  1602  	txn.NoticeError(myError{})
  1603  	s := ExternalSegment{
  1604  		StartTime: txn.StartSegmentNow(),
  1605  		URL:       "http://example.com/",
  1606  	}
  1607  	txn.End()
  1608  	err := s.End()
  1609  	if err != errAlreadyEnded {
  1610  		t.Error(err)
  1611  	}
  1612  	app.ExpectMetrics(t, webErrorMetrics)
  1613  	app.ExpectErrorEvents(t, []internal.WantEvent{{
  1614  		Intrinsics: map[string]interface{}{
  1615  			"error.class":     "newrelic.myError",
  1616  			"error.message":   "my msg",
  1617  			"transactionName": "WebTransaction/Go/hello",
  1618  		},
  1619  	}})
  1620  	app.ExpectTxnEvents(t, []internal.WantEvent{{
  1621  		Intrinsics: map[string]interface{}{
  1622  			"name":             "WebTransaction/Go/hello",
  1623  			"nr.apdexPerfZone": "F",
  1624  		},
  1625  	}})
  1626  }
  1627  
  1628  func TestRoundTripper(t *testing.T) {
  1629  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
  1630  	txn := app.StartTransaction("hello", nil, nil)
  1631  	url := "http://example.com/"
  1632  	req, err := http.NewRequest("GET", url, nil)
  1633  	if err != nil {
  1634  		t.Fatal(err)
  1635  	}
  1636  	req.Header.Add("zip", "zap")
  1637  	client := &http.Client{}
  1638  	inner := roundTripperFunc(func(r *http.Request) (*http.Response, error) {
  1639  		catHdr := r.Header.Get(DistributedTracePayloadHeader)
  1640  		if "" == catHdr {
  1641  			t.Error("cat header missing")
  1642  		}
  1643  		// Test that headers are preserved during reqest cloning:
  1644  		if z := r.Header.Get("zip"); z != "zap" {
  1645  			t.Error("missing header", z)
  1646  		}
  1647  		if r.URL.String() != url {
  1648  			t.Error(r.URL.String())
  1649  		}
  1650  		return nil, errors.New("hello")
  1651  	})
  1652  	client.Transport = NewRoundTripper(txn, inner)
  1653  	resp, err := client.Do(req)
  1654  	if resp != nil || err == nil {
  1655  		t.Error(resp, err.Error())
  1656  	}
  1657  	// Ensure that the request was cloned:
  1658  	catHdr := req.Header.Get(DistributedTracePayloadHeader)
  1659  	if "" != catHdr {
  1660  		t.Error("cat header unexpectedly present")
  1661  	}
  1662  	txn.NoticeError(myError{})
  1663  	txn.End()
  1664  	scope := "OtherTransaction/Go/hello"
  1665  	app.ExpectMetrics(t, append([]internal.WantMetric{
  1666  		{Name: "External/all", Scope: "", Forced: true, Data: nil},
  1667  		{Name: "External/allOther", Scope: "", Forced: true, Data: nil},
  1668  		{Name: "External/example.com/all", Scope: "", Forced: false, Data: nil},
  1669  		{Name: "External/example.com/http/GET", Scope: scope, Forced: false, Data: nil},
  1670  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Data: nil},
  1671  		{Name: "ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Data: nil},
  1672  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Data: nil},
  1673  		{Name: "ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Data: nil},
  1674  		{Name: "Supportability/DistributedTrace/CreatePayload/Success", Scope: "", Data: nil},
  1675  	}, backgroundErrorMetrics...))
  1676  	app.ExpectErrorEvents(t, []internal.WantEvent{{
  1677  		Intrinsics: map[string]interface{}{
  1678  			"error.class":       "newrelic.myError",
  1679  			"error.message":     "my msg",
  1680  			"transactionName":   "OtherTransaction/Go/hello",
  1681  			"externalCallCount": 1,
  1682  			"externalDuration":  internal.MatchAnything,
  1683  			"guid":              internal.MatchAnything,
  1684  			"traceId":           internal.MatchAnything,
  1685  			"priority":          internal.MatchAnything,
  1686  			"sampled":           internal.MatchAnything,
  1687  		},
  1688  	}})
  1689  	app.ExpectTxnEvents(t, []internal.WantEvent{{
  1690  		Intrinsics: map[string]interface{}{
  1691  			"name":              "OtherTransaction/Go/hello",
  1692  			"externalCallCount": 1,
  1693  			"externalDuration":  internal.MatchAnything,
  1694  			"guid":              internal.MatchAnything,
  1695  			"traceId":           internal.MatchAnything,
  1696  			"priority":          internal.MatchAnything,
  1697  			"sampled":           internal.MatchAnything,
  1698  		},
  1699  	}})
  1700  }
  1701  
  1702  func TestRoundTripperOldCAT(t *testing.T) {
  1703  	app := testApp(nil, nil, t)
  1704  	txn := app.StartTransaction("hello", nil, nil)
  1705  	url := "http://example.com/"
  1706  	client := &http.Client{}
  1707  	inner := roundTripperFunc(func(r *http.Request) (*http.Response, error) {
  1708  		// TODO test that request headers have been set here.
  1709  		if r.URL.String() != url {
  1710  			t.Error(r.URL.String())
  1711  		}
  1712  		return nil, errors.New("hello")
  1713  	})
  1714  	client.Transport = NewRoundTripper(txn, inner)
  1715  	resp, err := client.Get(url)
  1716  	if resp != nil || err == nil {
  1717  		t.Error(resp, err.Error())
  1718  	}
  1719  	txn.NoticeError(myError{})
  1720  	txn.End()
  1721  	scope := "OtherTransaction/Go/hello"
  1722  	app.ExpectMetrics(t, append([]internal.WantMetric{
  1723  		{Name: "External/all", Scope: "", Forced: true, Data: nil},
  1724  		{Name: "External/allOther", Scope: "", Forced: true, Data: nil},
  1725  		{Name: "External/example.com/all", Scope: "", Forced: false, Data: nil},
  1726  		{Name: "External/example.com/http/GET", Scope: scope, Forced: false, Data: nil},
  1727  	}, backgroundErrorMetrics...))
  1728  	app.ExpectErrorEvents(t, []internal.WantEvent{{
  1729  		Intrinsics: map[string]interface{}{
  1730  			"error.class":       "newrelic.myError",
  1731  			"error.message":     "my msg",
  1732  			"transactionName":   "OtherTransaction/Go/hello",
  1733  			"externalCallCount": 1,
  1734  			"externalDuration":  internal.MatchAnything,
  1735  		},
  1736  	}})
  1737  	app.ExpectTxnEvents(t, []internal.WantEvent{{
  1738  		Intrinsics: map[string]interface{}{
  1739  			"name":              "OtherTransaction/Go/hello",
  1740  			"externalCallCount": 1,
  1741  			"externalDuration":  internal.MatchAnything,
  1742  			"nr.tripId":         internal.MatchAnything,
  1743  			"nr.guid":           internal.MatchAnything,
  1744  			"nr.pathHash":       internal.MatchAnything,
  1745  		},
  1746  	}})
  1747  }
  1748  
  1749  func TestTraceBelowThreshold(t *testing.T) {
  1750  	app := testApp(nil, nil, t)
  1751  	txn := app.StartTransaction("hello", nil, helloRequest)
  1752  	txn.End()
  1753  	app.ExpectTxnTraces(t, []internal.WantTxnTrace{})
  1754  }
  1755  
  1756  func TestTraceBelowThresholdBackground(t *testing.T) {
  1757  	app := testApp(nil, nil, t)
  1758  	txn := app.StartTransaction("hello", nil, nil)
  1759  	txn.End()
  1760  	app.ExpectTxnTraces(t, []internal.WantTxnTrace{})
  1761  }
  1762  
  1763  func TestTraceNoSegments(t *testing.T) {
  1764  	cfgfn := func(cfg *Config) {
  1765  		cfg.TransactionTracer.Threshold.IsApdexFailing = false
  1766  		cfg.TransactionTracer.Threshold.Duration = 0
  1767  		cfg.TransactionTracer.SegmentThreshold = 0
  1768  	}
  1769  	app := testApp(nil, cfgfn, t)
  1770  	txn := app.StartTransaction("hello", nil, helloRequest)
  1771  	txn.End()
  1772  	app.ExpectTxnTraces(t, []internal.WantTxnTrace{{
  1773  		MetricName:  "WebTransaction/Go/hello",
  1774  		NumSegments: 0,
  1775  	}})
  1776  }
  1777  
  1778  func TestTraceDisabledLocally(t *testing.T) {
  1779  	cfgfn := func(cfg *Config) {
  1780  		cfg.TransactionTracer.Threshold.IsApdexFailing = false
  1781  		cfg.TransactionTracer.Threshold.Duration = 0
  1782  		cfg.TransactionTracer.SegmentThreshold = 0
  1783  		cfg.TransactionTracer.Enabled = false
  1784  	}
  1785  	app := testApp(nil, cfgfn, t)
  1786  	txn := app.StartTransaction("hello", nil, helloRequest)
  1787  	txn.End()
  1788  	app.ExpectTxnTraces(t, []internal.WantTxnTrace{})
  1789  }
  1790  
  1791  func TestTraceDisabledByServerSideConfig(t *testing.T) {
  1792  	// Test that server-side-config trace-enabled-setting can disable transaction
  1793  	// traces.
  1794  	cfgfn := func(cfg *Config) {
  1795  		cfg.TransactionTracer.Threshold.IsApdexFailing = false
  1796  		cfg.TransactionTracer.Threshold.Duration = 0
  1797  		cfg.TransactionTracer.SegmentThreshold = 0
  1798  	}
  1799  	replyfn := func(reply *internal.ConnectReply) {
  1800  		json.Unmarshal([]byte(`{"agent_config":{"transaction_tracer.enabled":false}}`), reply)
  1801  	}
  1802  	app := testApp(replyfn, cfgfn, t)
  1803  	txn := app.StartTransaction("hello", nil, helloRequest)
  1804  	txn.End()
  1805  	app.ExpectTxnTraces(t, []internal.WantTxnTrace{})
  1806  }
  1807  
  1808  func TestTraceEnabledByServerSideConfig(t *testing.T) {
  1809  	// Test that server-side-config trace-enabled-setting can enable
  1810  	// transaction traces (and hence server-side-config has priority).
  1811  	cfgfn := func(cfg *Config) {
  1812  		cfg.TransactionTracer.Threshold.IsApdexFailing = false
  1813  		cfg.TransactionTracer.Threshold.Duration = 0
  1814  		cfg.TransactionTracer.SegmentThreshold = 0
  1815  		cfg.TransactionTracer.Enabled = false
  1816  	}
  1817  	replyfn := func(reply *internal.ConnectReply) {
  1818  		json.Unmarshal([]byte(`{"agent_config":{"transaction_tracer.enabled":true}}`), reply)
  1819  	}
  1820  	app := testApp(replyfn, cfgfn, t)
  1821  	txn := app.StartTransaction("hello", nil, helloRequest)
  1822  	txn.End()
  1823  	app.ExpectTxnTraces(t, []internal.WantTxnTrace{{
  1824  		MetricName:  "WebTransaction/Go/hello",
  1825  		NumSegments: 0,
  1826  	}})
  1827  }
  1828  
  1829  func TestTraceDisabledRemotelyOverridesServerSideConfig(t *testing.T) {
  1830  	// Test that the connect reply "collect_traces" setting overrides the
  1831  	// "transaction_tracer.enabled" server side config setting.
  1832  	cfgfn := func(cfg *Config) {
  1833  		cfg.TransactionTracer.Threshold.IsApdexFailing = false
  1834  		cfg.TransactionTracer.Threshold.Duration = 0
  1835  		cfg.TransactionTracer.SegmentThreshold = 0
  1836  		cfg.TransactionTracer.Enabled = true
  1837  	}
  1838  	replyfn := func(reply *internal.ConnectReply) {
  1839  		json.Unmarshal([]byte(`{"agent_config":{"transaction_tracer.enabled":true},"collect_traces":false}`), reply)
  1840  	}
  1841  	app := testApp(replyfn, cfgfn, t)
  1842  	txn := app.StartTransaction("hello", nil, helloRequest)
  1843  	txn.End()
  1844  	app.ExpectTxnTraces(t, []internal.WantTxnTrace{})
  1845  }
  1846  
  1847  func TestTraceDisabledRemotely(t *testing.T) {
  1848  	cfgfn := func(cfg *Config) {
  1849  		cfg.TransactionTracer.Threshold.IsApdexFailing = false
  1850  		cfg.TransactionTracer.Threshold.Duration = 0
  1851  		cfg.TransactionTracer.SegmentThreshold = 0
  1852  	}
  1853  	replyfn := func(reply *internal.ConnectReply) {
  1854  		reply.CollectTraces = false
  1855  	}
  1856  	app := testApp(replyfn, cfgfn, t)
  1857  	txn := app.StartTransaction("hello", nil, helloRequest)
  1858  	txn.End()
  1859  	app.ExpectTxnTraces(t, []internal.WantTxnTrace{})
  1860  }
  1861  
  1862  func TestTraceWithSegments(t *testing.T) {
  1863  	cfgfn := func(cfg *Config) {
  1864  		cfg.TransactionTracer.Threshold.IsApdexFailing = false
  1865  		cfg.TransactionTracer.Threshold.Duration = 0
  1866  		cfg.TransactionTracer.SegmentThreshold = 0
  1867  	}
  1868  	app := testApp(nil, cfgfn, t)
  1869  	txn := app.StartTransaction("hello", nil, helloRequest)
  1870  	s1 := StartSegment(txn, "s1")
  1871  	s1.End()
  1872  	s2 := ExternalSegment{
  1873  		StartTime: StartSegmentNow(txn),
  1874  		URL:       "http://example.com",
  1875  	}
  1876  	s2.End()
  1877  	s3 := DatastoreSegment{
  1878  		StartTime:  StartSegmentNow(txn),
  1879  		Product:    DatastoreMySQL,
  1880  		Collection: "my_table",
  1881  		Operation:  "SELECT",
  1882  	}
  1883  	s3.End()
  1884  	txn.End()
  1885  	app.ExpectTxnTraces(t, []internal.WantTxnTrace{{
  1886  		MetricName:  "WebTransaction/Go/hello",
  1887  		NumSegments: 3,
  1888  	}})
  1889  }
  1890  
  1891  func TestTraceSegmentsBelowThreshold(t *testing.T) {
  1892  	cfgfn := func(cfg *Config) {
  1893  		cfg.TransactionTracer.Threshold.IsApdexFailing = false
  1894  		cfg.TransactionTracer.Threshold.Duration = 0
  1895  		cfg.TransactionTracer.SegmentThreshold = 1 * time.Hour
  1896  	}
  1897  	app := testApp(nil, cfgfn, t)
  1898  	txn := app.StartTransaction("hello", nil, helloRequest)
  1899  	s1 := StartSegment(txn, "s1")
  1900  	s1.End()
  1901  	s2 := ExternalSegment{
  1902  		StartTime: StartSegmentNow(txn),
  1903  		URL:       "http://example.com",
  1904  	}
  1905  	s2.End()
  1906  	s3 := DatastoreSegment{
  1907  		StartTime:  StartSegmentNow(txn),
  1908  		Product:    DatastoreMySQL,
  1909  		Collection: "my_table",
  1910  		Operation:  "SELECT",
  1911  	}
  1912  	s3.End()
  1913  	txn.End()
  1914  	app.ExpectTxnTraces(t, []internal.WantTxnTrace{{
  1915  		MetricName:  "WebTransaction/Go/hello",
  1916  		NumSegments: 0,
  1917  	}})
  1918  }
  1919  
  1920  func TestNoticeErrorTxnEvents(t *testing.T) {
  1921  	app := testApp(nil, nil, t)
  1922  	txn := app.StartTransaction("hello", nil, nil)
  1923  	err := txn.NoticeError(myError{})
  1924  	if nil != err {
  1925  		t.Error(err)
  1926  	}
  1927  	txn.End()
  1928  	app.ExpectTxnEvents(t, []internal.WantEvent{{
  1929  		Intrinsics: map[string]interface{}{
  1930  			"name":  "OtherTransaction/Go/hello",
  1931  			"error": true,
  1932  		},
  1933  	}})
  1934  }
  1935  
  1936  func TestTransactionApplication(t *testing.T) {
  1937  	txn := testApp(nil, nil, t).StartTransaction("hello", nil, nil)
  1938  	app := txn.Application()
  1939  	err := app.RecordCustomMetric("myMetric", 123.0)
  1940  	if nil != err {
  1941  		t.Error(err)
  1942  	}
  1943  	expectData := []float64{1, 123.0, 123.0, 123.0, 123.0, 123.0 * 123.0}
  1944  	app.(expectApp).ExpectMetrics(t, []internal.WantMetric{
  1945  		{Name: "Custom/myMetric", Scope: "", Forced: false, Data: expectData},
  1946  	})
  1947  }
  1948  
  1949  func TestNilSegmentPointerEnd(t *testing.T) {
  1950  	var basicSegment *Segment
  1951  	var datastoreSegment *DatastoreSegment
  1952  	var externalSegment *ExternalSegment
  1953  
  1954  	// These calls on nil pointer receivers should not panic.
  1955  	basicSegment.End()
  1956  	datastoreSegment.End()
  1957  	externalSegment.End()
  1958  }
  1959  
  1960  type flushWriter struct{}
  1961  
  1962  func (f flushWriter) WriteHeader(int)           {}
  1963  func (f flushWriter) Write([]byte) (int, error) { return 0, nil }
  1964  func (f flushWriter) Header() http.Header       { return nil }
  1965  func (f flushWriter) Flush()                    {}
  1966  
  1967  func TestAsync(t *testing.T) {
  1968  	app := testApp(nil, nil, t)
  1969  	txn := app.StartTransaction("hello", flushWriter{}, nil)
  1970  	if _, ok := txn.(http.Flusher); !ok {
  1971  		t.Error("transaction should have flush")
  1972  	}
  1973  	s1 := StartSegment(txn, "mainThread")
  1974  	asyncThread := txn.NewGoroutine()
  1975  	// Test that the async transaction reference has the correct optional
  1976  	// interface behavior.
  1977  	if _, ok := asyncThread.(http.Flusher); !ok {
  1978  		t.Error("async transaction reference should have flush")
  1979  	}
  1980  	s2 := StartSegment(asyncThread, "asyncThread")
  1981  	// End segments in interleaved order.
  1982  	s1.End()
  1983  	s2.End()
  1984  	// Test that the async transaction reference has the expected
  1985  	// transaction method behavior.
  1986  	asyncThread.AddAttribute("zip", "zap")
  1987  	// Test that the transaction ends when the async transaction is ended.
  1988  	if err := asyncThread.End(); nil != err {
  1989  		t.Error(err)
  1990  	}
  1991  	threadAfterEnd := asyncThread.NewGoroutine()
  1992  	if _, ok := threadAfterEnd.(http.Flusher); !ok {
  1993  		t.Error("after end transaction reference should have flush")
  1994  	}
  1995  	if err := threadAfterEnd.End(); err != errAlreadyEnded {
  1996  		t.Error(err)
  1997  	}
  1998  	app.ExpectTxnEvents(t, []internal.WantEvent{{
  1999  		Intrinsics: map[string]interface{}{
  2000  			"name": "OtherTransaction/Go/hello",
  2001  		},
  2002  		UserAttributes: map[string]interface{}{
  2003  			"zip": "zap",
  2004  		},
  2005  	}})
  2006  	app.ExpectMetrics(t, []internal.WantMetric{
  2007  		{Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
  2008  		{Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil},
  2009  		{Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil},
  2010  		{Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
  2011  		{Name: "Custom/mainThread", Scope: "", Forced: false, Data: nil},
  2012  		{Name: "Custom/mainThread", Scope: "OtherTransaction/Go/hello", Forced: false, Data: nil},
  2013  		{Name: "Custom/asyncThread", Scope: "", Forced: false, Data: nil},
  2014  		{Name: "Custom/asyncThread", Scope: "OtherTransaction/Go/hello", Forced: false, Data: nil},
  2015  	})
  2016  }
  2017  
  2018  func TestMessageProducerSegmentBasic(t *testing.T) {
  2019  	replyfn := func(reply *internal.ConnectReply) {
  2020  		reply.AdaptiveSampler = internal.SampleEverything{}
  2021  	}
  2022  	cfgfn := func(cfg *Config) {
  2023  		cfg.DistributedTracer.Enabled = true
  2024  	}
  2025  	app := testApp(replyfn, cfgfn, t)
  2026  	txn := app.StartTransaction("hello", nil, nil)
  2027  	s := MessageProducerSegment{
  2028  		StartTime:       StartSegmentNow(txn),
  2029  		Library:         "RabbitMQ",
  2030  		DestinationType: MessageQueue,
  2031  		DestinationName: "myQueue",
  2032  	}
  2033  	err := s.End()
  2034  	if err != nil {
  2035  		t.Error(err)
  2036  	}
  2037  	txn.End()
  2038  	app.ExpectMetrics(t, []internal.WantMetric{
  2039  		{Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
  2040  		{Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil},
  2041  		{Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil},
  2042  		{Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
  2043  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil},
  2044  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil},
  2045  		{Name: "MessageBroker/RabbitMQ/Queue/Produce/Named/myQueue", Scope: "", Forced: false, Data: nil},
  2046  		{Name: "MessageBroker/RabbitMQ/Queue/Produce/Named/myQueue", Scope: "OtherTransaction/Go/hello", Forced: false, Data: nil},
  2047  	})
  2048  	app.ExpectSpanEvents(t, []internal.WantEvent{
  2049  		{
  2050  			Intrinsics: map[string]interface{}{
  2051  				"name":          "OtherTransaction/Go/hello",
  2052  				"sampled":       true,
  2053  				"category":      "generic",
  2054  				"nr.entryPoint": true,
  2055  			},
  2056  			UserAttributes:  map[string]interface{}{},
  2057  			AgentAttributes: map[string]interface{}{},
  2058  		},
  2059  		{
  2060  			Intrinsics: map[string]interface{}{
  2061  				"parentId": internal.MatchAnything,
  2062  				"name":     "MessageBroker/RabbitMQ/Queue/Produce/Named/myQueue",
  2063  				"category": "generic",
  2064  			},
  2065  			UserAttributes:  map[string]interface{}{},
  2066  			AgentAttributes: map[string]interface{}{},
  2067  		},
  2068  	})
  2069  }
  2070  
  2071  func TestMessageProducerSegmentMissingDestinationType(t *testing.T) {
  2072  	app := testApp(nil, nil, t)
  2073  	txn := app.StartTransaction("hello", nil, nil)
  2074  	s := MessageProducerSegment{
  2075  		StartTime:       StartSegmentNow(txn),
  2076  		Library:         "RabbitMQ",
  2077  		DestinationName: "myQueue",
  2078  	}
  2079  	err := s.End()
  2080  	if err != nil {
  2081  		t.Error(err)
  2082  	}
  2083  	txn.End()
  2084  	app.ExpectMetrics(t, []internal.WantMetric{
  2085  		{Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
  2086  		{Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil},
  2087  		{Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil},
  2088  		{Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
  2089  		{Name: "MessageBroker/RabbitMQ/Queue/Produce/Named/myQueue", Scope: "", Forced: false, Data: nil},
  2090  		{Name: "MessageBroker/RabbitMQ/Queue/Produce/Named/myQueue", Scope: "OtherTransaction/Go/hello", Forced: false, Data: nil},
  2091  	})
  2092  }
  2093  
  2094  func TestMessageProducerSegmentTemp(t *testing.T) {
  2095  	app := testApp(nil, nil, t)
  2096  	txn := app.StartTransaction("hello", nil, nil)
  2097  	s := MessageProducerSegment{
  2098  		StartTime:            StartSegmentNow(txn),
  2099  		Library:              "RabbitMQ",
  2100  		DestinationType:      MessageQueue,
  2101  		DestinationTemporary: true,
  2102  		DestinationName:      "myQueue0123456789",
  2103  	}
  2104  	err := s.End()
  2105  	if err != nil {
  2106  		t.Error(err)
  2107  	}
  2108  	txn.End()
  2109  	app.ExpectMetrics(t, []internal.WantMetric{
  2110  		{Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
  2111  		{Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil},
  2112  		{Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil},
  2113  		{Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
  2114  		{Name: "MessageBroker/RabbitMQ/Queue/Produce/Temp", Scope: "", Forced: false, Data: nil},
  2115  		{Name: "MessageBroker/RabbitMQ/Queue/Produce/Temp", Scope: "OtherTransaction/Go/hello", Forced: false, Data: nil},
  2116  	})
  2117  }
  2118  
  2119  func TestMessageProducerSegmentNoName(t *testing.T) {
  2120  	app := testApp(nil, nil, t)
  2121  	txn := app.StartTransaction("hello", nil, nil)
  2122  	s := MessageProducerSegment{
  2123  		StartTime:       StartSegmentNow(txn),
  2124  		Library:         "RabbitMQ",
  2125  		DestinationType: MessageQueue,
  2126  	}
  2127  	err := s.End()
  2128  	if err != nil {
  2129  		t.Error(err)
  2130  	}
  2131  	txn.End()
  2132  	app.ExpectMetrics(t, []internal.WantMetric{
  2133  		{Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
  2134  		{Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil},
  2135  		{Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil},
  2136  		{Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
  2137  		{Name: "MessageBroker/RabbitMQ/Queue/Produce/Named/Unknown", Scope: "", Forced: false, Data: nil},
  2138  		{Name: "MessageBroker/RabbitMQ/Queue/Produce/Named/Unknown", Scope: "OtherTransaction/Go/hello", Forced: false, Data: nil},
  2139  	})
  2140  }
  2141  
  2142  func TestMessageProducerSegmentTxnEnded(t *testing.T) {
  2143  	app := testApp(nil, nil, t)
  2144  	txn := app.StartTransaction("hello", nil, nil)
  2145  	s := MessageProducerSegment{
  2146  		StartTime:            StartSegmentNow(txn),
  2147  		Library:              "RabbitMQ",
  2148  		DestinationType:      MessageQueue,
  2149  		DestinationTemporary: true,
  2150  		DestinationName:      "myQueue0123456789",
  2151  	}
  2152  	txn.End()
  2153  	err := s.End()
  2154  	if err != errAlreadyEnded {
  2155  		t.Error("expected already ended error", err)
  2156  	}
  2157  	app.ExpectMetrics(t, []internal.WantMetric{
  2158  		{Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
  2159  		{Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil},
  2160  		{Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil},
  2161  		{Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
  2162  	})
  2163  }
  2164  
  2165  func TestMessageProducerSegmentNilTxn(t *testing.T) {
  2166  	s := MessageProducerSegment{
  2167  		StartTime:            StartSegmentNow(nil),
  2168  		Library:              "RabbitMQ",
  2169  		DestinationType:      MessageQueue,
  2170  		DestinationTemporary: true,
  2171  		DestinationName:      "myQueue0123456789",
  2172  	}
  2173  	s.End()
  2174  }
  2175  
  2176  func TestMessageProducerSegmentNilSegment(t *testing.T) {
  2177  	var s *MessageProducerSegment
  2178  	s.End()
  2179  }