github.com/lulzWill/go-agent@v2.1.2+incompatible/internal/expect.go (about)

     1  package internal
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"runtime"
     7  )
     8  
     9  var (
    10  	// Unfortunately, the resolution of time.Now() on Windows is coarse: Two
    11  	// sequential calls to time.Now() may return the same value, and tests
    12  	// which expect non-zero durations may fail.  To avoid adding sleep
    13  	// statements or mocking time.Now(), those tests are skipped on Windows.
    14  	doDurationTests = runtime.GOOS != `windows`
    15  )
    16  
    17  // Validator is used for testing.
    18  type Validator interface {
    19  	Error(...interface{})
    20  }
    21  
    22  func validateStringField(v Validator, fieldName, v1, v2 string) {
    23  	if v1 != v2 {
    24  		v.Error(fieldName, v1, v2)
    25  	}
    26  }
    27  
    28  type addValidatorField struct {
    29  	field    interface{}
    30  	original Validator
    31  }
    32  
    33  func (a addValidatorField) Error(fields ...interface{}) {
    34  	fields = append([]interface{}{a.field}, fields...)
    35  	a.original.Error(fields...)
    36  }
    37  
    38  // ExtendValidator is used to add more context to a validator.
    39  func ExtendValidator(v Validator, field interface{}) Validator {
    40  	return addValidatorField{
    41  		field:    field,
    42  		original: v,
    43  	}
    44  }
    45  
    46  // WantMetric is a metric expectation.  If Data is nil, then any data values are
    47  // acceptable.
    48  type WantMetric struct {
    49  	Name   string
    50  	Scope  string
    51  	Forced interface{} // true, false, or nil
    52  	Data   []float64
    53  }
    54  
    55  // WantError is a traced error expectation.
    56  type WantError struct {
    57  	TxnName         string
    58  	Msg             string
    59  	Klass           string
    60  	Caller          string
    61  	URL             string
    62  	UserAttributes  map[string]interface{}
    63  	AgentAttributes map[string]interface{}
    64  }
    65  
    66  func uniquePointer() *struct{} {
    67  	s := struct{}{}
    68  	return &s
    69  }
    70  
    71  var (
    72  	// MatchAnything is for use when matching attributes.
    73  	MatchAnything = uniquePointer()
    74  )
    75  
    76  // WantEvent is a transaction or error event expectation.
    77  type WantEvent struct {
    78  	Intrinsics      map[string]interface{}
    79  	UserAttributes  map[string]interface{}
    80  	AgentAttributes map[string]interface{}
    81  }
    82  
    83  // WantTxnTrace is a transaction trace expectation.
    84  type WantTxnTrace struct {
    85  	MetricName      string
    86  	CleanURL        string
    87  	NumSegments     int
    88  	UserAttributes  map[string]interface{}
    89  	AgentAttributes map[string]interface{}
    90  }
    91  
    92  // WantSlowQuery is a slowQuery expectation.
    93  type WantSlowQuery struct {
    94  	Count        int32
    95  	MetricName   string
    96  	Query        string
    97  	TxnName      string
    98  	TxnURL       string
    99  	DatabaseName string
   100  	Host         string
   101  	PortPathOrID string
   102  	Params       map[string]interface{}
   103  }
   104  
   105  // Expect exposes methods that allow for testing whether the correct data was
   106  // captured.
   107  type Expect interface {
   108  	ExpectCustomEvents(t Validator, want []WantEvent)
   109  	ExpectErrors(t Validator, want []WantError)
   110  	ExpectErrorEvents(t Validator, want []WantEvent)
   111  	ExpectTxnEvents(t Validator, want []WantEvent)
   112  	ExpectMetrics(t Validator, want []WantMetric)
   113  	ExpectTxnTraces(t Validator, want []WantTxnTrace)
   114  	ExpectSlowQueries(t Validator, want []WantSlowQuery)
   115  }
   116  
   117  func expectMetricField(t Validator, id metricID, v1, v2 float64, fieldName string) {
   118  	if v1 != v2 {
   119  		t.Error("metric fields do not match", id, v1, v2, fieldName)
   120  	}
   121  }
   122  
   123  // ExpectMetrics allows testing of metrics.
   124  func ExpectMetrics(t Validator, mt *metricTable, expect []WantMetric) {
   125  	if len(mt.metrics) != len(expect) {
   126  		t.Error("metric counts do not match expectations", len(mt.metrics), len(expect))
   127  	}
   128  	expectedIds := make(map[metricID]struct{})
   129  	for _, e := range expect {
   130  		id := metricID{Name: e.Name, Scope: e.Scope}
   131  		expectedIds[id] = struct{}{}
   132  		m := mt.metrics[id]
   133  		if nil == m {
   134  			t.Error("unable to find metric", id)
   135  			continue
   136  		}
   137  
   138  		if b, ok := e.Forced.(bool); ok {
   139  			if b != (forced == m.forced) {
   140  				t.Error("metric forced incorrect", b, m.forced, id)
   141  			}
   142  		}
   143  
   144  		if nil != e.Data {
   145  			expectMetricField(t, id, e.Data[0], m.data.countSatisfied, "countSatisfied")
   146  			expectMetricField(t, id, e.Data[1], m.data.totalTolerated, "totalTolerated")
   147  			expectMetricField(t, id, e.Data[2], m.data.exclusiveFailed, "exclusiveFailed")
   148  			expectMetricField(t, id, e.Data[3], m.data.min, "min")
   149  			expectMetricField(t, id, e.Data[4], m.data.max, "max")
   150  			expectMetricField(t, id, e.Data[5], m.data.sumSquares, "sumSquares")
   151  		}
   152  	}
   153  	for id := range mt.metrics {
   154  		if _, ok := expectedIds[id]; !ok {
   155  			t.Error("expected metrics does not contain", id.Name, id.Scope)
   156  		}
   157  	}
   158  }
   159  
   160  func expectAttributes(v Validator, exists map[string]interface{}, expect map[string]interface{}) {
   161  	// TODO: This params comparison can be made smarter: Alert differences
   162  	// based on sub/super set behavior.
   163  	if len(exists) != len(expect) {
   164  		v.Error("attributes length difference", len(exists), len(expect))
   165  	}
   166  	for key, val := range expect {
   167  		found, ok := exists[key]
   168  		if !ok {
   169  			v.Error("expected attribute not found: ", key)
   170  			continue
   171  		}
   172  		if val == MatchAnything {
   173  			continue
   174  		}
   175  		v1 := fmt.Sprint(found)
   176  		v2 := fmt.Sprint(val)
   177  		if v1 != v2 {
   178  			v.Error("value difference", fmt.Sprintf("key=%s", key), v1, v2)
   179  		}
   180  	}
   181  	for key, val := range exists {
   182  		_, ok := expect[key]
   183  		if !ok {
   184  			v.Error("unexpected attribute present: ", key, val)
   185  			continue
   186  		}
   187  	}
   188  }
   189  
   190  // ExpectCustomEvents allows testing of custom events.
   191  func ExpectCustomEvents(v Validator, cs *customEvents, expect []WantEvent) {
   192  	if len(cs.events.events) != len(expect) {
   193  		v.Error("number of custom events does not match", len(cs.events.events),
   194  			len(expect))
   195  		return
   196  	}
   197  	for i, e := range expect {
   198  		event, ok := cs.events.events[i].jsonWriter.(*CustomEvent)
   199  		if !ok {
   200  			v.Error("wrong custom event")
   201  		} else {
   202  			expectEvent(v, event, e)
   203  		}
   204  	}
   205  }
   206  
   207  func expectEvent(v Validator, e json.Marshaler, expect WantEvent) {
   208  	js, err := e.MarshalJSON()
   209  	if nil != err {
   210  		v.Error("unable to marshal event", err)
   211  		return
   212  	}
   213  	var event []map[string]interface{}
   214  	err = json.Unmarshal(js, &event)
   215  	if nil != err {
   216  		v.Error("unable to parse event json", err)
   217  		return
   218  	}
   219  	intrinsics := event[0]
   220  	userAttributes := event[1]
   221  	agentAttributes := event[2]
   222  
   223  	if nil != expect.Intrinsics {
   224  		expectAttributes(v, intrinsics, expect.Intrinsics)
   225  	}
   226  	if nil != expect.UserAttributes {
   227  		expectAttributes(v, userAttributes, expect.UserAttributes)
   228  	}
   229  	if nil != expect.AgentAttributes {
   230  		expectAttributes(v, agentAttributes, expect.AgentAttributes)
   231  	}
   232  }
   233  
   234  // Second attributes have priority.
   235  func mergeAttributes(a1, a2 map[string]interface{}) map[string]interface{} {
   236  	a := make(map[string]interface{})
   237  	for k, v := range a1 {
   238  		a[k] = v
   239  	}
   240  	for k, v := range a2 {
   241  		a[k] = v
   242  	}
   243  	return a
   244  }
   245  
   246  // ExpectErrorEvents allows testing of error events.
   247  func ExpectErrorEvents(v Validator, events *errorEvents, expect []WantEvent) {
   248  	if len(events.events.events) != len(expect) {
   249  		v.Error("number of custom events does not match",
   250  			len(events.events.events), len(expect))
   251  		return
   252  	}
   253  	for i, e := range expect {
   254  		event, ok := events.events.events[i].jsonWriter.(*ErrorEvent)
   255  		if !ok {
   256  			v.Error("wrong error event")
   257  		} else {
   258  			if nil != e.Intrinsics {
   259  				e.Intrinsics = mergeAttributes(map[string]interface{}{
   260  					// The following intrinsics should always be present in
   261  					// error events:
   262  					"type":      "TransactionError",
   263  					"timestamp": MatchAnything,
   264  					"duration":  MatchAnything,
   265  				}, e.Intrinsics)
   266  			}
   267  			expectEvent(v, event, e)
   268  		}
   269  	}
   270  }
   271  
   272  // ExpectTxnEvents allows testing of txn events.
   273  func ExpectTxnEvents(v Validator, events *txnEvents, expect []WantEvent) {
   274  	if len(events.events.events) != len(expect) {
   275  		v.Error("number of txn events does not match",
   276  			len(events.events.events), len(expect))
   277  		return
   278  	}
   279  	for i, e := range expect {
   280  		event, ok := events.events.events[i].jsonWriter.(*TxnEvent)
   281  		if !ok {
   282  			v.Error("wrong txn event")
   283  		} else {
   284  			if nil != e.Intrinsics {
   285  				e.Intrinsics = mergeAttributes(map[string]interface{}{
   286  					// The following intrinsics should always be present in
   287  					// txn events:
   288  					"type":      "Transaction",
   289  					"timestamp": MatchAnything,
   290  					"duration":  MatchAnything,
   291  				}, e.Intrinsics)
   292  			}
   293  			expectEvent(v, event, e)
   294  		}
   295  	}
   296  }
   297  
   298  func expectError(v Validator, err *tracedError, expect WantError) {
   299  	caller := topCallerNameBase(err.ErrorData.Stack)
   300  	validateStringField(v, "caller", expect.Caller, caller)
   301  	validateStringField(v, "txnName", expect.TxnName, err.FinalName)
   302  	validateStringField(v, "klass", expect.Klass, err.Klass)
   303  	validateStringField(v, "msg", expect.Msg, err.Msg)
   304  	validateStringField(v, "URL", expect.URL, err.CleanURL)
   305  	js, errr := err.MarshalJSON()
   306  	if nil != errr {
   307  		v.Error("unable to marshal error json", errr)
   308  		return
   309  	}
   310  	var unmarshalled []interface{}
   311  	errr = json.Unmarshal(js, &unmarshalled)
   312  	if nil != errr {
   313  		v.Error("unable to unmarshal error json", errr)
   314  		return
   315  	}
   316  	attributes := unmarshalled[4].(map[string]interface{})
   317  	agentAttributes := attributes["agentAttributes"].(map[string]interface{})
   318  	userAttributes := attributes["userAttributes"].(map[string]interface{})
   319  
   320  	if nil != expect.UserAttributes {
   321  		expectAttributes(v, userAttributes, expect.UserAttributes)
   322  	}
   323  	if nil != expect.AgentAttributes {
   324  		expectAttributes(v, agentAttributes, expect.AgentAttributes)
   325  	}
   326  }
   327  
   328  // ExpectErrors allows testing of errors.
   329  func ExpectErrors(v Validator, errors harvestErrors, expect []WantError) {
   330  	if len(errors) != len(expect) {
   331  		v.Error("number of errors mismatch", len(errors), len(expect))
   332  		return
   333  	}
   334  	for i, e := range expect {
   335  		expectError(v, errors[i], e)
   336  	}
   337  }
   338  
   339  func countSegments(node []interface{}) int {
   340  	count := 1
   341  	children := node[4].([]interface{})
   342  	for _, c := range children {
   343  		node := c.([]interface{})
   344  		count += countSegments(node)
   345  	}
   346  	return count
   347  }
   348  
   349  func expectTxnTrace(v Validator, got json.Marshaler, expect WantTxnTrace) {
   350  	js, err := got.MarshalJSON()
   351  	if nil != err {
   352  		v.Error("unable to marshal txn trace json", err)
   353  		return
   354  	}
   355  	var unmarshalled []interface{}
   356  	err = json.Unmarshal(js, &unmarshalled)
   357  	if nil != err {
   358  		v.Error("unable to unmarshal error json", err)
   359  		return
   360  	}
   361  	duration := unmarshalled[1].(float64)
   362  	name := unmarshalled[2].(string)
   363  	cleanURL := unmarshalled[3].(string)
   364  	traceData := unmarshalled[4].([]interface{})
   365  
   366  	rootNode := traceData[3].([]interface{})
   367  	attributes := traceData[4].(map[string]interface{})
   368  	userAttributes := attributes["userAttributes"].(map[string]interface{})
   369  	agentAttributes := attributes["agentAttributes"].(map[string]interface{})
   370  
   371  	validateStringField(v, "metric name", expect.MetricName, name)
   372  	validateStringField(v, "request url", expect.CleanURL, cleanURL)
   373  
   374  	if doDurationTests && 0 == duration {
   375  		v.Error("zero trace duration")
   376  	}
   377  
   378  	if nil != expect.UserAttributes {
   379  		expectAttributes(v, userAttributes, expect.UserAttributes)
   380  	}
   381  	if nil != expect.AgentAttributes {
   382  		expectAttributes(v, agentAttributes, expect.AgentAttributes)
   383  	}
   384  	numSegments := countSegments(rootNode)
   385  	// The expectation segment count does not include the two root nodes.
   386  	numSegments -= 2
   387  	if expect.NumSegments != numSegments {
   388  		v.Error("wrong number of segments", expect.NumSegments, numSegments)
   389  	}
   390  }
   391  
   392  // ExpectTxnTraces allows testing of transaction traces.
   393  func ExpectTxnTraces(v Validator, traces *harvestTraces, want []WantTxnTrace) {
   394  	if len(want) != traces.Len() {
   395  		v.Error("number of traces do not match", len(want), traces.Len())
   396  	}
   397  
   398  	actual := traces.slice()
   399  	for i, expected := range want {
   400  		expectTxnTrace(v, actual[i], expected)
   401  	}
   402  }
   403  
   404  func expectSlowQuery(t Validator, slowQuery *slowQuery, want WantSlowQuery) {
   405  	if slowQuery.Count != want.Count {
   406  		t.Error("wrong Count field", slowQuery.Count, want.Count)
   407  	}
   408  	validateStringField(t, "MetricName", slowQuery.DatastoreMetric, want.MetricName)
   409  	validateStringField(t, "Query", slowQuery.ParameterizedQuery, want.Query)
   410  	validateStringField(t, "TxnName", slowQuery.TxnName, want.TxnName)
   411  	validateStringField(t, "TxnURL", slowQuery.TxnURL, want.TxnURL)
   412  	validateStringField(t, "DatabaseName", slowQuery.DatabaseName, want.DatabaseName)
   413  	validateStringField(t, "Host", slowQuery.Host, want.Host)
   414  	validateStringField(t, "PortPathOrID", slowQuery.PortPathOrID, want.PortPathOrID)
   415  	expectAttributes(t, map[string]interface{}(slowQuery.QueryParameters), want.Params)
   416  }
   417  
   418  // ExpectSlowQueries allows testing of slow queries.
   419  func ExpectSlowQueries(t Validator, slowQueries *slowQueries, want []WantSlowQuery) {
   420  	if len(want) != len(slowQueries.priorityQueue) {
   421  		t.Error("wrong number of slow queries",
   422  			"expected", len(want), "got", len(slowQueries.priorityQueue))
   423  		return
   424  	}
   425  	for _, s := range want {
   426  		idx, ok := slowQueries.lookup[s.Query]
   427  		if !ok {
   428  			t.Error("unable to find slow query", s.Query)
   429  			continue
   430  		}
   431  		expectSlowQuery(t, slowQueries.priorityQueue[idx], s)
   432  	}
   433  }