expect.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. package internal
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "runtime"
  6. )
  7. var (
  8. // Unfortunately, the resolution of time.Now() on Windows is coarse: Two
  9. // sequential calls to time.Now() may return the same value, and tests
  10. // which expect non-zero durations may fail. To avoid adding sleep
  11. // statements or mocking time.Now(), those tests are skipped on Windows.
  12. doDurationTests = runtime.GOOS != `windows`
  13. )
  14. // Validator is used for testing.
  15. type Validator interface {
  16. Error(...interface{})
  17. }
  18. func validateStringField(v Validator, fieldName, v1, v2 string) {
  19. if v1 != v2 {
  20. v.Error(fieldName, v1, v2)
  21. }
  22. }
  23. type addValidatorField struct {
  24. field interface{}
  25. original Validator
  26. }
  27. func (a addValidatorField) Error(fields ...interface{}) {
  28. fields = append([]interface{}{a.field}, fields...)
  29. a.original.Error(fields...)
  30. }
  31. // ExtendValidator is used to add more context to a validator.
  32. func ExtendValidator(v Validator, field interface{}) Validator {
  33. return addValidatorField{
  34. field: field,
  35. original: v,
  36. }
  37. }
  38. // WantMetric is a metric expectation. If Data is nil, then any data values are
  39. // acceptable.
  40. type WantMetric struct {
  41. Name string
  42. Scope string
  43. Forced interface{} // true, false, or nil
  44. Data []float64
  45. }
  46. // WantError is a traced error expectation.
  47. type WantError struct {
  48. TxnName string
  49. Msg string
  50. Klass string
  51. Caller string
  52. URL string
  53. UserAttributes map[string]interface{}
  54. AgentAttributes map[string]interface{}
  55. }
  56. func uniquePointer() *struct{} {
  57. s := struct{}{}
  58. return &s
  59. }
  60. var (
  61. // MatchAnything is for use when matching attributes.
  62. MatchAnything = uniquePointer()
  63. )
  64. // WantEvent is a transaction or error event expectation.
  65. type WantEvent struct {
  66. Intrinsics map[string]interface{}
  67. UserAttributes map[string]interface{}
  68. AgentAttributes map[string]interface{}
  69. }
  70. // WantTxnTrace is a transaction trace expectation.
  71. type WantTxnTrace struct {
  72. MetricName string
  73. CleanURL string
  74. NumSegments int
  75. UserAttributes map[string]interface{}
  76. AgentAttributes map[string]interface{}
  77. }
  78. // WantSlowQuery is a slowQuery expectation.
  79. type WantSlowQuery struct {
  80. Count int32
  81. MetricName string
  82. Query string
  83. TxnName string
  84. TxnURL string
  85. DatabaseName string
  86. Host string
  87. PortPathOrID string
  88. Params map[string]interface{}
  89. }
  90. // Expect exposes methods that allow for testing whether the correct data was
  91. // captured.
  92. type Expect interface {
  93. ExpectCustomEvents(t Validator, want []WantEvent)
  94. ExpectErrors(t Validator, want []WantError)
  95. ExpectErrorEvents(t Validator, want []WantEvent)
  96. ExpectErrorEventsPresent(t Validator, want []WantEvent)
  97. ExpectErrorEventsAbsent(t Validator, names []string)
  98. ExpectTxnEvents(t Validator, want []WantEvent)
  99. ExpectTxnEventsPresent(t Validator, want []WantEvent)
  100. ExpectTxnEventsAbsent(t Validator, names []string)
  101. ExpectMetrics(t Validator, want []WantMetric)
  102. ExpectMetricsPresent(t Validator, want []WantMetric)
  103. ExpectTxnTraces(t Validator, want []WantTxnTrace)
  104. ExpectSlowQueries(t Validator, want []WantSlowQuery)
  105. ExpectSpanEvents(t Validator, want []WantEvent)
  106. ExpectSpanEventsPresent(t Validator, want []WantEvent)
  107. ExpectSpanEventsAbsent(t Validator, names []string)
  108. ExpectSpanEventsCount(t Validator, c int)
  109. }
  110. func expectMetricField(t Validator, id metricID, v1, v2 float64, fieldName string) {
  111. if v1 != v2 {
  112. t.Error("metric fields do not match", id, v1, v2, fieldName)
  113. }
  114. }
  115. // ExpectMetricsPresent allows testing of metrics with requiring an exact match
  116. func ExpectMetricsPresent(t Validator, mt *metricTable, expect []WantMetric) {
  117. expectedIds := make(map[metricID]struct{})
  118. for _, e := range expect {
  119. id := metricID{Name: e.Name, Scope: e.Scope}
  120. expectedIds[id] = struct{}{}
  121. m := mt.metrics[id]
  122. if nil == m {
  123. t.Error("unable to find metric", id)
  124. continue
  125. }
  126. if b, ok := e.Forced.(bool); ok {
  127. if b != (forced == m.forced) {
  128. t.Error("metric forced incorrect", b, m.forced, id)
  129. }
  130. }
  131. if nil != e.Data {
  132. expectMetricField(t, id, e.Data[0], m.data.countSatisfied, "countSatisfied")
  133. expectMetricField(t, id, e.Data[1], m.data.totalTolerated, "totalTolerated")
  134. expectMetricField(t, id, e.Data[2], m.data.exclusiveFailed, "exclusiveFailed")
  135. expectMetricField(t, id, e.Data[3], m.data.min, "min")
  136. expectMetricField(t, id, e.Data[4], m.data.max, "max")
  137. expectMetricField(t, id, e.Data[5], m.data.sumSquares, "sumSquares")
  138. }
  139. }
  140. }
  141. // ExpectMetrics allows testing of metrics. It passes if mt exactly matches expect.
  142. func ExpectMetrics(t Validator, mt *metricTable, expect []WantMetric) {
  143. if len(mt.metrics) != len(expect) {
  144. t.Error("metric counts do not match expectations", len(mt.metrics), len(expect))
  145. }
  146. expectedIds := make(map[metricID]struct{})
  147. for _, e := range expect {
  148. id := metricID{Name: e.Name, Scope: e.Scope}
  149. expectedIds[id] = struct{}{}
  150. m := mt.metrics[id]
  151. if nil == m {
  152. t.Error("unable to find metric", id)
  153. continue
  154. }
  155. if b, ok := e.Forced.(bool); ok {
  156. if b != (forced == m.forced) {
  157. t.Error("metric forced incorrect", b, m.forced, id)
  158. }
  159. }
  160. if nil != e.Data {
  161. expectMetricField(t, id, e.Data[0], m.data.countSatisfied, "countSatisfied")
  162. expectMetricField(t, id, e.Data[1], m.data.totalTolerated, "totalTolerated")
  163. expectMetricField(t, id, e.Data[2], m.data.exclusiveFailed, "exclusiveFailed")
  164. expectMetricField(t, id, e.Data[3], m.data.min, "min")
  165. expectMetricField(t, id, e.Data[4], m.data.max, "max")
  166. expectMetricField(t, id, e.Data[5], m.data.sumSquares, "sumSquares")
  167. }
  168. }
  169. for id := range mt.metrics {
  170. if _, ok := expectedIds[id]; !ok {
  171. t.Error("expected metrics does not contain", id.Name, id.Scope)
  172. }
  173. }
  174. }
  175. func expectAttributesPresent(v Validator, exists map[string]interface{}, expect map[string]interface{}) {
  176. for key, val := range expect {
  177. found, ok := exists[key]
  178. if !ok {
  179. v.Error("expected attribute not found: ", key)
  180. continue
  181. }
  182. if val == MatchAnything {
  183. continue
  184. }
  185. v1 := fmt.Sprint(found)
  186. v2 := fmt.Sprint(val)
  187. if v1 != v2 {
  188. v.Error("value difference", fmt.Sprintf("key=%s", key), v1, v2)
  189. }
  190. }
  191. }
  192. func expectAttributes(v Validator, exists map[string]interface{}, expect map[string]interface{}) {
  193. // TODO: This params comparison can be made smarter: Alert differences
  194. // based on sub/super set behavior.
  195. if len(exists) != len(expect) {
  196. v.Error("attributes length difference", len(exists), len(expect))
  197. }
  198. for key, val := range expect {
  199. found, ok := exists[key]
  200. if !ok {
  201. v.Error("expected attribute not found: ", key)
  202. continue
  203. }
  204. if val == MatchAnything {
  205. continue
  206. }
  207. v1 := fmt.Sprint(found)
  208. v2 := fmt.Sprint(val)
  209. if v1 != v2 {
  210. v.Error("value difference", fmt.Sprintf("key=%s", key), v1, v2)
  211. }
  212. }
  213. for key, val := range exists {
  214. _, ok := expect[key]
  215. if !ok {
  216. v.Error("unexpected attribute present: ", key, val)
  217. continue
  218. }
  219. }
  220. }
  221. // ExpectCustomEvents allows testing of custom events. It passes if cs exactly matches expect.
  222. func ExpectCustomEvents(v Validator, cs *customEvents, expect []WantEvent) {
  223. if len(cs.events.events) != len(expect) {
  224. v.Error("number of custom events does not match", len(cs.events.events),
  225. len(expect))
  226. return
  227. }
  228. for i, e := range expect {
  229. event, ok := cs.events.events[i].jsonWriter.(*CustomEvent)
  230. if !ok {
  231. v.Error("wrong custom event")
  232. } else {
  233. expectEvent(v, event, e)
  234. }
  235. }
  236. }
  237. func expectEventAbsent(v Validator, e json.Marshaler, names []string) {
  238. js, err := e.MarshalJSON()
  239. if nil != err {
  240. v.Error("unable to marshal event", err)
  241. return
  242. }
  243. var event []map[string]interface{}
  244. err = json.Unmarshal(js, &event)
  245. if nil != err {
  246. v.Error("unable to parse event json", err)
  247. return
  248. }
  249. intrinsics := event[0]
  250. userAttributes := event[1]
  251. agentAttributes := event[2]
  252. for _, name := range names {
  253. if _, ok := intrinsics[name]; ok {
  254. v.Error("unexpected key found", name)
  255. }
  256. if _, ok := userAttributes[name]; ok {
  257. v.Error("unexpected key found", name)
  258. }
  259. if _, ok := agentAttributes[name]; ok {
  260. v.Error("unexpected key found", name)
  261. }
  262. }
  263. }
  264. func expectEventPresent(v Validator, e json.Marshaler, expect WantEvent) {
  265. js, err := e.MarshalJSON()
  266. if nil != err {
  267. v.Error("unable to marshal event", err)
  268. return
  269. }
  270. var event []map[string]interface{}
  271. err = json.Unmarshal(js, &event)
  272. if nil != err {
  273. v.Error("unable to parse event json", err)
  274. return
  275. }
  276. intrinsics := event[0]
  277. userAttributes := event[1]
  278. agentAttributes := event[2]
  279. if nil != expect.Intrinsics {
  280. expectAttributesPresent(v, intrinsics, expect.Intrinsics)
  281. }
  282. if nil != expect.UserAttributes {
  283. expectAttributesPresent(v, userAttributes, expect.UserAttributes)
  284. }
  285. if nil != expect.AgentAttributes {
  286. expectAttributesPresent(v, agentAttributes, expect.AgentAttributes)
  287. }
  288. }
  289. func expectEvent(v Validator, e json.Marshaler, expect WantEvent) {
  290. js, err := e.MarshalJSON()
  291. if nil != err {
  292. v.Error("unable to marshal event", err)
  293. return
  294. }
  295. var event []map[string]interface{}
  296. err = json.Unmarshal(js, &event)
  297. if nil != err {
  298. v.Error("unable to parse event json", err)
  299. return
  300. }
  301. intrinsics := event[0]
  302. userAttributes := event[1]
  303. agentAttributes := event[2]
  304. if nil != expect.Intrinsics {
  305. expectAttributes(v, intrinsics, expect.Intrinsics)
  306. }
  307. if nil != expect.UserAttributes {
  308. expectAttributes(v, userAttributes, expect.UserAttributes)
  309. }
  310. if nil != expect.AgentAttributes {
  311. expectAttributes(v, agentAttributes, expect.AgentAttributes)
  312. }
  313. }
  314. // Second attributes have priority.
  315. func mergeAttributes(a1, a2 map[string]interface{}) map[string]interface{} {
  316. a := make(map[string]interface{})
  317. for k, v := range a1 {
  318. a[k] = v
  319. }
  320. for k, v := range a2 {
  321. a[k] = v
  322. }
  323. return a
  324. }
  325. // ExpectErrorEventsPresent allows testing of events with requiring an exact match
  326. func ExpectErrorEventsPresent(v Validator, events *errorEvents, expect []WantEvent) {
  327. for i, e := range expect {
  328. event, ok := events.events.events[i].jsonWriter.(*ErrorEvent)
  329. if !ok {
  330. v.Error("wrong span event in ExpectErrorEventsPresent")
  331. } else {
  332. expectEventPresent(v, event, e)
  333. }
  334. }
  335. }
  336. // ExpectErrorEventsAbsent allows testing that a set of attribute names are absent from the event data
  337. func ExpectErrorEventsAbsent(v Validator, events *errorEvents, names []string) {
  338. for _, eventHarvested := range events.events.events {
  339. event, ok := eventHarvested.jsonWriter.(*ErrorEvent)
  340. if !ok {
  341. v.Error("wrong span event in ExpectErrorEventsAbsent")
  342. } else {
  343. expectEventAbsent(v, event, names)
  344. }
  345. }
  346. }
  347. // ExpectErrorEvents allows testing of error events. It passes if events exactly matches expect.
  348. func ExpectErrorEvents(v Validator, events *errorEvents, expect []WantEvent) {
  349. if len(events.events.events) != len(expect) {
  350. v.Error("number of custom events does not match",
  351. len(events.events.events), len(expect))
  352. return
  353. }
  354. for i, e := range expect {
  355. event, ok := events.events.events[i].jsonWriter.(*ErrorEvent)
  356. if !ok {
  357. v.Error("wrong error event")
  358. } else {
  359. if nil != e.Intrinsics {
  360. e.Intrinsics = mergeAttributes(map[string]interface{}{
  361. // The following intrinsics should always be present in
  362. // error events:
  363. "type": "TransactionError",
  364. "timestamp": MatchAnything,
  365. "duration": MatchAnything,
  366. }, e.Intrinsics)
  367. }
  368. expectEvent(v, event, e)
  369. }
  370. }
  371. }
  372. // ExpectSpanEventsCount allows us to count how many events the system generated
  373. func ExpectSpanEventsCount(v Validator, events *spanEvents, c int) {
  374. len := len(events.events.events)
  375. if len != c {
  376. v.Error(fmt.Sprintf("expected %d span events, found %d", c, len))
  377. }
  378. }
  379. // ExpectSpanEventsPresent allows us to test for the presence and value of events
  380. // without also requiring an exact match
  381. func ExpectSpanEventsPresent(v Validator, events *spanEvents, expect []WantEvent) {
  382. for i, e := range expect {
  383. event, ok := events.events.events[i].jsonWriter.(*SpanEvent)
  384. if !ok {
  385. v.Error("wrong span event in ExpectSpanEventsPresent")
  386. } else {
  387. expectEventPresent(v, event, e)
  388. }
  389. }
  390. }
  391. // ExpectSpanEventsAbsent allows us to ensure that a set of attribute names are absent
  392. // from the event data
  393. func ExpectSpanEventsAbsent(v Validator, events *spanEvents, names []string) {
  394. for _, eventHarvested := range events.events.events {
  395. event, ok := eventHarvested.jsonWriter.(*SpanEvent)
  396. if !ok {
  397. v.Error("wrong span event in ExpectSpanEventsAbsent")
  398. } else {
  399. expectEventAbsent(v, event, names)
  400. }
  401. }
  402. }
  403. // ExpectSpanEvents allows testing of span events. It passes if events exactly matches expect.
  404. func ExpectSpanEvents(v Validator, events *spanEvents, expect []WantEvent) {
  405. if len(events.events.events) != len(expect) {
  406. v.Error("number of txn events does not match",
  407. len(events.events.events), len(expect))
  408. return
  409. }
  410. for i, e := range expect {
  411. event, ok := events.events.events[i].jsonWriter.(*SpanEvent)
  412. if !ok {
  413. v.Error("wrong span event")
  414. } else {
  415. if nil != e.Intrinsics {
  416. e.Intrinsics = mergeAttributes(map[string]interface{}{
  417. // The following intrinsics should always be present in
  418. // span events:
  419. "type": "Transaction",
  420. "timestamp": MatchAnything,
  421. "duration": MatchAnything,
  422. }, e.Intrinsics)
  423. }
  424. expectEvent(v, event, e)
  425. }
  426. }
  427. }
  428. // ExpectTxnEventsPresent allows us to test for the presence and value of events
  429. // without also requiring an exact match
  430. func ExpectTxnEventsPresent(v Validator, events *txnEvents, expect []WantEvent) {
  431. for i, e := range expect {
  432. event, ok := events.events.events[i].jsonWriter.(*TxnEvent)
  433. if !ok {
  434. v.Error("wrong txn event in ExpectTxnEventsPresent")
  435. } else {
  436. expectEventPresent(v, event, e)
  437. }
  438. }
  439. }
  440. // ExpectTxnEventsAbsent allows us to ensure that a set of attribute names are absent
  441. // from the event data
  442. func ExpectTxnEventsAbsent(v Validator, events *txnEvents, names []string) {
  443. for _, eventHarvested := range events.events.events {
  444. event, ok := eventHarvested.jsonWriter.(*TxnEvent)
  445. if !ok {
  446. v.Error("wrong txn event in ExpectTxnEventsAbsent")
  447. } else {
  448. expectEventAbsent(v, event, names)
  449. }
  450. }
  451. }
  452. // ExpectTxnEvents allows testing of txn events.
  453. func ExpectTxnEvents(v Validator, events *txnEvents, expect []WantEvent) {
  454. if len(events.events.events) != len(expect) {
  455. v.Error("number of txn events does not match",
  456. len(events.events.events), len(expect))
  457. return
  458. }
  459. for i, e := range expect {
  460. event, ok := events.events.events[i].jsonWriter.(*TxnEvent)
  461. if !ok {
  462. v.Error("wrong txn event")
  463. } else {
  464. if nil != e.Intrinsics {
  465. e.Intrinsics = mergeAttributes(map[string]interface{}{
  466. // The following intrinsics should always be present in
  467. // txn events:
  468. "type": "Transaction",
  469. "timestamp": MatchAnything,
  470. "duration": MatchAnything,
  471. "error": MatchAnything,
  472. }, e.Intrinsics)
  473. }
  474. expectEvent(v, event, e)
  475. }
  476. }
  477. }
  478. func expectError(v Validator, err *tracedError, expect WantError) {
  479. caller := topCallerNameBase(err.ErrorData.Stack)
  480. validateStringField(v, "caller", expect.Caller, caller)
  481. validateStringField(v, "txnName", expect.TxnName, err.FinalName)
  482. validateStringField(v, "klass", expect.Klass, err.Klass)
  483. validateStringField(v, "msg", expect.Msg, err.Msg)
  484. validateStringField(v, "URL", expect.URL, err.CleanURL)
  485. js, errr := err.MarshalJSON()
  486. if nil != errr {
  487. v.Error("unable to marshal error json", errr)
  488. return
  489. }
  490. var unmarshalled []interface{}
  491. errr = json.Unmarshal(js, &unmarshalled)
  492. if nil != errr {
  493. v.Error("unable to unmarshal error json", errr)
  494. return
  495. }
  496. attributes := unmarshalled[4].(map[string]interface{})
  497. agentAttributes := attributes["agentAttributes"].(map[string]interface{})
  498. userAttributes := attributes["userAttributes"].(map[string]interface{})
  499. if nil != expect.UserAttributes {
  500. expectAttributes(v, userAttributes, expect.UserAttributes)
  501. }
  502. if nil != expect.AgentAttributes {
  503. expectAttributes(v, agentAttributes, expect.AgentAttributes)
  504. }
  505. }
  506. // ExpectErrors allows testing of errors.
  507. func ExpectErrors(v Validator, errors harvestErrors, expect []WantError) {
  508. if len(errors) != len(expect) {
  509. v.Error("number of errors mismatch", len(errors), len(expect))
  510. return
  511. }
  512. for i, e := range expect {
  513. expectError(v, errors[i], e)
  514. }
  515. }
  516. func countSegments(node []interface{}) int {
  517. count := 1
  518. children := node[4].([]interface{})
  519. for _, c := range children {
  520. node := c.([]interface{})
  521. count += countSegments(node)
  522. }
  523. return count
  524. }
  525. func expectTxnTrace(v Validator, got json.Marshaler, expect WantTxnTrace) {
  526. js, err := got.MarshalJSON()
  527. if nil != err {
  528. v.Error("unable to marshal txn trace json", err)
  529. return
  530. }
  531. var unmarshalled []interface{}
  532. err = json.Unmarshal(js, &unmarshalled)
  533. if nil != err {
  534. v.Error("unable to unmarshal error json", err)
  535. return
  536. }
  537. duration := unmarshalled[1].(float64)
  538. name := unmarshalled[2].(string)
  539. cleanURL := unmarshalled[3].(string)
  540. traceData := unmarshalled[4].([]interface{})
  541. rootNode := traceData[3].([]interface{})
  542. attributes := traceData[4].(map[string]interface{})
  543. userAttributes := attributes["userAttributes"].(map[string]interface{})
  544. agentAttributes := attributes["agentAttributes"].(map[string]interface{})
  545. validateStringField(v, "metric name", expect.MetricName, name)
  546. validateStringField(v, "request url", expect.CleanURL, cleanURL)
  547. if doDurationTests && 0 == duration {
  548. v.Error("zero trace duration")
  549. }
  550. if nil != expect.UserAttributes {
  551. expectAttributes(v, userAttributes, expect.UserAttributes)
  552. }
  553. if nil != expect.AgentAttributes {
  554. expectAttributes(v, agentAttributes, expect.AgentAttributes)
  555. }
  556. numSegments := countSegments(rootNode)
  557. // The expectation segment count does not include the two root nodes.
  558. numSegments -= 2
  559. if expect.NumSegments != numSegments {
  560. v.Error("wrong number of segments", expect.NumSegments, numSegments)
  561. }
  562. }
  563. // ExpectTxnTraces allows testing of transaction traces.
  564. func ExpectTxnTraces(v Validator, traces *harvestTraces, want []WantTxnTrace) {
  565. if len(want) != traces.Len() {
  566. v.Error("number of traces do not match", len(want), traces.Len())
  567. }
  568. actual := traces.slice()
  569. for i, expected := range want {
  570. expectTxnTrace(v, actual[i], expected)
  571. }
  572. }
  573. func expectSlowQuery(t Validator, slowQuery *slowQuery, want WantSlowQuery) {
  574. if slowQuery.Count != want.Count {
  575. t.Error("wrong Count field", slowQuery.Count, want.Count)
  576. }
  577. validateStringField(t, "MetricName", slowQuery.DatastoreMetric, want.MetricName)
  578. validateStringField(t, "Query", slowQuery.ParameterizedQuery, want.Query)
  579. validateStringField(t, "TxnEvent.FinalName", slowQuery.TxnEvent.FinalName, want.TxnName)
  580. validateStringField(t, "TxnEvent.CleanURL", slowQuery.TxnEvent.CleanURL, want.TxnURL)
  581. validateStringField(t, "DatabaseName", slowQuery.DatabaseName, want.DatabaseName)
  582. validateStringField(t, "Host", slowQuery.Host, want.Host)
  583. validateStringField(t, "PortPathOrID", slowQuery.PortPathOrID, want.PortPathOrID)
  584. expectAttributes(t, map[string]interface{}(slowQuery.QueryParameters), want.Params)
  585. }
  586. // ExpectSlowQueries allows testing of slow queries.
  587. func ExpectSlowQueries(t Validator, slowQueries *slowQueries, want []WantSlowQuery) {
  588. if len(want) != len(slowQueries.priorityQueue) {
  589. t.Error("wrong number of slow queries",
  590. "expected", len(want), "got", len(slowQueries.priorityQueue))
  591. return
  592. }
  593. for _, s := range want {
  594. idx, ok := slowQueries.lookup[s.Query]
  595. if !ok {
  596. t.Error("unable to find slow query", s.Query)
  597. continue
  598. }
  599. expectSlowQuery(t, slowQueries.priorityQueue[idx], s)
  600. }
  601. }