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  }