github.com/newrelic/go-agent@v3.26.0+incompatible/internal_set_web_request_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  	"net/http"
     8  	"net/url"
     9  	"testing"
    10  
    11  	"github.com/newrelic/go-agent/internal"
    12  )
    13  
    14  type customRequest struct {
    15  	header    http.Header
    16  	u         *url.URL
    17  	method    string
    18  	transport TransportType
    19  }
    20  
    21  func (r customRequest) Header() http.Header      { return r.header }
    22  func (r customRequest) URL() *url.URL            { return r.u }
    23  func (r customRequest) Method() string           { return r.method }
    24  func (r customRequest) Transport() TransportType { return r.transport }
    25  
    26  var (
    27  	sampleHTTPRequest = func() *http.Request {
    28  		req, err := http.NewRequest("GET", "http://www.newrelic.com", nil)
    29  		if nil != err {
    30  			panic(err)
    31  		}
    32  		req.Header.Set("Accept", "myaccept")
    33  		req.Header.Set("Content-Type", "mycontent")
    34  		req.Header.Set("Host", "myhost")
    35  		req.Header.Set("Content-Length", "123")
    36  		return req
    37  	}()
    38  	sampleCustomRequest = func() customRequest {
    39  		u, err := url.Parse("http://www.newrelic.com")
    40  		if nil != err {
    41  			panic(err)
    42  		}
    43  		hdr := make(http.Header)
    44  		hdr.Set("Accept", "myaccept")
    45  		hdr.Set("Content-Type", "mycontent")
    46  		hdr.Set("Host", "myhost")
    47  		hdr.Set("Content-Length", "123")
    48  		return customRequest{
    49  			header:    hdr,
    50  			u:         u,
    51  			method:    "GET",
    52  			transport: TransportHTTP,
    53  		}
    54  	}()
    55  	sampleRequestAgentAttributes = map[string]interface{}{
    56  		AttributeRequestMethod:        "GET",
    57  		AttributeRequestAccept:        "myaccept",
    58  		AttributeRequestContentType:   "mycontent",
    59  		AttributeRequestContentLength: 123,
    60  		AttributeRequestHost:          "myhost",
    61  		AttributeRequestURI:           "http://www.newrelic.com",
    62  	}
    63  )
    64  
    65  func TestSetWebRequestNil(t *testing.T) {
    66  	// Test that using SetWebRequest with nil marks the transaction as a web
    67  	// transaction.
    68  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
    69  	txn := app.StartTransaction("hello", nil, nil)
    70  	err := txn.SetWebRequest(nil)
    71  	if err != nil {
    72  		t.Error("unexpected error", err)
    73  	}
    74  	txn.End()
    75  	app.ExpectMetrics(t, []internal.WantMetric{
    76  		{Name: "WebTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
    77  		{Name: "WebTransaction", Scope: "", Forced: true, Data: nil},
    78  		{Name: "WebTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
    79  		{Name: "WebTransactionTotalTime", Scope: "", Forced: true, Data: nil},
    80  		{Name: "HttpDispatcher", Scope: "", Forced: true, Data: nil},
    81  		{Name: "Apdex", Scope: "", Forced: true, Data: nil},
    82  		{Name: "Apdex/Go/hello", Scope: "", Forced: false, Data: nil},
    83  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil},
    84  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", Scope: "", Forced: false, Data: nil},
    85  	})
    86  	app.ExpectTxnEvents(t, []internal.WantEvent{{
    87  		AgentAttributes: map[string]interface{}{},
    88  		Intrinsics: map[string]interface{}{
    89  			"name":             "WebTransaction/Go/hello",
    90  			"guid":             internal.MatchAnything,
    91  			"sampled":          internal.MatchAnything,
    92  			"priority":         internal.MatchAnything,
    93  			"traceId":          internal.MatchAnything,
    94  			"nr.apdexPerfZone": internal.MatchAnything,
    95  		},
    96  	}})
    97  }
    98  
    99  func TestSetWebRequestNilPointer(t *testing.T) {
   100  	// Test that calling NewWebRequest with a nil pointer is safe and
   101  	// returns a nil interface that SetWebRequest handles safely.
   102  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   103  	txn := app.StartTransaction("hello", nil, nil)
   104  	err := txn.SetWebRequest(NewWebRequest(nil))
   105  	if err != nil {
   106  		t.Error("unexpected error", err)
   107  	}
   108  	txn.End()
   109  	app.ExpectMetrics(t, []internal.WantMetric{
   110  		{Name: "WebTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
   111  		{Name: "WebTransaction", Scope: "", Forced: true, Data: nil},
   112  		{Name: "WebTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
   113  		{Name: "WebTransactionTotalTime", Scope: "", Forced: true, Data: nil},
   114  		{Name: "HttpDispatcher", Scope: "", Forced: true, Data: nil},
   115  		{Name: "Apdex", Scope: "", Forced: true, Data: nil},
   116  		{Name: "Apdex/Go/hello", Scope: "", Forced: false, Data: nil},
   117  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil},
   118  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", Scope: "", Forced: false, Data: nil},
   119  	})
   120  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   121  		AgentAttributes: map[string]interface{}{},
   122  		Intrinsics: map[string]interface{}{
   123  			"name":             "WebTransaction/Go/hello",
   124  			"guid":             internal.MatchAnything,
   125  			"sampled":          internal.MatchAnything,
   126  			"priority":         internal.MatchAnything,
   127  			"traceId":          internal.MatchAnything,
   128  			"nr.apdexPerfZone": internal.MatchAnything,
   129  		},
   130  	}})
   131  }
   132  
   133  func TestSetWebRequestHTTPRequest(t *testing.T) {
   134  	// Test that NewWebRequest correctly turns an *http.Request into a
   135  	// WebRequest that SetWebRequest uses as expected.
   136  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   137  	txn := app.StartTransaction("hello", nil, nil)
   138  	err := txn.SetWebRequest(NewWebRequest(sampleHTTPRequest))
   139  	if err != nil {
   140  		t.Error("unexpected error", err)
   141  	}
   142  	txn.End()
   143  	app.ExpectMetrics(t, []internal.WantMetric{
   144  		{Name: "WebTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
   145  		{Name: "WebTransaction", Scope: "", Forced: true, Data: nil},
   146  		{Name: "WebTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
   147  		{Name: "WebTransactionTotalTime", Scope: "", Forced: true, Data: nil},
   148  		{Name: "HttpDispatcher", Scope: "", Forced: true, Data: nil},
   149  		{Name: "Apdex", Scope: "", Forced: true, Data: nil},
   150  		{Name: "Apdex/Go/hello", Scope: "", Forced: false, Data: nil},
   151  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil},
   152  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", Scope: "", Forced: false, Data: nil},
   153  	})
   154  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   155  		AgentAttributes: sampleRequestAgentAttributes,
   156  		Intrinsics: map[string]interface{}{
   157  			"name":             "WebTransaction/Go/hello",
   158  			"guid":             internal.MatchAnything,
   159  			"sampled":          internal.MatchAnything,
   160  			"priority":         internal.MatchAnything,
   161  			"traceId":          internal.MatchAnything,
   162  			"nr.apdexPerfZone": internal.MatchAnything,
   163  		},
   164  	}})
   165  }
   166  
   167  func TestSetWebRequestCustomRequest(t *testing.T) {
   168  	// Test that a custom type which implements WebRequest is used by
   169  	// SetWebRequest as expected.
   170  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   171  	txn := app.StartTransaction("hello", nil, nil)
   172  	err := txn.SetWebRequest(sampleCustomRequest)
   173  	if err != nil {
   174  		t.Error("unexpected error", err)
   175  	}
   176  	txn.End()
   177  	app.ExpectMetrics(t, []internal.WantMetric{
   178  		{Name: "WebTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
   179  		{Name: "WebTransaction", Scope: "", Forced: true, Data: nil},
   180  		{Name: "WebTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
   181  		{Name: "WebTransactionTotalTime", Scope: "", Forced: true, Data: nil},
   182  		{Name: "HttpDispatcher", Scope: "", Forced: true, Data: nil},
   183  		{Name: "Apdex", Scope: "", Forced: true, Data: nil},
   184  		{Name: "Apdex/Go/hello", Scope: "", Forced: false, Data: nil},
   185  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil},
   186  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", Scope: "", Forced: false, Data: nil},
   187  	})
   188  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   189  		AgentAttributes: sampleRequestAgentAttributes,
   190  		Intrinsics: map[string]interface{}{
   191  			"name":             "WebTransaction/Go/hello",
   192  			"guid":             internal.MatchAnything,
   193  			"sampled":          internal.MatchAnything,
   194  			"priority":         internal.MatchAnything,
   195  			"traceId":          internal.MatchAnything,
   196  			"nr.apdexPerfZone": internal.MatchAnything,
   197  		},
   198  	}})
   199  }
   200  
   201  func TestSetWebRequestAlreadyEnded(t *testing.T) {
   202  	// Test that SetWebRequest returns an error if called after
   203  	// Transaction.End.
   204  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   205  	txn := app.StartTransaction("hello", nil, nil)
   206  	txn.End()
   207  	err := txn.SetWebRequest(sampleCustomRequest)
   208  	if err != errAlreadyEnded {
   209  		t.Error("incorrect error", err)
   210  	}
   211  	app.ExpectMetrics(t, []internal.WantMetric{
   212  		{Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
   213  		{Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil},
   214  		{Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
   215  		{Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil},
   216  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil},
   217  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil},
   218  	})
   219  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   220  		AgentAttributes: map[string]interface{}{},
   221  		Intrinsics: map[string]interface{}{
   222  			"name":     "OtherTransaction/Go/hello",
   223  			"guid":     internal.MatchAnything,
   224  			"sampled":  internal.MatchAnything,
   225  			"priority": internal.MatchAnything,
   226  			"traceId":  internal.MatchAnything,
   227  		},
   228  	}})
   229  }
   230  
   231  func TestSetWebRequestWithDistributedTracing(t *testing.T) {
   232  	// Test that the WebRequest.Transport() return value is used as the
   233  	// distributed tracing transport if a distributed tracing header is
   234  	// found in the WebRequest.Header().
   235  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   236  	payload := makePayload(app, nil)
   237  	// Copy sampleCustomRequest to avoid modifying it since it is used in
   238  	// other tests.
   239  	req := sampleCustomRequest
   240  	req.header = map[string][]string{
   241  		DistributedTracePayloadHeader: {payload.Text()},
   242  	}
   243  	txn := app.StartTransaction("hello", nil, nil)
   244  	err := txn.SetWebRequest(req)
   245  	if nil != err {
   246  		t.Error("unexpected error", err)
   247  	}
   248  	txn.End()
   249  	app.ExpectMetrics(t, []internal.WantMetric{
   250  		{Name: "WebTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
   251  		{Name: "WebTransaction", Scope: "", Forced: true, Data: nil},
   252  		{Name: "WebTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
   253  		{Name: "WebTransactionTotalTime", Scope: "", Forced: true, Data: nil},
   254  		{Name: "HttpDispatcher", Scope: "", Forced: true, Data: nil},
   255  		{Name: "Apdex", Scope: "", Forced: true, Data: nil},
   256  		{Name: "Apdex/Go/hello", Scope: "", Forced: false, Data: nil},
   257  		{Name: "DurationByCaller/App/123/456/HTTP/all", Scope: "", Forced: false, Data: nil},
   258  		{Name: "DurationByCaller/App/123/456/HTTP/allWeb", Scope: "", Forced: false, Data: nil},
   259  		{Name: "TransportDuration/App/123/456/HTTP/all", Scope: "", Forced: false, Data: nil},
   260  		{Name: "TransportDuration/App/123/456/HTTP/allWeb", Scope: "", Forced: false, Data: nil},
   261  		{Name: "Supportability/DistributedTrace/AcceptPayload/Success", Scope: "", Forced: true, Data: singleCount},
   262  	})
   263  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   264  		AgentAttributes: map[string]interface{}{
   265  			"request.method": "GET",
   266  			"request.uri":    "http://www.newrelic.com",
   267  		},
   268  		Intrinsics: map[string]interface{}{
   269  			"name":                     "WebTransaction/Go/hello",
   270  			"parent.type":              "App",
   271  			"parent.account":           "123",
   272  			"parent.app":               "456",
   273  			"parent.transportType":     "HTTP",
   274  			"parent.transportDuration": internal.MatchAnything,
   275  			"parentId":                 internal.MatchAnything,
   276  			"traceId":                  internal.MatchAnything,
   277  			"parentSpanId":             internal.MatchAnything,
   278  			"guid":                     internal.MatchAnything,
   279  			"sampled":                  internal.MatchAnything,
   280  			"priority":                 internal.MatchAnything,
   281  			"nr.apdexPerfZone":         internal.MatchAnything,
   282  		},
   283  	}})
   284  }
   285  
   286  type incompleteRequest struct{}
   287  
   288  func (r incompleteRequest) Header() http.Header      { return nil }
   289  func (r incompleteRequest) URL() *url.URL            { return nil }
   290  func (r incompleteRequest) Method() string           { return "" }
   291  func (r incompleteRequest) Transport() TransportType { return TransportUnknown }
   292  
   293  func TestSetWebRequestIncompleteRequest(t *testing.T) {
   294  	// Test SetWebRequest will safely handle situations where the request's
   295  	// URL() and Header() methods return nil.
   296  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   297  	txn := app.StartTransaction("hello", nil, nil)
   298  	err := txn.SetWebRequest(incompleteRequest{})
   299  	if err != nil {
   300  		t.Error("unexpected error", err)
   301  	}
   302  	txn.End()
   303  	app.ExpectMetrics(t, []internal.WantMetric{
   304  		{Name: "WebTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
   305  		{Name: "WebTransaction", Scope: "", Forced: true, Data: nil},
   306  		{Name: "WebTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
   307  		{Name: "WebTransactionTotalTime", Scope: "", Forced: true, Data: nil},
   308  		{Name: "HttpDispatcher", Scope: "", Forced: true, Data: nil},
   309  		{Name: "Apdex", Scope: "", Forced: true, Data: nil},
   310  		{Name: "Apdex/Go/hello", Scope: "", Forced: false, Data: nil},
   311  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil},
   312  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", Scope: "", Forced: false, Data: nil},
   313  	})
   314  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   315  		AgentAttributes: map[string]interface{}{},
   316  		Intrinsics: map[string]interface{}{
   317  			"name":             "WebTransaction/Go/hello",
   318  			"guid":             internal.MatchAnything,
   319  			"sampled":          internal.MatchAnything,
   320  			"priority":         internal.MatchAnything,
   321  			"traceId":          internal.MatchAnything,
   322  			"nr.apdexPerfZone": internal.MatchAnything,
   323  		},
   324  	}})
   325  }