github.com/newrelic/go-agent@v3.26.0+incompatible/internal/analytics_events_test.go (about) 1 // Copyright 2020 New Relic Corporation. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package internal 5 6 import ( 7 "bytes" 8 "strconv" 9 "testing" 10 "time" 11 ) 12 13 var ( 14 agentRunID = `12345` 15 ) 16 17 type priorityWriter Priority 18 19 func (x priorityWriter) WriteJSON(buf *bytes.Buffer) { 20 buf.WriteString(strconv.FormatFloat(float64(x), 'f', -1, 32)) 21 } 22 23 func sampleAnalyticsEvent(priority Priority) analyticsEvent { 24 return analyticsEvent{ 25 priority, 26 priorityWriter(priority), 27 } 28 } 29 30 func TestBasic(t *testing.T) { 31 events := newAnalyticsEvents(10) 32 events.addEvent(sampleAnalyticsEvent(0.5)) 33 events.addEvent(sampleAnalyticsEvent(0.5)) 34 events.addEvent(sampleAnalyticsEvent(0.5)) 35 36 json, err := events.CollectorJSON(agentRunID) 37 if nil != err { 38 t.Fatal(err) 39 } 40 41 expected := `["12345",{"reservoir_size":10,"events_seen":3},[0.5,0.5,0.5]]` 42 43 if string(json) != expected { 44 t.Error(string(json), expected) 45 } 46 if 3 != events.numSeen { 47 t.Error(events.numSeen) 48 } 49 if 3 != events.NumSaved() { 50 t.Error(events.NumSaved()) 51 } 52 } 53 54 func TestEmpty(t *testing.T) { 55 events := newAnalyticsEvents(10) 56 json, err := events.CollectorJSON(agentRunID) 57 if nil != err { 58 t.Fatal(err) 59 } 60 if nil != json { 61 t.Error(string(json)) 62 } 63 if 0 != events.numSeen { 64 t.Error(events.numSeen) 65 } 66 if 0 != events.NumSaved() { 67 t.Error(events.NumSaved()) 68 } 69 } 70 71 func TestSampling(t *testing.T) { 72 events := newAnalyticsEvents(3) 73 events.addEvent(sampleAnalyticsEvent(0.999999)) 74 events.addEvent(sampleAnalyticsEvent(0.1)) 75 events.addEvent(sampleAnalyticsEvent(0.9)) 76 events.addEvent(sampleAnalyticsEvent(0.2)) 77 events.addEvent(sampleAnalyticsEvent(0.8)) 78 events.addEvent(sampleAnalyticsEvent(0.3)) 79 80 json, err := events.CollectorJSON(agentRunID) 81 if nil != err { 82 t.Fatal(err) 83 } 84 if string(json) != `["12345",{"reservoir_size":3,"events_seen":6},[0.8,0.999999,0.9]]` { 85 t.Error(string(json)) 86 } 87 if 6 != events.numSeen { 88 t.Error(events.numSeen) 89 } 90 if 3 != events.NumSaved() { 91 t.Error(events.NumSaved()) 92 } 93 } 94 95 func TestMergeEmpty(t *testing.T) { 96 e1 := newAnalyticsEvents(10) 97 e2 := newAnalyticsEvents(10) 98 e1.Merge(e2) 99 json, err := e1.CollectorJSON(agentRunID) 100 if nil != err { 101 t.Fatal(err) 102 } 103 if nil != json { 104 t.Error(string(json)) 105 } 106 if 0 != e1.numSeen { 107 t.Error(e1.numSeen) 108 } 109 if 0 != e1.NumSaved() { 110 t.Error(e1.NumSaved()) 111 } 112 } 113 114 func TestMergeFull(t *testing.T) { 115 e1 := newAnalyticsEvents(2) 116 e2 := newAnalyticsEvents(3) 117 118 e1.addEvent(sampleAnalyticsEvent(0.1)) 119 e1.addEvent(sampleAnalyticsEvent(0.15)) 120 e1.addEvent(sampleAnalyticsEvent(0.25)) 121 122 e2.addEvent(sampleAnalyticsEvent(0.06)) 123 e2.addEvent(sampleAnalyticsEvent(0.12)) 124 e2.addEvent(sampleAnalyticsEvent(0.18)) 125 e2.addEvent(sampleAnalyticsEvent(0.24)) 126 127 e1.Merge(e2) 128 json, err := e1.CollectorJSON(agentRunID) 129 if nil != err { 130 t.Fatal(err) 131 } 132 if string(json) != `["12345",{"reservoir_size":2,"events_seen":7},[0.24,0.25]]` { 133 t.Error(string(json)) 134 } 135 if 7 != e1.numSeen { 136 t.Error(e1.numSeen) 137 } 138 if 2 != e1.NumSaved() { 139 t.Error(e1.NumSaved()) 140 } 141 } 142 143 func TestAnalyticsEventMergeFailedSuccess(t *testing.T) { 144 e1 := newAnalyticsEvents(2) 145 e2 := newAnalyticsEvents(3) 146 147 e1.addEvent(sampleAnalyticsEvent(0.1)) 148 e1.addEvent(sampleAnalyticsEvent(0.15)) 149 e1.addEvent(sampleAnalyticsEvent(0.25)) 150 151 e2.addEvent(sampleAnalyticsEvent(0.06)) 152 e2.addEvent(sampleAnalyticsEvent(0.12)) 153 e2.addEvent(sampleAnalyticsEvent(0.18)) 154 e2.addEvent(sampleAnalyticsEvent(0.24)) 155 156 e1.mergeFailed(e2) 157 158 json, err := e1.CollectorJSON(agentRunID) 159 if nil != err { 160 t.Fatal(err) 161 } 162 if string(json) != `["12345",{"reservoir_size":2,"events_seen":7},[0.24,0.25]]` { 163 t.Error(string(json)) 164 } 165 if 7 != e1.numSeen { 166 t.Error(e1.numSeen) 167 } 168 if 2 != e1.NumSaved() { 169 t.Error(e1.NumSaved()) 170 } 171 if 1 != e1.failedHarvests { 172 t.Error(e1.failedHarvests) 173 } 174 } 175 176 func TestAnalyticsEventMergeFailedLimitReached(t *testing.T) { 177 e1 := newAnalyticsEvents(2) 178 e2 := newAnalyticsEvents(3) 179 180 e1.addEvent(sampleAnalyticsEvent(0.1)) 181 e1.addEvent(sampleAnalyticsEvent(0.15)) 182 e1.addEvent(sampleAnalyticsEvent(0.25)) 183 184 e2.addEvent(sampleAnalyticsEvent(0.06)) 185 e2.addEvent(sampleAnalyticsEvent(0.12)) 186 e2.addEvent(sampleAnalyticsEvent(0.18)) 187 e2.addEvent(sampleAnalyticsEvent(0.24)) 188 189 e2.failedHarvests = failedEventsAttemptsLimit 190 191 e1.mergeFailed(e2) 192 193 json, err := e1.CollectorJSON(agentRunID) 194 if nil != err { 195 t.Fatal(err) 196 } 197 if string(json) != `["12345",{"reservoir_size":2,"events_seen":3},[0.15,0.25]]` { 198 t.Error(string(json)) 199 } 200 if 3 != e1.numSeen { 201 t.Error(e1.numSeen) 202 } 203 if 2 != e1.NumSaved() { 204 t.Error(e1.NumSaved()) 205 } 206 if 0 != e1.failedHarvests { 207 t.Error(e1.failedHarvests) 208 } 209 } 210 211 func analyticsEventBenchmarkHelper(b *testing.B, w jsonWriter) { 212 events := newAnalyticsEvents(MaxTxnEvents) 213 event := analyticsEvent{0, w} 214 for n := 0; n < MaxTxnEvents; n++ { 215 events.addEvent(event) 216 } 217 218 b.ReportAllocs() 219 b.ResetTimer() 220 221 for n := 0; n < b.N; n++ { 222 js, err := events.CollectorJSON(agentRunID) 223 if nil != err { 224 b.Fatal(err, js) 225 } 226 } 227 } 228 229 func BenchmarkTxnEventsCollectorJSON(b *testing.B) { 230 event := &TxnEvent{ 231 FinalName: "WebTransaction/Go/zip/zap", 232 Start: time.Now(), 233 Duration: 2 * time.Second, 234 Queuing: 1 * time.Second, 235 Zone: ApdexSatisfying, 236 Attrs: nil, 237 } 238 analyticsEventBenchmarkHelper(b, event) 239 } 240 241 func BenchmarkCustomEventsCollectorJSON(b *testing.B) { 242 now := time.Now() 243 ce, err := CreateCustomEvent("myEventType", map[string]interface{}{ 244 "string": "myString", 245 "bool": true, 246 "int64": int64(123), 247 }, now) 248 if nil != err { 249 b.Fatal(err) 250 } 251 analyticsEventBenchmarkHelper(b, ce) 252 } 253 254 func BenchmarkErrorEventsCollectorJSON(b *testing.B) { 255 e := TxnErrorFromResponseCode(time.Now(), 503) 256 e.Stack = GetStackTrace() 257 258 txnName := "WebTransaction/Go/zip/zap" 259 event := &ErrorEvent{ 260 ErrorData: e, 261 TxnEvent: TxnEvent{ 262 FinalName: txnName, 263 Duration: 3 * time.Second, 264 Attrs: nil, 265 }, 266 } 267 analyticsEventBenchmarkHelper(b, event) 268 } 269 270 func TestSplitFull(t *testing.T) { 271 events := newAnalyticsEvents(10) 272 for i := 0; i < 15; i++ { 273 events.addEvent(sampleAnalyticsEvent(Priority(float32(i) / 10.0))) 274 } 275 // Test that the capacity cannot exceed the max. 276 if 10 != events.capacity() { 277 t.Error(events.capacity()) 278 } 279 e1, e2 := events.split() 280 j1, err1 := e1.CollectorJSON(agentRunID) 281 j2, err2 := e2.CollectorJSON(agentRunID) 282 if err1 != nil || err2 != nil { 283 t.Fatal(err1, err2) 284 } 285 if string(j1) != `["12345",{"reservoir_size":5,"events_seen":5},[0.5,0.7,0.6,0.8,0.9]]` { 286 t.Error(string(j1)) 287 } 288 if string(j2) != `["12345",{"reservoir_size":5,"events_seen":10},[1.1,1.4,1,1.3,1.2]]` { 289 t.Error(string(j2)) 290 } 291 } 292 293 func TestSplitNotFullOdd(t *testing.T) { 294 events := newAnalyticsEvents(10) 295 for i := 0; i < 7; i++ { 296 events.addEvent(sampleAnalyticsEvent(Priority(float32(i) / 10.0))) 297 } 298 e1, e2 := events.split() 299 j1, err1 := e1.CollectorJSON(agentRunID) 300 j2, err2 := e2.CollectorJSON(agentRunID) 301 if err1 != nil || err2 != nil { 302 t.Fatal(err1, err2) 303 } 304 if string(j1) != `["12345",{"reservoir_size":3,"events_seen":3},[0,0.1,0.2]]` { 305 t.Error(string(j1)) 306 } 307 if string(j2) != `["12345",{"reservoir_size":4,"events_seen":4},[0.3,0.4,0.5,0.6]]` { 308 t.Error(string(j2)) 309 } 310 } 311 312 func TestSplitNotFullEven(t *testing.T) { 313 events := newAnalyticsEvents(10) 314 for i := 0; i < 8; i++ { 315 events.addEvent(sampleAnalyticsEvent(Priority(float32(i) / 10.0))) 316 } 317 e1, e2 := events.split() 318 j1, err1 := e1.CollectorJSON(agentRunID) 319 j2, err2 := e2.CollectorJSON(agentRunID) 320 if err1 != nil || err2 != nil { 321 t.Fatal(err1, err2) 322 } 323 if string(j1) != `["12345",{"reservoir_size":4,"events_seen":4},[0,0.1,0.2,0.3]]` { 324 t.Error(string(j1)) 325 } 326 if string(j2) != `["12345",{"reservoir_size":4,"events_seen":4},[0.4,0.5,0.6,0.7]]` { 327 t.Error(string(j2)) 328 } 329 } 330 331 func TestAnalyticsEventsZeroCapacity(t *testing.T) { 332 // Analytics events methods should be safe when configurable harvest 333 // settings have an event limit of zero. 334 events := newAnalyticsEvents(0) 335 if 0 != events.NumSeen() || 0 != events.NumSaved() || 0 != events.capacity() { 336 t.Error(events.NumSeen(), events.NumSaved(), events.capacity()) 337 } 338 events.addEvent(sampleAnalyticsEvent(0.5)) 339 if 1 != events.NumSeen() || 0 != events.NumSaved() || 0 != events.capacity() { 340 t.Error(events.NumSeen(), events.NumSaved(), events.capacity()) 341 } 342 js, err := events.CollectorJSON("agentRunID") 343 if err != nil || js != nil { 344 t.Error(err, string(js)) 345 } 346 }