github.com/newrelic/go-agent@v3.26.0+incompatible/internal_serverless_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 "bytes" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/newrelic/go-agent/internal" 13 ) 14 15 func TestServerlessDistributedTracingConfigPresent(t *testing.T) { 16 cfgFn := func(cfg *Config) { 17 cfg.ServerlessMode.Enabled = true 18 cfg.DistributedTracer.Enabled = true 19 cfg.ServerlessMode.AccountID = "123" 20 cfg.ServerlessMode.TrustedAccountKey = "trustkey" 21 cfg.ServerlessMode.PrimaryAppID = "456" 22 } 23 app := testApp(nil, cfgFn, t) 24 payload := app.StartTransaction("hello", nil, nil).CreateDistributedTracePayload() 25 txn := app.StartTransaction("hello", nil, nil) 26 txn.AcceptDistributedTracePayload(TransportHTTP, payload) 27 txn.End() 28 app.ExpectMetrics(t, []internal.WantMetric{ 29 {Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil}, 30 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 31 {Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil}, 32 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 33 {Name: "DurationByCaller/App/123/456/HTTP/all", Scope: "", Forced: false, Data: nil}, 34 {Name: "DurationByCaller/App/123/456/HTTP/allOther", Scope: "", Forced: false, Data: nil}, 35 {Name: "TransportDuration/App/123/456/HTTP/all", Scope: "", Forced: false, Data: nil}, 36 {Name: "TransportDuration/App/123/456/HTTP/allOther", Scope: "", Forced: false, Data: nil}, 37 {Name: "Supportability/DistributedTrace/AcceptPayload/Success", Scope: "", Forced: true, Data: singleCount}, 38 }) 39 } 40 41 func TestServerlessDistributedTracingConfigPartiallyPresent(t *testing.T) { 42 // This tests that if ServerlessMode.PrimaryAppID is unset it should 43 // default to "Unknown". 44 cfgFn := func(cfg *Config) { 45 cfg.ServerlessMode.Enabled = true 46 cfg.DistributedTracer.Enabled = true 47 cfg.ServerlessMode.AccountID = "123" 48 cfg.ServerlessMode.TrustedAccountKey = "trustkey" 49 } 50 app := testApp(nil, cfgFn, t) 51 payload := app.StartTransaction("hello", nil, nil).CreateDistributedTracePayload() 52 txn := app.StartTransaction("hello", nil, nil) 53 txn.AcceptDistributedTracePayload(TransportHTTP, payload) 54 txn.End() 55 app.ExpectMetrics(t, []internal.WantMetric{ 56 {Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil}, 57 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 58 {Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil}, 59 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 60 {Name: "DurationByCaller/App/123/Unknown/HTTP/all", Scope: "", Forced: false, Data: nil}, 61 {Name: "DurationByCaller/App/123/Unknown/HTTP/allOther", Scope: "", Forced: false, Data: nil}, 62 {Name: "TransportDuration/App/123/Unknown/HTTP/all", Scope: "", Forced: false, Data: nil}, 63 {Name: "TransportDuration/App/123/Unknown/HTTP/allOther", Scope: "", Forced: false, Data: nil}, 64 {Name: "Supportability/DistributedTrace/AcceptPayload/Success", Scope: "", Forced: true, Data: singleCount}, 65 }) 66 } 67 68 func TestServerlessDistributedTracingConfigTrustKeyAbsent(t *testing.T) { 69 // Test that distributed tracing works if only AccountID has been set. 70 cfgFn := func(cfg *Config) { 71 cfg.ServerlessMode.Enabled = true 72 cfg.DistributedTracer.Enabled = true 73 cfg.ServerlessMode.AccountID = "123" 74 } 75 app := testApp(nil, cfgFn, t) 76 payload := app.StartTransaction("hello", nil, nil).CreateDistributedTracePayload() 77 txn := app.StartTransaction("hello", nil, nil) 78 txn.AcceptDistributedTracePayload(TransportHTTP, payload) 79 txn.End() 80 app.ExpectMetrics(t, []internal.WantMetric{ 81 {Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil}, 82 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 83 {Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil}, 84 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 85 {Name: "DurationByCaller/App/123/Unknown/HTTP/all", Scope: "", Forced: false, Data: nil}, 86 {Name: "DurationByCaller/App/123/Unknown/HTTP/allOther", Scope: "", Forced: false, Data: nil}, 87 {Name: "TransportDuration/App/123/Unknown/HTTP/all", Scope: "", Forced: false, Data: nil}, 88 {Name: "TransportDuration/App/123/Unknown/HTTP/allOther", Scope: "", Forced: false, Data: nil}, 89 {Name: "Supportability/DistributedTrace/AcceptPayload/Success", Scope: "", Forced: true, Data: singleCount}, 90 }) 91 } 92 93 func TestServerlessDistributedTracingConfigAbsent(t *testing.T) { 94 // Test that payloads do not get created or accepted when distributed 95 // tracing configuration is not present. 96 cfgFn := func(cfg *Config) { 97 cfg.ServerlessMode.Enabled = true 98 cfg.DistributedTracer.Enabled = true 99 } 100 app := testApp(nil, cfgFn, t) 101 txn := app.StartTransaction("hello", nil, nil) 102 payload := txn.CreateDistributedTracePayload() 103 if "" != payload.Text() { 104 t.Error(payload.Text()) 105 } 106 nonemptyPayload := func() DistributedTracePayload { 107 app := testApp(nil, func(cfg *Config) { 108 cfgFn(cfg) 109 cfg.ServerlessMode.AccountID = "123" 110 cfg.ServerlessMode.TrustedAccountKey = "trustkey" 111 cfg.ServerlessMode.PrimaryAppID = "456" 112 }, t) 113 return app.StartTransaction("hello", nil, nil).CreateDistributedTracePayload() 114 }() 115 if "" == nonemptyPayload.Text() { 116 t.Error(nonemptyPayload.Text()) 117 } 118 err := txn.AcceptDistributedTracePayload(TransportHTTP, nonemptyPayload) 119 if err != nil { 120 t.Error(err) 121 } 122 txn.End() 123 app.ExpectMetrics(t, []internal.WantMetric{ 124 {Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil}, 125 {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, 126 {Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil}, 127 {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 128 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, 129 {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil}, 130 }) 131 } 132 133 func TestServerlessLowApdex(t *testing.T) { 134 apdex := -1 * time.Second 135 cfgFn := func(cfg *Config) { 136 cfg.ServerlessMode.Enabled = true 137 cfg.ServerlessMode.ApdexThreshold = apdex 138 } 139 app := testApp(nil, cfgFn, t) 140 txn := app.StartTransaction("hello", nil, nil) 141 txn.SetWebRequest(nil) // only web gets apdex 142 txn.End() 143 144 app.ExpectMetrics(t, []internal.WantMetric{ 145 {Name: "WebTransaction/Go/hello", Scope: "", Forced: true, Data: nil}, 146 {Name: "WebTransaction", Scope: "", Forced: true, Data: nil}, 147 {Name: "WebTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil}, 148 {Name: "WebTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 149 {Name: "HttpDispatcher", Scope: "", Forced: true, Data: nil}, 150 // third apdex field is failed count 151 {Name: "Apdex", Scope: "", Forced: true, Data: []float64{0, 0, 1, apdex.Seconds(), apdex.Seconds(), 0}}, 152 {Name: "Apdex/Go/hello", Scope: "", Forced: false, Data: []float64{0, 0, 1, apdex.Seconds(), apdex.Seconds(), 0}}, 153 }) 154 } 155 156 func TestServerlessHighApdex(t *testing.T) { 157 apdex := 1 * time.Hour 158 cfgFn := func(cfg *Config) { 159 cfg.ServerlessMode.Enabled = true 160 cfg.ServerlessMode.ApdexThreshold = apdex 161 } 162 app := testApp(nil, cfgFn, t) 163 txn := app.StartTransaction("hello", nil, nil) 164 txn.SetWebRequest(nil) // only web gets apdex 165 txn.End() 166 167 app.ExpectMetrics(t, []internal.WantMetric{ 168 {Name: "WebTransaction/Go/hello", Scope: "", Forced: true, Data: nil}, 169 {Name: "WebTransaction", Scope: "", Forced: true, Data: nil}, 170 {Name: "WebTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil}, 171 {Name: "WebTransactionTotalTime", Scope: "", Forced: true, Data: nil}, 172 {Name: "HttpDispatcher", Scope: "", Forced: true, Data: nil}, 173 // first apdex field is satisfied count 174 {Name: "Apdex", Scope: "", Forced: true, Data: []float64{1, 0, 0, apdex.Seconds(), apdex.Seconds(), 0}}, 175 {Name: "Apdex/Go/hello", Scope: "", Forced: false, Data: []float64{1, 0, 0, apdex.Seconds(), apdex.Seconds(), 0}}, 176 }) 177 } 178 179 func TestServerlessRecordCustomMetric(t *testing.T) { 180 cfgFn := func(cfg *Config) { cfg.ServerlessMode.Enabled = true } 181 app := testApp(nil, cfgFn, t) 182 err := app.RecordCustomMetric("myMetric", 123.0) 183 if err != errMetricServerless { 184 t.Error(err) 185 } 186 } 187 188 func TestServerlessRecordCustomEvent(t *testing.T) { 189 cfgFn := func(cfg *Config) { cfg.ServerlessMode.Enabled = true } 190 app := testApp(nil, cfgFn, t) 191 192 attributes := map[string]interface{}{"zip": 1} 193 err := app.RecordCustomEvent("myType", attributes) 194 if err != nil { 195 t.Error(err) 196 } 197 app.ExpectCustomEvents(t, []internal.WantEvent{{ 198 Intrinsics: map[string]interface{}{ 199 "type": "myType", 200 "timestamp": internal.MatchAnything, 201 }, 202 UserAttributes: attributes, 203 }}) 204 205 buf := &bytes.Buffer{} 206 internal.ServerlessWrite(app, "my-arn", buf) 207 208 _, data, err := internal.ParseServerlessPayload(buf.Bytes()) 209 if err != nil { 210 t.Fatal(err) 211 } 212 213 // Data should contain only custom events. Dynamic timestamp makes exact 214 // comparison difficult. 215 eventData := string(data["custom_event_data"]) 216 if !strings.Contains(eventData, `{"zip":1}`) { 217 t.Error(eventData) 218 } 219 if len(data) != 1 { 220 t.Fatal(data) 221 } 222 } 223 224 func TestServerlessJSON(t *testing.T) { 225 cfgFn := func(cfg *Config) { 226 cfg.ServerlessMode.Enabled = true 227 } 228 app := testApp(nil, cfgFn, t) 229 txn := app.StartTransaction("hello", nil, nil) 230 txn.(internal.AddAgentAttributer).AddAgentAttribute(internal.AttributeAWSLambdaARN, "thearn", nil) 231 txn.End() 232 233 buf := &bytes.Buffer{} 234 internal.ServerlessWrite(app, "lambda-test-arn", buf) 235 236 metadata, data, err := internal.ParseServerlessPayload(buf.Bytes()) 237 if err != nil { 238 t.Fatal(err) 239 } 240 241 // Data should contain txn event and metrics. Timestamps make exact 242 // JSON comparison tough. 243 if v := data["metric_data"]; nil == v { 244 t.Fatal(data) 245 } 246 if v := data["analytic_event_data"]; nil == v { 247 t.Fatal(data) 248 } 249 if v := string(metadata["arn"]); v != `"lambda-test-arn"` { 250 t.Fatal(v) 251 } 252 if v := string(metadata["agent_version"]); v != `"`+Version+`"` { 253 t.Fatal(v) 254 } 255 } 256 257 func validSampler(s internal.AdaptiveSampler) bool { 258 _, isSampleEverything := s.(internal.SampleEverything) 259 _, isSampleNothing := s.(internal.SampleEverything) 260 return (nil != s) && !isSampleEverything && !isSampleNothing 261 } 262 263 func TestServerlessConnectReply(t *testing.T) { 264 cfg := NewConfig("", "") 265 cfg.ServerlessMode.ApdexThreshold = 2 * time.Second 266 cfg.ServerlessMode.AccountID = "the-account-id" 267 cfg.ServerlessMode.TrustedAccountKey = "the-trust-key" 268 cfg.ServerlessMode.PrimaryAppID = "the-primary-app" 269 reply := newServerlessConnectReply(cfg) 270 if reply.ApdexThresholdSeconds != 2 { 271 t.Error(reply.ApdexThresholdSeconds) 272 } 273 if reply.AccountID != "the-account-id" { 274 t.Error(reply.AccountID) 275 } 276 if reply.TrustedAccountKey != "the-trust-key" { 277 t.Error(reply.TrustedAccountKey) 278 } 279 if reply.PrimaryAppID != "the-primary-app" { 280 t.Error(reply.PrimaryAppID) 281 } 282 if !validSampler(reply.AdaptiveSampler) { 283 t.Error(reply.AdaptiveSampler) 284 } 285 286 // Now test the defaults: 287 cfg = NewConfig("", "") 288 reply = newServerlessConnectReply(cfg) 289 if reply.ApdexThresholdSeconds != 0.5 { 290 t.Error(reply.ApdexThresholdSeconds) 291 } 292 if reply.AccountID != "" { 293 t.Error(reply.AccountID) 294 } 295 if reply.TrustedAccountKey != "" { 296 t.Error(reply.TrustedAccountKey) 297 } 298 if reply.PrimaryAppID != "Unknown" { 299 t.Error(reply.PrimaryAppID) 300 } 301 if !validSampler(reply.AdaptiveSampler) { 302 t.Error(reply.AdaptiveSampler) 303 } 304 }