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  }