github.com/newrelic/go-agent@v3.26.0+incompatible/internal_distributed_trace_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/base64"
     8  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  	"net/url"
    12  	"reflect"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/newrelic/go-agent/internal"
    18  	"github.com/newrelic/go-agent/internal/crossagent"
    19  )
    20  
    21  type PayloadTest struct {
    22  	V *[2]int                `json:"v,omitempty"`
    23  	D map[string]interface{} `json:"d,omitempty"`
    24  }
    25  
    26  func distributedTracingReplyFields(reply *internal.ConnectReply) {
    27  	reply.AccountID = "123"
    28  	reply.AppID = "456"
    29  	reply.PrimaryAppID = "456"
    30  	reply.TrustedAccounts = map[int]struct{}{
    31  		123: {},
    32  	}
    33  	reply.TrustedAccountKey = "123"
    34  
    35  	reply.AdaptiveSampler = internal.SampleEverything{}
    36  }
    37  
    38  func distributedTracingReplyFieldsNeedTrustKey(reply *internal.ConnectReply) {
    39  	reply.AccountID = "123"
    40  	reply.AppID = "456"
    41  	reply.PrimaryAppID = "456"
    42  	reply.TrustedAccounts = map[int]struct{}{
    43  		123: {},
    44  	}
    45  	reply.TrustedAccountKey = "789"
    46  }
    47  
    48  func makePayload(app Application, u *url.URL) DistributedTracePayload {
    49  	txn := app.StartTransaction("hello", nil, nil)
    50  	return txn.CreateDistributedTracePayload()
    51  }
    52  
    53  func enableOldCATDisableBetterCat(cfg *Config) {
    54  	cfg.CrossApplicationTracer.Enabled = true
    55  	cfg.DistributedTracer.Enabled = false
    56  }
    57  
    58  func disableCAT(cfg *Config) {
    59  	cfg.CrossApplicationTracer.Enabled = false
    60  	cfg.DistributedTracer.Enabled = false
    61  }
    62  
    63  func enableBetterCAT(cfg *Config) {
    64  	cfg.CrossApplicationTracer.Enabled = false
    65  	cfg.DistributedTracer.Enabled = true
    66  }
    67  
    68  func disableSpanEvents(cfg *Config) {
    69  	cfg.CrossApplicationTracer.Enabled = false
    70  	cfg.DistributedTracer.Enabled = true
    71  	cfg.SpanEvents.Enabled = false
    72  }
    73  
    74  func disableDistributedTracerEnableSpanEvents(cfg *Config) {
    75  	cfg.CrossApplicationTracer.Enabled = true
    76  	cfg.DistributedTracer.Enabled = false
    77  	cfg.SpanEvents.Enabled = true
    78  }
    79  
    80  var (
    81  	distributedTracingSuccessMetrics = []internal.WantMetric{
    82  		{Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
    83  		{Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil},
    84  		{Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
    85  		{Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil},
    86  		{Name: "DurationByCaller/App/123/456/HTTP/all", Scope: "", Forced: false, Data: nil},
    87  		{Name: "DurationByCaller/App/123/456/HTTP/allOther", Scope: "", Forced: false, Data: nil},
    88  		{Name: "TransportDuration/App/123/456/HTTP/all", Scope: "", Forced: false, Data: nil},
    89  		{Name: "TransportDuration/App/123/456/HTTP/allOther", Scope: "", Forced: false, Data: nil},
    90  		{Name: "Supportability/DistributedTrace/AcceptPayload/Success", Scope: "", Forced: true, Data: singleCount},
    91  	}
    92  )
    93  
    94  func TestPayloadConnection(t *testing.T) {
    95  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
    96  	payload := makePayload(app, nil)
    97  	ip, ok := payload.(internal.Payload)
    98  	if !ok {
    99  		t.Fatal(payload)
   100  	}
   101  	txn := app.StartTransaction("hello", nil, nil)
   102  	err := txn.AcceptDistributedTracePayload(TransportHTTP, payload)
   103  	if nil != err {
   104  		t.Error(err)
   105  	}
   106  	err = txn.End()
   107  	if nil != err {
   108  		t.Error(err)
   109  	}
   110  	app.ExpectMetrics(t, distributedTracingSuccessMetrics)
   111  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   112  		Intrinsics: map[string]interface{}{
   113  			"name":                     "OtherTransaction/Go/hello",
   114  			"parent.type":              "App",
   115  			"parent.account":           "123",
   116  			"parent.app":               "456",
   117  			"parent.transportType":     "HTTP",
   118  			"parent.transportDuration": internal.MatchAnything,
   119  			"parentId":                 ip.TransactionID,
   120  			"traceId":                  ip.TransactionID,
   121  			"parentSpanId":             ip.ID,
   122  			"guid":                     internal.MatchAnything,
   123  			"sampled":                  internal.MatchAnything,
   124  			"priority":                 internal.MatchAnything,
   125  		},
   126  	}})
   127  }
   128  
   129  func TestAcceptMultiple(t *testing.T) {
   130  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   131  	payload := makePayload(app, nil)
   132  	ip, ok := payload.(internal.Payload)
   133  	if !ok {
   134  		t.Fatal(payload)
   135  	}
   136  	txn := app.StartTransaction("hello", nil, nil)
   137  	err := txn.AcceptDistributedTracePayload(TransportHTTP, payload)
   138  	if nil != err {
   139  		t.Error(err)
   140  	}
   141  	err = txn.AcceptDistributedTracePayload(TransportHTTP, payload)
   142  	if err != errAlreadyAccepted {
   143  		t.Error(err)
   144  	}
   145  	err = txn.End()
   146  	if nil != err {
   147  		t.Error(err)
   148  	}
   149  	app.ExpectMetrics(t, append([]internal.WantMetric{
   150  		{Name: "Supportability/DistributedTrace/AcceptPayload/Ignored/Multiple", Scope: "", Forced: true, Data: singleCount},
   151  	}, distributedTracingSuccessMetrics...))
   152  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   153  		Intrinsics: map[string]interface{}{
   154  			"name":                     "OtherTransaction/Go/hello",
   155  			"parent.type":              "App",
   156  			"parent.account":           "123",
   157  			"parent.app":               "456",
   158  			"parent.transportType":     "HTTP",
   159  			"parent.transportDuration": internal.MatchAnything,
   160  			"parentId":                 ip.TransactionID,
   161  			"traceId":                  ip.TransactionID,
   162  			"parentSpanId":             ip.ID,
   163  			"guid":                     internal.MatchAnything,
   164  			"sampled":                  internal.MatchAnything,
   165  			"priority":                 internal.MatchAnything,
   166  		},
   167  	}})
   168  }
   169  
   170  func TestPayloadConnectionText(t *testing.T) {
   171  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   172  	payload := makePayload(app, nil)
   173  	ip, ok := payload.(internal.Payload)
   174  	if !ok {
   175  		t.Fatal(payload)
   176  	}
   177  	txn := app.StartTransaction("hello", nil, nil)
   178  	err := txn.AcceptDistributedTracePayload(TransportHTTP, payload.Text())
   179  	if nil != err {
   180  		t.Error(err)
   181  	}
   182  	err = txn.End()
   183  	if nil != err {
   184  		t.Error(err)
   185  	}
   186  	app.ExpectMetrics(t, distributedTracingSuccessMetrics)
   187  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   188  		Intrinsics: map[string]interface{}{
   189  			"name":                     "OtherTransaction/Go/hello",
   190  			"parent.type":              "App",
   191  			"parent.account":           "123",
   192  			"parent.app":               "456",
   193  			"parent.transportType":     "HTTP",
   194  			"parent.transportDuration": internal.MatchAnything,
   195  			"parentId":                 ip.TransactionID,
   196  			"traceId":                  ip.TransactionID,
   197  			"parentSpanId":             ip.ID,
   198  			"guid":                     internal.MatchAnything,
   199  			"sampled":                  internal.MatchAnything,
   200  			"priority":                 internal.MatchAnything,
   201  		},
   202  	}})
   203  }
   204  
   205  func validBase64(s string) bool {
   206  	_, err := base64.StdEncoding.DecodeString(s)
   207  	return err == nil
   208  }
   209  
   210  func TestPayloadConnectionHTTPSafe(t *testing.T) {
   211  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   212  	payload := makePayload(app, nil)
   213  	ip, ok := payload.(internal.Payload)
   214  	if !ok {
   215  		t.Fatal(payload)
   216  	}
   217  	txn := app.StartTransaction("hello", nil, nil)
   218  	p := payload.HTTPSafe()
   219  	if !validBase64(p) {
   220  		t.Error(p)
   221  	}
   222  	err := txn.AcceptDistributedTracePayload(TransportHTTP, p)
   223  	if nil != err {
   224  		t.Error(err)
   225  	}
   226  	err = txn.End()
   227  	if nil != err {
   228  		t.Error(err)
   229  	}
   230  	app.ExpectMetrics(t, distributedTracingSuccessMetrics)
   231  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   232  		Intrinsics: map[string]interface{}{
   233  			"name":                     "OtherTransaction/Go/hello",
   234  			"parent.type":              "App",
   235  			"parent.account":           "123",
   236  			"parent.app":               "456",
   237  			"parent.transportType":     "HTTP",
   238  			"parent.transportDuration": internal.MatchAnything,
   239  			"parentId":                 ip.TransactionID,
   240  			"traceId":                  ip.TransactionID,
   241  			"parentSpanId":             ip.ID,
   242  			"guid":                     internal.MatchAnything,
   243  			"sampled":                  internal.MatchAnything,
   244  			"priority":                 internal.MatchAnything,
   245  		},
   246  	}})
   247  }
   248  
   249  func TestPayloadConnectionNotConnected(t *testing.T) {
   250  	app := testApp(nil, enableBetterCAT, t)
   251  	payload := makePayload(app, nil)
   252  	txn := app.StartTransaction("hello", nil, nil)
   253  	if nil == payload {
   254  		t.Fatal(payload)
   255  	}
   256  	if "" != payload.Text() {
   257  		t.Error(payload.Text())
   258  	}
   259  	if "" != payload.HTTPSafe() {
   260  		t.Error(payload.HTTPSafe())
   261  	}
   262  	err := txn.AcceptDistributedTracePayload(TransportHTTP, payload)
   263  	if nil != err {
   264  		t.Error(err)
   265  	}
   266  	err = txn.End()
   267  	if nil != err {
   268  		t.Error(err)
   269  	}
   270  	app.ExpectMetrics(t, backgroundMetricsUnknownCaller)
   271  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   272  		Intrinsics: map[string]interface{}{
   273  			"name":     "OtherTransaction/Go/hello",
   274  			"guid":     internal.MatchAnything,
   275  			"traceId":  internal.MatchAnything,
   276  			"priority": internal.MatchAnything,
   277  			"sampled":  internal.MatchAnything,
   278  		},
   279  	}})
   280  }
   281  
   282  func TestPayloadConnectionBetterCatDisabled(t *testing.T) {
   283  	app := testApp(nil, disableCAT, t)
   284  	payload := makePayload(app, nil)
   285  	txn := app.StartTransaction("hello", nil, nil)
   286  	if nil == payload {
   287  		t.Fatal(payload)
   288  	}
   289  	if "" != payload.Text() {
   290  		t.Error(payload.Text())
   291  	}
   292  	if "" != payload.HTTPSafe() {
   293  		t.Error(payload.HTTPSafe())
   294  	}
   295  	err := txn.AcceptDistributedTracePayload(TransportHTTP, payload)
   296  	if err == nil {
   297  		t.Error("missing expected error")
   298  	}
   299  	if errInboundPayloadDTDisabled != err {
   300  		t.Error(err)
   301  	}
   302  	err = txn.End()
   303  	if nil != err {
   304  		t.Error(err)
   305  	}
   306  }
   307  
   308  func TestPayloadTransactionsDisabled(t *testing.T) {
   309  	cfgFn := func(cfg *Config) {
   310  		cfg.DistributedTracer.Enabled = true
   311  		cfg.SpanEvents.Enabled = true
   312  		cfg.TransactionEvents.Enabled = false
   313  	}
   314  	app := testApp(nil, cfgFn, t)
   315  	txn := app.StartTransaction("hello", nil, nil)
   316  
   317  	payload := txn.CreateDistributedTracePayload()
   318  	if nil == payload {
   319  		t.Fatal(payload)
   320  	}
   321  	if "" != payload.Text() {
   322  		t.Error(payload.Text())
   323  	}
   324  	if "" != payload.HTTPSafe() {
   325  		t.Error(payload.HTTPSafe())
   326  	}
   327  	err := txn.End()
   328  	if nil != err {
   329  		t.Error(err)
   330  	}
   331  }
   332  
   333  func TestPayloadConnectionEmptyString(t *testing.T) {
   334  	app := testApp(nil, enableBetterCAT, t)
   335  	txn := app.StartTransaction("hello", nil, nil)
   336  	err := txn.AcceptDistributedTracePayload(TransportHTTP, "")
   337  	if nil != err {
   338  		t.Error(err)
   339  	}
   340  	err = txn.End()
   341  	if nil != err {
   342  		t.Error(err)
   343  	}
   344  	app.ExpectMetrics(t, backgroundMetricsUnknownCaller)
   345  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   346  		Intrinsics: map[string]interface{}{
   347  			"name":     "OtherTransaction/Go/hello",
   348  			"guid":     internal.MatchAnything,
   349  			"traceId":  internal.MatchAnything,
   350  			"priority": internal.MatchAnything,
   351  			"sampled":  internal.MatchAnything,
   352  		},
   353  	}})
   354  }
   355  
   356  func TestCreatePayloadFinished(t *testing.T) {
   357  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   358  	txn := app.StartTransaction("hello", nil, nil)
   359  	txn.End()
   360  	payload := txn.CreateDistributedTracePayload()
   361  	if nil == payload {
   362  		t.Fatal(payload)
   363  	}
   364  	if "" != payload.Text() {
   365  		t.Error(payload.Text())
   366  	}
   367  	if "" != payload.HTTPSafe() {
   368  		t.Error(payload.HTTPSafe())
   369  	}
   370  }
   371  
   372  func TestAcceptPayloadFinished(t *testing.T) {
   373  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   374  	payload := makePayload(app, nil)
   375  	txn := app.StartTransaction("hello", nil, nil)
   376  	err := txn.End()
   377  	if nil != err {
   378  		t.Error(err)
   379  	}
   380  	err = txn.AcceptDistributedTracePayload(TransportHTTP, payload)
   381  	if err != errAlreadyEnded {
   382  		t.Fatal(err)
   383  	}
   384  	app.ExpectMetrics(t, backgroundMetricsUnknownCaller)
   385  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   386  		Intrinsics: map[string]interface{}{
   387  			"name":     "OtherTransaction/Go/hello",
   388  			"guid":     internal.MatchAnything,
   389  			"traceId":  internal.MatchAnything,
   390  			"priority": internal.MatchAnything,
   391  			"sampled":  internal.MatchAnything,
   392  		},
   393  	}})
   394  }
   395  
   396  func TestPayloadTypeUnknown(t *testing.T) {
   397  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   398  	txn := app.StartTransaction("hello", nil, nil)
   399  	invalidPayload := 22
   400  	err := txn.AcceptDistributedTracePayload(TransportHTTP, invalidPayload)
   401  	if nil != err {
   402  		t.Error(err)
   403  	}
   404  	err = txn.End()
   405  	if nil != err {
   406  		t.Error(err)
   407  	}
   408  	app.ExpectMetrics(t, backgroundMetricsUnknownCaller)
   409  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   410  		Intrinsics: map[string]interface{}{
   411  			"name":     "OtherTransaction/Go/hello",
   412  			"guid":     internal.MatchAnything,
   413  			"traceId":  internal.MatchAnything,
   414  			"priority": internal.MatchAnything,
   415  			"sampled":  internal.MatchAnything,
   416  		},
   417  	}})
   418  }
   419  
   420  func TestPayloadAcceptAfterCreate(t *testing.T) {
   421  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   422  	payload := makePayload(app, nil)
   423  	txn := app.StartTransaction("hello", nil, nil)
   424  	txn.CreateDistributedTracePayload()
   425  	err := txn.AcceptDistributedTracePayload(TransportHTTP, payload)
   426  	if errOutboundPayloadCreated != err {
   427  		t.Error(err)
   428  	}
   429  	err = txn.End()
   430  	if nil != err {
   431  		t.Error(err)
   432  	}
   433  	app.ExpectMetrics(t, append([]internal.WantMetric{
   434  		{Name: "Supportability/DistributedTrace/CreatePayload/Success", Scope: "", Forced: true, Data: singleCount},
   435  		{Name: "Supportability/DistributedTrace/AcceptPayload/Ignored/CreateBeforeAccept", Scope: "", Forced: true, Data: singleCount},
   436  	}, backgroundMetricsUnknownCaller...))
   437  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   438  		Intrinsics: map[string]interface{}{
   439  			"name":     "OtherTransaction/Go/hello",
   440  			"guid":     internal.MatchAnything,
   441  			"traceId":  internal.MatchAnything,
   442  			"priority": internal.MatchAnything,
   443  			"sampled":  internal.MatchAnything,
   444  		},
   445  	}})
   446  }
   447  
   448  func TestPayloadFromApplicationEmptyTransportType(t *testing.T) {
   449  	// A user has two options when it comes to TransportType.  They can either use one of the
   450  	// defined vars, like TransportHTTP, or create their own empty variable. The name field inside of
   451  	// the TransportType struct is not exported outside of the package so users cannot modify its value.
   452  	// When they make the attempt, Go reports:
   453  	//
   454  	// implicit assignment of unexported field 'name' in newrelic.TransportType literal.
   455  	//
   456  	// This test makes sure an empty TransportType resolves to "Unknown"
   457  	var emptyTransport TransportType
   458  
   459  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   460  	txn := app.StartTransaction("hello", nil, nil)
   461  	err := txn.AcceptDistributedTracePayload(emptyTransport,
   462  		`{
   463                                "v":[0,1],
   464                                "d":{
   465                                "ty":"App",
   466                                "ap":"456",
   467                                "ac":"123",
   468                                "id":"id",
   469                                "tr":"traceID",
   470                                "ti":1488325987402
   471                                }
   472  		}`)
   473  	if nil != err {
   474  		t.Error(err)
   475  	}
   476  	err = txn.End()
   477  	if nil != err {
   478  		t.Error(err)
   479  	}
   480  	app.ExpectMetrics(t, []internal.WantMetric{
   481  		{Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
   482  		{Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil},
   483  		{Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
   484  		{Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil},
   485  		{Name: "DurationByCaller/App/123/456/Unknown/all", Scope: "", Forced: false, Data: nil},
   486  		{Name: "DurationByCaller/App/123/456/Unknown/allOther", Scope: "", Forced: false, Data: nil},
   487  		{Name: "TransportDuration/App/123/456/Unknown/all", Scope: "", Forced: false, Data: nil},
   488  		{Name: "TransportDuration/App/123/456/Unknown/allOther", Scope: "", Forced: false, Data: nil},
   489  		{Name: "Supportability/DistributedTrace/AcceptPayload/Success", Scope: "", Forced: true, Data: singleCount},
   490  	})
   491  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   492  		Intrinsics: map[string]interface{}{
   493  			"name":                     "OtherTransaction/Go/hello",
   494  			"parent.type":              "App",
   495  			"parent.account":           "123",
   496  			"parent.app":               "456",
   497  			"parent.transportType":     "Unknown",
   498  			"parent.transportDuration": internal.MatchAnything,
   499  			"sampled":                  internal.MatchAnything,
   500  			"priority":                 internal.MatchAnything,
   501  			"traceId":                  "traceID",
   502  			"parentSpanId":             "id",
   503  			"guid":                     internal.MatchAnything,
   504  		},
   505  	}})
   506  }
   507  
   508  func TestPayloadFutureVersion(t *testing.T) {
   509  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   510  	txn := app.StartTransaction("hello", nil, nil)
   511  	err := txn.AcceptDistributedTracePayload(TransportHTTP,
   512  		`{
   513  			"v":[100,0],
   514  			"d":{
   515  				"ty":"App",
   516  				"ap":"456",
   517  				"ac":"123",
   518  				"ti":1488325987402
   519  			}
   520  		}`)
   521  	if nil == err {
   522  		t.Error("missing expected error here")
   523  	}
   524  	err = txn.End()
   525  	if nil != err {
   526  		t.Error(err)
   527  	}
   528  	app.ExpectMetrics(t, append([]internal.WantMetric{
   529  		{Name: "Supportability/DistributedTrace/AcceptPayload/Ignored/MajorVersion", Scope: "", Forced: true, Data: singleCount},
   530  	}, backgroundMetricsUnknownCaller...))
   531  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   532  		Intrinsics: map[string]interface{}{
   533  			"name":     "OtherTransaction/Go/hello",
   534  			"sampled":  internal.MatchAnything,
   535  			"priority": internal.MatchAnything,
   536  			"traceId":  internal.MatchAnything,
   537  			"guid":     internal.MatchAnything,
   538  		},
   539  	}})
   540  }
   541  
   542  func TestPayloadParsingError(t *testing.T) {
   543  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   544  	txn := app.StartTransaction("hello", nil, nil)
   545  	err := txn.AcceptDistributedTracePayload(TransportHTTP,
   546  		`{
   547  			"v":[0,1],
   548  			"d":[]
   549  		}`)
   550  	if nil == err {
   551  		t.Error("missing expected parsing error")
   552  	}
   553  	err = txn.End()
   554  	if nil != err {
   555  		t.Error(err)
   556  	}
   557  	app.ExpectMetrics(t, append([]internal.WantMetric{
   558  		{Name: "Supportability/DistributedTrace/AcceptPayload/ParseException", Scope: "", Forced: true, Data: singleCount},
   559  	}, backgroundMetricsUnknownCaller...))
   560  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   561  		Intrinsics: map[string]interface{}{
   562  			"name":     "OtherTransaction/Go/hello",
   563  			"sampled":  internal.MatchAnything,
   564  			"priority": internal.MatchAnything,
   565  			"traceId":  internal.MatchAnything,
   566  			"guid":     internal.MatchAnything,
   567  		},
   568  	}})
   569  }
   570  
   571  func TestPayloadFromFuture(t *testing.T) {
   572  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   573  	payload := makePayload(app, nil)
   574  	ip, ok := payload.(internal.Payload)
   575  	if !ok {
   576  		t.Fatal(payload)
   577  	}
   578  	ip.Timestamp.Set(time.Now().Add(1 * time.Hour))
   579  	txn := app.StartTransaction("hello", nil, nil)
   580  	err := txn.AcceptDistributedTracePayload(TransportHTTP, ip)
   581  	if nil != err {
   582  		t.Error(err)
   583  	}
   584  	err = txn.End()
   585  	if nil != err {
   586  		t.Error(err)
   587  	}
   588  	app.ExpectMetrics(t, distributedTracingSuccessMetrics)
   589  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   590  		Intrinsics: map[string]interface{}{
   591  			"name":                     "OtherTransaction/Go/hello",
   592  			"parent.type":              "App",
   593  			"parent.account":           "123",
   594  			"parent.app":               "456",
   595  			"parent.transportType":     "HTTP",
   596  			"parent.transportDuration": 0,
   597  			"parentId":                 ip.TransactionID,
   598  			"traceId":                  ip.TransactionID,
   599  			"parentSpanId":             ip.ID,
   600  			"guid":                     internal.MatchAnything,
   601  			"sampled":                  internal.MatchAnything,
   602  			"priority":                 internal.MatchAnything,
   603  		},
   604  	}})
   605  }
   606  
   607  func TestPayloadUntrustedAccount(t *testing.T) {
   608  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   609  	payload := makePayload(app, nil)
   610  	ip, ok := payload.(internal.Payload)
   611  	if !ok {
   612  		t.Fatal(payload)
   613  	}
   614  	ip.Account = "12345"
   615  	txn := app.StartTransaction("hello", nil, nil)
   616  	err := txn.AcceptDistributedTracePayload(TransportHTTP, ip)
   617  
   618  	if err != errTrustedAccountKey {
   619  		t.Error(err)
   620  	}
   621  	err = txn.End()
   622  	if nil != err {
   623  		t.Error(err)
   624  	}
   625  	app.ExpectMetrics(t, append([]internal.WantMetric{
   626  		{Name: "Supportability/DistributedTrace/AcceptPayload/Ignored/UntrustedAccount", Scope: "", Forced: true, Data: singleCount},
   627  	}, backgroundMetricsUnknownCaller...))
   628  	app.ExpectTxnEvents(t, []internal.WantEvent{{
   629  		Intrinsics: map[string]interface{}{
   630  			"name":     "OtherTransaction/Go/hello",
   631  			"guid":     internal.MatchAnything,
   632  			"traceId":  internal.MatchAnything,
   633  			"priority": internal.MatchAnything,
   634  			"sampled":  internal.MatchAnything,
   635  		},
   636  	}})
   637  }
   638  
   639  func TestPayloadMissingVersion(t *testing.T) {
   640  	// ensures that a complete distributed trace payload without a version fails
   641  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   642  	txn := app.StartTransaction("hello", nil, nil)
   643  	err := txn.AcceptDistributedTracePayload(TransportHTTP,
   644  		`{
   645  			"d":{
   646  				"ty":"App",
   647  				"ap":"456",
   648  				"ac":"123",
   649  				"id":"id",
   650  				"tr":"traceID",
   651  				"ti":1488325987402
   652  			}
   653  		}`)
   654  	if nil == err {
   655  		t.Log("Expected error from missing Version (v)")
   656  		t.Fail()
   657  	}
   658  	err = txn.End()
   659  	if nil != err {
   660  		t.Error(err)
   661  	}
   662  }
   663  
   664  func TestTrustedAccountKeyPayloadHasKeyAndMatches(t *testing.T) {
   665  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   666  
   667  	// fixture has a "tk" of 123, which matches the trusted_account_key
   668  	// from distributedTracingReplyFields.
   669  	p := `{
   670  		"v":[0,1],
   671  		"d":{
   672  			"ty":"App",
   673  			"ap":"456",
   674  			"ac":"321",
   675  			"id":"id",
   676  			"tr":"traceID",
   677  			"ti":1488325987402,
   678  			"tk":"123"
   679  		}
   680  	}`
   681  	txn := app.StartTransaction("hello", nil, nil)
   682  	err := txn.AcceptDistributedTracePayload(TransportHTTP, p)
   683  	if nil != err {
   684  		t.Error(err)
   685  	}
   686  	err = txn.End()
   687  	if nil != err {
   688  		t.Error(err)
   689  	}
   690  }
   691  
   692  func TestTrustedAccountKeyPayloadHasKeyAndDoesNotMatch(t *testing.T) {
   693  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   694  
   695  	// fixture has a "tk" of 1234, which does not match the
   696  	// trusted_account_key from distributedTracingReplyFields.
   697  	p := `{
   698  		"v":[0,1],
   699  		"d":{
   700  			"ty":"App",
   701  			"ap":"456",
   702  			"ac":"321",
   703  			"id":"id",
   704  			"tr":"traceID",
   705  			"ti":1488325987402,
   706  			"tk":"1234"
   707  		}
   708  	}`
   709  	txn := app.StartTransaction("hello", nil, nil)
   710  	err := txn.AcceptDistributedTracePayload(TransportHTTP, p)
   711  	if err != errTrustedAccountKey {
   712  		t.Error("Expected ErrTrustedAccountKey from mismatched trustkeys", err)
   713  	}
   714  	err = txn.End()
   715  	if nil != err {
   716  		t.Error(err)
   717  	}
   718  }
   719  
   720  func TestTrustedAccountKeyPayloadMissingKeyAndAccountIdMatches(t *testing.T) {
   721  
   722  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   723  
   724  	// fixture has no trust key but its account id of 123 matches
   725  	// trusted_account_key from distributedTracingReplyFields.
   726  	p := `{
   727  		"v":[0,1],
   728  		"d":{
   729  			"ty":"App",
   730  			"ap":"456",
   731  			"ac":"123",
   732  			"id":"id",
   733  			"tr":"traceID",
   734  			"ti":1488325987402
   735  		}
   736  	}`
   737  	txn := app.StartTransaction("hello", nil, nil)
   738  	err := txn.AcceptDistributedTracePayload(TransportHTTP, p)
   739  	if nil != err {
   740  		t.Error(err)
   741  	}
   742  	err = txn.End()
   743  	if nil != err {
   744  		t.Error(err)
   745  	}
   746  
   747  }
   748  
   749  func TestTrustedAccountKeyPayloadMissingKeyAndAccountIdDoesNotMatch(t *testing.T) {
   750  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   751  
   752  	// fixture has no trust key and its account id of 1234 does not match the
   753  	// trusted_account_key from distributedTracingReplyFields.
   754  	p := `{
   755  		"v":[0,1],
   756  		"d":{
   757  			"ty":"App",
   758  			"ap":"456",
   759  			"ac":"1234",
   760  			"id":"id",
   761  			"tr":"traceID",
   762  			"ti":1488325987402
   763  		}
   764  	}`
   765  	txn := app.StartTransaction("hello", nil, nil)
   766  	err := txn.AcceptDistributedTracePayload(TransportHTTP, p)
   767  	if err != errTrustedAccountKey {
   768  		t.Error("Expected ErrTrustedAccountKey from mismatched trustkeys", err)
   769  	}
   770  	err = txn.End()
   771  	if nil != err {
   772  		t.Error(err)
   773  	}
   774  }
   775  
   776  var (
   777  	backgroundUnknownCaller = []internal.WantMetric{
   778  		{Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
   779  		{Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil},
   780  		{Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
   781  		{Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil},
   782  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil},
   783  		{Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil},
   784  	}
   785  )
   786  
   787  func TestNilPayload(t *testing.T) {
   788  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   789  
   790  	txn := app.StartTransaction("hello", nil, nil)
   791  	err := txn.AcceptDistributedTracePayload(TransportHTTP, nil)
   792  
   793  	if nil != err {
   794  		t.Error(err)
   795  	}
   796  
   797  	err = txn.End()
   798  	if nil != err {
   799  		t.Error(err)
   800  	}
   801  
   802  	app.ExpectMetrics(t, append([]internal.WantMetric{
   803  		{Name: "Supportability/DistributedTrace/AcceptPayload/Ignored/Null", Scope: "", Forced: true, Data: singleCount},
   804  	}, backgroundUnknownCaller...))
   805  }
   806  
   807  func TestNoticeErrorPayload(t *testing.T) {
   808  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   809  
   810  	txn := app.StartTransaction("hello", nil, nil)
   811  	txn.NoticeError(errors.New("oh no"))
   812  
   813  	err := txn.End()
   814  	if nil != err {
   815  		t.Error(err)
   816  	}
   817  
   818  	app.ExpectMetrics(t, append([]internal.WantMetric{
   819  		{Name: "Errors/all", Scope: "", Forced: true, Data: nil},
   820  		{Name: "Errors/allOther", Scope: "", Forced: true, Data: nil},
   821  		{Name: "Errors/OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
   822  		{Name: "ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil},
   823  		{Name: "ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/allOther", Scope: "", Forced: false, Data: nil},
   824  	}, backgroundUnknownCaller...))
   825  }
   826  
   827  func TestMissingIDsForSupportabilityMetric(t *testing.T) {
   828  	p := `{
   829  		"v":[0,1],
   830  		"d":{
   831  			"ty":"App",
   832  			"ap":"456",
   833  			"ac":"123",
   834  			"tr":"traceID",
   835  			"ti":1488325987402
   836  		}
   837  	}`
   838  
   839  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   840  
   841  	txn := app.StartTransaction("hello", nil, nil)
   842  	err := txn.AcceptDistributedTracePayload(TransportHTTP, p)
   843  
   844  	if nil == err {
   845  		t.Log("Expected error from missing guid and transactionId")
   846  		t.Fail()
   847  	}
   848  
   849  	err = txn.End()
   850  	if nil != err {
   851  		t.Error(err)
   852  	}
   853  
   854  	app.ExpectMetrics(t, append([]internal.WantMetric{
   855  		{Name: "Supportability/DistributedTrace/AcceptPayload/ParseException", Scope: "", Forced: true, Data: nil},
   856  	}, backgroundUnknownCaller...))
   857  }
   858  
   859  func TestMissingVersionForSupportabilityMetric(t *testing.T) {
   860  	p := `{
   861  		"d":{
   862  			"ty":"App",
   863  			"ap":"456",
   864  			"ac":"123",
   865  			"id":"id",
   866  			"tr":"traceID",
   867  			"ti":1488325987402
   868  		}
   869  	}`
   870  
   871  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   872  
   873  	txn := app.StartTransaction("hello", nil, nil)
   874  	err := txn.AcceptDistributedTracePayload(TransportHTTP, p)
   875  
   876  	if nil == err {
   877  		t.Log("Expected error from missing version")
   878  		t.Fail()
   879  	}
   880  
   881  	err = txn.End()
   882  	if nil != err {
   883  		t.Error(err)
   884  	}
   885  
   886  	app.ExpectMetrics(t, append([]internal.WantMetric{
   887  		{Name: "Supportability/DistributedTrace/AcceptPayload/ParseException", Scope: "", Forced: true, Data: nil},
   888  	}, backgroundUnknownCaller...))
   889  }
   890  
   891  func TestMissingFieldForSupportabilityMetric(t *testing.T) {
   892  	p := `{
   893  		"v":[0,1],
   894  		"d":{
   895  			"ty":"App",
   896  			"ap":"456",
   897  			"id":"id",
   898  			"tr":"traceID",
   899  			"ti":1488325987402
   900  		}
   901  	}`
   902  
   903  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   904  
   905  	txn := app.StartTransaction("hello", nil, nil)
   906  	err := txn.AcceptDistributedTracePayload(TransportHTTP, p)
   907  
   908  	if nil == err {
   909  		t.Log("Expected error from missing ac field")
   910  		t.Fail()
   911  	}
   912  
   913  	err = txn.End()
   914  	if nil != err {
   915  		t.Error(err)
   916  	}
   917  
   918  	app.ExpectMetrics(t, append([]internal.WantMetric{
   919  		{Name: "Supportability/DistributedTrace/AcceptPayload/ParseException", Scope: "", Forced: true, Data: nil},
   920  	}, backgroundUnknownCaller...))
   921  }
   922  
   923  func TestParseExceptionSupportabilityMetric(t *testing.T) {
   924  	p := `{
   925  		"v":[0,1],
   926  		"d":{
   927  			"ty":"App",
   928  			"ap":"456",
   929  			"id":"id",
   930  			"tr":"traceID",
   931  			"ti":1488325987402
   932  		}
   933  	`
   934  
   935  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   936  
   937  	txn := app.StartTransaction("hello", nil, nil)
   938  	err := txn.AcceptDistributedTracePayload(TransportHTTP, p)
   939  
   940  	if nil == err {
   941  		t.Log("Expected error from invalid json")
   942  		t.Fail()
   943  	}
   944  
   945  	err = txn.End()
   946  	if nil != err {
   947  		t.Error(err)
   948  	}
   949  
   950  	app.ExpectMetrics(t, append([]internal.WantMetric{
   951  		{Name: "Supportability/DistributedTrace/AcceptPayload/ParseException", Scope: "", Forced: true, Data: nil},
   952  	}, backgroundUnknownCaller...))
   953  }
   954  
   955  func TestErrorsByCaller(t *testing.T) {
   956  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
   957  
   958  	txn := app.StartTransaction("hello", nil, nil)
   959  	payload := makePayload(app, nil)
   960  	err := txn.AcceptDistributedTracePayload(TransportHTTP, payload)
   961  
   962  	if nil != err {
   963  		t.Error(err)
   964  	}
   965  
   966  	txn.NoticeError(errors.New("oh no"))
   967  
   968  	err = txn.End()
   969  	if nil != err {
   970  		t.Error(err)
   971  	}
   972  
   973  	app.ExpectMetrics(t, []internal.WantMetric{
   974  		{Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
   975  		{Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil},
   976  		{Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
   977  		{Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil},
   978  
   979  		{Name: "TransportDuration/App/123/456/HTTP/allOther", Scope: "", Forced: false, Data: nil},
   980  		{Name: "Supportability/DistributedTrace/AcceptPayload/Success", Scope: "", Forced: true, Data: nil},
   981  		{Name: "DurationByCaller/App/123/456/HTTP/all", Scope: "", Forced: false, Data: nil},
   982  		{Name: "DurationByCaller/App/123/456/HTTP/allOther", Scope: "", Forced: false, Data: nil},
   983  		{Name: "TransportDuration/App/123/456/HTTP/all", Scope: "", Forced: false, Data: nil},
   984  
   985  		{Name: "ErrorsByCaller/App/123/456/HTTP/all", Scope: "", Forced: false, Data: nil},
   986  		{Name: "ErrorsByCaller/App/123/456/HTTP/allOther", Scope: "", Forced: false, Data: nil},
   987  		{Name: "Errors/all", Scope: "", Forced: true, Data: nil},
   988  		{Name: "Errors/allOther", Scope: "", Forced: true, Data: nil},
   989  		{Name: "Errors/OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
   990  	})
   991  }
   992  
   993  func TestCreateDistributedTraceCatDisabled(t *testing.T) {
   994  
   995  	// when distributed tracing is disabled, CreateDistributedTracePayload
   996  	// should return a value that indicates an empty payload. Examples of
   997  	// this depend on language but may be nil/null/None or an empty payload
   998  	// object.
   999  
  1000  	app := testApp(distributedTracingReplyFields, disableCAT, t)
  1001  	txn := app.StartTransaction("hello", nil, nil)
  1002  
  1003  	p := txn.CreateDistributedTracePayload()
  1004  
  1005  	// empty/shim payload objects return empty strings
  1006  	if "" != p.Text() {
  1007  		t.Log("Non empty string response for .Text() method")
  1008  		t.Fail()
  1009  	}
  1010  
  1011  	if "" != p.HTTPSafe() {
  1012  		t.Log("Non empty string response for .HTTPSafe() method")
  1013  		t.Fail()
  1014  	}
  1015  
  1016  	err := txn.End()
  1017  	if nil != err {
  1018  		t.Error(err)
  1019  	}
  1020  
  1021  	app.ExpectMetrics(t, []internal.WantMetric{
  1022  		{Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
  1023  		{Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil},
  1024  		{Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
  1025  		{Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil},
  1026  	})
  1027  
  1028  }
  1029  
  1030  func TestCreateDistributedTraceBetterCatDisabled(t *testing.T) {
  1031  
  1032  	// when distributed tracing is disabled, CreateDistributedTracePayload
  1033  	// should return a value that indicates an empty payload. Examples of
  1034  	// this depend on language but may be nil/null/None or an empty payload
  1035  	// object.
  1036  
  1037  	app := testApp(distributedTracingReplyFields, enableOldCATDisableBetterCat, t)
  1038  	txn := app.StartTransaction("hello", nil, nil)
  1039  
  1040  	p := txn.CreateDistributedTracePayload()
  1041  
  1042  	// empty/shim payload objects return empty strings
  1043  	if "" != p.Text() {
  1044  		t.Log("Non empty string response for .Text() method")
  1045  		t.Fail()
  1046  	}
  1047  
  1048  	if "" != p.HTTPSafe() {
  1049  		t.Log("Non empty string response for .HTTPSafe() method")
  1050  		t.Fail()
  1051  	}
  1052  
  1053  	err := txn.End()
  1054  	if nil != err {
  1055  		t.Error(err)
  1056  	}
  1057  
  1058  	app.ExpectMetrics(t, []internal.WantMetric{
  1059  		{Name: "OtherTransaction/Go/hello", Scope: "", Forced: true, Data: nil},
  1060  		{Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil},
  1061  		{Name: "OtherTransactionTotalTime/Go/hello", Scope: "", Forced: false, Data: nil},
  1062  		{Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil},
  1063  	})
  1064  
  1065  }
  1066  
  1067  func TestCreateDistributedTraceBetterCatEnabled(t *testing.T) {
  1068  
  1069  	// When distributed tracing is enabled and the application is connected,
  1070  	// CreateDistributedTracePayload should return a valid payload object
  1071  
  1072  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
  1073  	txn := app.StartTransaction("hello", nil, nil)
  1074  
  1075  	p := txn.CreateDistributedTracePayload()
  1076  
  1077  	// empty/shim payload objects return empty strings
  1078  	if "" == p.Text() {
  1079  		t.Log("Empty string response for .Text() method")
  1080  		t.Fail()
  1081  	}
  1082  
  1083  	if "" == p.HTTPSafe() {
  1084  		t.Log("Empty string response for .HTTPSafe() method")
  1085  		t.Fail()
  1086  	}
  1087  
  1088  	err := txn.End()
  1089  	if nil != err {
  1090  		t.Error(err)
  1091  	}
  1092  
  1093  	app.ExpectMetrics(t, append([]internal.WantMetric{
  1094  		{Name: "Supportability/DistributedTrace/CreatePayload/Success", Scope: "", Forced: true, Data: nil},
  1095  	}, backgroundUnknownCaller...))
  1096  }
  1097  
  1098  func isZeroValue(x interface{}) bool {
  1099  	// https://stackoverflow.com/questions/13901819/quick-way-to-detect-empty-values-via-reflection-in-go
  1100  	return nil == x || x == reflect.Zero(reflect.TypeOf(x)).Interface()
  1101  }
  1102  
  1103  func testPayloadFieldsPresent(t *testing.T, p DistributedTracePayload, keys ...string) {
  1104  	out := struct {
  1105  		Version []int                  `json:"v"`
  1106  		Data    map[string]interface{} `json:"d"`
  1107  	}{}
  1108  	if err := json.Unmarshal([]byte(p.Text()), &out); nil != err {
  1109  		t.Fatal("unable to unmarshal payload Text", err)
  1110  	}
  1111  	for _, key := range keys {
  1112  		val, ok := out.Data[key]
  1113  		if !ok {
  1114  			t.Fatal("required key missing", key)
  1115  		}
  1116  		if isZeroValue(val) {
  1117  			t.Fatal("value has default value", key, val)
  1118  		}
  1119  	}
  1120  }
  1121  
  1122  func TestCreateDistributedTraceRequiredFields(t *testing.T) {
  1123  
  1124  	// creates a distributed trace payload and then checks
  1125  	// to ensure the required fields are in place
  1126  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
  1127  	txn := app.StartTransaction("hello", nil, nil)
  1128  
  1129  	p := txn.CreateDistributedTracePayload()
  1130  
  1131  	testPayloadFieldsPresent(t, p, "ty", "ac", "ap", "tr", "ti")
  1132  
  1133  	err := txn.End()
  1134  	if nil != err {
  1135  		t.Error(err)
  1136  	}
  1137  
  1138  	app.ExpectMetrics(t, append([]internal.WantMetric{
  1139  		{Name: "Supportability/DistributedTrace/CreatePayload/Success", Scope: "", Forced: true, Data: nil},
  1140  	}, backgroundUnknownCaller...))
  1141  }
  1142  
  1143  func TestCreateDistributedTraceTrustKeyAbsent(t *testing.T) {
  1144  
  1145  	// creates a distributed trace payload and then checks
  1146  	// to ensure the required fields are in place
  1147  	var payloadData PayloadTest
  1148  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
  1149  	txn := app.StartTransaction("hello", nil, nil)
  1150  
  1151  	p := txn.CreateDistributedTracePayload()
  1152  
  1153  	if err := json.Unmarshal([]byte(p.Text()), &payloadData); nil != err {
  1154  		t.Log("Could not marshall payload into test struct")
  1155  		t.Error(err)
  1156  	}
  1157  
  1158  	if nil != payloadData.D["tk"] {
  1159  		t.Log("Did not expect trust key (tk) to be there")
  1160  		t.Log(p.Text())
  1161  		t.Fail()
  1162  	}
  1163  
  1164  	err := txn.End()
  1165  	if nil != err {
  1166  		t.Error(err)
  1167  	}
  1168  
  1169  	app.ExpectMetrics(t, append([]internal.WantMetric{
  1170  		{Name: "Supportability/DistributedTrace/CreatePayload/Success", Scope: "", Forced: true, Data: nil},
  1171  	}, backgroundUnknownCaller...))
  1172  }
  1173  
  1174  func TestCreateDistributedTraceTrustKeyNeeded(t *testing.T) {
  1175  
  1176  	// creates a distributed trace payload and then checks
  1177  	// to ensure the required fields are in place
  1178  	var payloadData PayloadTest
  1179  	app := testApp(distributedTracingReplyFieldsNeedTrustKey, enableBetterCAT, t)
  1180  	txn := app.StartTransaction("hello", nil, nil)
  1181  
  1182  	p := txn.CreateDistributedTracePayload()
  1183  
  1184  	if err := json.Unmarshal([]byte(p.Text()), &payloadData); nil != err {
  1185  		t.Log("Could not marshall payload into test struct")
  1186  		t.Error(err)
  1187  	}
  1188  
  1189  	testPayloadFieldsPresent(t, p, "tk")
  1190  
  1191  	err := txn.End()
  1192  	if nil != err {
  1193  		t.Error(err)
  1194  	}
  1195  
  1196  	app.ExpectMetrics(t, append([]internal.WantMetric{
  1197  		{Name: "Supportability/DistributedTrace/CreatePayload/Success", Scope: "", Forced: true, Data: nil},
  1198  	}, backgroundUnknownCaller...))
  1199  }
  1200  
  1201  func TestCreateDistributedTraceAfterAcceptSampledTrue(t *testing.T) {
  1202  
  1203  	// simulates 1. reading distributed trace payload from non-header external storage
  1204  	// (for queues, other customer integrations); 2. Accpeting that Payload; 3. Creating
  1205  	// a new payload
  1206  
  1207  	// tests that the required fields, plus priority and sampled are set
  1208  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
  1209  
  1210  	// fixture has a "tk" of 123, which matches the trusted_account_key
  1211  	// from distributedTracingReplyFields.
  1212  	p := `{
  1213  	"v":[0,1],
  1214  	"d":{
  1215  		"ty":"App",
  1216  		"ap":"456",
  1217  		"ac":"321",
  1218  		"id":"id",
  1219  		"tr":"traceID",
  1220  		"ti":1488325987402,
  1221  		"tk":"123",
  1222  		"sa":true
  1223  	}
  1224  }`
  1225  	txn := app.StartTransaction("hello", nil, nil)
  1226  	err := txn.AcceptDistributedTracePayload(TransportHTTP, p)
  1227  	if nil != err {
  1228  		t.Error(err)
  1229  	}
  1230  
  1231  	payload := txn.CreateDistributedTracePayload()
  1232  
  1233  	testPayloadFieldsPresent(t, payload,
  1234  		"ty", "ac", "ap", "tr", "ti", "pr", "sa")
  1235  
  1236  	err = txn.End()
  1237  	if nil != err {
  1238  		t.Error(err)
  1239  	}
  1240  }
  1241  
  1242  func TestCreateDistributedTraceAfterAcceptSampledNotSet(t *testing.T) {
  1243  
  1244  	// simulates 1. reading distributed trace payload from non-header external storage
  1245  	// (for queues, other customer integrations); 2. Accpeting that Payload; 3. Creating
  1246  	// a new payload
  1247  
  1248  	// tests that the required fields, plus priority and sampled are set.  When "sa"
  1249  	// is not set, the payload should pickup on sampled value of the transaction
  1250  	app := testApp(distributedTracingReplyFields, enableBetterCAT, t)
  1251  
  1252  	// fixture has a "tk" of 123, which matches the trusted_account_key
  1253  	// from distributedTracingReplyFields.
  1254  	p := `{
  1255  	"v":[0,1],
  1256  	"d":{
  1257  		"ty":"App",
  1258  		"ap":"456",
  1259  		"ac":"321",
  1260  		"id":"id",
  1261  		"tr":"traceID",
  1262  		"ti":1488325987402,
  1263  		"tk":"123",
  1264  		"pr":0.54343
  1265  	}
  1266  }`
  1267  	txn := app.StartTransaction("hello", nil, nil)
  1268  	err := txn.AcceptDistributedTracePayload(TransportHTTP, p)
  1269  	if nil != err {
  1270  		t.Error(err)
  1271  	}
  1272  
  1273  	payload := txn.CreateDistributedTracePayload()
  1274  	testPayloadFieldsPresent(t, payload,
  1275  		"ty", "ac", "ap", "id", "tr", "ti", "pr", "sa")
  1276  
  1277  	err = txn.End()
  1278  	if nil != err {
  1279  		t.Error(err)
  1280  	}
  1281  }
  1282  
  1283  type fieldExpectations struct {
  1284  	Exact      map[string]interface{} `json:"exact,omitempty"`
  1285  	Expected   []string               `json:"expected,omitempty"`
  1286  	Unexpected []string               `json:"unexpected,omitempty"`
  1287  }
  1288  
  1289  type distributedTraceTestcase struct {
  1290  	TestName          string            `json:"test_name"`
  1291  	Comment           string            `json:"comment,omitempty"`
  1292  	TrustedAccountKey string            `json:"trusted_account_key"`
  1293  	AccountID         string            `json:"account_id"`
  1294  	WebTransaction    bool              `json:"web_transaction"`
  1295  	RaisesException   bool              `json:"raises_exception"`
  1296  	ForceSampledTrue  bool              `json:"force_sampled_true"`
  1297  	SpanEventsEnabled bool              `json:"span_events_enabled"`
  1298  	MajorVersion      int               `json:"major_version"`
  1299  	MinorVersion      int               `json:"minor_version"`
  1300  	TransportType     string            `json:"transport_type"`
  1301  	InboundPayloads   []json.RawMessage `json:"inbound_payloads"`
  1302  
  1303  	OutboundPayloads []fieldExpectations `json:"outbound_payloads,omitempty"`
  1304  
  1305  	Intrinsics struct {
  1306  		TargetEvents     []string           `json:"target_events"`
  1307  		Common           *fieldExpectations `json:"common,omitempty"`
  1308  		Transaction      *fieldExpectations `json:"Transaction,omitempty"`
  1309  		Span             *fieldExpectations `json:"Span,omitempty"`
  1310  		TransactionError *fieldExpectations `json:"TransactionError,omitempty"`
  1311  	} `json:"intrinsics"`
  1312  
  1313  	ExpectedMetrics [][2]interface{} `json:"expected_metrics"`
  1314  }
  1315  
  1316  func (fe *fieldExpectations) add(intrinsics map[string]interface{}) {
  1317  	if nil != fe {
  1318  		for k, v := range fe.Exact {
  1319  			intrinsics[k] = v
  1320  		}
  1321  		for _, v := range fe.Expected {
  1322  			intrinsics[v] = internal.MatchAnything
  1323  		}
  1324  	}
  1325  }
  1326  
  1327  func (fe *fieldExpectations) unexpected() []string {
  1328  	if nil != fe {
  1329  		return fe.Unexpected
  1330  	}
  1331  	return nil
  1332  }
  1333  
  1334  // getTransport ensures that our transport names match cross agent test values.
  1335  func getTransport(transport string) TransportType {
  1336  	switch transport {
  1337  	case TransportHTTP.name:
  1338  		return TransportHTTP
  1339  	case TransportHTTPS.name:
  1340  		return TransportHTTPS
  1341  	case TransportKafka.name:
  1342  		return TransportKafka
  1343  	case TransportJMS.name:
  1344  		return TransportJMS
  1345  	case TransportIronMQ.name:
  1346  		return TransportIronMQ
  1347  	case TransportAMQP.name:
  1348  		return TransportAMQP
  1349  	case TransportQueue.name:
  1350  		return TransportQueue
  1351  	case TransportOther.name:
  1352  		return TransportOther
  1353  	default:
  1354  		return TransportUnknown
  1355  	}
  1356  }
  1357  
  1358  func runDistributedTraceCrossAgentTestcase(tst *testing.T, tc distributedTraceTestcase, extraAsserts func(expectApp, internal.Validator)) {
  1359  	t := internal.ExtendValidator(tst, "test="+tc.TestName)
  1360  	configCallback := enableBetterCAT
  1361  	if false == tc.SpanEventsEnabled {
  1362  		configCallback = disableSpanEvents
  1363  	}
  1364  
  1365  	app := testApp(func(reply *internal.ConnectReply) {
  1366  		reply.AccountID = tc.AccountID
  1367  		reply.AppID = "456"
  1368  		reply.PrimaryAppID = "456"
  1369  		reply.TrustedAccountKey = tc.TrustedAccountKey
  1370  
  1371  		// if cross agent tests ever include logic for sampling
  1372  		// we'll need to revisit this testing sampler
  1373  		reply.AdaptiveSampler = internal.SampleEverything{}
  1374  
  1375  	}, configCallback, tst)
  1376  
  1377  	txn := app.StartTransaction("hello", nil, nil)
  1378  	if tc.WebTransaction {
  1379  		txn.SetWebRequest(nil)
  1380  	}
  1381  
  1382  	// If the tests wants us to have an error, give 'em an error
  1383  	if tc.RaisesException {
  1384  		txn.NoticeError(errors.New("my error message"))
  1385  	}
  1386  
  1387  	// If there are no inbound payloads, invoke Accept on an empty inbound payload.
  1388  	if nil == tc.InboundPayloads {
  1389  		txn.AcceptDistributedTracePayload(getTransport(tc.TransportType), nil)
  1390  	}
  1391  
  1392  	for _, value := range tc.InboundPayloads {
  1393  		// Note that the error return value is not tested here because
  1394  		// some of the tests are intentionally errors.
  1395  		txn.AcceptDistributedTracePayload(getTransport(tc.TransportType), string(value))
  1396  	}
  1397  
  1398  	//call create each time an outbound payload appears in the testcase
  1399  	for _, expect := range tc.OutboundPayloads {
  1400  		actual := txn.CreateDistributedTracePayload().Text()
  1401  		assertTestCaseOutboundPayload(expect, t, actual)
  1402  	}
  1403  
  1404  	err := txn.End()
  1405  	if nil != err {
  1406  		t.Error(err)
  1407  	}
  1408  
  1409  	// create WantMetrics and assert
  1410  	wantMetrics := []internal.WantMetric{}
  1411  	for _, metric := range tc.ExpectedMetrics {
  1412  		wantMetrics = append(wantMetrics,
  1413  			internal.WantMetric{Name: metric[0].(string), Scope: "", Forced: nil, Data: nil})
  1414  	}
  1415  	app.ExpectMetricsPresent(t, wantMetrics)
  1416  
  1417  	// Add extra fields that are not listed in the JSON file so that we can
  1418  	// always do exact intrinsic set match.
  1419  
  1420  	extraTxnFields := &fieldExpectations{Expected: []string{"name"}}
  1421  	if tc.WebTransaction {
  1422  		extraTxnFields.Expected = append(extraTxnFields.Expected, "nr.apdexPerfZone")
  1423  	}
  1424  
  1425  	extraSpanFields := &fieldExpectations{
  1426  		Expected: []string{"name", "category", "nr.entryPoint"},
  1427  	}
  1428  
  1429  	// There is a single test with an error (named "exception"), so these
  1430  	// error expectations can be hard coded. TODO: Move some of these.
  1431  	// fields into the cross agent tests.
  1432  	extraErrorFields := &fieldExpectations{
  1433  		Expected: []string{"parent.type", "parent.account", "parent.app",
  1434  			"parent.transportType", "error.message", "transactionName",
  1435  			"parent.transportDuration", "error.class"},
  1436  	}
  1437  
  1438  	for _, value := range tc.Intrinsics.TargetEvents {
  1439  		switch value {
  1440  		case "Transaction":
  1441  			assertTestCaseIntrinsics(t,
  1442  				app.ExpectTxnEvents,
  1443  				tc.Intrinsics.Common,
  1444  				tc.Intrinsics.Transaction,
  1445  				extraTxnFields)
  1446  		case "Span":
  1447  			assertTestCaseIntrinsics(t,
  1448  				app.ExpectSpanEvents,
  1449  				tc.Intrinsics.Common,
  1450  				tc.Intrinsics.Span,
  1451  				extraSpanFields)
  1452  
  1453  		case "TransactionError":
  1454  			assertTestCaseIntrinsics(t,
  1455  				app.ExpectErrorEvents,
  1456  				tc.Intrinsics.Common,
  1457  				tc.Intrinsics.TransactionError,
  1458  				extraErrorFields)
  1459  		}
  1460  	}
  1461  
  1462  	extraAsserts(app, t)
  1463  }
  1464  
  1465  func assertTestCaseOutboundPayload(expect fieldExpectations, t internal.Validator, actual string) {
  1466  	type outboundTestcase struct {
  1467  		Version [2]uint                `json:"v"`
  1468  		Data    map[string]interface{} `json:"d"`
  1469  	}
  1470  	var actualPayload outboundTestcase
  1471  	err := json.Unmarshal([]byte(actual), &actualPayload)
  1472  	if nil != err {
  1473  		t.Error(err)
  1474  	}
  1475  	// Affirm that the exact values are in the payload.
  1476  	for k, v := range expect.Exact {
  1477  		if k != "v" {
  1478  			field := strings.Split(k, ".")[1]
  1479  			if v != actualPayload.Data[field] {
  1480  				t.Error(fmt.Sprintf("exact outbound payload field mismatch key=%s wanted=%v got=%v",
  1481  					k, v, actualPayload.Data[field]))
  1482  			}
  1483  		}
  1484  	}
  1485  	// Affirm that the expected values are in the actual payload.
  1486  	for _, e := range expect.Expected {
  1487  		field := strings.Split(e, ".")[1]
  1488  		if nil == actualPayload.Data[field] {
  1489  			t.Error(fmt.Sprintf("expected outbound payload field missing key=%s", e))
  1490  		}
  1491  	}
  1492  	// Affirm that the unexpected values are not in the actual payload.
  1493  	for _, u := range expect.Unexpected {
  1494  		field := strings.Split(u, ".")[1]
  1495  		if nil != actualPayload.Data[field] {
  1496  			t.Error(fmt.Sprintf("unexpected outbound payload field present key=%s", u))
  1497  		}
  1498  	}
  1499  }
  1500  
  1501  func assertTestCaseIntrinsics(t internal.Validator,
  1502  	expect func(internal.Validator, []internal.WantEvent),
  1503  	fields ...*fieldExpectations) {
  1504  
  1505  	intrinsics := map[string]interface{}{}
  1506  	for _, f := range fields {
  1507  		f.add(intrinsics)
  1508  	}
  1509  	expect(t, []internal.WantEvent{{Intrinsics: intrinsics}})
  1510  }
  1511  
  1512  func TestDistributedTraceCrossAgent(t *testing.T) {
  1513  	var tcs []distributedTraceTestcase
  1514  	data, err := crossagent.ReadFile(`distributed_tracing/distributed_tracing.json`)
  1515  	if nil != err {
  1516  		t.Fatal(err)
  1517  	}
  1518  	if err := json.Unmarshal(data, &tcs); nil != err {
  1519  		t.Fatal(err)
  1520  	}
  1521  	// Test that we are correctly parsing all of the testcase fields by
  1522  	// comparing an opaque object from original JSON to an object from JSON
  1523  	// created by our testcases.
  1524  	backToJSON, err := json.Marshal(tcs)
  1525  	if nil != err {
  1526  		t.Fatal(err)
  1527  	}
  1528  	var fromFile []map[string]interface{}
  1529  	var fromMarshalled []map[string]interface{}
  1530  	if err := json.Unmarshal(data, &fromFile); nil != err {
  1531  		t.Fatal(err)
  1532  	}
  1533  	if err := json.Unmarshal(backToJSON, &fromMarshalled); nil != err {
  1534  		t.Fatal(err)
  1535  	}
  1536  	if !reflect.DeepEqual(fromFile, fromMarshalled) {
  1537  		t.Error(internal.CompactJSONString(string(data)), "\n",
  1538  			internal.CompactJSONString(string(backToJSON)))
  1539  	}
  1540  
  1541  	// Iterate over all cross-agent tests
  1542  	for _, tc := range tcs {
  1543  		extraAsserts := func(app expectApp, t internal.Validator) {}
  1544  		if "spans_disabled_in_child" == tc.TestName {
  1545  			// if span events are disabled but distributed tracing is enabled, then
  1546  			// we expect there are zero span events
  1547  			extraAsserts = func(app expectApp, t internal.Validator) {
  1548  				app.ExpectSpanEvents(t, nil)
  1549  			}
  1550  		}
  1551  		runDistributedTraceCrossAgentTestcase(t, tc, extraAsserts)
  1552  	}
  1553  }
  1554  
  1555  func TestDistributedTraceDisabledSpanEventsEnabled(t *testing.T) {
  1556  	app := testApp(distributedTracingReplyFields, disableDistributedTracerEnableSpanEvents, t)
  1557  	payload := makePayload(app, nil)
  1558  	txn := app.StartTransaction("hello", nil, nil)
  1559  	err := txn.AcceptDistributedTracePayload(TransportHTTP, payload)
  1560  	if err != errInboundPayloadDTDisabled {
  1561  		t.Fatal("we expected an error with DT disabled", err)
  1562  	}
  1563  	err = txn.End()
  1564  	if nil != err {
  1565  		t.Error(err)
  1566  	}
  1567  
  1568  	// ensure no span events created
  1569  	app.ExpectSpanEvents(t, nil)
  1570  }
  1571  
  1572  func TestCreatePayloadAppNotConnected(t *testing.T) {
  1573  	// Test that an app which isn't connected does not create distributed
  1574  	// trace payloads.
  1575  	app := testApp(nil, enableBetterCAT, t)
  1576  	txn := app.StartTransaction("hello", nil, nil)
  1577  	payload := txn.CreateDistributedTracePayload()
  1578  	if payload.Text() != "" || payload.HTTPSafe() != "" {
  1579  		t.Error(payload.Text(), payload.HTTPSafe())
  1580  	}
  1581  }
  1582  func TestCreatePayloadReplyMissingTrustKey(t *testing.T) {
  1583  	// Test that an app whose reply is missing the trust key does not create
  1584  	// distributed trace payloads.
  1585  	app := testApp(func(reply *internal.ConnectReply) {
  1586  		distributedTracingReplyFields(reply)
  1587  		reply.TrustedAccountKey = ""
  1588  	}, enableBetterCAT, t)
  1589  	txn := app.StartTransaction("hello", nil, nil)
  1590  	payload := txn.CreateDistributedTracePayload()
  1591  	if payload.Text() != "" || payload.HTTPSafe() != "" {
  1592  		t.Error(payload.Text(), payload.HTTPSafe())
  1593  	}
  1594  }
  1595  
  1596  func TestAcceptPayloadAppNotConnected(t *testing.T) {
  1597  	// Test that an app which isn't connected does not accept distributed
  1598  	// trace payloads.
  1599  	app := testApp(nil, enableBetterCAT, t)
  1600  	payload := testApp(distributedTracingReplyFields, enableBetterCAT, t).
  1601  		StartTransaction("name", nil, nil).
  1602  		CreateDistributedTracePayload()
  1603  	if payload.Text() == "" {
  1604  		t.Fatal(payload)
  1605  	}
  1606  	txn := app.StartTransaction("hello", nil, nil)
  1607  	err := txn.AcceptDistributedTracePayload(TransportHTTP, payload)
  1608  	if nil != err {
  1609  		t.Error(err)
  1610  	}
  1611  	txn.End()
  1612  	app.ExpectMetrics(t, backgroundUnknownCaller)
  1613  }
  1614  
  1615  func TestAcceptPayloadReplyMissingTrustKey(t *testing.T) {
  1616  	// Test that an app whose reply is missing a trust key does not accept
  1617  	// distributed trace payloads.
  1618  	app := testApp(func(reply *internal.ConnectReply) {
  1619  		distributedTracingReplyFields(reply)
  1620  		reply.TrustedAccountKey = ""
  1621  	}, enableBetterCAT, t)
  1622  	payload := testApp(distributedTracingReplyFields, enableBetterCAT, t).
  1623  		StartTransaction("name", nil, nil).
  1624  		CreateDistributedTracePayload()
  1625  	if payload.Text() == "" {
  1626  		t.Fatal(payload)
  1627  	}
  1628  	txn := app.StartTransaction("hello", nil, nil)
  1629  	err := txn.AcceptDistributedTracePayload(TransportHTTP, payload)
  1630  	if nil != err {
  1631  		t.Error(err)
  1632  	}
  1633  	txn.End()
  1634  	app.ExpectMetrics(t, backgroundUnknownCaller)
  1635  }