github.com/newrelic/go-agent@v3.26.0+incompatible/_integrations/nrnats/nrnats_test.go (about) 1 // Copyright 2020 New Relic Corporation. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package nrnats 5 6 import ( 7 "os" 8 "sync" 9 "testing" 10 "time" 11 12 "github.com/nats-io/nats-server/test" 13 "github.com/nats-io/nats.go" 14 newrelic "github.com/newrelic/go-agent" 15 "github.com/newrelic/go-agent/internal" 16 "github.com/newrelic/go-agent/internal/integrationsupport" 17 ) 18 19 func TestMain(m *testing.M) { 20 s := test.RunDefaultServer() 21 defer s.Shutdown() 22 os.Exit(m.Run()) 23 } 24 25 func testApp() integrationsupport.ExpectApp { 26 return integrationsupport.NewTestApp(integrationsupport.SampleEverythingReplyFn, cfgFn) 27 } 28 29 var cfgFn = func(cfg *newrelic.Config) { 30 cfg.Enabled = false 31 cfg.DistributedTracer.Enabled = true 32 cfg.TransactionTracer.SegmentThreshold = 0 33 cfg.TransactionTracer.Threshold.IsApdexFailing = false 34 cfg.TransactionTracer.Threshold.Duration = 0 35 cfg.Attributes.Include = append(cfg.Attributes.Include, 36 newrelic.AttributeMessageRoutingKey, 37 newrelic.AttributeMessageQueueName, 38 newrelic.AttributeMessageExchangeType, 39 newrelic.AttributeMessageReplyTo, 40 newrelic.AttributeMessageCorrelationID, 41 ) 42 } 43 44 func TestStartPublishSegmentNilTxn(t *testing.T) { 45 // Make sure that a nil transaction does not cause panics 46 nc, err := nats.Connect(nats.DefaultURL) 47 if nil != err { 48 t.Fatal(err) 49 } 50 defer nc.Close() 51 52 StartPublishSegment(nil, nc, "mysubject").End() 53 } 54 55 func TestStartPublishSegmentNilConn(t *testing.T) { 56 // Make sure that a nil nats.Conn does not cause panics and does not record 57 // metrics 58 app := testApp() 59 txn := app.StartTransaction("testing", nil, nil) 60 StartPublishSegment(txn, nil, "mysubject").End() 61 txn.End() 62 63 app.ExpectMetrics(t, []internal.WantMetric{ 64 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, 65 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil}, 66 {Name: "OtherTransaction/Go/testing", Scope: "", Forced: true, Data: nil}, 67 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 68 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 69 {Name: "OtherTransactionTotalTime/Go/testing", Scope: "", Forced: false, Data: nil}, 70 }) 71 } 72 73 func TestStartPublishSegmentBasic(t *testing.T) { 74 app := testApp() 75 txn := app.StartTransaction("testing", nil, nil) 76 nc, err := nats.Connect(nats.DefaultURL) 77 if nil != err { 78 t.Fatal(err) 79 } 80 defer nc.Close() 81 82 StartPublishSegment(txn, nc, "mysubject").End() 83 txn.End() 84 85 app.ExpectMetrics(t, []internal.WantMetric{ 86 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, 87 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil}, 88 {Name: "MessageBroker/NATS/Topic/Produce/Named/mysubject", Scope: "", Forced: false, Data: nil}, 89 {Name: "MessageBroker/NATS/Topic/Produce/Named/mysubject", Scope: "OtherTransaction/Go/testing", Forced: false, Data: nil}, 90 {Name: "OtherTransaction/Go/testing", Scope: "", Forced: true, Data: nil}, 91 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 92 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 93 {Name: "OtherTransactionTotalTime/Go/testing", Scope: "", Forced: false, Data: nil}, 94 }) 95 app.ExpectSpanEvents(t, []internal.WantEvent{ 96 { 97 Intrinsics: map[string]interface{}{ 98 "category": "generic", 99 "name": "OtherTransaction/Go/testing", 100 "nr.entryPoint": true, 101 }, 102 UserAttributes: map[string]interface{}{}, 103 AgentAttributes: map[string]interface{}{}, 104 }, 105 { 106 Intrinsics: map[string]interface{}{ 107 "category": "generic", 108 "name": "MessageBroker/NATS/Topic/Produce/Named/mysubject", 109 "parentId": internal.MatchAnything, 110 }, 111 UserAttributes: map[string]interface{}{}, 112 AgentAttributes: map[string]interface{}{}, 113 }, 114 }) 115 app.ExpectTxnTraces(t, []internal.WantTxnTrace{{ 116 MetricName: "OtherTransaction/Go/testing", 117 Root: internal.WantTraceSegment{ 118 SegmentName: "ROOT", 119 Attributes: map[string]interface{}{}, 120 Children: []internal.WantTraceSegment{{ 121 SegmentName: "OtherTransaction/Go/testing", 122 Attributes: map[string]interface{}{"exclusive_duration_millis": internal.MatchAnything}, 123 Children: []internal.WantTraceSegment{ 124 { 125 SegmentName: "MessageBroker/NATS/Topic/Produce/Named/mysubject", 126 Attributes: map[string]interface{}{}, 127 }, 128 }, 129 }}, 130 }, 131 }, 132 }) 133 } 134 135 func TestSubWrapperWithNilApp(t *testing.T) { 136 nc, err := nats.Connect(nats.DefaultURL) 137 if err != nil { 138 t.Fatal("Error connecting to NATS server", err) 139 } 140 wg := sync.WaitGroup{} 141 nc.Subscribe("subject1", SubWrapper(nil, func(msg *nats.Msg) { 142 wg.Done() 143 })) 144 wg.Add(1) 145 nc.Publish("subject1", []byte("data")) 146 wg.Wait() 147 } 148 149 func TestSubWrapper(t *testing.T) { 150 nc, err := nats.Connect(nats.DefaultURL) 151 if err != nil { 152 t.Fatal("Error connecting to NATS server", err) 153 } 154 wg := sync.WaitGroup{} 155 app := testApp() 156 nc.QueueSubscribe("subject2", "queue1", WgWrapper(&wg, SubWrapper(app, func(msg *nats.Msg) {}))) 157 wg.Add(1) 158 nc.Request("subject2", []byte("data"), time.Second) 159 wg.Wait() 160 161 app.ExpectMetrics(t, []internal.WantMetric{ 162 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 163 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 164 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, 165 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil}, 166 {Name: "OtherTransaction/Go/Message/NATS/Topic/Named/subject2", Scope: "", Forced: true, Data: nil}, 167 {Name: "OtherTransactionTotalTime/Go/Message/NATS/Topic/Named/subject2", Scope: "", Forced: false, Data: nil}, 168 }) 169 app.ExpectTxnEvents(t, []internal.WantEvent{ 170 { 171 Intrinsics: map[string]interface{}{ 172 "name": "OtherTransaction/Go/Message/NATS/Topic/Named/subject2", 173 "guid": internal.MatchAnything, 174 "priority": internal.MatchAnything, 175 "sampled": internal.MatchAnything, 176 "traceId": internal.MatchAnything, 177 }, 178 AgentAttributes: map[string]interface{}{ 179 "message.replyTo": internal.MatchAnything, // starts with _INBOX 180 "message.routingKey": "subject2", 181 "message.queueName": "queue1", 182 }, 183 UserAttributes: map[string]interface{}{}, 184 }, 185 }) 186 } 187 188 func TestStartPublishSegmentNaming(t *testing.T) { 189 testCases := []struct { 190 subject string 191 metric string 192 }{ 193 {subject: "", metric: "MessageBroker/NATS/Topic/Produce/Named/Unknown"}, 194 {subject: "mysubject", metric: "MessageBroker/NATS/Topic/Produce/Named/mysubject"}, 195 {subject: "_INBOX.asldfkjsldfjskd.ldskfjls", metric: "MessageBroker/NATS/Topic/Produce/Temp"}, 196 } 197 198 nc, err := nats.Connect(nats.DefaultURL) 199 if nil != err { 200 t.Fatal(err) 201 } 202 defer nc.Close() 203 204 for _, tc := range testCases { 205 app := testApp() 206 txn := app.StartTransaction("testing", nil, nil) 207 StartPublishSegment(txn, nc, tc.subject).End() 208 txn.End() 209 210 app.ExpectMetrics(t, []internal.WantMetric{ 211 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, 212 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil}, 213 {Name: "OtherTransaction/Go/testing", Scope: "", Forced: true, Data: nil}, 214 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 215 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 216 {Name: "OtherTransactionTotalTime/Go/testing", Scope: "", Forced: false, Data: nil}, 217 {Name: tc.metric, Scope: "", Forced: false, Data: nil}, 218 {Name: tc.metric, Scope: "OtherTransaction/Go/testing", Forced: false, Data: nil}, 219 }) 220 } 221 } 222 223 // Wrapper function to ensure that the NR wrapper is done recording transaction data before wg.Done() is called 224 func WgWrapper(wg *sync.WaitGroup, nrWrap func(msg *nats.Msg)) func(msg *nats.Msg) { 225 return func(msg *nats.Msg) { 226 nrWrap(msg) 227 wg.Done() 228 } 229 }