github.com/newrelic/go-agent@v3.26.0+incompatible/app_run_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 "encoding/json" 8 "fmt" 9 "reflect" 10 "testing" 11 "time" 12 13 "github.com/newrelic/go-agent/internal" 14 ) 15 16 func TestResponseCodeIsError(t *testing.T) { 17 cfg := NewConfig("my app", "0123456789012345678901234567890123456789") 18 cfg.ErrorCollector.IgnoreStatusCodes = append(cfg.ErrorCollector.IgnoreStatusCodes, 504) 19 run := newAppRun(cfg, internal.ConnectReplyDefaults()) 20 21 for _, tc := range []struct { 22 Code int 23 IsError bool 24 }{ 25 {Code: 0, IsError: false}, // gRPC 26 {Code: 1, IsError: true}, // gRPC 27 {Code: 5, IsError: false}, // gRPC 28 {Code: 6, IsError: true}, // gRPC 29 {Code: 99, IsError: true}, 30 {Code: 100, IsError: false}, 31 {Code: 199, IsError: false}, 32 {Code: 200, IsError: false}, 33 {Code: 300, IsError: false}, 34 {Code: 399, IsError: false}, 35 {Code: 400, IsError: true}, 36 {Code: 404, IsError: false}, 37 {Code: 503, IsError: true}, 38 {Code: 504, IsError: false}, 39 } { 40 if is := run.responseCodeIsError(tc.Code); is != tc.IsError { 41 t.Errorf("responseCodeIsError for %d, wanted=%v got=%v", 42 tc.Code, tc.IsError, is) 43 } 44 } 45 46 } 47 48 func TestCrossAppTracingEnabled(t *testing.T) { 49 // CAT should be enabled by default. 50 cfg := NewConfig("my app", "0123456789012345678901234567890123456789") 51 run := newAppRun(cfg, internal.ConnectReplyDefaults()) 52 if enabled := run.Config.CrossApplicationTracer.Enabled; !enabled { 53 t.Error(enabled) 54 } 55 56 // DT gets priority over CAT. 57 cfg = NewConfig("my app", "0123456789012345678901234567890123456789") 58 cfg.DistributedTracer.Enabled = true 59 cfg.CrossApplicationTracer.Enabled = true 60 run = newAppRun(cfg, internal.ConnectReplyDefaults()) 61 if enabled := run.Config.CrossApplicationTracer.Enabled; enabled { 62 t.Error(enabled) 63 } 64 65 cfg = NewConfig("my app", "0123456789012345678901234567890123456789") 66 cfg.DistributedTracer.Enabled = false 67 cfg.CrossApplicationTracer.Enabled = false 68 run = newAppRun(cfg, internal.ConnectReplyDefaults()) 69 if enabled := run.Config.CrossApplicationTracer.Enabled; enabled { 70 t.Error(enabled) 71 } 72 73 cfg = NewConfig("my app", "0123456789012345678901234567890123456789") 74 cfg.DistributedTracer.Enabled = false 75 cfg.CrossApplicationTracer.Enabled = true 76 run = newAppRun(cfg, internal.ConnectReplyDefaults()) 77 if enabled := run.Config.CrossApplicationTracer.Enabled; !enabled { 78 t.Error(enabled) 79 } 80 } 81 82 func TestTxnTraceThreshold(t *testing.T) { 83 // Test that the default txn trace threshold is the failing apdex. 84 cfg := NewConfig("my app", "0123456789012345678901234567890123456789") 85 run := newAppRun(cfg, internal.ConnectReplyDefaults()) 86 threshold := run.txnTraceThreshold(1 * time.Second) 87 if threshold != 4*time.Second { 88 t.Error(threshold) 89 } 90 91 // Test that the trace threshold can be assigned to a fixed value. 92 cfg = NewConfig("my app", "0123456789012345678901234567890123456789") 93 cfg.TransactionTracer.Threshold.IsApdexFailing = false 94 cfg.TransactionTracer.Threshold.Duration = 3 * time.Second 95 run = newAppRun(cfg, internal.ConnectReplyDefaults()) 96 threshold = run.txnTraceThreshold(1 * time.Second) 97 if threshold != 3*time.Second { 98 t.Error(threshold) 99 } 100 101 // Test that the trace threshold can be overwritten by server-side-config. 102 // with "apdex_f". 103 cfg = NewConfig("my app", "0123456789012345678901234567890123456789") 104 cfg.TransactionTracer.Threshold.IsApdexFailing = false 105 cfg.TransactionTracer.Threshold.Duration = 3 * time.Second 106 reply := internal.ConnectReplyDefaults() 107 json.Unmarshal([]byte(`{"agent_config":{"transaction_tracer.transaction_threshold":"apdex_f"}}`), &reply) 108 run = newAppRun(cfg, reply) 109 threshold = run.txnTraceThreshold(1 * time.Second) 110 if threshold != 4*time.Second { 111 t.Error(threshold) 112 } 113 114 // Test that the trace threshold can be overwritten by server-side-config. 115 // with a numberic value. 116 cfg = NewConfig("my app", "0123456789012345678901234567890123456789") 117 reply = internal.ConnectReplyDefaults() 118 json.Unmarshal([]byte(`{"agent_config":{"transaction_tracer.transaction_threshold":3}}`), &reply) 119 run = newAppRun(cfg, reply) 120 threshold = run.txnTraceThreshold(1 * time.Second) 121 if threshold != 3*time.Second { 122 t.Error(threshold) 123 } 124 } 125 126 var cfg = NewConfig("name", "license") 127 128 func TestEmptyReplyEventHarvestDefaults(t *testing.T) { 129 var run internal.HarvestConfigurer = newAppRun(cfg, &internal.ConnectReply{}) 130 assertHarvestConfig(t, &run, expectHarvestConfig{ 131 maxTxnEvents: internal.MaxTxnEvents, 132 maxCustomEvents: internal.MaxCustomEvents, 133 maxErrorEvents: internal.MaxErrorEvents, 134 maxSpanEvents: internal.MaxSpanEvents, 135 periods: map[internal.HarvestTypes]time.Duration{ 136 internal.HarvestTypesAll: 60 * time.Second, 137 0: 60 * time.Second, 138 }, 139 }) 140 } 141 142 func TestEventHarvestFieldsAllPopulated(t *testing.T) { 143 reply, err := internal.ConstructConnectReply([]byte(`{"return_value":{ 144 "event_harvest_config": { 145 "report_period_ms": 5000, 146 "harvest_limits": { 147 "analytic_event_data": 1, 148 "custom_event_data": 2, 149 "span_event_data": 3, 150 "error_event_data": 4 151 } 152 } 153 }}`), internal.PreconnectReply{}) 154 if nil != err { 155 t.Fatal(err) 156 } 157 var run internal.HarvestConfigurer = newAppRun(cfg, reply) 158 assertHarvestConfig(t, &run, expectHarvestConfig{ 159 maxTxnEvents: 1, 160 maxCustomEvents: 2, 161 maxErrorEvents: 4, 162 maxSpanEvents: 3, 163 periods: map[internal.HarvestTypes]time.Duration{ 164 internal.HarvestMetricsTraces: 60 * time.Second, 165 internal.HarvestTypesEvents: 5 * time.Second, 166 }, 167 }) 168 } 169 170 func TestZeroReportPeriod(t *testing.T) { 171 reply, err := internal.ConstructConnectReply([]byte(`{"return_value":{ 172 "event_harvest_config": { 173 "report_period_ms": 0 174 } 175 }}`), internal.PreconnectReply{}) 176 if nil != err { 177 t.Fatal(err) 178 } 179 var run internal.HarvestConfigurer = newAppRun(cfg, reply) 180 assertHarvestConfig(t, &run, expectHarvestConfig{ 181 maxTxnEvents: internal.MaxTxnEvents, 182 maxCustomEvents: internal.MaxCustomEvents, 183 maxErrorEvents: internal.MaxErrorEvents, 184 maxSpanEvents: internal.MaxSpanEvents, 185 periods: map[internal.HarvestTypes]time.Duration{ 186 internal.HarvestTypesAll: 60 * time.Second, 187 0: 60 * time.Second, 188 }, 189 }) 190 } 191 192 func TestEventHarvestFieldsOnlySpanEvents(t *testing.T) { 193 reply, err := internal.ConstructConnectReply([]byte(`{"return_value":{ 194 "event_harvest_config": { 195 "report_period_ms": 5000, 196 "harvest_limits": { "span_event_data": 3 } 197 }}}`), internal.PreconnectReply{}) 198 if nil != err { 199 t.Fatal(err) 200 } 201 var run internal.HarvestConfigurer = newAppRun(cfg, reply) 202 assertHarvestConfig(t, &run, expectHarvestConfig{ 203 maxTxnEvents: internal.MaxTxnEvents, 204 maxCustomEvents: internal.MaxCustomEvents, 205 maxErrorEvents: internal.MaxErrorEvents, 206 maxSpanEvents: 3, 207 periods: map[internal.HarvestTypes]time.Duration{ 208 internal.HarvestTypesAll ^ internal.HarvestSpanEvents: 60 * time.Second, 209 internal.HarvestSpanEvents: 5 * time.Second, 210 }, 211 }) 212 } 213 214 func TestEventHarvestFieldsOnlyTxnEvents(t *testing.T) { 215 reply, err := internal.ConstructConnectReply([]byte(`{"return_value":{ 216 "event_harvest_config": { 217 "report_period_ms": 5000, 218 "harvest_limits": { "analytic_event_data": 3 } 219 }}}`), internal.PreconnectReply{}) 220 if nil != err { 221 t.Fatal(err) 222 } 223 var run internal.HarvestConfigurer = newAppRun(cfg, reply) 224 assertHarvestConfig(t, &run, expectHarvestConfig{ 225 maxTxnEvents: 3, 226 maxCustomEvents: internal.MaxCustomEvents, 227 maxErrorEvents: internal.MaxErrorEvents, 228 maxSpanEvents: internal.MaxSpanEvents, 229 periods: map[internal.HarvestTypes]time.Duration{ 230 internal.HarvestTypesAll ^ internal.HarvestTxnEvents: 60 * time.Second, 231 internal.HarvestTxnEvents: 5 * time.Second, 232 }, 233 }) 234 } 235 236 func TestEventHarvestFieldsOnlyErrorEvents(t *testing.T) { 237 reply, err := internal.ConstructConnectReply([]byte(`{"return_value":{ 238 "event_harvest_config": { 239 "report_period_ms": 5000, 240 "harvest_limits": { "error_event_data": 3 } 241 }}}`), internal.PreconnectReply{}) 242 if nil != err { 243 t.Fatal(err) 244 } 245 var run internal.HarvestConfigurer = newAppRun(cfg, reply) 246 assertHarvestConfig(t, &run, expectHarvestConfig{ 247 maxTxnEvents: internal.MaxTxnEvents, 248 maxCustomEvents: internal.MaxCustomEvents, 249 maxErrorEvents: 3, 250 maxSpanEvents: internal.MaxSpanEvents, 251 periods: map[internal.HarvestTypes]time.Duration{ 252 internal.HarvestTypesAll ^ internal.HarvestErrorEvents: 60 * time.Second, 253 internal.HarvestErrorEvents: 5 * time.Second, 254 }, 255 }) 256 } 257 258 func TestEventHarvestFieldsOnlyCustomEvents(t *testing.T) { 259 reply, err := internal.ConstructConnectReply([]byte(`{"return_value":{ 260 "event_harvest_config": { 261 "report_period_ms": 5000, 262 "harvest_limits": { "custom_event_data": 3 } 263 }}}`), internal.PreconnectReply{}) 264 if nil != err { 265 t.Fatal(err) 266 } 267 var run internal.HarvestConfigurer = newAppRun(cfg, reply) 268 assertHarvestConfig(t, &run, expectHarvestConfig{ 269 maxTxnEvents: internal.MaxTxnEvents, 270 maxCustomEvents: 3, 271 maxErrorEvents: internal.MaxErrorEvents, 272 maxSpanEvents: internal.MaxSpanEvents, 273 periods: map[internal.HarvestTypes]time.Duration{ 274 internal.HarvestTypesAll ^ internal.HarvestCustomEvents: 60 * time.Second, 275 internal.HarvestCustomEvents: 5 * time.Second, 276 }, 277 }) 278 } 279 280 func TestConfigurableHarvestNegativeReportPeriod(t *testing.T) { 281 h, err := internal.ConstructConnectReply([]byte(`{"return_value":{ 282 "event_harvest_config": { 283 "report_period_ms": -1 284 }}}`), internal.PreconnectReply{}) 285 if nil != err { 286 t.Fatal(err) 287 } 288 expect := time.Duration(internal.DefaultConfigurableEventHarvestMs) * time.Millisecond 289 if period := h.ConfigurablePeriod(); period != expect { 290 t.Fatal(expect, period) 291 } 292 } 293 294 func TestReplyTraceIDGenerator(t *testing.T) { 295 // Test that the default connect reply has a populated trace id 296 // generator that works. 297 reply := internal.ConnectReplyDefaults() 298 id1 := reply.TraceIDGenerator.GenerateTraceID() 299 id2 := reply.TraceIDGenerator.GenerateTraceID() 300 if len(id1) != 16 || len(id2) != 16 || id1 == id2 { 301 t.Error(id1, id2) 302 } 303 } 304 305 func TestConfigurableTxnEvents_withCollResponse(t *testing.T) { 306 h, err := internal.ConstructConnectReply([]byte( 307 `{"return_value":{ 308 "event_harvest_config": { 309 "report_period_ms": 10000, 310 "harvest_limits": { 311 "analytic_event_data": 15 312 } 313 } 314 }}`), internal.PreconnectReply{}) 315 if nil != err { 316 t.Fatal(err) 317 } 318 result := newAppRun(cfg, h).MaxTxnEvents() 319 if result != 15 { 320 t.Error(fmt.Sprintf("Unexpected max number of txn events, expected %d but got %d", 15, result)) 321 } 322 } 323 324 func TestConfigurableTxnEvents_notInCollResponse(t *testing.T) { 325 reply, err := internal.ConstructConnectReply([]byte( 326 `{"return_value":{ 327 "event_harvest_config": { 328 "report_period_ms": 10000 329 } 330 }}`), internal.PreconnectReply{}) 331 if nil != err { 332 t.Fatal(err) 333 } 334 expected := 10 335 cfg.TransactionEvents.MaxSamplesStored = expected 336 result := newAppRun(cfg, reply).MaxTxnEvents() 337 if result != expected { 338 t.Error(fmt.Sprintf("Unexpected max number of txn events, expected %d but got %d", expected, result)) 339 } 340 } 341 342 func TestConfigurableTxnEvents_configMoreThanMax(t *testing.T) { 343 h, err := internal.ConstructConnectReply([]byte( 344 `{"return_value":{ 345 "event_harvest_config": { 346 "report_period_ms": 10000 347 } 348 }}`), internal.PreconnectReply{}) 349 if nil != err { 350 t.Fatal(err) 351 } 352 cfg.TransactionEvents.MaxSamplesStored = internal.MaxTxnEvents + 100 353 result := newAppRun(cfg, h).MaxTxnEvents() 354 if result != internal.MaxTxnEvents { 355 t.Error(fmt.Sprintf("Unexpected max number of txn events, expected %d but got %d", internal.MaxTxnEvents, result)) 356 } 357 } 358 359 type expectHarvestConfig struct { 360 maxTxnEvents int 361 maxCustomEvents int 362 maxErrorEvents int 363 maxSpanEvents int 364 periods map[internal.HarvestTypes]time.Duration 365 } 366 367 func assertHarvestConfig(t testing.TB, hc *internal.HarvestConfigurer, expect expectHarvestConfig) { 368 if h, ok := t.(interface { 369 Helper() 370 }); ok { 371 h.Helper() 372 } 373 if max := (*hc).MaxTxnEvents(); max != expect.maxTxnEvents { 374 t.Error(max, expect.maxTxnEvents) 375 } 376 if max := (*hc).MaxCustomEvents(); max != expect.maxCustomEvents { 377 t.Error(max, expect.maxCustomEvents) 378 } 379 if max := (*hc).MaxSpanEvents(); max != expect.maxSpanEvents { 380 t.Error(max, expect.maxSpanEvents) 381 } 382 if max := (*hc).MaxErrorEvents(); max != expect.maxErrorEvents { 383 t.Error(max, expect.maxErrorEvents) 384 } 385 if periods := (*hc).ReportPeriods(); !reflect.DeepEqual(periods, expect.periods) { 386 t.Error(periods, expect.periods) 387 } 388 }