github.com/newrelic/go-agent@v3.26.0+incompatible/internal/txn_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  	"encoding/json"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/newrelic/go-agent/internal/cat"
    12  )
    13  
    14  func testTxnEventJSON(t testing.TB, e *TxnEvent, expect string) {
    15  	// Type assertion to support early Go versions.
    16  	if h, ok := t.(interface {
    17  		Helper()
    18  	}); ok {
    19  		h.Helper()
    20  	}
    21  	js, err := json.Marshal(e)
    22  	if nil != err {
    23  		t.Error(err)
    24  		return
    25  	}
    26  	expect = CompactJSONString(expect)
    27  	if string(js) != expect {
    28  		t.Errorf("\nexpect=%s\nactual=%s\n", expect, string(js))
    29  	}
    30  }
    31  
    32  var (
    33  	sampleTxnEvent = TxnEvent{
    34  		FinalName: "myName",
    35  		BetterCAT: BetterCAT{
    36  			Enabled:  true,
    37  			ID:       "txn-id",
    38  			Priority: 0.5,
    39  		},
    40  		Start:     timeFromUnixMilliseconds(1488393111000),
    41  		Duration:  2 * time.Second,
    42  		TotalTime: 3 * time.Second,
    43  		Zone:      ApdexNone,
    44  		Attrs:     nil,
    45  	}
    46  
    47  	sampleTxnEventWithOldCAT = TxnEvent{
    48  		FinalName: "myOldName",
    49  		BetterCAT: BetterCAT{
    50  			Enabled: false,
    51  		},
    52  		Start:     timeFromUnixMilliseconds(1488393111000),
    53  		Duration:  2 * time.Second,
    54  		TotalTime: 3 * time.Second,
    55  		Zone:      ApdexNone,
    56  		Attrs:     nil,
    57  	}
    58  
    59  	sampleTxnEventWithError = TxnEvent{
    60  		FinalName: "myName",
    61  		BetterCAT: BetterCAT{
    62  			Enabled:  true,
    63  			ID:       "txn-id",
    64  			Priority: 0.5,
    65  		},
    66  		Start:     timeFromUnixMilliseconds(1488393111000),
    67  		Duration:  2 * time.Second,
    68  		TotalTime: 3 * time.Second,
    69  		Zone:      ApdexNone,
    70  		Attrs:     nil,
    71  		HasError:  true,
    72  	}
    73  )
    74  
    75  func TestTxnEventMarshal(t *testing.T) {
    76  	e := sampleTxnEvent
    77  	testTxnEventJSON(t, &e, `[
    78  	{
    79  		"type":"Transaction",
    80  		"name":"myName",
    81  		"timestamp":1.488393111e+09,
    82  		"error":false,
    83  		"duration":2,
    84  		"totalTime":3,
    85  		"guid":"txn-id",
    86  		"traceId":"txn-id",
    87  		"priority":0.500000,
    88  		"sampled":false
    89  	},
    90  	{},
    91  	{}]`)
    92  }
    93  
    94  func TestTxnEventMarshalWithApdex(t *testing.T) {
    95  	e := sampleTxnEvent
    96  	e.Zone = ApdexFailing
    97  	testTxnEventJSON(t, &e, `[
    98  	{
    99  		"type":"Transaction",
   100  		"name":"myName",
   101  		"timestamp":1.488393111e+09,
   102  		"nr.apdexPerfZone":"F",
   103  		"error":false,
   104  		"duration":2,
   105  		"totalTime":3,
   106  		"guid":"txn-id",
   107  		"traceId":"txn-id",
   108  		"priority":0.500000,
   109  		"sampled":false
   110  	},
   111  	{},
   112  	{}]`)
   113  }
   114  
   115  func TestTxnEventMarshalWithDatastoreExternal(t *testing.T) {
   116  	e := sampleTxnEvent
   117  	e.DatastoreExternalTotals = DatastoreExternalTotals{
   118  		externalCallCount:  22,
   119  		externalDuration:   1122334 * time.Millisecond,
   120  		datastoreCallCount: 33,
   121  		datastoreDuration:  5566778 * time.Millisecond,
   122  	}
   123  	testTxnEventJSON(t, &e, `[
   124  	{
   125  		"type":"Transaction",
   126  		"name":"myName",
   127  		"timestamp":1.488393111e+09,
   128  		"error":false,
   129  		"duration":2,
   130  		"externalCallCount":22,
   131  		"externalDuration":1122.334,
   132  		"databaseCallCount":33,
   133  		"databaseDuration":5566.778,
   134  		"totalTime":3,
   135  		"guid":"txn-id",
   136  		"traceId":"txn-id",
   137  		"priority":0.500000,
   138  		"sampled":false
   139  	},
   140  	{},
   141  	{}]`)
   142  }
   143  
   144  func TestTxnEventMarshalWithInboundCaller(t *testing.T) {
   145  	e := sampleTxnEvent
   146  	e.BetterCAT.Inbound = &Payload{
   147  		payloadCaller: payloadCaller{
   148  			TransportType: "HTTP",
   149  			Type:          "Browser",
   150  			App:           "caller-app",
   151  			Account:       "caller-account",
   152  		},
   153  		ID:                "caller-id",
   154  		TransactionID:     "caller-parent-id",
   155  		TracedID:          "trip-id",
   156  		TransportDuration: 2 * time.Second,
   157  	}
   158  	testTxnEventJSON(t, &e, `[
   159  	{
   160  		"type":"Transaction",
   161  		"name":"myName",
   162  		"timestamp":1.488393111e+09,
   163  		"error":false,
   164  		"duration":2,
   165  		"totalTime":3,
   166  		"parent.type": "Browser",
   167  		"parent.app": "caller-app",
   168  		"parent.account": "caller-account",
   169  		"parent.transportType": "HTTP",
   170  		"parent.transportDuration": 2,
   171  		"guid":"txn-id",
   172  		"traceId":"trip-id",
   173  		"priority":0.500000,
   174  		"sampled":false,
   175  		"parentId": "caller-parent-id",
   176  		"parentSpanId": "caller-id"
   177  	},
   178  	{},
   179  	{}]`)
   180  }
   181  
   182  func TestTxnEventMarshalWithInboundCallerOldCAT(t *testing.T) {
   183  	e := sampleTxnEventWithOldCAT
   184  	e.BetterCAT.Inbound = &Payload{
   185  		payloadCaller: payloadCaller{
   186  			TransportType: "HTTP",
   187  			Type:          "Browser",
   188  			App:           "caller-app",
   189  			Account:       "caller-account",
   190  		},
   191  		ID:                "caller-id",
   192  		TransactionID:     "caller-parent-id",
   193  		TracedID:          "trip-id",
   194  		TransportDuration: 2 * time.Second,
   195  	}
   196  	testTxnEventJSON(t, &e, `[
   197  	{
   198  		"type":"Transaction",
   199  		"name":"myOldName",
   200  		"timestamp":1.488393111e+09,
   201  		"error":false,
   202  		"duration":2,
   203  		"totalTime":3
   204  	},
   205  	{},
   206  	{}]`)
   207  }
   208  
   209  func TestTxnEventMarshalWithAttributes(t *testing.T) {
   210  	aci := sampleAttributeConfigInput
   211  	aci.TransactionEvents.Exclude = append(aci.TransactionEvents.Exclude, "zap")
   212  	aci.TransactionEvents.Exclude = append(aci.TransactionEvents.Exclude, AttributeHostDisplayName.name())
   213  	cfg := CreateAttributeConfig(aci, true)
   214  	attr := NewAttributes(cfg)
   215  	attr.Agent.Add(AttributeHostDisplayName, "exclude me", nil)
   216  	attr.Agent.Add(attributeRequestMethod, "GET", nil)
   217  	AddUserAttribute(attr, "zap", 123, DestAll)
   218  	AddUserAttribute(attr, "zip", 456, DestAll)
   219  	e := sampleTxnEvent
   220  	e.Attrs = attr
   221  	testTxnEventJSON(t, &e, `[
   222  	{
   223  		"type":"Transaction",
   224  		"name":"myName",
   225  		"timestamp":1.488393111e+09,
   226  		"error":false,
   227  		"duration":2,
   228  		"totalTime":3,
   229  		"guid":"txn-id",
   230  		"traceId":"txn-id",
   231  		"priority":0.500000,
   232  		"sampled":false
   233  	},
   234  	{
   235  		"zip":456
   236  	},
   237  	{
   238  		"request.method":"GET"
   239  	}]`)
   240  }
   241  
   242  func TestTxnEventsPayloadsEmpty(t *testing.T) {
   243  	events := newTxnEvents(10)
   244  	ps := events.payloads(5)
   245  	if len(ps) != 1 {
   246  		t.Error(ps)
   247  	}
   248  	if data, err := ps[0].Data("agentRunID", time.Now()); data != nil || err != nil {
   249  		t.Error(data, err)
   250  	}
   251  }
   252  
   253  func TestTxnEventsPayloadsUnderLimit(t *testing.T) {
   254  	events := newTxnEvents(10)
   255  	for i := 0; i < 4; i++ {
   256  		events.AddTxnEvent(&TxnEvent{}, Priority(float32(i)/10.0))
   257  	}
   258  	ps := events.payloads(5)
   259  	if len(ps) != 1 {
   260  		t.Error(ps)
   261  	}
   262  	if data, err := ps[0].Data("agentRunID", time.Now()); data == nil || err != nil {
   263  		t.Error(data, err)
   264  	}
   265  }
   266  
   267  func TestTxnEventsPayloadsOverLimit(t *testing.T) {
   268  	events := newTxnEvents(10)
   269  	for i := 0; i < 6; i++ {
   270  		events.AddTxnEvent(&TxnEvent{}, Priority(float32(i)/10.0))
   271  	}
   272  	ps := events.payloads(5)
   273  	if len(ps) != 2 {
   274  		t.Error(ps)
   275  	}
   276  	if data, err := ps[0].Data("agentRunID", time.Now()); data == nil || err != nil {
   277  		t.Error(data, err)
   278  	}
   279  	if data, err := ps[1].Data("agentRunID", time.Now()); data == nil || err != nil {
   280  		t.Error(data, err)
   281  	}
   282  }
   283  
   284  func TestTxnEventsSynthetics(t *testing.T) {
   285  	events := newTxnEvents(1)
   286  
   287  	regular := &TxnEvent{
   288  		FinalName: "Regular",
   289  		Start:     time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC),
   290  		Duration:  2 * time.Second,
   291  		Zone:      ApdexNone,
   292  		Attrs:     nil,
   293  	}
   294  
   295  	synthetics := &TxnEvent{
   296  		FinalName: "Synthetics",
   297  		Start:     time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC),
   298  		Duration:  2 * time.Second,
   299  		Zone:      ApdexNone,
   300  		Attrs:     nil,
   301  		CrossProcess: TxnCrossProcess{
   302  			Type: txnCrossProcessSynthetics,
   303  			Synthetics: &cat.SyntheticsHeader{
   304  				ResourceID: "resource",
   305  				JobID:      "job",
   306  				MonitorID:  "monitor",
   307  			},
   308  		},
   309  	}
   310  
   311  	events.AddTxnEvent(regular, 1.99999)
   312  
   313  	// Check that the event was saved.
   314  	if saved := events.analyticsEvents.events[0].jsonWriter; saved != regular {
   315  		t.Errorf("unexpected saved event: expected=%v; got=%v", regular, saved)
   316  	}
   317  
   318  	// The priority sampling algorithm is implemented using isLowerPriority().  In
   319  	// the case of an event pool with a single event, an incoming event with the
   320  	// same priority would kick out the event already in the pool.  To really test
   321  	// whether synthetics are given highest deference, add a synthetics event
   322  	// with a really low priority and affirm it kicks out the event already in the
   323  	// pool.
   324  	events.AddTxnEvent(synthetics, 0.0)
   325  
   326  	// Check that the event was saved and its priority was appropriately augmented.
   327  	if saved := events.analyticsEvents.events[0].jsonWriter; saved != synthetics {
   328  		t.Errorf("unexpected saved event: expected=%v; got=%v", synthetics, saved)
   329  	}
   330  
   331  	if priority := events.analyticsEvents.events[0].priority; priority != 2.0 {
   332  		t.Errorf("synthetics event has unexpected priority: %f", priority)
   333  	}
   334  }
   335  
   336  func TestTxnEventMarshalWithError(t *testing.T) {
   337  	e := sampleTxnEventWithError
   338  	testTxnEventJSON(t, &e, `[
   339  	{
   340  		"type":"Transaction",
   341  		"name":"myName",
   342  		"timestamp":1.488393111e+09,
   343  		"error":true,
   344  		"duration":2,
   345  		"totalTime":3,
   346  		"guid":"txn-id",
   347  		"traceId":"txn-id",
   348  		"priority":0.500000,
   349  		"sampled":false
   350  	},
   351  	{},
   352  	{}]`)
   353  }