github.com/newrelic/go-agent@v3.26.0+incompatible/_integrations/nrmicro/nrmicro_test.go (about) 1 // Copyright 2020 New Relic Corporation. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package nrmicro 5 6 import ( 7 "context" 8 "errors" 9 "sync" 10 "testing" 11 "time" 12 13 "github.com/micro/go-micro" 14 "github.com/micro/go-micro/broker" 15 bmemory "github.com/micro/go-micro/broker/memory" 16 "github.com/micro/go-micro/client" 17 "github.com/micro/go-micro/client/selector" 18 microerrors "github.com/micro/go-micro/errors" 19 "github.com/micro/go-micro/metadata" 20 rmemory "github.com/micro/go-micro/registry/memory" 21 "github.com/micro/go-micro/server" 22 newrelic "github.com/newrelic/go-agent" 23 proto "github.com/newrelic/go-agent/_integrations/nrmicro/example/proto" 24 "github.com/newrelic/go-agent/internal" 25 "github.com/newrelic/go-agent/internal/integrationsupport" 26 ) 27 28 const ( 29 missingHeaders = "HEADERS NOT FOUND" 30 missingMetadata = "METADATA NOT FOUND" 31 serverName = "testing" 32 topic = "topic" 33 ) 34 35 type TestRequest struct{} 36 37 type TestResponse struct { 38 RequestHeaders string 39 } 40 41 func dtHeadersFound(hdr string) bool { 42 return hdr != "" && hdr != missingMetadata && hdr != missingHeaders 43 } 44 45 type TestHandler struct{} 46 47 func (t *TestHandler) Method(ctx context.Context, req *TestRequest, rsp *TestResponse) error { 48 rsp.RequestHeaders = getDTRequestHeaderVal(ctx) 49 defer newrelic.StartSegment(newrelic.FromContext(ctx), "Method").End() 50 return nil 51 } 52 53 func (t *TestHandler) StreamingMethod(ctx context.Context, stream server.Stream) error { 54 if err := stream.Send(getDTRequestHeaderVal(ctx)); nil != err { 55 return err 56 } 57 return nil 58 } 59 60 type TestHandlerWithError struct{} 61 62 func (t *TestHandlerWithError) Method(ctx context.Context, req *TestRequest, rsp *TestResponse) error { 63 rsp.RequestHeaders = getDTRequestHeaderVal(ctx) 64 return microerrors.Unauthorized("id", "format") 65 } 66 67 type TestHandlerWithNonMicroError struct{} 68 69 func (t *TestHandlerWithNonMicroError) Method(ctx context.Context, req *TestRequest, rsp *TestResponse) error { 70 rsp.RequestHeaders = getDTRequestHeaderVal(ctx) 71 return errors.New("Non-Micro Error") 72 } 73 74 func getDTRequestHeaderVal(ctx context.Context) string { 75 if md, ok := metadata.FromContext(ctx); ok { 76 if dtHeader, ok := md[newrelic.DistributedTracePayloadHeader]; ok { 77 return dtHeader 78 } 79 return missingHeaders 80 } 81 return missingMetadata 82 } 83 84 func createTestApp() integrationsupport.ExpectApp { 85 return integrationsupport.NewTestApp(replyFn, cfgFn) 86 } 87 88 var replyFn = func(reply *internal.ConnectReply) { 89 reply.AdaptiveSampler = internal.SampleEverything{} 90 reply.AccountID = "123" 91 reply.TrustedAccountKey = "123" 92 reply.PrimaryAppID = "456" 93 } 94 95 var cfgFn = func(cfg *newrelic.Config) { 96 cfg.Enabled = false 97 cfg.DistributedTracer.Enabled = true 98 cfg.TransactionTracer.SegmentThreshold = 0 99 cfg.TransactionTracer.Threshold.IsApdexFailing = false 100 cfg.TransactionTracer.Threshold.Duration = 0 101 cfg.Attributes.Include = append(cfg.Attributes.Include, 102 newrelic.AttributeMessageRoutingKey, 103 newrelic.AttributeMessageQueueName, 104 newrelic.AttributeMessageExchangeType, 105 newrelic.AttributeMessageReplyTo, 106 newrelic.AttributeMessageCorrelationID, 107 ) 108 } 109 110 func newTestWrappedClientAndServer(app newrelic.Application, wrapperOption client.Option, t *testing.T) (client.Client, server.Server) { 111 registry := rmemory.NewRegistry() 112 sel := selector.NewSelector(selector.Registry(registry)) 113 c := client.NewClient( 114 client.Selector(sel), 115 wrapperOption, 116 ) 117 s := server.NewServer( 118 server.Name(serverName), 119 server.Registry(registry), 120 server.WrapHandler(HandlerWrapper(app)), 121 ) 122 s.Handle(s.NewHandler(new(TestHandler))) 123 s.Handle(s.NewHandler(new(TestHandlerWithError))) 124 s.Handle(s.NewHandler(new(TestHandlerWithNonMicroError))) 125 126 if err := s.Start(); nil != err { 127 t.Fatal(err) 128 } 129 return c, s 130 } 131 132 func TestClientCallWithNoTransaction(t *testing.T) { 133 c, s := newTestWrappedClientAndServer(createTestApp(), client.Wrap(ClientWrapper()), t) 134 defer s.Stop() 135 testClientCallWithNoTransaction(c, t) 136 } 137 138 func TestClientCallWrapperWithNoTransaction(t *testing.T) { 139 c, s := newTestWrappedClientAndServer(createTestApp(), client.WrapCall(CallWrapper()), t) 140 defer s.Stop() 141 testClientCallWithNoTransaction(c, t) 142 } 143 144 func testClientCallWithNoTransaction(c client.Client, t *testing.T) { 145 146 ctx := context.Background() 147 req := c.NewRequest(serverName, "TestHandler.Method", &TestRequest{}, client.WithContentType("application/json")) 148 rsp := TestResponse{} 149 if err := c.Call(ctx, req, &rsp); nil != err { 150 t.Fatal("Error calling test client:", err) 151 } 152 if rsp.RequestHeaders != missingHeaders { 153 t.Error("Header should not be here", rsp.RequestHeaders) 154 } 155 } 156 157 func TestClientCallWithTransaction(t *testing.T) { 158 c, s := newTestWrappedClientAndServer(createTestApp(), client.Wrap(ClientWrapper()), t) 159 defer s.Stop() 160 testClientCallWithTransaction(c, t) 161 } 162 163 func TestClientCallWrapperWithTransaction(t *testing.T) { 164 c, s := newTestWrappedClientAndServer(createTestApp(), client.WrapCall(CallWrapper()), t) 165 defer s.Stop() 166 testClientCallWithTransaction(c, t) 167 } 168 169 func testClientCallWithTransaction(c client.Client, t *testing.T) { 170 171 req := c.NewRequest(serverName, "TestHandler.Method", &TestRequest{}, client.WithContentType("application/json")) 172 rsp := TestResponse{} 173 app := createTestApp() 174 txn := app.StartTransaction("name", nil, nil) 175 ctx := newrelic.NewContext(context.Background(), txn) 176 if err := c.Call(ctx, req, &rsp); nil != err { 177 t.Fatal("Error calling test client:", err) 178 } 179 if !dtHeadersFound(rsp.RequestHeaders) { 180 t.Error("Incorrect header:", rsp.RequestHeaders) 181 } 182 183 txn.End() 184 app.ExpectMetrics(t, []internal.WantMetric{ 185 {Name: "OtherTransaction/Go/name", Scope: "", Forced: true, Data: nil}, 186 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 187 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 188 {Name: "OtherTransactionTotalTime/Go/name", Scope: "", Forced: false, Data: nil}, 189 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, 190 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil}, 191 {Name: "External/all", Scope: "", Forced: true, Data: nil}, 192 {Name: "External/allOther", Scope: "", Forced: true, Data: nil}, 193 {Name: "External/testing/all", Scope: "", Forced: false, Data: nil}, 194 {Name: "External/testing/Micro/TestHandler.Method", Scope: "OtherTransaction/Go/name", Forced: false, Data: nil}, 195 {Name: "Supportability/DistributedTrace/CreatePayload/Success", Scope: "", Forced: true, Data: nil}, 196 }) 197 app.ExpectSpanEvents(t, []internal.WantEvent{ 198 { 199 Intrinsics: map[string]interface{}{ 200 "category": "generic", 201 "name": "OtherTransaction/Go/name", 202 "nr.entryPoint": true, 203 }, 204 UserAttributes: map[string]interface{}{}, 205 AgentAttributes: map[string]interface{}{}, 206 }, 207 { 208 Intrinsics: map[string]interface{}{ 209 "category": "http", 210 "component": "Micro", 211 "name": "External/testing/Micro/TestHandler.Method", 212 "parentId": internal.MatchAnything, 213 "span.kind": "client", 214 }, 215 UserAttributes: map[string]interface{}{}, 216 AgentAttributes: map[string]interface{}{}, 217 }, 218 }) 219 app.ExpectTxnTraces(t, []internal.WantTxnTrace{{ 220 MetricName: "OtherTransaction/Go/name", 221 Root: internal.WantTraceSegment{ 222 SegmentName: "ROOT", 223 Attributes: map[string]interface{}{}, 224 Children: []internal.WantTraceSegment{{ 225 SegmentName: "OtherTransaction/Go/name", 226 Attributes: map[string]interface{}{"exclusive_duration_millis": internal.MatchAnything}, 227 Children: []internal.WantTraceSegment{ 228 { 229 SegmentName: "External/testing/Micro/TestHandler.Method", 230 Attributes: map[string]interface{}{}, 231 }, 232 }, 233 }}, 234 }, 235 }}) 236 } 237 238 func TestClientCallMetadata(t *testing.T) { 239 c, s := newTestWrappedClientAndServer(createTestApp(), client.Wrap(ClientWrapper()), t) 240 defer s.Stop() 241 testClientCallMetadata(c, t) 242 } 243 244 func TestCallMetadata(t *testing.T) { 245 c, s := newTestWrappedClientAndServer(createTestApp(), client.WrapCall(CallWrapper()), t) 246 defer s.Stop() 247 testClientCallMetadata(c, t) 248 } 249 250 func testClientCallMetadata(c client.Client, t *testing.T) { 251 // test that context metadata is not changed by the newrelic wrapper 252 req := c.NewRequest(serverName, "TestHandler.Method", &TestRequest{}, client.WithContentType("application/json")) 253 rsp := TestResponse{} 254 app := createTestApp() 255 txn := app.StartTransaction("name", nil, nil) 256 ctx := newrelic.NewContext(context.Background(), txn) 257 md := metadata.Metadata{ 258 "zip": "zap", 259 } 260 ctx = metadata.NewContext(ctx, md) 261 if err := c.Call(ctx, req, &rsp); nil != err { 262 t.Fatal("Error calling test client:", err) 263 } 264 if len(md) != 1 || md["zip"] != "zap" { 265 t.Error("metadata changed:", md) 266 } 267 } 268 269 func waitOrTimeout(t *testing.T, wg *sync.WaitGroup) { 270 ch := make(chan struct{}) 271 go func() { 272 defer close(ch) 273 wg.Wait() 274 }() 275 select { 276 case <-ch: 277 case <-time.After(time.Second): 278 t.Fatal("timeout waiting for message") 279 } 280 } 281 282 func TestClientPublishWithNoTransaction(t *testing.T) { 283 c, _, b := newTestClientServerAndBroker(createTestApp(), t) 284 285 var wg sync.WaitGroup 286 if err := b.Connect(); nil != err { 287 t.Fatal("broker connect error:", err) 288 } 289 defer b.Disconnect() 290 if _, err := b.Subscribe(topic, func(e broker.Event) error { 291 defer wg.Done() 292 h := e.Message().Header 293 if _, ok := h[newrelic.DistributedTracePayloadHeader]; ok { 294 t.Error("Distributed tracing headers found", h) 295 } 296 return nil 297 }); nil != err { 298 t.Fatal("Failure to subscribe to broker:", err) 299 } 300 301 ctx := context.Background() 302 msg := c.NewMessage(topic, "hello world") 303 wg.Add(1) 304 if err := c.Publish(ctx, msg); nil != err { 305 t.Fatal("Error calling test client:", err) 306 } 307 waitOrTimeout(t, &wg) 308 } 309 310 func TestClientPublishWithTransaction(t *testing.T) { 311 c, _, b := newTestClientServerAndBroker(createTestApp(), t) 312 313 var wg sync.WaitGroup 314 if err := b.Connect(); nil != err { 315 t.Fatal("broker connect error:", err) 316 } 317 defer b.Disconnect() 318 if _, err := b.Subscribe(topic, func(e broker.Event) error { 319 defer wg.Done() 320 h := e.Message().Header 321 if _, ok := h[newrelic.DistributedTracePayloadHeader]; !ok { 322 t.Error("Distributed tracing headers not found", h) 323 } 324 return nil 325 }); nil != err { 326 t.Fatal("Failure to subscribe to broker:", err) 327 } 328 329 app := createTestApp() 330 txn := app.StartTransaction("name", nil, nil) 331 ctx := newrelic.NewContext(context.Background(), txn) 332 msg := c.NewMessage(topic, "hello world") 333 wg.Add(1) 334 if err := c.Publish(ctx, msg); nil != err { 335 t.Fatal("Error calling test client:", err) 336 } 337 waitOrTimeout(t, &wg) 338 339 txn.End() 340 app.ExpectMetrics(t, []internal.WantMetric{ 341 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, 342 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil}, 343 {Name: "MessageBroker/Micro/Topic/Produce/Named/topic", Scope: "", Forced: false, Data: nil}, 344 {Name: "MessageBroker/Micro/Topic/Produce/Named/topic", Scope: "OtherTransaction/Go/name", Forced: false, Data: nil}, 345 {Name: "OtherTransaction/Go/name", Scope: "", Forced: true, Data: nil}, 346 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 347 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 348 {Name: "OtherTransactionTotalTime/Go/name", Scope: "", Forced: false, Data: nil}, 349 {Name: "Supportability/DistributedTrace/CreatePayload/Success", Scope: "", Forced: true, Data: nil}, 350 }) 351 app.ExpectSpanEvents(t, []internal.WantEvent{ 352 { 353 Intrinsics: map[string]interface{}{ 354 "category": "generic", 355 "name": "OtherTransaction/Go/name", 356 "nr.entryPoint": true, 357 }, 358 UserAttributes: map[string]interface{}{}, 359 AgentAttributes: map[string]interface{}{}, 360 }, 361 { 362 Intrinsics: map[string]interface{}{ 363 "category": "generic", 364 "name": "MessageBroker/Micro/Topic/Produce/Named/topic", 365 "parentId": internal.MatchAnything, 366 }, 367 UserAttributes: map[string]interface{}{}, 368 AgentAttributes: map[string]interface{}{}, 369 }, 370 }) 371 app.ExpectTxnTraces(t, []internal.WantTxnTrace{{ 372 MetricName: "OtherTransaction/Go/name", 373 Root: internal.WantTraceSegment{ 374 SegmentName: "ROOT", 375 Attributes: map[string]interface{}{}, 376 Children: []internal.WantTraceSegment{{ 377 SegmentName: "OtherTransaction/Go/name", 378 Attributes: map[string]interface{}{"exclusive_duration_millis": internal.MatchAnything}, 379 Children: []internal.WantTraceSegment{ 380 { 381 SegmentName: "MessageBroker/Micro/Topic/Produce/Named/topic", 382 Attributes: map[string]interface{}{}, 383 }, 384 }, 385 }}, 386 }, 387 }}) 388 } 389 390 func TestExtractHost(t *testing.T) { 391 testcases := []struct { 392 input string 393 expect string 394 }{ 395 { 396 input: "192.168.0.10", 397 expect: "192.168.0.10", 398 }, 399 { 400 input: "192.168.0.10:1234", 401 expect: "192.168.0.10:1234", 402 }, 403 { 404 input: "unix:///path/to/file", 405 expect: "localhost", 406 }, 407 { 408 input: "nats://127.0.0.1:4222", 409 expect: "127.0.0.1:4222", 410 }, 411 { 412 input: "scheme://user:pass@host.com:5432/path?k=v#f", 413 expect: "host.com:5432", 414 }, 415 } 416 417 for _, test := range testcases { 418 if actual := extractHost(test.input); actual != test.expect { 419 t.Errorf("incorrect host value extracted: actual=%s expected=%s", actual, test.expect) 420 } 421 } 422 } 423 424 func TestClientStreamWrapperWithNoTransaction(t *testing.T) { 425 c, s := newTestWrappedClientAndServer(createTestApp(), client.Wrap(ClientWrapper()), t) 426 defer s.Stop() 427 428 ctx := context.Background() 429 req := c.NewRequest( 430 serverName, 431 "TestHandler.StreamingMethod", 432 &TestRequest{}, 433 client.WithContentType("application/json"), 434 client.StreamingRequest(), 435 ) 436 stream, err := c.Stream(ctx, req) 437 defer stream.Close() 438 if nil != err { 439 t.Fatal("Error calling test client:", err) 440 } 441 442 var resp string 443 err = stream.Recv(&resp) 444 if nil != err { 445 t.Fatal(err) 446 } 447 if dtHeadersFound(resp) { 448 t.Error("dt headers found:", resp) 449 } 450 451 err = stream.Recv(&resp) 452 if nil == err { 453 t.Fatal("should have received EOF error from server") 454 } 455 } 456 457 func TestClientStreamWrapperWithTransaction(t *testing.T) { 458 c, s := newTestWrappedClientAndServer(createTestApp(), client.Wrap(ClientWrapper()), t) 459 defer s.Stop() 460 461 app := createTestApp() 462 txn := app.StartTransaction("name", nil, nil) 463 ctx := newrelic.NewContext(context.Background(), txn) 464 req := c.NewRequest( 465 serverName, 466 "TestHandler.StreamingMethod", 467 &TestRequest{}, 468 client.WithContentType("application/json"), 469 client.StreamingRequest(), 470 ) 471 stream, err := c.Stream(ctx, req) 472 defer stream.Close() 473 if nil != err { 474 t.Fatal("Error calling test client:", err) 475 } 476 477 var resp string 478 // second outgoing request to server, ensures we only create a single 479 // metric for the entire streaming cycle 480 if err := stream.Send(&resp); nil != err { 481 t.Fatal(err) 482 } 483 484 // receive the distributed trace headers from the server 485 if err := stream.Recv(&resp); nil != err { 486 t.Fatal(err) 487 } 488 if !dtHeadersFound(resp) { 489 t.Error("dt headers not found:", resp) 490 } 491 492 // exhaust the stream 493 if err := stream.Recv(&resp); nil == err { 494 t.Fatal("should have received EOF error from server") 495 } 496 497 txn.End() 498 app.ExpectMetrics(t, []internal.WantMetric{ 499 {Name: "OtherTransaction/Go/name", Scope: "", Forced: true, Data: nil}, 500 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 501 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 502 {Name: "OtherTransactionTotalTime/Go/name", Scope: "", Forced: false, Data: nil}, 503 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, 504 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil}, 505 {Name: "External/all", Scope: "", Forced: true, Data: nil}, 506 {Name: "External/allOther", Scope: "", Forced: true, Data: nil}, 507 {Name: "External/testing/all", Scope: "", Forced: false, Data: nil}, 508 {Name: "External/testing/Micro/TestHandler.StreamingMethod", Scope: "OtherTransaction/Go/name", Forced: false, Data: []float64{1}}, 509 {Name: "Supportability/DistributedTrace/CreatePayload/Success", Scope: "", Forced: true, Data: nil}, 510 }) 511 app.ExpectSpanEvents(t, []internal.WantEvent{ 512 { 513 Intrinsics: map[string]interface{}{ 514 "category": "generic", 515 "name": "OtherTransaction/Go/name", 516 "nr.entryPoint": true, 517 }, 518 UserAttributes: map[string]interface{}{}, 519 AgentAttributes: map[string]interface{}{}, 520 }, 521 { 522 Intrinsics: map[string]interface{}{ 523 "category": "http", 524 "component": "Micro", 525 "name": "External/testing/Micro/TestHandler.StreamingMethod", 526 "parentId": internal.MatchAnything, 527 "span.kind": "client", 528 }, 529 UserAttributes: map[string]interface{}{}, 530 AgentAttributes: map[string]interface{}{}, 531 }, 532 }) 533 app.ExpectTxnTraces(t, []internal.WantTxnTrace{{ 534 MetricName: "OtherTransaction/Go/name", 535 Root: internal.WantTraceSegment{ 536 SegmentName: "ROOT", 537 Attributes: map[string]interface{}{}, 538 Children: []internal.WantTraceSegment{{ 539 SegmentName: "OtherTransaction/Go/name", 540 Attributes: map[string]interface{}{"exclusive_duration_millis": internal.MatchAnything}, 541 Children: []internal.WantTraceSegment{ 542 { 543 SegmentName: "External/testing/Micro/TestHandler.StreamingMethod", 544 Attributes: map[string]interface{}{}, 545 }, 546 }, 547 }}, 548 }, 549 }}) 550 } 551 552 func TestServerWrapperWithNoApp(t *testing.T) { 553 c, s := newTestWrappedClientAndServer(nil, client.Wrap(ClientWrapper()), t) 554 defer s.Stop() 555 ctx := context.Background() 556 req := c.NewRequest(serverName, "TestHandler.Method", &TestRequest{}, client.WithContentType("application/json")) 557 rsp := TestResponse{} 558 if err := c.Call(ctx, req, &rsp); nil != err { 559 t.Fatal("Error calling test client:", err) 560 } 561 if rsp.RequestHeaders != missingHeaders { 562 t.Error("Header should not be here", rsp.RequestHeaders) 563 } 564 } 565 566 func TestServerWrapperWithApp(t *testing.T) { 567 app := createTestApp() 568 c, s := newTestWrappedClientAndServer(app, client.Wrap(ClientWrapper()), t) 569 defer s.Stop() 570 ctx := context.Background() 571 txn := app.StartTransaction("txn", nil, nil) 572 defer txn.End() 573 ctx = newrelic.NewContext(ctx, txn) 574 req := c.NewRequest(serverName, "TestHandler.Method", &TestRequest{}, client.WithContentType("application/json")) 575 rsp := TestResponse{} 576 if err := c.Call(ctx, req, &rsp); nil != err { 577 t.Fatal("Error calling test client:", err) 578 } 579 app.ExpectMetrics(t, []internal.WantMetric{ 580 {Name: "DurationByCaller/App/123/456/HTTP/allWeb", Scope: "", Forced: false, Data: nil}, 581 {Name: "TransportDuration/App/123/456/HTTP/allWeb", Scope: "", Forced: false, Data: nil}, 582 {Name: "DurationByCaller/App/123/456/HTTP/all", Scope: "", Forced: false, Data: nil}, 583 {Name: "TransportDuration/App/123/456/HTTP/all", Scope: "", Forced: false, Data: nil}, 584 {Name: "Supportability/DistributedTrace/AcceptPayload/Success", Scope: "", Forced: true, Data: nil}, 585 {Name: "Apdex", Scope: "", Forced: true, Data: nil}, 586 {Name: "Apdex/Go/TestHandler.Method", Scope: "", Forced: false, Data: nil}, 587 {Name: "HttpDispatcher", Scope: "", Forced: true, Data: nil}, 588 {Name: "WebTransaction/Go/TestHandler.Method", Scope: "", Forced: true, Data: nil}, 589 {Name: "WebTransaction", Scope: "", Forced: true, Data: nil}, 590 {Name: "WebTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 591 {Name: "WebTransactionTotalTime/Go/TestHandler.Method", Scope: "", Forced: false, Data: nil}, 592 {Name: "Custom/Method", Scope: "", Forced: false, Data: nil}, 593 {Name: "Custom/Method", Scope: "WebTransaction/Go/TestHandler.Method", Forced: false, Data: nil}, 594 }) 595 app.ExpectSpanEvents(t, []internal.WantEvent{ 596 { 597 Intrinsics: map[string]interface{}{ 598 "category": "generic", 599 "name": "WebTransaction/Go/TestHandler.Method", 600 "nr.entryPoint": true, 601 "parentId": internal.MatchAnything, 602 }, 603 UserAttributes: map[string]interface{}{}, 604 AgentAttributes: map[string]interface{}{}, 605 }, 606 { 607 Intrinsics: map[string]interface{}{ 608 "category": "generic", 609 "name": "Custom/Method", 610 "parentId": internal.MatchAnything, 611 }, 612 UserAttributes: map[string]interface{}{}, 613 AgentAttributes: map[string]interface{}{}, 614 }, 615 }) 616 app.ExpectTxnTraces(t, []internal.WantTxnTrace{{ 617 MetricName: "WebTransaction/Go/TestHandler.Method", 618 Root: internal.WantTraceSegment{ 619 SegmentName: "ROOT", 620 Attributes: map[string]interface{}{}, 621 Children: []internal.WantTraceSegment{{ 622 SegmentName: "WebTransaction/Go/TestHandler.Method", 623 Attributes: map[string]interface{}{"exclusive_duration_millis": internal.MatchAnything}, 624 Children: []internal.WantTraceSegment{ 625 { 626 SegmentName: "Custom/Method", 627 Attributes: map[string]interface{}{}, 628 }, 629 }, 630 }}, 631 }, 632 }}) 633 app.ExpectTxnEvents(t, []internal.WantEvent{{ 634 Intrinsics: map[string]interface{}{ 635 "name": "WebTransaction/Go/TestHandler.Method", 636 "guid": internal.MatchAnything, 637 "priority": internal.MatchAnything, 638 "sampled": internal.MatchAnything, 639 "traceId": internal.MatchAnything, 640 "nr.apdexPerfZone": "S", 641 "parent.account": 123, 642 "parent.transportType": "HTTP", 643 "parent.app": 456, 644 "parentId": internal.MatchAnything, 645 "parent.type": "App", 646 "parent.transportDuration": internal.MatchAnything, 647 "parentSpanId": internal.MatchAnything, 648 }, 649 UserAttributes: map[string]interface{}{}, 650 AgentAttributes: map[string]interface{}{ 651 "request.method": "TestHandler.Method", 652 "request.uri": "micro://testing/TestHandler.Method", 653 "request.headers.accept": "application/json", 654 "request.headers.contentType": "application/json", 655 "request.headers.contentLength": 3, 656 "httpResponseCode": "200", 657 }, 658 }}) 659 } 660 661 func TestServerWrapperWithAppReturnsError(t *testing.T) { 662 app := createTestApp() 663 c, s := newTestWrappedClientAndServer(app, client.Wrap(ClientWrapper()), t) 664 defer s.Stop() 665 ctx := context.Background() 666 req := c.NewRequest(serverName, "TestHandlerWithError.Method", &TestRequest{}, client.WithContentType("application/json")) 667 rsp := TestResponse{} 668 if err := c.Call(ctx, req, &rsp); nil == err { 669 t.Fatal("Expected an error but did not get one") 670 } 671 app.ExpectMetrics(t, []internal.WantMetric{ 672 {Name: "Apdex/Go/TestHandlerWithError.Method", Scope: "", Forced: false, Data: nil}, 673 {Name: "Errors/all", Scope: "", Forced: true, Data: nil}, 674 {Name: "Errors/allWeb", Scope: "", Forced: true, Data: nil}, 675 {Name: "Errors/WebTransaction/Go/TestHandlerWithError.Method", Scope: "", Forced: true, Data: nil}, 676 {Name: "ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, 677 {Name: "ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", Scope: "", Forced: false, Data: nil}, 678 {Name: "WebTransaction/Go/TestHandlerWithError.Method", Scope: "", Forced: true, Data: nil}, 679 {Name: "WebTransactionTotalTime/Go/TestHandlerWithError.Method", Scope: "", Forced: false, Data: nil}, 680 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, 681 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", Scope: "", Forced: false, Data: nil}, 682 {Name: "HttpDispatcher", Scope: "", Forced: true, Data: nil}, 683 {Name: "WebTransaction", Scope: "", Forced: true, Data: nil}, 684 {Name: "WebTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 685 {Name: "Apdex", Scope: "", Forced: true, Data: nil}, 686 }) 687 app.ExpectSpanEvents(t, []internal.WantEvent{ 688 { 689 Intrinsics: map[string]interface{}{ 690 "category": "generic", 691 "name": "WebTransaction/Go/TestHandlerWithError.Method", 692 "nr.entryPoint": true, 693 }, 694 UserAttributes: map[string]interface{}{}, 695 AgentAttributes: map[string]interface{}{}, 696 }, 697 }) 698 app.ExpectTxnTraces(t, []internal.WantTxnTrace{{ 699 MetricName: "WebTransaction/Go/TestHandlerWithError.Method", 700 Root: internal.WantTraceSegment{ 701 SegmentName: "ROOT", 702 Attributes: map[string]interface{}{}, 703 Children: []internal.WantTraceSegment{{ 704 SegmentName: "WebTransaction/Go/TestHandlerWithError.Method", 705 Attributes: map[string]interface{}{"exclusive_duration_millis": internal.MatchAnything}, 706 Children: []internal.WantTraceSegment{}, 707 }}, 708 }, 709 }}) 710 app.ExpectTxnEvents(t, []internal.WantEvent{{ 711 Intrinsics: map[string]interface{}{ 712 "name": "WebTransaction/Go/TestHandlerWithError.Method", 713 "guid": internal.MatchAnything, 714 "priority": internal.MatchAnything, 715 "sampled": internal.MatchAnything, 716 "traceId": internal.MatchAnything, 717 "nr.apdexPerfZone": internal.MatchAnything, 718 }, 719 UserAttributes: map[string]interface{}{}, 720 AgentAttributes: map[string]interface{}{ 721 "request.method": "TestHandlerWithError.Method", 722 "request.uri": "micro://testing/TestHandlerWithError.Method", 723 "request.headers.accept": "application/json", 724 "request.headers.contentType": "application/json", 725 "request.headers.contentLength": 3, 726 "httpResponseCode": 401, 727 }, 728 }}) 729 app.ExpectErrors(t, []internal.WantError{{ 730 TxnName: "WebTransaction/Go/TestHandlerWithError.Method", 731 Msg: "Unauthorized", 732 Klass: "401", 733 }}) 734 app.ExpectErrorEvents(t, []internal.WantEvent{{ 735 Intrinsics: map[string]interface{}{ 736 "error.message": "Unauthorized", 737 "error.class": "401", 738 "transactionName": "WebTransaction/Go/TestHandlerWithError.Method", 739 "traceId": internal.MatchAnything, 740 "priority": internal.MatchAnything, 741 "guid": internal.MatchAnything, 742 "sampled": "true", 743 }, 744 }}) 745 } 746 747 func TestServerWrapperWithAppReturnsNonMicroError(t *testing.T) { 748 app := createTestApp() 749 c, s := newTestWrappedClientAndServer(app, client.Wrap(ClientWrapper()), t) 750 defer s.Stop() 751 ctx := context.Background() 752 req := c.NewRequest("testing", "TestHandlerWithNonMicroError.Method", &TestRequest{}, client.WithContentType("application/json")) 753 rsp := TestResponse{} 754 if err := c.Call(ctx, req, &rsp); nil == err { 755 t.Fatal("Expected an error but did not get one") 756 } 757 app.ExpectMetrics(t, []internal.WantMetric{ 758 {Name: "Apdex/Go/TestHandlerWithNonMicroError.Method", Scope: "", Forced: false, Data: nil}, 759 {Name: "Errors/all", Scope: "", Forced: true, Data: nil}, 760 {Name: "Errors/allWeb", Scope: "", Forced: true, Data: nil}, 761 {Name: "Errors/WebTransaction/Go/TestHandlerWithNonMicroError.Method", Scope: "", Forced: true, Data: nil}, 762 {Name: "ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, 763 {Name: "ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", Scope: "", Forced: false, Data: nil}, 764 {Name: "WebTransaction/Go/TestHandlerWithNonMicroError.Method", Scope: "", Forced: true, Data: nil}, 765 {Name: "WebTransactionTotalTime/Go/TestHandlerWithNonMicroError.Method", Scope: "", Forced: false, Data: nil}, 766 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, 767 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", Scope: "", Forced: false, Data: nil}, 768 {Name: "HttpDispatcher", Scope: "", Forced: true, Data: nil}, 769 {Name: "WebTransaction", Scope: "", Forced: true, Data: nil}, 770 {Name: "WebTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 771 {Name: "Apdex", Scope: "", Forced: true, Data: nil}, 772 }) 773 app.ExpectTxnEvents(t, []internal.WantEvent{{ 774 Intrinsics: map[string]interface{}{ 775 "name": "WebTransaction/Go/TestHandlerWithNonMicroError.Method", 776 "guid": internal.MatchAnything, 777 "priority": internal.MatchAnything, 778 "sampled": internal.MatchAnything, 779 "traceId": internal.MatchAnything, 780 "nr.apdexPerfZone": internal.MatchAnything, 781 }, 782 UserAttributes: map[string]interface{}{}, 783 AgentAttributes: map[string]interface{}{ 784 "request.method": "TestHandlerWithNonMicroError.Method", 785 "request.uri": "micro://testing/TestHandlerWithNonMicroError.Method", 786 "request.headers.accept": "application/json", 787 "request.headers.contentType": "application/json", 788 "request.headers.contentLength": 3, 789 "httpResponseCode": 500, 790 }, 791 }}) 792 app.ExpectErrors(t, []internal.WantError{{ 793 TxnName: "WebTransaction/Go/TestHandlerWithNonMicroError.Method", 794 Msg: "Internal Server Error", 795 Klass: "500", 796 }}) 797 app.ExpectErrorEvents(t, []internal.WantEvent{{ 798 Intrinsics: map[string]interface{}{ 799 "error.message": "Internal Server Error", 800 "error.class": "500", 801 "transactionName": "WebTransaction/Go/TestHandlerWithNonMicroError.Method", 802 "traceId": internal.MatchAnything, 803 "priority": internal.MatchAnything, 804 "guid": internal.MatchAnything, 805 "sampled": "true", 806 }, 807 }}) 808 } 809 810 func TestServerSubscribeNoApp(t *testing.T) { 811 c, s, b := newTestClientServerAndBroker(nil, t) 812 defer s.Stop() 813 814 var wg sync.WaitGroup 815 if err := b.Connect(); nil != err { 816 t.Fatal("broker connect error:", err) 817 } 818 defer b.Disconnect() 819 err := micro.RegisterSubscriber(topic, s, func(ctx context.Context, msg *proto.HelloRequest) error { 820 defer wg.Done() 821 return nil 822 }) 823 if err != nil { 824 t.Fatal("error registering subscriber", err) 825 } 826 if err := s.Start(); nil != err { 827 t.Fatal(err) 828 } 829 830 ctx := context.Background() 831 msg := c.NewMessage(topic, &proto.HelloRequest{Name: "test"}) 832 wg.Add(1) 833 if err := c.Publish(ctx, msg); nil != err { 834 t.Fatal("Error calling publish:", err) 835 } 836 waitOrTimeout(t, &wg) 837 } 838 839 func TestServerSubscribe(t *testing.T) { 840 app := createTestApp() 841 c, s, _ := newTestClientServerAndBroker(app, t) 842 843 var wg sync.WaitGroup 844 err := micro.RegisterSubscriber(topic, s, func(ctx context.Context, msg *proto.HelloRequest) error { 845 txn := newrelic.FromContext(ctx) 846 defer newrelic.StartSegment(txn, "segment").End() 847 defer wg.Done() 848 return nil 849 }) 850 if err != nil { 851 t.Fatal("error registering subscriber", err) 852 } 853 if err := s.Start(); nil != err { 854 t.Fatal(err) 855 } 856 857 ctx := context.Background() 858 msg := c.NewMessage(topic, &proto.HelloRequest{Name: "test"}) 859 wg.Add(1) 860 txn := app.StartTransaction("pub", nil, nil) 861 ctx = newrelic.NewContext(ctx, txn) 862 if err := c.Publish(ctx, msg); nil != err { 863 t.Fatal("Error calling publish:", err) 864 } 865 defer txn.End() 866 waitOrTimeout(t, &wg) 867 s.Stop() 868 869 app.ExpectMetrics(t, []internal.WantMetric{ 870 {Name: "OtherTransaction/Go/Message/Micro/Topic/Named/topic", Scope: "", Forced: true, Data: nil}, 871 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 872 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 873 {Name: "OtherTransactionTotalTime/Go/Message/Micro/Topic/Named/topic", Scope: "", Forced: false, Data: nil}, 874 {Name: "Custom/segment", Scope: "", Forced: false, Data: nil}, 875 {Name: "Custom/segment", Scope: "OtherTransaction/Go/Message/Micro/Topic/Named/topic", Forced: false, Data: nil}, 876 {Name: "TransportDuration/App/123/456/HTTP/allOther", Scope: "", Forced: false, Data: nil}, 877 {Name: "Supportability/DistributedTrace/AcceptPayload/Success", Scope: "", Forced: true, Data: nil}, 878 {Name: "DurationByCaller/App/123/456/HTTP/all", Scope: "", Forced: false, Data: nil}, 879 {Name: "DurationByCaller/App/123/456/HTTP/allOther", Scope: "", Forced: false, Data: nil}, 880 {Name: "TransportDuration/App/123/456/HTTP/all", Scope: "", Forced: false, Data: nil}, 881 }) 882 app.ExpectSpanEvents(t, []internal.WantEvent{ 883 { 884 Intrinsics: map[string]interface{}{ 885 "category": "generic", 886 "name": "OtherTransaction/Go/Message/Micro/Topic/Named/topic", 887 "nr.entryPoint": true, 888 "parentId": internal.MatchAnything, 889 }, 890 UserAttributes: map[string]interface{}{}, 891 AgentAttributes: map[string]interface{}{}, 892 }, 893 { 894 Intrinsics: map[string]interface{}{ 895 "category": "generic", 896 "name": "Custom/segment", 897 "parentId": internal.MatchAnything, 898 }, 899 UserAttributes: map[string]interface{}{}, 900 AgentAttributes: map[string]interface{}{}, 901 }, 902 }) 903 app.ExpectTxnEvents(t, []internal.WantEvent{ 904 { 905 Intrinsics: map[string]interface{}{ 906 "guid": internal.MatchAnything, 907 "name": "OtherTransaction/Go/Message/Micro/Topic/Named/topic", 908 "parent.account": 123, 909 "parent.app": 456, 910 "parent.transportDuration": internal.MatchAnything, 911 "parent.transportType": "HTTP", 912 "parent.type": "App", 913 "parentId": internal.MatchAnything, 914 "parentSpanId": internal.MatchAnything, 915 "priority": internal.MatchAnything, 916 "sampled": internal.MatchAnything, 917 "traceId": internal.MatchAnything, 918 }, 919 AgentAttributes: map[string]interface{}{ 920 "message.routingKey": "topic", 921 }, 922 UserAttributes: map[string]interface{}{}, 923 }, 924 }) 925 app.ExpectTxnTraces(t, []internal.WantTxnTrace{{ 926 MetricName: "OtherTransaction/Go/Message/Micro/Topic/Named/topic", 927 Root: internal.WantTraceSegment{ 928 SegmentName: "ROOT", 929 Attributes: map[string]interface{}{}, 930 Children: []internal.WantTraceSegment{{ 931 SegmentName: "OtherTransaction/Go/Message/Micro/Topic/Named/topic", 932 Attributes: map[string]interface{}{"exclusive_duration_millis": internal.MatchAnything}, 933 Children: []internal.WantTraceSegment{{ 934 SegmentName: "Custom/segment", 935 Attributes: map[string]interface{}{}, 936 Children: []internal.WantTraceSegment{}}, 937 }, 938 }}, 939 }, 940 }}) 941 } 942 943 func TestServerSubscribeWithError(t *testing.T) { 944 app := createTestApp() 945 c, s, _ := newTestClientServerAndBroker(app, t) 946 947 var wg sync.WaitGroup 948 err := micro.RegisterSubscriber(topic, s, func(ctx context.Context, msg *proto.HelloRequest) error { 949 defer wg.Done() 950 return errors.New("subscriber error") 951 }) 952 if err != nil { 953 t.Fatal("error registering subscriber", err) 954 } 955 if err := s.Start(); nil != err { 956 t.Fatal(err) 957 } 958 959 ctx := context.Background() 960 msg := c.NewMessage(topic, &proto.HelloRequest{Name: "test"}) 961 wg.Add(1) 962 if err := c.Publish(ctx, msg); nil == err { 963 t.Fatal("Expected error but didn't get one") 964 } 965 waitOrTimeout(t, &wg) 966 s.Stop() 967 968 app.ExpectMetrics(t, []internal.WantMetric{ 969 {Name: "OtherTransaction/Go/Message/Micro/Topic/Named/topic", Scope: "", Forced: true, Data: nil}, 970 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 971 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 972 {Name: "OtherTransactionTotalTime/Go/Message/Micro/Topic/Named/topic", Scope: "", Forced: false, Data: nil}, 973 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, 974 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil}, 975 {Name: "Errors/all", Scope: "", Forced: true, Data: nil}, 976 {Name: "Errors/allOther", Scope: "", Forced: true, Data: nil}, 977 {Name: "ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, 978 {Name: "ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil}, 979 {Name: "Errors/OtherTransaction/Go/Message/Micro/Topic/Named/topic", Scope: "", Forced: true, Data: nil}, 980 }) 981 app.ExpectSpanEvents(t, []internal.WantEvent{ 982 { 983 Intrinsics: map[string]interface{}{ 984 "category": "generic", 985 "name": "OtherTransaction/Go/Message/Micro/Topic/Named/topic", 986 "nr.entryPoint": true, 987 }, 988 UserAttributes: map[string]interface{}{}, 989 AgentAttributes: map[string]interface{}{}, 990 }, 991 }) 992 app.ExpectTxnTraces(t, []internal.WantTxnTrace{{ 993 MetricName: "OtherTransaction/Go/Message/Micro/Topic/Named/topic", 994 Root: internal.WantTraceSegment{ 995 SegmentName: "ROOT", 996 Attributes: map[string]interface{}{}, 997 Children: []internal.WantTraceSegment{{ 998 SegmentName: "OtherTransaction/Go/Message/Micro/Topic/Named/topic", 999 Attributes: map[string]interface{}{"exclusive_duration_millis": internal.MatchAnything}, 1000 Children: []internal.WantTraceSegment{}, 1001 }}, 1002 }, 1003 }}) 1004 app.ExpectErrors(t, []internal.WantError{{ 1005 TxnName: "OtherTransaction/Go/Message/Micro/Topic/Named/topic", 1006 Msg: "subscriber error", 1007 Klass: "*errors.errorString", 1008 }}) 1009 app.ExpectErrorEvents(t, []internal.WantEvent{{ 1010 Intrinsics: map[string]interface{}{ 1011 "error.message": "subscriber error", 1012 "error.class": "*errors.errorString", 1013 "transactionName": "OtherTransaction/Go/Message/Micro/Topic/Named/topic", 1014 "traceId": internal.MatchAnything, 1015 "priority": internal.MatchAnything, 1016 "guid": internal.MatchAnything, 1017 "sampled": "true", 1018 }, 1019 }}) 1020 } 1021 1022 func newTestClientServerAndBroker(app newrelic.Application, t *testing.T) (client.Client, server.Server, broker.Broker) { 1023 b := bmemory.NewBroker() 1024 c := client.NewClient( 1025 client.Broker(b), 1026 client.Wrap(ClientWrapper()), 1027 ) 1028 s := server.NewServer( 1029 server.Name(serverName), 1030 server.Broker(b), 1031 server.WrapSubscriber(SubscriberWrapper(app)), 1032 ) 1033 return c, s, b 1034 }