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 }