github.com/newrelic/go-agent@v3.26.0+incompatible/_integrations/nrmongo/nrmongo_test.go (about) 1 // Copyright 2020 New Relic Corporation. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package nrmongo 5 6 import ( 7 "context" 8 "testing" 9 10 newrelic "github.com/newrelic/go-agent" 11 "github.com/newrelic/go-agent/internal" 12 "github.com/newrelic/go-agent/internal/integrationsupport" 13 "go.mongodb.org/mongo-driver/bson" 14 "go.mongodb.org/mongo-driver/bson/primitive" 15 "go.mongodb.org/mongo-driver/event" 16 ) 17 18 var ( 19 connID = "localhost:27017[-1]" 20 reqID int64 = 10 21 raw, _ = bson.Marshal(bson.D{primitive.E{Key: "commName", Value: "collName"}, {Key: "$db", Value: "testing"}}) 22 ste = &event.CommandStartedEvent{ 23 Command: raw, 24 DatabaseName: "testdb", 25 CommandName: "commName", 26 RequestID: reqID, 27 ConnectionID: connID, 28 } 29 finishedEvent = event.CommandFinishedEvent{ 30 DurationNanos: 5, 31 CommandName: "name", 32 RequestID: reqID, 33 ConnectionID: connID, 34 } 35 se = &event.CommandSucceededEvent{ 36 CommandFinishedEvent: finishedEvent, 37 Reply: nil, 38 } 39 fe = &event.CommandFailedEvent{ 40 CommandFinishedEvent: finishedEvent, 41 Failure: "failureCause", 42 } 43 ) 44 45 func TestOrigMonitorsAreCalled(t *testing.T) { 46 var started, succeeded, failed bool 47 origMonitor := &event.CommandMonitor{ 48 Started: func(ctx context.Context, e *event.CommandStartedEvent) { started = true }, 49 Succeeded: func(ctx context.Context, e *event.CommandSucceededEvent) { succeeded = true }, 50 Failed: func(ctx context.Context, e *event.CommandFailedEvent) { failed = true }, 51 } 52 ctx := context.Background() 53 nrMonitor := NewCommandMonitor(origMonitor) 54 55 nrMonitor.Started(ctx, ste) 56 if !started { 57 t.Error("started not called") 58 } 59 nrMonitor.Succeeded(ctx, se) 60 if !succeeded { 61 t.Error("succeeded not called") 62 } 63 nrMonitor.Failed(ctx, fe) 64 if !failed { 65 t.Error("failed not called") 66 } 67 } 68 69 func TestClientOptsWithNullFunctions(t *testing.T) { 70 origMonitor := &event.CommandMonitor{} // the monitor isn't nil, but its functions are. 71 ctx := context.Background() 72 nrMonitor := NewCommandMonitor(origMonitor) 73 74 // Verifying no nil pointer dereferences 75 nrMonitor.Started(ctx, ste) 76 nrMonitor.Succeeded(ctx, se) 77 nrMonitor.Failed(ctx, fe) 78 } 79 80 func TestHostAndPort(t *testing.T) { 81 type hostAndPort struct { 82 host string 83 port string 84 } 85 testCases := map[string]hostAndPort{ 86 "localhost:8080[-1]": {host: "localhost", port: "8080"}, 87 "something.com:987[-789]": {host: "something.com", port: "987"}, 88 "thisformatiswrong": {host: "", port: ""}, 89 "somethingunix.sock[-876]": {host: "somethingunix.sock", port: ""}, 90 "/var/dir/path/somethingunix.sock[-876]": {host: "/var/dir/path/somethingunix.sock", port: ""}, 91 } 92 for test, expected := range testCases { 93 h, p := calcHostAndPort(test) 94 if expected.host != h { 95 t.Errorf("unexpected host - expected %s, got %s", expected.host, h) 96 } 97 if expected.port != p { 98 t.Errorf("unexpected port - expected %s, got %s", expected.port, p) 99 } 100 } 101 } 102 103 func TestMonitor(t *testing.T) { 104 var started, succeeded, failed bool 105 origMonitor := &event.CommandMonitor{ 106 Started: func(ctx context.Context, e *event.CommandStartedEvent) { started = true }, 107 Succeeded: func(ctx context.Context, e *event.CommandSucceededEvent) { succeeded = true }, 108 Failed: func(ctx context.Context, e *event.CommandFailedEvent) { failed = true }, 109 } 110 nrMonitor := mongoMonitor{ 111 segmentMap: make(map[int64]*newrelic.DatastoreSegment), 112 origCommMon: origMonitor, 113 } 114 app := createTestApp() 115 txn := app.StartTransaction("txnName", nil, nil) 116 ctx := newrelic.NewContext(context.Background(), txn) 117 nrMonitor.started(ctx, ste) 118 if !started { 119 t.Error("Original monitor not started") 120 } 121 if len(nrMonitor.segmentMap) != 1 { 122 t.Errorf("Wrong number of segments, expected 1 but got %d", len(nrMonitor.segmentMap)) 123 } 124 nrMonitor.succeeded(ctx, se) 125 if !succeeded { 126 t.Error("Original monitor not succeeded") 127 } 128 if len(nrMonitor.segmentMap) != 0 { 129 t.Errorf("Wrong number of segments, expected 0 but got %d", len(nrMonitor.segmentMap)) 130 } 131 txn.End() 132 133 app.ExpectMetrics(t, []internal.WantMetric{ 134 {Name: "OtherTransactionTotalTime/Go/txnName", Scope: "", Forced: false, Data: nil}, 135 {Name: "Datastore/instance/MongoDB/" + internal.ThisHost + "/27017", Scope: "", Forced: false, Data: nil}, 136 {Name: "Datastore/operation/MongoDB/commName", Scope: "", Forced: false, Data: nil}, 137 {Name: "OtherTransaction/Go/txnName", Scope: "", Forced: true, Data: nil}, 138 {Name: "Datastore/all", Scope: "", Forced: true, Data: nil}, 139 {Name: "Datastore/allOther", Scope: "", Forced: true, Data: []float64{1.0}}, 140 {Name: "Datastore/MongoDB/all", Scope: "", Forced: true, Data: []float64{1.0}}, 141 {Name: "Datastore/MongoDB/allOther", Scope: "", Forced: true, Data: []float64{1.0}}, 142 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 143 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 144 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, 145 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil}, 146 {Name: "Datastore/statement/MongoDB/collName/commName", Scope: "", Forced: false, Data: []float64{1.0}}, 147 {Name: "Datastore/statement/MongoDB/collName/commName", Scope: "OtherTransaction/Go/txnName", Forced: false, Data: []float64{1.0}}, 148 }) 149 app.ExpectSpanEvents(t, []internal.WantEvent{ 150 { 151 Intrinsics: map[string]interface{}{ 152 "name": "OtherTransaction/Go/txnName", 153 "sampled": true, 154 "category": "generic", 155 "nr.entryPoint": true, 156 }, 157 UserAttributes: map[string]interface{}{}, 158 AgentAttributes: map[string]interface{}{}, 159 }, 160 { 161 Intrinsics: map[string]interface{}{ 162 "name": "Datastore/statement/MongoDB/collName/commName", 163 "sampled": true, 164 "category": "datastore", 165 "component": "MongoDB", 166 "span.kind": "client", 167 "parentId": internal.MatchAnything, 168 }, 169 UserAttributes: map[string]interface{}{}, 170 AgentAttributes: map[string]interface{}{ 171 "peer.address": internal.ThisHost + ":27017", 172 "peer.hostname": internal.ThisHost, 173 "db.statement": "'commName' on 'collName' using 'MongoDB'", 174 "db.instance": "testdb", 175 "db.collection": "collName", 176 }, 177 }, 178 }) 179 180 txn = app.StartTransaction("txnName", nil, nil) 181 ctx = newrelic.NewContext(context.Background(), txn) 182 nrMonitor.started(ctx, ste) 183 if len(nrMonitor.segmentMap) != 1 { 184 t.Errorf("Wrong number of segments, expected 1 but got %d", len(nrMonitor.segmentMap)) 185 } 186 nrMonitor.failed(ctx, fe) 187 if !failed { 188 t.Error("Original monitor not succeeded") 189 } 190 if len(nrMonitor.segmentMap) != 0 { 191 t.Errorf("Wrong number of segments, expected 0 but got %d", len(nrMonitor.segmentMap)) 192 } 193 txn.End() 194 195 app.ExpectMetrics(t, []internal.WantMetric{ 196 {Name: "OtherTransactionTotalTime/Go/txnName", Scope: "", Forced: false, Data: nil}, 197 {Name: "Datastore/instance/MongoDB/" + internal.ThisHost + "/27017", Scope: "", Forced: false, Data: nil}, 198 {Name: "Datastore/operation/MongoDB/commName", Scope: "", Forced: false, Data: nil}, 199 {Name: "OtherTransaction/Go/txnName", Scope: "", Forced: true, Data: nil}, 200 {Name: "Datastore/all", Scope: "", Forced: true, Data: nil}, 201 {Name: "Datastore/allOther", Scope: "", Forced: true, Data: []float64{2.0}}, 202 {Name: "Datastore/MongoDB/all", Scope: "", Forced: true, Data: []float64{2.0}}, 203 {Name: "Datastore/MongoDB/allOther", Scope: "", Forced: true, Data: []float64{2.0}}, 204 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 205 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 206 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, 207 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil}, 208 {Name: "Datastore/statement/MongoDB/collName/commName", Scope: "", Forced: false, Data: []float64{2.0}}, 209 {Name: "Datastore/statement/MongoDB/collName/commName", Scope: "OtherTransaction/Go/txnName", Forced: false, Data: []float64{2.0}}, 210 }) 211 } 212 213 func TestCollName(t *testing.T) { 214 command := "find" 215 ex1, _ := bson.Marshal(bson.D{{Key: command, Value: "numbers"}, {Key: "$db", Value: "testing"}}) 216 ex2, _ := bson.Marshal(bson.D{{Key: "filter", Value: ""}}) 217 testCases := map[string]bson.Raw{ 218 "numbers": ex1, 219 "": ex2, 220 } 221 for name, raw := range testCases { 222 e := event.CommandStartedEvent{ 223 Command: raw, 224 CommandName: command, 225 } 226 result := collName(&e) 227 if result != name { 228 t.Errorf("Wrong collection name: %s", result) 229 } 230 } 231 232 } 233 234 func createTestApp() integrationsupport.ExpectApp { 235 return integrationsupport.NewTestApp(replyFn, cfgFn) 236 } 237 238 var cfgFn = func(cfg *newrelic.Config) { 239 cfg.Enabled = false 240 cfg.DistributedTracer.Enabled = true 241 cfg.TransactionTracer.SegmentThreshold = 0 242 cfg.TransactionTracer.Threshold.IsApdexFailing = false 243 cfg.TransactionTracer.Threshold.Duration = 0 244 } 245 246 var replyFn = func(reply *internal.ConnectReply) { 247 reply.AdaptiveSampler = internal.SampleEverything{} 248 }