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

     1  package internal
     2  
     3  import (
     4  	"net/url"
     5  	"strconv"
     6  	"strings"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/lulzWill/go-agent/internal/crossagent"
    11  )
    12  
    13  func TestStartEndSegment(t *testing.T) {
    14  	start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC)
    15  
    16  	tr := &TxnData{}
    17  	token := StartSegment(tr, start)
    18  	stop := start.Add(1 * time.Second)
    19  	end, err := endSegment(tr, token, stop)
    20  	if nil != err {
    21  		t.Error(err)
    22  	}
    23  	if end.exclusive != end.duration {
    24  		t.Error(end.exclusive, end.duration)
    25  	}
    26  	if end.duration != 1*time.Second {
    27  		t.Error(end.duration)
    28  	}
    29  	if end.start.Time != start {
    30  		t.Error(end.start, start)
    31  	}
    32  	if end.stop.Time != stop {
    33  		t.Error(end.stop, stop)
    34  	}
    35  }
    36  
    37  func TestMultipleChildren(t *testing.T) {
    38  	start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC)
    39  	tr := &TxnData{}
    40  
    41  	t1 := StartSegment(tr, start.Add(1*time.Second))
    42  	t2 := StartSegment(tr, start.Add(2*time.Second))
    43  	end2, err2 := endSegment(tr, t2, start.Add(3*time.Second))
    44  	t3 := StartSegment(tr, start.Add(4*time.Second))
    45  	end3, err3 := endSegment(tr, t3, start.Add(5*time.Second))
    46  	end1, err1 := endSegment(tr, t1, start.Add(6*time.Second))
    47  	t4 := StartSegment(tr, start.Add(7*time.Second))
    48  	end4, err4 := endSegment(tr, t4, start.Add(8*time.Second))
    49  
    50  	if nil != err1 || end1.duration != 5*time.Second || end1.exclusive != 3*time.Second {
    51  		t.Error(end1, err1)
    52  	}
    53  	if nil != err2 || end2.duration != end2.exclusive || end2.duration != time.Second {
    54  		t.Error(end2, err2)
    55  	}
    56  	if nil != err3 || end3.duration != end3.exclusive || end3.duration != time.Second {
    57  		t.Error(end3, err3)
    58  	}
    59  	if nil != err4 || end4.duration != end4.exclusive || end4.duration != time.Second {
    60  		t.Error(end4, err4)
    61  	}
    62  	children := TracerRootChildren(tr)
    63  	if children != 6*time.Second {
    64  		t.Error(children)
    65  	}
    66  }
    67  
    68  func TestInvalidStart(t *testing.T) {
    69  	start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC)
    70  	tr := &TxnData{}
    71  
    72  	end, err := endSegment(tr, SegmentStartTime{}, start.Add(1*time.Second))
    73  	if err != errMalformedSegment {
    74  		t.Error(end, err)
    75  	}
    76  	StartSegment(tr, start.Add(2*time.Second))
    77  	end, err = endSegment(tr, SegmentStartTime{}, start.Add(3*time.Second))
    78  	if err != errMalformedSegment {
    79  		t.Error(end, err)
    80  	}
    81  }
    82  
    83  func TestSegmentAlreadyEnded(t *testing.T) {
    84  	start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC)
    85  	tr := &TxnData{}
    86  
    87  	t1 := StartSegment(tr, start.Add(1*time.Second))
    88  	end, err := endSegment(tr, t1, start.Add(2*time.Second))
    89  	if err != nil {
    90  		t.Error(end, err)
    91  	}
    92  	end, err = endSegment(tr, t1, start.Add(3*time.Second))
    93  	if err != errSegmentOrder {
    94  		t.Error(end, err)
    95  	}
    96  }
    97  
    98  func TestSegmentBadStamp(t *testing.T) {
    99  	start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC)
   100  	tr := &TxnData{}
   101  
   102  	t1 := StartSegment(tr, start.Add(1*time.Second))
   103  	t1.Stamp++
   104  	end, err := endSegment(tr, t1, start.Add(2*time.Second))
   105  	if err != errSegmentOrder {
   106  		t.Error(end, err)
   107  	}
   108  }
   109  
   110  func TestSegmentBadDepth(t *testing.T) {
   111  	start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC)
   112  	tr := &TxnData{}
   113  
   114  	t1 := StartSegment(tr, start.Add(1*time.Second))
   115  	t1.Depth++
   116  	end, err := endSegment(tr, t1, start.Add(2*time.Second))
   117  	if err != errSegmentOrder {
   118  		t.Error(end, err)
   119  	}
   120  }
   121  
   122  func TestSegmentNegativeDepth(t *testing.T) {
   123  	start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC)
   124  	tr := &TxnData{}
   125  
   126  	t1 := StartSegment(tr, start.Add(1*time.Second))
   127  	t1.Depth = -1
   128  	end, err := endSegment(tr, t1, start.Add(2*time.Second))
   129  	if err != errMalformedSegment {
   130  		t.Error(end, err)
   131  	}
   132  }
   133  
   134  func TestSegmentOutOfOrder(t *testing.T) {
   135  	start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC)
   136  	tr := &TxnData{}
   137  
   138  	t1 := StartSegment(tr, start.Add(1*time.Second))
   139  	t2 := StartSegment(tr, start.Add(2*time.Second))
   140  	t3 := StartSegment(tr, start.Add(3*time.Second))
   141  	end2, err2 := endSegment(tr, t2, start.Add(4*time.Second))
   142  	end3, err3 := endSegment(tr, t3, start.Add(5*time.Second))
   143  	t4 := StartSegment(tr, start.Add(6*time.Second))
   144  	end4, err4 := endSegment(tr, t4, start.Add(7*time.Second))
   145  	end1, err1 := endSegment(tr, t1, start.Add(8*time.Second))
   146  
   147  	if nil != err1 ||
   148  		end1.duration != 7*time.Second ||
   149  		end1.exclusive != 4*time.Second {
   150  		t.Error(end1, err1)
   151  	}
   152  	if nil != err2 || end2.duration != end2.exclusive || end2.duration != 2*time.Second {
   153  		t.Error(end2, err2)
   154  	}
   155  	if err3 != errSegmentOrder {
   156  		t.Error(end3, err3)
   157  	}
   158  	if nil != err4 || end4.duration != end4.exclusive || end4.duration != 1*time.Second {
   159  		t.Error(end4, err4)
   160  	}
   161  }
   162  
   163  //                                          |-t3-|    |-t4-|
   164  //                           |-t2-|    |-never-finished----------
   165  //            |-t1-|    |--never-finished------------------------
   166  //       |-------alpha------------------------------------------|
   167  //  0    1    2    3    4    5    6    7    8    9    10   11   12
   168  func TestLostChildren(t *testing.T) {
   169  	start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC)
   170  	tr := &TxnData{}
   171  
   172  	alpha := StartSegment(tr, start.Add(1*time.Second))
   173  	t1 := StartSegment(tr, start.Add(2*time.Second))
   174  	EndBasicSegment(tr, t1, start.Add(3*time.Second), "t1")
   175  	StartSegment(tr, start.Add(4*time.Second))
   176  	t2 := StartSegment(tr, start.Add(5*time.Second))
   177  	EndBasicSegment(tr, t2, start.Add(6*time.Second), "t2")
   178  	StartSegment(tr, start.Add(7*time.Second))
   179  	t3 := StartSegment(tr, start.Add(8*time.Second))
   180  	EndBasicSegment(tr, t3, start.Add(9*time.Second), "t3")
   181  	t4 := StartSegment(tr, start.Add(10*time.Second))
   182  	EndBasicSegment(tr, t4, start.Add(11*time.Second), "t4")
   183  	EndBasicSegment(tr, alpha, start.Add(12*time.Second), "alpha")
   184  
   185  	metrics := newMetricTable(100, time.Now())
   186  	tr.FinalName = "WebTransaction/Go/zip"
   187  	tr.IsWeb = true
   188  	MergeBreakdownMetrics(tr, metrics)
   189  	ExpectMetrics(t, metrics, []WantMetric{
   190  		{"Custom/alpha", "", false, []float64{1, 11, 7, 11, 11, 121}},
   191  		{"Custom/t1", "", false, []float64{1, 1, 1, 1, 1, 1}},
   192  		{"Custom/t2", "", false, []float64{1, 1, 1, 1, 1, 1}},
   193  		{"Custom/t3", "", false, []float64{1, 1, 1, 1, 1, 1}},
   194  		{"Custom/t4", "", false, []float64{1, 1, 1, 1, 1, 1}},
   195  		{"Custom/alpha", tr.FinalName, false, []float64{1, 11, 7, 11, 11, 121}},
   196  		{"Custom/t1", tr.FinalName, false, []float64{1, 1, 1, 1, 1, 1}},
   197  		{"Custom/t2", tr.FinalName, false, []float64{1, 1, 1, 1, 1, 1}},
   198  		{"Custom/t3", tr.FinalName, false, []float64{1, 1, 1, 1, 1, 1}},
   199  		{"Custom/t4", tr.FinalName, false, []float64{1, 1, 1, 1, 1, 1}},
   200  	})
   201  }
   202  
   203  //                                          |-t3-|    |-t4-|
   204  //                           |-t2-|    |-never-finished----------
   205  //            |-t1-|    |--never-finished------------------------
   206  //  |-------root-------------------------------------------------
   207  //  0    1    2    3    4    5    6    7    8    9    10   11   12
   208  func TestLostChildrenRoot(t *testing.T) {
   209  	start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC)
   210  	tr := &TxnData{}
   211  
   212  	t1 := StartSegment(tr, start.Add(2*time.Second))
   213  	EndBasicSegment(tr, t1, start.Add(3*time.Second), "t1")
   214  	StartSegment(tr, start.Add(4*time.Second))
   215  	t2 := StartSegment(tr, start.Add(5*time.Second))
   216  	EndBasicSegment(tr, t2, start.Add(6*time.Second), "t2")
   217  	StartSegment(tr, start.Add(7*time.Second))
   218  	t3 := StartSegment(tr, start.Add(8*time.Second))
   219  	EndBasicSegment(tr, t3, start.Add(9*time.Second), "t3")
   220  	t4 := StartSegment(tr, start.Add(10*time.Second))
   221  	EndBasicSegment(tr, t4, start.Add(11*time.Second), "t4")
   222  
   223  	children := TracerRootChildren(tr)
   224  	if children != 4*time.Second {
   225  		t.Error(children)
   226  	}
   227  
   228  	metrics := newMetricTable(100, time.Now())
   229  	tr.FinalName = "WebTransaction/Go/zip"
   230  	tr.IsWeb = true
   231  	MergeBreakdownMetrics(tr, metrics)
   232  	ExpectMetrics(t, metrics, []WantMetric{
   233  		{"Custom/t1", "", false, []float64{1, 1, 1, 1, 1, 1}},
   234  		{"Custom/t2", "", false, []float64{1, 1, 1, 1, 1, 1}},
   235  		{"Custom/t3", "", false, []float64{1, 1, 1, 1, 1, 1}},
   236  		{"Custom/t4", "", false, []float64{1, 1, 1, 1, 1, 1}},
   237  		{"Custom/t1", tr.FinalName, false, []float64{1, 1, 1, 1, 1, 1}},
   238  		{"Custom/t2", tr.FinalName, false, []float64{1, 1, 1, 1, 1, 1}},
   239  		{"Custom/t3", tr.FinalName, false, []float64{1, 1, 1, 1, 1, 1}},
   240  		{"Custom/t4", tr.FinalName, false, []float64{1, 1, 1, 1, 1, 1}},
   241  	})
   242  }
   243  
   244  func TestSegmentBasic(t *testing.T) {
   245  	start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC)
   246  	tr := &TxnData{}
   247  
   248  	t1 := StartSegment(tr, start.Add(1*time.Second))
   249  	t2 := StartSegment(tr, start.Add(2*time.Second))
   250  	EndBasicSegment(tr, t2, start.Add(3*time.Second), "t2")
   251  	EndBasicSegment(tr, t1, start.Add(4*time.Second), "t1")
   252  	t3 := StartSegment(tr, start.Add(5*time.Second))
   253  	t4 := StartSegment(tr, start.Add(6*time.Second))
   254  	EndBasicSegment(tr, t3, start.Add(7*time.Second), "t3")
   255  	EndBasicSegment(tr, t4, start.Add(8*time.Second), "out-of-order")
   256  	t5 := StartSegment(tr, start.Add(9*time.Second))
   257  	EndBasicSegment(tr, t5, start.Add(10*time.Second), "t1")
   258  
   259  	metrics := newMetricTable(100, time.Now())
   260  	tr.FinalName = "WebTransaction/Go/zip"
   261  	tr.IsWeb = true
   262  	MergeBreakdownMetrics(tr, metrics)
   263  	ExpectMetrics(t, metrics, []WantMetric{
   264  		{"Custom/t1", "", false, []float64{2, 4, 3, 1, 3, 10}},
   265  		{"Custom/t2", "", false, []float64{1, 1, 1, 1, 1, 1}},
   266  		{"Custom/t3", "", false, []float64{1, 2, 2, 2, 2, 4}},
   267  		{"Custom/t1", tr.FinalName, false, []float64{2, 4, 3, 1, 3, 10}},
   268  		{"Custom/t2", tr.FinalName, false, []float64{1, 1, 1, 1, 1, 1}},
   269  		{"Custom/t3", tr.FinalName, false, []float64{1, 2, 2, 2, 2, 4}},
   270  	})
   271  }
   272  
   273  func parseURL(raw string) *url.URL {
   274  	u, _ := url.Parse(raw)
   275  	return u
   276  }
   277  
   278  func TestSegmentExternal(t *testing.T) {
   279  	start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC)
   280  	tr := &TxnData{}
   281  
   282  	t1 := StartSegment(tr, start.Add(1*time.Second))
   283  	t2 := StartSegment(tr, start.Add(2*time.Second))
   284  	EndExternalSegment(tr, t2, start.Add(3*time.Second), nil, nil)
   285  	EndExternalSegment(tr, t1, start.Add(4*time.Second), parseURL("http://f1.com"), nil)
   286  	t3 := StartSegment(tr, start.Add(5*time.Second))
   287  	EndExternalSegment(tr, t3, start.Add(6*time.Second), parseURL("http://f1.com"), nil)
   288  	t4 := StartSegment(tr, start.Add(7*time.Second))
   289  	t4.Stamp++
   290  	EndExternalSegment(tr, t4, start.Add(8*time.Second), parseURL("http://invalid-token.com"), nil)
   291  
   292  	if tr.externalCallCount != 3 {
   293  		t.Error(tr.externalCallCount)
   294  	}
   295  	if tr.externalDuration != 5*time.Second {
   296  		t.Error(tr.externalDuration)
   297  	}
   298  	metrics := newMetricTable(100, time.Now())
   299  	tr.FinalName = "WebTransaction/Go/zip"
   300  	tr.IsWeb = true
   301  	MergeBreakdownMetrics(tr, metrics)
   302  	ExpectMetrics(t, metrics, []WantMetric{
   303  		{"External/all", "", true, []float64{3, 5, 4, 1, 3, 11}},
   304  		{"External/allWeb", "", true, []float64{3, 5, 4, 1, 3, 11}},
   305  		{"External/f1.com/all", "", false, []float64{2, 4, 3, 1, 3, 10}},
   306  		{"External/unknown/all", "", false, []float64{1, 1, 1, 1, 1, 1}},
   307  		{"External/f1.com/all", tr.FinalName, false, []float64{2, 4, 3, 1, 3, 10}},
   308  		{"External/unknown/all", tr.FinalName, false, []float64{1, 1, 1, 1, 1, 1}},
   309  	})
   310  
   311  	metrics = newMetricTable(100, time.Now())
   312  	tr.FinalName = "OtherTransaction/Go/zip"
   313  	tr.IsWeb = false
   314  	MergeBreakdownMetrics(tr, metrics)
   315  	ExpectMetrics(t, metrics, []WantMetric{
   316  		{"External/all", "", true, []float64{3, 5, 4, 1, 3, 11}},
   317  		{"External/allOther", "", true, []float64{3, 5, 4, 1, 3, 11}},
   318  		{"External/f1.com/all", "", false, []float64{2, 4, 3, 1, 3, 10}},
   319  		{"External/unknown/all", "", false, []float64{1, 1, 1, 1, 1, 1}},
   320  		{"External/f1.com/all", tr.FinalName, false, []float64{2, 4, 3, 1, 3, 10}},
   321  		{"External/unknown/all", tr.FinalName, false, []float64{1, 1, 1, 1, 1, 1}},
   322  	})
   323  }
   324  
   325  func TestSegmentDatastore(t *testing.T) {
   326  	start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC)
   327  	tr := &TxnData{}
   328  
   329  	t1 := StartSegment(tr, start.Add(1*time.Second))
   330  	t2 := StartSegment(tr, start.Add(2*time.Second))
   331  	EndDatastoreSegment(EndDatastoreParams{
   332  		Tracer:     tr,
   333  		Start:      t2,
   334  		Now:        start.Add(3 * time.Second),
   335  		Product:    "MySQL",
   336  		Operation:  "SELECT",
   337  		Collection: "my_table",
   338  	})
   339  	EndDatastoreSegment(EndDatastoreParams{
   340  		Tracer:    tr,
   341  		Start:     t1,
   342  		Now:       start.Add(4 * time.Second),
   343  		Product:   "MySQL",
   344  		Operation: "SELECT",
   345  		// missing collection
   346  	})
   347  	t3 := StartSegment(tr, start.Add(5*time.Second))
   348  	EndDatastoreSegment(EndDatastoreParams{
   349  		Tracer:    tr,
   350  		Start:     t3,
   351  		Now:       start.Add(6 * time.Second),
   352  		Product:   "MySQL",
   353  		Operation: "SELECT",
   354  		// missing collection
   355  	})
   356  	t4 := StartSegment(tr, start.Add(7*time.Second))
   357  	t4.Stamp++
   358  	EndDatastoreSegment(EndDatastoreParams{
   359  		Tracer:    tr,
   360  		Start:     t4,
   361  		Now:       start.Add(8 * time.Second),
   362  		Product:   "MySQL",
   363  		Operation: "invalid-token",
   364  	})
   365  	t5 := StartSegment(tr, start.Add(9*time.Second))
   366  	EndDatastoreSegment(EndDatastoreParams{
   367  		Tracer: tr,
   368  		Start:  t5,
   369  		Now:    start.Add(10 * time.Second),
   370  		// missing datastore, collection, and operation
   371  	})
   372  
   373  	if tr.datastoreCallCount != 4 {
   374  		t.Error(tr.datastoreCallCount)
   375  	}
   376  	if tr.datastoreDuration != 6*time.Second {
   377  		t.Error(tr.datastoreDuration)
   378  	}
   379  	metrics := newMetricTable(100, time.Now())
   380  	tr.FinalName = "WebTransaction/Go/zip"
   381  	tr.IsWeb = true
   382  	MergeBreakdownMetrics(tr, metrics)
   383  	ExpectMetrics(t, metrics, []WantMetric{
   384  		{"Datastore/all", "", true, []float64{4, 6, 5, 1, 3, 12}},
   385  		{"Datastore/allWeb", "", true, []float64{4, 6, 5, 1, 3, 12}},
   386  		{"Datastore/MySQL/all", "", true, []float64{3, 5, 4, 1, 3, 11}},
   387  		{"Datastore/MySQL/allWeb", "", true, []float64{3, 5, 4, 1, 3, 11}},
   388  		{"Datastore/Unknown/all", "", true, []float64{1, 1, 1, 1, 1, 1}},
   389  		{"Datastore/Unknown/allWeb", "", true, []float64{1, 1, 1, 1, 1, 1}},
   390  		{"Datastore/operation/MySQL/SELECT", "", false, []float64{3, 5, 4, 1, 3, 11}},
   391  		{"Datastore/operation/MySQL/SELECT", tr.FinalName, false, []float64{2, 4, 3, 1, 3, 10}},
   392  		{"Datastore/operation/Unknown/other", "", false, []float64{1, 1, 1, 1, 1, 1}},
   393  		{"Datastore/operation/Unknown/other", tr.FinalName, false, []float64{1, 1, 1, 1, 1, 1}},
   394  		{"Datastore/statement/MySQL/my_table/SELECT", "", false, []float64{1, 1, 1, 1, 1, 1}},
   395  		{"Datastore/statement/MySQL/my_table/SELECT", tr.FinalName, false, []float64{1, 1, 1, 1, 1, 1}},
   396  	})
   397  
   398  	metrics = newMetricTable(100, time.Now())
   399  	tr.FinalName = "OtherTransaction/Go/zip"
   400  	tr.IsWeb = false
   401  	MergeBreakdownMetrics(tr, metrics)
   402  	ExpectMetrics(t, metrics, []WantMetric{
   403  		{"Datastore/all", "", true, []float64{4, 6, 5, 1, 3, 12}},
   404  		{"Datastore/allOther", "", true, []float64{4, 6, 5, 1, 3, 12}},
   405  		{"Datastore/MySQL/all", "", true, []float64{3, 5, 4, 1, 3, 11}},
   406  		{"Datastore/MySQL/allOther", "", true, []float64{3, 5, 4, 1, 3, 11}},
   407  		{"Datastore/Unknown/all", "", true, []float64{1, 1, 1, 1, 1, 1}},
   408  		{"Datastore/Unknown/allOther", "", true, []float64{1, 1, 1, 1, 1, 1}},
   409  		{"Datastore/operation/MySQL/SELECT", "", false, []float64{3, 5, 4, 1, 3, 11}},
   410  		{"Datastore/operation/MySQL/SELECT", tr.FinalName, false, []float64{2, 4, 3, 1, 3, 10}},
   411  		{"Datastore/operation/Unknown/other", "", false, []float64{1, 1, 1, 1, 1, 1}},
   412  		{"Datastore/operation/Unknown/other", tr.FinalName, false, []float64{1, 1, 1, 1, 1, 1}},
   413  		{"Datastore/statement/MySQL/my_table/SELECT", "", false, []float64{1, 1, 1, 1, 1, 1}},
   414  		{"Datastore/statement/MySQL/my_table/SELECT", tr.FinalName, false, []float64{1, 1, 1, 1, 1, 1}},
   415  	})
   416  }
   417  
   418  func TestDatastoreInstancesCrossAgent(t *testing.T) {
   419  	var testcases []struct {
   420  		Name           string `json:"name"`
   421  		SystemHostname string `json:"system_hostname"`
   422  		DBHostname     string `json:"db_hostname"`
   423  		Product        string `json:"product"`
   424  		Port           int    `json:"port"`
   425  		Socket         string `json:"unix_socket"`
   426  		DatabasePath   string `json:"database_path"`
   427  		ExpectedMetric string `json:"expected_instance_metric"`
   428  	}
   429  
   430  	err := crossagent.ReadJSON("datastores/datastore_instances.json", &testcases)
   431  	if err != nil {
   432  		t.Fatal(err)
   433  	}
   434  
   435  	start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC)
   436  
   437  	for _, tc := range testcases {
   438  		portPathOrID := ""
   439  		if 0 != tc.Port {
   440  			portPathOrID = strconv.Itoa(tc.Port)
   441  		} else if "" != tc.Socket {
   442  			portPathOrID = tc.Socket
   443  		} else if "" != tc.DatabasePath {
   444  			portPathOrID = tc.DatabasePath
   445  			// These tests makes weird assumptions.
   446  			tc.DBHostname = "localhost"
   447  		}
   448  
   449  		tr := &TxnData{}
   450  		s := StartSegment(tr, start)
   451  		EndDatastoreSegment(EndDatastoreParams{
   452  			Tracer:       tr,
   453  			Start:        s,
   454  			Now:          start.Add(1 * time.Second),
   455  			Product:      tc.Product,
   456  			Operation:    "SELECT",
   457  			Collection:   "my_table",
   458  			PortPathOrID: portPathOrID,
   459  			Host:         tc.DBHostname,
   460  		})
   461  
   462  		expect := strings.Replace(tc.ExpectedMetric,
   463  			tc.SystemHostname, ThisHost, -1)
   464  
   465  		metrics := newMetricTable(100, time.Now())
   466  		tr.FinalName = "OtherTransaction/Go/zip"
   467  		tr.IsWeb = false
   468  		MergeBreakdownMetrics(tr, metrics)
   469  		data := []float64{1, 1, 1, 1, 1, 1}
   470  		ExpectMetrics(ExtendValidator(t, tc.Name), metrics, []WantMetric{
   471  			{"Datastore/all", "", true, data},
   472  			{"Datastore/allOther", "", true, data},
   473  			{"Datastore/" + tc.Product + "/all", "", true, data},
   474  			{"Datastore/" + tc.Product + "/allOther", "", true, data},
   475  			{"Datastore/operation/" + tc.Product + "/SELECT", "", false, data},
   476  			{"Datastore/statement/" + tc.Product + "/my_table/SELECT", "", false, data},
   477  			{"Datastore/statement/" + tc.Product + "/my_table/SELECT", tr.FinalName, false, data},
   478  			{expect, "", false, data},
   479  		})
   480  	}
   481  }