github.com/newrelic/go-agent@v3.26.0+incompatible/internal/harvest_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  	"testing"
     8  	"time"
     9  )
    10  
    11  func TestHarvestTimerAllFixed(t *testing.T) {
    12  	now := time.Now()
    13  	harvest := NewHarvest(now, &DfltHarvestCfgr{})
    14  	timer := harvest.timer
    15  	for _, tc := range []struct {
    16  		Elapsed time.Duration
    17  		Expect  HarvestTypes
    18  	}{
    19  		{60 * time.Second, 0},
    20  		{61 * time.Second, HarvestTypesAll},
    21  		{62 * time.Second, 0},
    22  		{120 * time.Second, 0},
    23  		{121 * time.Second, HarvestTypesAll},
    24  		{122 * time.Second, 0},
    25  	} {
    26  		if ready := timer.ready(now.Add(tc.Elapsed)); ready != tc.Expect {
    27  			t.Error(tc.Elapsed, ready, tc.Expect)
    28  		}
    29  	}
    30  }
    31  
    32  var one uint = 1
    33  var two uint = 2
    34  var three uint = 3
    35  var four uint = 4
    36  
    37  func TestHarvestTimerAllConfigurable(t *testing.T) {
    38  	now := time.Now()
    39  	harvest := NewHarvest(now, &DfltHarvestCfgr{
    40  		reportPeriods: map[HarvestTypes]time.Duration{
    41  			HarvestMetricsTraces: FixedHarvestPeriod,
    42  			HarvestTypesEvents:   time.Second * 30,
    43  		},
    44  		maxTxnEvents:    &one,
    45  		maxCustomEvents: &two,
    46  		maxSpanEvents:   &three,
    47  		maxErrorEvents:  &four,
    48  	})
    49  	timer := harvest.timer
    50  	for _, tc := range []struct {
    51  		Elapsed time.Duration
    52  		Expect  HarvestTypes
    53  	}{
    54  		{30 * time.Second, 0},
    55  		{31 * time.Second, HarvestTypesEvents},
    56  		{32 * time.Second, 0},
    57  		{61 * time.Second, HarvestTypesAll},
    58  		{62 * time.Second, 0},
    59  		{91 * time.Second, HarvestTypesEvents},
    60  		{92 * time.Second, 0},
    61  	} {
    62  		if ready := timer.ready(now.Add(tc.Elapsed)); ready != tc.Expect {
    63  			t.Error(tc.Elapsed, ready, tc.Expect)
    64  		}
    65  	}
    66  }
    67  
    68  func TestCreateFinalMetrics(t *testing.T) {
    69  	now := time.Now()
    70  
    71  	// If the harvest or metrics is nil then CreateFinalMetrics should
    72  	// not panic.
    73  	var nilHarvest *Harvest
    74  	nilHarvest.CreateFinalMetrics(nil, &DfltHarvestCfgr{})
    75  	emptyHarvest := &Harvest{}
    76  	emptyHarvest.CreateFinalMetrics(nil, &DfltHarvestCfgr{})
    77  
    78  	replyJSON := []byte(`{"return_value":{
    79  		"metric_name_rules":[{
    80  			"match_expression": "rename_me",
    81  			"replacement": "been_renamed"
    82  		}],
    83  		"event_harvest_config":{
    84  			"report_period_ms": 2000,
    85  			"harvest_limits": {
    86  				"analytic_event_data": 22,
    87  				"custom_event_data": 33,
    88  				"error_event_data": 44,
    89  				"span_event_data": 55
    90  			}
    91  		}
    92  	}}`)
    93  	reply, err := ConstructConnectReply(replyJSON, PreconnectReply{})
    94  	if err != nil {
    95  		t.Fatal(err)
    96  	}
    97  	var txnEvents uint = 22
    98  	var customEvents uint = 33
    99  	var errorEvents uint = 44
   100  	var spanEvents uint = 55
   101  	cfgr := &DfltHarvestCfgr{
   102  		reportPeriods: map[HarvestTypes]time.Duration{
   103  			HarvestMetricsTraces: FixedHarvestPeriod,
   104  			HarvestTypesEvents:   time.Second * 2,
   105  		},
   106  		maxTxnEvents:    &txnEvents,
   107  		maxCustomEvents: &customEvents,
   108  		maxErrorEvents:  &errorEvents,
   109  		maxSpanEvents:   &spanEvents,
   110  	}
   111  	h := NewHarvest(now, cfgr)
   112  	h.Metrics.addCount("rename_me", 1.0, unforced)
   113  	h.CreateFinalMetrics(reply, cfgr)
   114  	ExpectMetrics(t, h.Metrics, []WantMetric{
   115  		{instanceReporting, "", true, []float64{1, 0, 0, 0, 0, 0}},
   116  		{"been_renamed", "", false, []float64{1.0, 0, 0, 0, 0, 0}},
   117  		{"Supportability/EventHarvest/ReportPeriod", "", true, []float64{1, 2, 2, 2, 2, 2 * 2}},
   118  		{"Supportability/EventHarvest/AnalyticEventData/HarvestLimit", "", true, []float64{1, 22, 22, 22, 22, 22 * 22}},
   119  		{"Supportability/EventHarvest/CustomEventData/HarvestLimit", "", true, []float64{1, 33, 33, 33, 33, 33 * 33}},
   120  		{"Supportability/EventHarvest/ErrorEventData/HarvestLimit", "", true, []float64{1, 44, 44, 44, 44, 44 * 44}},
   121  		{"Supportability/EventHarvest/SpanEventData/HarvestLimit", "", true, []float64{1, 55, 55, 55, 55, 55 * 55}},
   122  	})
   123  
   124  	// Test again without any metric rules or event_harvest_config.
   125  
   126  	replyJSON = []byte(`{"return_value":{
   127  	}}`)
   128  	reply, err = ConstructConnectReply(replyJSON, PreconnectReply{})
   129  	if err != nil {
   130  		t.Fatal(err)
   131  	}
   132  	h = NewHarvest(now, &DfltHarvestCfgr{})
   133  	h.Metrics.addCount("rename_me", 1.0, unforced)
   134  	h.CreateFinalMetrics(reply, &DfltHarvestCfgr{})
   135  	ExpectMetrics(t, h.Metrics, []WantMetric{
   136  		{instanceReporting, "", true, []float64{1, 0, 0, 0, 0, 0}},
   137  		{"rename_me", "", false, []float64{1.0, 0, 0, 0, 0, 0}},
   138  		{"Supportability/EventHarvest/ReportPeriod", "", true, []float64{1, 60, 60, 60, 60, 60 * 60}},
   139  		{"Supportability/EventHarvest/AnalyticEventData/HarvestLimit", "", true, []float64{1, 10 * 1000, 10 * 1000, 10 * 1000, 10 * 1000, 10 * 1000 * 10 * 1000}},
   140  		{"Supportability/EventHarvest/CustomEventData/HarvestLimit", "", true, []float64{1, 10 * 1000, 10 * 1000, 10 * 1000, 10 * 1000, 10 * 1000 * 10 * 1000}},
   141  		{"Supportability/EventHarvest/ErrorEventData/HarvestLimit", "", true, []float64{1, 100, 100, 100, 100, 100 * 100}},
   142  		{"Supportability/EventHarvest/SpanEventData/HarvestLimit", "", true, []float64{1, 1000, 1000, 1000, 1000, 1000 * 1000}},
   143  	})
   144  }
   145  
   146  func TestEmptyPayloads(t *testing.T) {
   147  	h := NewHarvest(time.Now(), &DfltHarvestCfgr{})
   148  	payloads := h.Payloads(true)
   149  	if len(payloads) != 8 {
   150  		t.Error(len(payloads))
   151  	}
   152  	for _, p := range payloads {
   153  		d, err := p.Data("agentRunID", time.Now())
   154  		if d != nil || err != nil {
   155  			t.Error(d, err)
   156  		}
   157  	}
   158  }
   159  func TestPayloadsNilHarvest(t *testing.T) {
   160  	var nilHarvest *Harvest
   161  	payloads := nilHarvest.Payloads(true)
   162  	if len(payloads) != 0 {
   163  		t.Error(len(payloads))
   164  	}
   165  }
   166  
   167  func TestPayloadsEmptyHarvest(t *testing.T) {
   168  	h := &Harvest{}
   169  	payloads := h.Payloads(true)
   170  	if len(payloads) != 0 {
   171  		t.Error(len(payloads))
   172  	}
   173  }
   174  
   175  func TestHarvestNothingReady(t *testing.T) {
   176  	now := time.Now()
   177  	h := NewHarvest(now, &DfltHarvestCfgr{})
   178  	ready := h.Ready(now.Add(10 * time.Second))
   179  	if ready != nil {
   180  		t.Error("harvest should be nil")
   181  	}
   182  	payloads := ready.Payloads(true)
   183  	if len(payloads) != 0 {
   184  		t.Error(payloads)
   185  	}
   186  	ExpectMetrics(t, h.Metrics, []WantMetric{})
   187  }
   188  
   189  func TestHarvestCustomEventsReady(t *testing.T) {
   190  	now := time.Now()
   191  	fixedHarvestTypes := HarvestMetricsTraces & HarvestTxnEvents & HarvestSpanEvents & HarvestErrorEvents
   192  	h := NewHarvest(now, &DfltHarvestCfgr{
   193  		reportPeriods: map[HarvestTypes]time.Duration{
   194  			fixedHarvestTypes:   FixedHarvestPeriod,
   195  			HarvestCustomEvents: time.Second * 5,
   196  		},
   197  		maxCustomEvents: &three,
   198  	})
   199  	params := map[string]interface{}{"zip": 1}
   200  	ce, _ := CreateCustomEvent("myEvent", params, time.Now())
   201  	h.CustomEvents.Add(ce)
   202  	ready := h.Ready(now.Add(10 * time.Second))
   203  	payloads := ready.Payloads(true)
   204  	if len(payloads) != 1 {
   205  		t.Fatal(payloads)
   206  	}
   207  	p := payloads[0]
   208  	if m := p.EndpointMethod(); m != "custom_event_data" {
   209  		t.Error(m)
   210  	}
   211  	data, err := p.Data("agentRunID", now)
   212  	if nil != err || nil == data {
   213  		t.Error(err, data)
   214  	}
   215  	if h.CustomEvents.capacity() != 3 || h.CustomEvents.NumSaved() != 0 {
   216  		t.Fatal("custom events not correctly reset")
   217  	}
   218  	ExpectCustomEvents(t, ready.CustomEvents, []WantEvent{{
   219  		Intrinsics:     map[string]interface{}{"type": "myEvent", "timestamp": MatchAnything},
   220  		UserAttributes: params,
   221  	}})
   222  	ExpectMetrics(t, h.Metrics, []WantMetric{
   223  		{customEventsSeen, "", true, []float64{1, 0, 0, 0, 0, 0}},
   224  		{customEventsSent, "", true, []float64{1, 0, 0, 0, 0, 0}},
   225  	})
   226  }
   227  
   228  func TestHarvestTxnEventsReady(t *testing.T) {
   229  	now := time.Now()
   230  	fixedHarvestTypes := HarvestMetricsTraces & HarvestCustomEvents & HarvestSpanEvents & HarvestErrorEvents
   231  	h := NewHarvest(now, &DfltHarvestCfgr{
   232  		reportPeriods: map[HarvestTypes]time.Duration{
   233  			fixedHarvestTypes: FixedHarvestPeriod,
   234  			HarvestTxnEvents:  time.Second * 5,
   235  		},
   236  		maxTxnEvents: &three,
   237  	})
   238  	h.TxnEvents.AddTxnEvent(&TxnEvent{
   239  		FinalName: "finalName",
   240  		Start:     time.Now(),
   241  		Duration:  1 * time.Second,
   242  		TotalTime: 2 * time.Second,
   243  	}, 0)
   244  	ready := h.Ready(now.Add(10 * time.Second))
   245  	payloads := ready.Payloads(true)
   246  	if len(payloads) != 1 {
   247  		t.Fatal(payloads)
   248  	}
   249  	p := payloads[0]
   250  	if m := p.EndpointMethod(); m != "analytic_event_data" {
   251  		t.Error(m)
   252  	}
   253  	data, err := p.Data("agentRunID", now)
   254  	if nil != err || nil == data {
   255  		t.Error(err, data)
   256  	}
   257  	if h.TxnEvents.capacity() != 3 || h.TxnEvents.NumSaved() != 0 {
   258  		t.Fatal("txn events not correctly reset")
   259  	}
   260  	ExpectTxnEvents(t, ready.TxnEvents, []WantEvent{{
   261  		Intrinsics: map[string]interface{}{
   262  			"name":      "finalName",
   263  			"totalTime": 2.0,
   264  		},
   265  	}})
   266  	ExpectMetrics(t, h.Metrics, []WantMetric{
   267  		{txnEventsSeen, "", true, []float64{1, 0, 0, 0, 0, 0}},
   268  		{txnEventsSent, "", true, []float64{1, 0, 0, 0, 0, 0}},
   269  	})
   270  }
   271  
   272  func TestHarvestErrorEventsReady(t *testing.T) {
   273  	now := time.Now()
   274  	fixedHarvestTypes := HarvestMetricsTraces & HarvestCustomEvents & HarvestSpanEvents & HarvestTxnEvents
   275  	h := NewHarvest(now, &DfltHarvestCfgr{
   276  		reportPeriods: map[HarvestTypes]time.Duration{
   277  			fixedHarvestTypes:  FixedHarvestPeriod,
   278  			HarvestErrorEvents: time.Second * 5,
   279  		},
   280  		maxErrorEvents: &three,
   281  	})
   282  	h.ErrorEvents.Add(&ErrorEvent{
   283  		ErrorData: ErrorData{Klass: "klass", Msg: "msg", When: time.Now()},
   284  		TxnEvent:  TxnEvent{FinalName: "finalName", Duration: 1 * time.Second},
   285  	}, 0)
   286  	ready := h.Ready(now.Add(10 * time.Second))
   287  	payloads := ready.Payloads(true)
   288  	if len(payloads) != 1 {
   289  		t.Fatal(payloads)
   290  	}
   291  	p := payloads[0]
   292  	if m := p.EndpointMethod(); m != "error_event_data" {
   293  		t.Error(m)
   294  	}
   295  	data, err := p.Data("agentRunID", now)
   296  	if nil != err || nil == data {
   297  		t.Error(err, data)
   298  	}
   299  	if h.ErrorEvents.capacity() != 3 || h.ErrorEvents.NumSaved() != 0 {
   300  		t.Fatal("error events not correctly reset")
   301  	}
   302  	ExpectErrorEvents(t, ready.ErrorEvents, []WantEvent{{
   303  		Intrinsics: map[string]interface{}{
   304  			"error.class":     "klass",
   305  			"error.message":   "msg",
   306  			"transactionName": "finalName",
   307  		},
   308  	}})
   309  	ExpectMetrics(t, h.Metrics, []WantMetric{
   310  		{errorEventsSeen, "", true, []float64{1, 0, 0, 0, 0, 0}},
   311  		{errorEventsSent, "", true, []float64{1, 0, 0, 0, 0, 0}},
   312  	})
   313  }
   314  
   315  func TestHarvestSpanEventsReady(t *testing.T) {
   316  	now := time.Now()
   317  	fixedHarvestTypes := HarvestMetricsTraces & HarvestCustomEvents & HarvestTxnEvents & HarvestErrorEvents
   318  	h := NewHarvest(now, &DfltHarvestCfgr{
   319  		reportPeriods: map[HarvestTypes]time.Duration{
   320  			fixedHarvestTypes: FixedHarvestPeriod,
   321  			HarvestSpanEvents: time.Second * 5,
   322  		},
   323  		maxSpanEvents: &three,
   324  	})
   325  	h.SpanEvents.addEventPopulated(&sampleSpanEvent)
   326  	ready := h.Ready(now.Add(10 * time.Second))
   327  	payloads := ready.Payloads(true)
   328  	if len(payloads) != 1 {
   329  		t.Fatal(payloads)
   330  	}
   331  	p := payloads[0]
   332  	if m := p.EndpointMethod(); m != "span_event_data" {
   333  		t.Error(m)
   334  	}
   335  	data, err := p.Data("agentRunID", now)
   336  	if nil != err || nil == data {
   337  		t.Error(err, data)
   338  	}
   339  	if h.SpanEvents.capacity() != 3 || h.SpanEvents.NumSaved() != 0 {
   340  		t.Fatal("span events not correctly reset")
   341  	}
   342  	ExpectSpanEvents(t, ready.SpanEvents, []WantEvent{{
   343  		Intrinsics: map[string]interface{}{
   344  			"type":          "Span",
   345  			"name":          "myName",
   346  			"sampled":       true,
   347  			"priority":      0.5,
   348  			"category":      spanCategoryGeneric,
   349  			"nr.entryPoint": true,
   350  			"guid":          "guid",
   351  			"transactionId": "txn-id",
   352  			"traceId":       "trace-id",
   353  		},
   354  	}})
   355  	ExpectMetrics(t, h.Metrics, []WantMetric{
   356  		{spanEventsSeen, "", true, []float64{1, 0, 0, 0, 0, 0}},
   357  		{spanEventsSent, "", true, []float64{1, 0, 0, 0, 0, 0}},
   358  	})
   359  }
   360  
   361  func TestHarvestMetricsTracesReady(t *testing.T) {
   362  	now := time.Now()
   363  	h := NewHarvest(now, &DfltHarvestCfgr{
   364  		reportPeriods: map[HarvestTypes]time.Duration{
   365  			HarvestMetricsTraces: FixedHarvestPeriod,
   366  			HarvestTypesEvents:   time.Second * 65,
   367  		},
   368  		maxTxnEvents:    &one,
   369  		maxCustomEvents: &one,
   370  		maxErrorEvents:  &one,
   371  		maxSpanEvents:   &one,
   372  	})
   373  	h.Metrics.addCount("zip", 1, forced)
   374  
   375  	ers := NewTxnErrors(10)
   376  	ers.Add(ErrorData{When: time.Now(), Msg: "msg", Klass: "klass", Stack: GetStackTrace()})
   377  	MergeTxnErrors(&h.ErrorTraces, ers, TxnEvent{FinalName: "finalName", Attrs: nil})
   378  
   379  	h.TxnTraces.Witness(HarvestTrace{
   380  		TxnEvent: TxnEvent{
   381  			Start:     time.Now(),
   382  			Duration:  20 * time.Second,
   383  			TotalTime: 30 * time.Second,
   384  			FinalName: "WebTransaction/Go/hello",
   385  		},
   386  		Trace: TxnTrace{},
   387  	})
   388  
   389  	slows := newSlowQueries(maxTxnSlowQueries)
   390  	slows.observeInstance(slowQueryInstance{
   391  		Duration:           2 * time.Second,
   392  		DatastoreMetric:    "Datastore/statement/MySQL/users/INSERT",
   393  		ParameterizedQuery: "INSERT users",
   394  	})
   395  	h.SlowSQLs.Merge(slows, TxnEvent{FinalName: "finalName", Attrs: nil})
   396  
   397  	ready := h.Ready(now.Add(61 * time.Second))
   398  	payloads := ready.Payloads(true)
   399  	if len(payloads) != 4 {
   400  		t.Fatal(payloads)
   401  	}
   402  
   403  	ExpectMetrics(t, ready.Metrics, []WantMetric{
   404  		{"zip", "", true, []float64{1, 0, 0, 0, 0, 0}},
   405  	})
   406  	ExpectMetrics(t, h.Metrics, []WantMetric{})
   407  
   408  	ExpectErrors(t, ready.ErrorTraces, []WantError{{
   409  		TxnName: "finalName",
   410  		Msg:     "msg",
   411  		Klass:   "klass",
   412  	}})
   413  	ExpectErrors(t, h.ErrorTraces, []WantError{})
   414  
   415  	ExpectSlowQueries(t, ready.SlowSQLs, []WantSlowQuery{{
   416  		Count:      1,
   417  		MetricName: "Datastore/statement/MySQL/users/INSERT",
   418  		Query:      "INSERT users",
   419  		TxnName:    "finalName",
   420  	}})
   421  	ExpectSlowQueries(t, h.SlowSQLs, []WantSlowQuery{})
   422  
   423  	ExpectTxnTraces(t, ready.TxnTraces, []WantTxnTrace{{
   424  		MetricName: "WebTransaction/Go/hello",
   425  	}})
   426  	ExpectTxnTraces(t, h.TxnTraces, []WantTxnTrace{})
   427  }
   428  
   429  func TestMergeFailedHarvest(t *testing.T) {
   430  	start1 := time.Now()
   431  	start2 := start1.Add(1 * time.Minute)
   432  
   433  	h := NewHarvest(start1, &DfltHarvestCfgr{})
   434  	h.Metrics.addCount("zip", 1, forced)
   435  	h.TxnEvents.AddTxnEvent(&TxnEvent{
   436  		FinalName: "finalName",
   437  		Start:     time.Now(),
   438  		Duration:  1 * time.Second,
   439  		TotalTime: 2 * time.Second,
   440  	}, 0)
   441  	customEventParams := map[string]interface{}{"zip": 1}
   442  	ce, err := CreateCustomEvent("myEvent", customEventParams, time.Now())
   443  	if nil != err {
   444  		t.Fatal(err)
   445  	}
   446  	h.CustomEvents.Add(ce)
   447  	h.ErrorEvents.Add(&ErrorEvent{
   448  		ErrorData: ErrorData{
   449  			Klass: "klass",
   450  			Msg:   "msg",
   451  			When:  time.Now(),
   452  		},
   453  		TxnEvent: TxnEvent{
   454  			FinalName: "finalName",
   455  			Duration:  1 * time.Second,
   456  		},
   457  	}, 0)
   458  
   459  	ers := NewTxnErrors(10)
   460  	ers.Add(ErrorData{
   461  		When:  time.Now(),
   462  		Msg:   "msg",
   463  		Klass: "klass",
   464  		Stack: GetStackTrace(),
   465  	})
   466  	MergeTxnErrors(&h.ErrorTraces, ers, TxnEvent{
   467  		FinalName: "finalName",
   468  		Attrs:     nil,
   469  	})
   470  	h.SpanEvents.addEventPopulated(&sampleSpanEvent)
   471  
   472  	if start1 != h.Metrics.metricPeriodStart {
   473  		t.Error(h.Metrics.metricPeriodStart)
   474  	}
   475  	if 0 != h.Metrics.failedHarvests {
   476  		t.Error(h.Metrics.failedHarvests)
   477  	}
   478  	if 0 != h.CustomEvents.analyticsEvents.failedHarvests {
   479  		t.Error(h.CustomEvents.analyticsEvents.failedHarvests)
   480  	}
   481  	if 0 != h.TxnEvents.analyticsEvents.failedHarvests {
   482  		t.Error(h.TxnEvents.analyticsEvents.failedHarvests)
   483  	}
   484  	if 0 != h.ErrorEvents.analyticsEvents.failedHarvests {
   485  		t.Error(h.ErrorEvents.analyticsEvents.failedHarvests)
   486  	}
   487  	if 0 != h.SpanEvents.analyticsEvents.failedHarvests {
   488  		t.Error(h.SpanEvents.analyticsEvents.failedHarvests)
   489  	}
   490  	ExpectMetrics(t, h.Metrics, []WantMetric{
   491  		{"zip", "", true, []float64{1, 0, 0, 0, 0, 0}},
   492  	})
   493  	ExpectCustomEvents(t, h.CustomEvents, []WantEvent{{
   494  		Intrinsics: map[string]interface{}{
   495  			"type":      "myEvent",
   496  			"timestamp": MatchAnything,
   497  		},
   498  		UserAttributes: customEventParams,
   499  	}})
   500  	ExpectErrorEvents(t, h.ErrorEvents, []WantEvent{{
   501  		Intrinsics: map[string]interface{}{
   502  			"error.class":     "klass",
   503  			"error.message":   "msg",
   504  			"transactionName": "finalName",
   505  		},
   506  	}})
   507  	ExpectTxnEvents(t, h.TxnEvents, []WantEvent{{
   508  		Intrinsics: map[string]interface{}{
   509  			"name":      "finalName",
   510  			"totalTime": 2.0,
   511  		},
   512  	}})
   513  	ExpectSpanEvents(t, h.SpanEvents, []WantEvent{{
   514  		Intrinsics: map[string]interface{}{
   515  			"type":          "Span",
   516  			"name":          "myName",
   517  			"sampled":       true,
   518  			"priority":      0.5,
   519  			"category":      spanCategoryGeneric,
   520  			"nr.entryPoint": true,
   521  			"guid":          "guid",
   522  			"transactionId": "txn-id",
   523  			"traceId":       "trace-id",
   524  		},
   525  	}})
   526  	ExpectErrors(t, h.ErrorTraces, []WantError{{
   527  		TxnName: "finalName",
   528  		Msg:     "msg",
   529  		Klass:   "klass",
   530  	}})
   531  
   532  	nextHarvest := NewHarvest(start2, &DfltHarvestCfgr{})
   533  	if start2 != nextHarvest.Metrics.metricPeriodStart {
   534  		t.Error(nextHarvest.Metrics.metricPeriodStart)
   535  	}
   536  	payloads := h.Payloads(true)
   537  	for _, p := range payloads {
   538  		p.MergeIntoHarvest(nextHarvest)
   539  	}
   540  
   541  	if start1 != nextHarvest.Metrics.metricPeriodStart {
   542  		t.Error(nextHarvest.Metrics.metricPeriodStart)
   543  	}
   544  	if 1 != nextHarvest.Metrics.failedHarvests {
   545  		t.Error(nextHarvest.Metrics.failedHarvests)
   546  	}
   547  	if 1 != nextHarvest.CustomEvents.analyticsEvents.failedHarvests {
   548  		t.Error(nextHarvest.CustomEvents.analyticsEvents.failedHarvests)
   549  	}
   550  	if 1 != nextHarvest.TxnEvents.analyticsEvents.failedHarvests {
   551  		t.Error(nextHarvest.TxnEvents.analyticsEvents.failedHarvests)
   552  	}
   553  	if 1 != nextHarvest.ErrorEvents.analyticsEvents.failedHarvests {
   554  		t.Error(nextHarvest.ErrorEvents.analyticsEvents.failedHarvests)
   555  	}
   556  	if 1 != nextHarvest.SpanEvents.analyticsEvents.failedHarvests {
   557  		t.Error(nextHarvest.SpanEvents.analyticsEvents.failedHarvests)
   558  	}
   559  	ExpectMetrics(t, nextHarvest.Metrics, []WantMetric{
   560  		{"zip", "", true, []float64{1, 0, 0, 0, 0, 0}},
   561  	})
   562  	ExpectCustomEvents(t, nextHarvest.CustomEvents, []WantEvent{{
   563  		Intrinsics: map[string]interface{}{
   564  			"type":      "myEvent",
   565  			"timestamp": MatchAnything,
   566  		},
   567  		UserAttributes: customEventParams,
   568  	}})
   569  	ExpectErrorEvents(t, nextHarvest.ErrorEvents, []WantEvent{{
   570  		Intrinsics: map[string]interface{}{
   571  			"error.class":     "klass",
   572  			"error.message":   "msg",
   573  			"transactionName": "finalName",
   574  		},
   575  	}})
   576  	ExpectTxnEvents(t, nextHarvest.TxnEvents, []WantEvent{{
   577  		Intrinsics: map[string]interface{}{
   578  			"name":      "finalName",
   579  			"totalTime": 2.0,
   580  		},
   581  	}})
   582  	ExpectSpanEvents(t, h.SpanEvents, []WantEvent{{
   583  		Intrinsics: map[string]interface{}{
   584  			"type":          "Span",
   585  			"name":          "myName",
   586  			"sampled":       true,
   587  			"priority":      0.5,
   588  			"category":      spanCategoryGeneric,
   589  			"nr.entryPoint": true,
   590  			"guid":          "guid",
   591  			"transactionId": "txn-id",
   592  			"traceId":       "trace-id",
   593  		},
   594  	}})
   595  	ExpectErrors(t, nextHarvest.ErrorTraces, []WantError{})
   596  }
   597  
   598  func TestCreateTxnMetrics(t *testing.T) {
   599  	txnErr := &ErrorData{}
   600  	txnErrors := []*ErrorData{txnErr}
   601  	webName := "WebTransaction/zip/zap"
   602  	backgroundName := "OtherTransaction/zip/zap"
   603  	args := &TxnData{}
   604  	args.Duration = 123 * time.Second
   605  	args.TotalTime = 150 * time.Second
   606  	args.ApdexThreshold = 2 * time.Second
   607  
   608  	args.BetterCAT.Enabled = true
   609  
   610  	args.FinalName = webName
   611  	args.IsWeb = true
   612  	args.Errors = txnErrors
   613  	args.Zone = ApdexTolerating
   614  	metrics := newMetricTable(100, time.Now())
   615  	CreateTxnMetrics(args, metrics)
   616  	ExpectMetrics(t, metrics, []WantMetric{
   617  		{webName, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   618  		{webRollup, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   619  		{dispatcherMetric, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   620  		{"WebTransactionTotalTime", "", true, []float64{1, 150, 150, 150, 150, 150 * 150}},
   621  		{"WebTransactionTotalTime/zip/zap", "", false, []float64{1, 150, 150, 150, 150, 150 * 150}},
   622  		{"Errors/all", "", true, []float64{1, 0, 0, 0, 0, 0}},
   623  		{"Errors/allWeb", "", true, []float64{1, 0, 0, 0, 0, 0}},
   624  		{"Errors/" + webName, "", true, []float64{1, 0, 0, 0, 0, 0}},
   625  		{apdexRollup, "", true, []float64{0, 1, 0, 2, 2, 0}},
   626  		{"Apdex/zip/zap", "", false, []float64{0, 1, 0, 2, 2, 0}},
   627  		{"DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", "", false, []float64{1, 123, 123, 123, 123, 123 * 123}},
   628  		{"DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", "", false, []float64{1, 123, 123, 123, 123, 123 * 123}},
   629  		{"ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/all", "", false, []float64{1, 0, 0, 0, 0, 0}},
   630  		{"ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", "", false, []float64{1, 0, 0, 0, 0, 0}},
   631  	})
   632  
   633  	args.FinalName = webName
   634  	args.IsWeb = true
   635  	args.Errors = nil
   636  	args.Zone = ApdexTolerating
   637  	metrics = newMetricTable(100, time.Now())
   638  	CreateTxnMetrics(args, metrics)
   639  	ExpectMetrics(t, metrics, []WantMetric{
   640  		{webName, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   641  		{webRollup, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   642  		{dispatcherMetric, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   643  		{"WebTransactionTotalTime", "", true, []float64{1, 150, 150, 150, 150, 150 * 150}},
   644  		{"WebTransactionTotalTime/zip/zap", "", false, []float64{1, 150, 150, 150, 150, 150 * 150}},
   645  		{apdexRollup, "", true, []float64{0, 1, 0, 2, 2, 0}},
   646  		{"Apdex/zip/zap", "", false, []float64{0, 1, 0, 2, 2, 0}},
   647  		{"DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", "", false, []float64{1, 123, 123, 123, 123, 123 * 123}},
   648  		{"DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", "", false, []float64{1, 123, 123, 123, 123, 123 * 123}},
   649  	})
   650  
   651  	args.FinalName = backgroundName
   652  	args.IsWeb = false
   653  	args.Errors = txnErrors
   654  	args.Zone = ApdexNone
   655  	metrics = newMetricTable(100, time.Now())
   656  	CreateTxnMetrics(args, metrics)
   657  	ExpectMetrics(t, metrics, []WantMetric{
   658  		{backgroundName, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   659  		{backgroundRollup, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   660  		{"OtherTransactionTotalTime", "", true, []float64{1, 150, 150, 150, 150, 150 * 150}},
   661  		{"OtherTransactionTotalTime/zip/zap", "", false, []float64{1, 150, 150, 150, 150, 150 * 150}},
   662  		{"Errors/all", "", true, []float64{1, 0, 0, 0, 0, 0}},
   663  		{"Errors/allOther", "", true, []float64{1, 0, 0, 0, 0, 0}},
   664  		{"Errors/" + backgroundName, "", true, []float64{1, 0, 0, 0, 0, 0}},
   665  		{"DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", "", false, []float64{1, 123, 123, 123, 123, 123 * 123}},
   666  		{"DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", "", false, []float64{1, 123, 123, 123, 123, 123 * 123}},
   667  		{"ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/all", "", false, []float64{1, 0, 0, 0, 0, 0}},
   668  		{"ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/allOther", "", false, []float64{1, 0, 0, 0, 0, 0}},
   669  	})
   670  
   671  	args.FinalName = backgroundName
   672  	args.IsWeb = false
   673  	args.Errors = nil
   674  	args.Zone = ApdexNone
   675  	metrics = newMetricTable(100, time.Now())
   676  	CreateTxnMetrics(args, metrics)
   677  	ExpectMetrics(t, metrics, []WantMetric{
   678  		{backgroundName, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   679  		{backgroundRollup, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   680  		{"OtherTransactionTotalTime", "", true, []float64{1, 150, 150, 150, 150, 150 * 150}},
   681  		{"OtherTransactionTotalTime/zip/zap", "", false, []float64{1, 150, 150, 150, 150, 150 * 150}},
   682  		{"DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", "", false, []float64{1, 123, 123, 123, 123, 123 * 123}},
   683  		{"DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", "", false, []float64{1, 123, 123, 123, 123, 123 * 123}},
   684  	})
   685  
   686  }
   687  
   688  func TestHarvestSplitTxnEvents(t *testing.T) {
   689  	now := time.Now()
   690  	h := NewHarvest(now, &DfltHarvestCfgr{})
   691  	for i := 0; i < MaxTxnEvents; i++ {
   692  		h.TxnEvents.AddTxnEvent(&TxnEvent{}, Priority(float32(i)))
   693  	}
   694  
   695  	payloadsWithSplit := h.Payloads(true)
   696  	payloadsWithoutSplit := h.Payloads(false)
   697  
   698  	if len(payloadsWithSplit) != 9 {
   699  		t.Error(len(payloadsWithSplit))
   700  	}
   701  	if len(payloadsWithoutSplit) != 8 {
   702  		t.Error(len(payloadsWithoutSplit))
   703  	}
   704  }
   705  
   706  func TestCreateTxnMetricsOldCAT(t *testing.T) {
   707  	txnErr := &ErrorData{}
   708  	txnErrors := []*ErrorData{txnErr}
   709  	webName := "WebTransaction/zip/zap"
   710  	backgroundName := "OtherTransaction/zip/zap"
   711  	args := &TxnData{}
   712  	args.Duration = 123 * time.Second
   713  	args.TotalTime = 150 * time.Second
   714  	args.ApdexThreshold = 2 * time.Second
   715  
   716  	// When BetterCAT is disabled, affirm that the caller metrics are not created.
   717  	args.BetterCAT.Enabled = false
   718  
   719  	args.FinalName = webName
   720  	args.IsWeb = true
   721  	args.Errors = txnErrors
   722  	args.Zone = ApdexTolerating
   723  	metrics := newMetricTable(100, time.Now())
   724  	CreateTxnMetrics(args, metrics)
   725  	ExpectMetrics(t, metrics, []WantMetric{
   726  		{webName, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   727  		{webRollup, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   728  		{dispatcherMetric, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   729  		{"WebTransactionTotalTime", "", true, []float64{1, 150, 150, 150, 150, 150 * 150}},
   730  		{"WebTransactionTotalTime/zip/zap", "", false, []float64{1, 150, 150, 150, 150, 150 * 150}},
   731  		{"Errors/all", "", true, []float64{1, 0, 0, 0, 0, 0}},
   732  		{"Errors/allWeb", "", true, []float64{1, 0, 0, 0, 0, 0}},
   733  		{"Errors/" + webName, "", true, []float64{1, 0, 0, 0, 0, 0}},
   734  		{apdexRollup, "", true, []float64{0, 1, 0, 2, 2, 0}},
   735  		{"Apdex/zip/zap", "", false, []float64{0, 1, 0, 2, 2, 0}},
   736  	})
   737  
   738  	args.FinalName = webName
   739  	args.IsWeb = true
   740  	args.Errors = nil
   741  	args.Zone = ApdexTolerating
   742  	metrics = newMetricTable(100, time.Now())
   743  	CreateTxnMetrics(args, metrics)
   744  	ExpectMetrics(t, metrics, []WantMetric{
   745  		{webName, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   746  		{webRollup, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   747  		{dispatcherMetric, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   748  		{"WebTransactionTotalTime", "", true, []float64{1, 150, 150, 150, 150, 150 * 150}},
   749  		{"WebTransactionTotalTime/zip/zap", "", false, []float64{1, 150, 150, 150, 150, 150 * 150}},
   750  		{apdexRollup, "", true, []float64{0, 1, 0, 2, 2, 0}},
   751  		{"Apdex/zip/zap", "", false, []float64{0, 1, 0, 2, 2, 0}},
   752  	})
   753  
   754  	args.FinalName = backgroundName
   755  	args.IsWeb = false
   756  	args.Errors = txnErrors
   757  	args.Zone = ApdexNone
   758  	metrics = newMetricTable(100, time.Now())
   759  	CreateTxnMetrics(args, metrics)
   760  	ExpectMetrics(t, metrics, []WantMetric{
   761  		{backgroundName, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   762  		{backgroundRollup, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   763  		{"OtherTransactionTotalTime", "", true, []float64{1, 150, 150, 150, 150, 150 * 150}},
   764  		{"OtherTransactionTotalTime/zip/zap", "", false, []float64{1, 150, 150, 150, 150, 150 * 150}},
   765  		{"Errors/all", "", true, []float64{1, 0, 0, 0, 0, 0}},
   766  		{"Errors/allOther", "", true, []float64{1, 0, 0, 0, 0, 0}},
   767  		{"Errors/" + backgroundName, "", true, []float64{1, 0, 0, 0, 0, 0}},
   768  	})
   769  
   770  	args.FinalName = backgroundName
   771  	args.IsWeb = false
   772  	args.Errors = nil
   773  	args.Zone = ApdexNone
   774  	metrics = newMetricTable(100, time.Now())
   775  	CreateTxnMetrics(args, metrics)
   776  	ExpectMetrics(t, metrics, []WantMetric{
   777  		{backgroundName, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   778  		{backgroundRollup, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}},
   779  		{"OtherTransactionTotalTime", "", true, []float64{1, 150, 150, 150, 150, 150 * 150}},
   780  		{"OtherTransactionTotalTime/zip/zap", "", false, []float64{1, 150, 150, 150, 150, 150 * 150}},
   781  	})
   782  }
   783  
   784  func TestNewHarvestSetsDefaultValues(t *testing.T) {
   785  	now := time.Now()
   786  	h := NewHarvest(now, &DfltHarvestCfgr{})
   787  
   788  	if cp := h.TxnEvents.capacity(); cp != MaxTxnEvents {
   789  		t.Error("wrong txn event capacity", cp)
   790  	}
   791  	if cp := h.CustomEvents.capacity(); cp != MaxCustomEvents {
   792  		t.Error("wrong custom event capacity", cp)
   793  	}
   794  	if cp := h.ErrorEvents.capacity(); cp != MaxErrorEvents {
   795  		t.Error("wrong error event capacity", cp)
   796  	}
   797  	if cp := h.SpanEvents.capacity(); cp != MaxSpanEvents {
   798  		t.Error("wrong span event capacity", cp)
   799  	}
   800  }
   801  
   802  func TestNewHarvestUsesConnectReply(t *testing.T) {
   803  	now := time.Now()
   804  	h := NewHarvest(now, &DfltHarvestCfgr{
   805  		reportPeriods: map[HarvestTypes]time.Duration{
   806  			HarvestMetricsTraces: FixedHarvestPeriod,
   807  			HarvestTypesEvents:   time.Second * 5,
   808  		},
   809  		maxTxnEvents:    &one,
   810  		maxCustomEvents: &two,
   811  		maxErrorEvents:  &three,
   812  		maxSpanEvents:   &four,
   813  	})
   814  
   815  	if cp := h.TxnEvents.capacity(); cp != 1 {
   816  		t.Error("wrong txn event capacity", cp)
   817  	}
   818  	if cp := h.CustomEvents.capacity(); cp != 2 {
   819  		t.Error("wrong custom event capacity", cp)
   820  	}
   821  	if cp := h.ErrorEvents.capacity(); cp != 3 {
   822  		t.Error("wrong error event capacity", cp)
   823  	}
   824  	if cp := h.SpanEvents.capacity(); cp != 4 {
   825  		t.Error("wrong span event capacity", cp)
   826  	}
   827  }
   828  
   829  func TestConfigurableHarvestZeroHarvestLimits(t *testing.T) {
   830  	now := time.Now()
   831  
   832  	var zero uint
   833  	h := NewHarvest(now, &DfltHarvestCfgr{
   834  		reportPeriods: map[HarvestTypes]time.Duration{
   835  			HarvestMetricsTraces: FixedHarvestPeriod,
   836  			HarvestTypesEvents:   time.Second * 5,
   837  		},
   838  		maxTxnEvents:    &zero,
   839  		maxCustomEvents: &zero,
   840  		maxErrorEvents:  &zero,
   841  		maxSpanEvents:   &zero,
   842  	})
   843  	if cp := h.TxnEvents.capacity(); cp != 0 {
   844  		t.Error("wrong txn event capacity", cp)
   845  	}
   846  	if cp := h.CustomEvents.capacity(); cp != 0 {
   847  		t.Error("wrong custom event capacity", cp)
   848  	}
   849  	if cp := h.ErrorEvents.capacity(); cp != 0 {
   850  		t.Error("wrong error event capacity", cp)
   851  	}
   852  	if cp := h.SpanEvents.capacity(); cp != 0 {
   853  		t.Error("wrong error event capacity", cp)
   854  	}
   855  
   856  	// Add events to ensure that adding events to zero-capacity pools is
   857  	// safe.
   858  	h.TxnEvents.AddTxnEvent(&TxnEvent{}, 1.0)
   859  	h.CustomEvents.Add(&CustomEvent{})
   860  	h.ErrorEvents.Add(&ErrorEvent{}, 1.0)
   861  	h.SpanEvents.addEventPopulated(&sampleSpanEvent)
   862  
   863  	// Create the payloads to ensure doing so with zero-capacity pools is
   864  	// safe.
   865  	payloads := h.Ready(now.Add(2 * time.Minute)).Payloads(false)
   866  	for _, p := range payloads {
   867  		js, err := p.Data("agentRunID", now.Add(2*time.Minute))
   868  		if nil != err {
   869  			t.Error(err)
   870  			continue
   871  		}
   872  		// Only metric data should be present.
   873  		if (p.EndpointMethod() == "metric_data") !=
   874  			(string(js) != "") {
   875  			t.Error(p.EndpointMethod(), string(js))
   876  		}
   877  	}
   878  }