dd-trace 5.43.0 → 5.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/package.json +4 -4
  2. package/packages/datadog-instrumentations/src/cucumber.js +61 -23
  3. package/packages/datadog-instrumentations/src/dd-trace-api.js +7 -0
  4. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  5. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  6. package/packages/datadog-instrumentations/src/jest.js +134 -48
  7. package/packages/datadog-instrumentations/src/mocha/main.js +20 -4
  8. package/packages/datadog-instrumentations/src/mocha/utils.js +89 -30
  9. package/packages/datadog-instrumentations/src/mocha/worker.js +3 -1
  10. package/packages/datadog-instrumentations/src/playwright.js +97 -17
  11. package/packages/datadog-instrumentations/src/router.js +1 -0
  12. package/packages/datadog-instrumentations/src/tedious.js +13 -10
  13. package/packages/datadog-instrumentations/src/vitest.js +77 -17
  14. package/packages/datadog-plugin-cucumber/src/index.js +24 -1
  15. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +69 -20
  16. package/packages/datadog-plugin-cypress/src/support.js +39 -10
  17. package/packages/datadog-plugin-google-cloud-vertexai/src/index.js +8 -186
  18. package/packages/datadog-plugin-google-cloud-vertexai/src/tracing.js +186 -0
  19. package/packages/datadog-plugin-google-cloud-vertexai/src/utils.js +19 -0
  20. package/packages/datadog-plugin-jest/src/index.js +38 -5
  21. package/packages/datadog-plugin-mocha/src/index.js +28 -5
  22. package/packages/datadog-plugin-playwright/src/index.js +22 -2
  23. package/packages/datadog-plugin-tedious/src/index.js +14 -9
  24. package/packages/datadog-plugin-vitest/src/index.js +46 -14
  25. package/packages/dd-trace/src/appsec/blocking.js +2 -0
  26. package/packages/dd-trace/src/appsec/graphql.js +3 -1
  27. package/packages/dd-trace/src/appsec/reporter.js +13 -8
  28. package/packages/dd-trace/src/appsec/sdk/track_event.js +7 -0
  29. package/packages/dd-trace/src/appsec/telemetry/common.js +6 -3
  30. package/packages/dd-trace/src/appsec/telemetry/index.js +28 -5
  31. package/packages/dd-trace/src/appsec/telemetry/user.js +9 -1
  32. package/packages/dd-trace/src/appsec/telemetry/waf.js +29 -9
  33. package/packages/dd-trace/src/appsec/waf/waf_manager.js +16 -7
  34. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +5 -2
  35. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +3 -1
  36. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +4 -2
  37. package/packages/dd-trace/src/dogstatsd.js +94 -77
  38. package/packages/dd-trace/src/histogram.js +12 -23
  39. package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
  40. package/packages/dd-trace/src/llmobs/index.js +3 -0
  41. package/packages/dd-trace/src/llmobs/noop.js +3 -3
  42. package/packages/dd-trace/src/llmobs/plugins/base.js +1 -1
  43. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +2 -1
  44. package/packages/dd-trace/src/llmobs/plugins/vertexai.js +196 -0
  45. package/packages/dd-trace/src/llmobs/sdk.js +2 -0
  46. package/packages/dd-trace/src/llmobs/span_processor.js +6 -0
  47. package/packages/dd-trace/src/llmobs/tagger.js +8 -2
  48. package/packages/dd-trace/src/llmobs/telemetry.js +108 -1
  49. package/packages/dd-trace/src/llmobs/writers/base.js +4 -0
  50. package/packages/dd-trace/src/llmobs/writers/spans/base.js +10 -1
  51. package/packages/dd-trace/src/plugin_manager.js +0 -3
  52. package/packages/dd-trace/src/plugins/ci_plugin.js +16 -26
  53. package/packages/dd-trace/src/plugins/database.js +4 -4
  54. package/packages/dd-trace/src/plugins/plugin.js +2 -0
  55. package/packages/dd-trace/src/plugins/util/test.js +62 -1
  56. package/packages/dd-trace/src/remote_config/manager.js +5 -0
  57. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +7 -6
  58. package/packages/dd-trace/src/telemetry/send-data.js +5 -1
@@ -3,7 +3,9 @@
3
3
  const {
4
4
  getTestSuitePath,
5
5
  removeEfdStringFromTestName,
6
- addEfdStringToTestName
6
+ addEfdStringToTestName,
7
+ addAttemptToFixStringToTestName,
8
+ removeAttemptToFixStringFromTestName
7
9
  } = require('../../../dd-trace/src/plugins/util/test')
8
10
  const { channel, AsyncResource } = require('../helpers/instrument')
9
11
  const shimmer = require('../../../datadog-shimmer')
@@ -26,7 +28,9 @@ const testToStartLine = new WeakMap()
26
28
  const testFileToSuiteAr = new Map()
27
29
  const wrappedFunctions = new WeakSet()
28
30
  const newTests = {}
31
+ const testsAttemptToFix = new Set()
29
32
  const testsQuarantined = new Set()
33
+ const testsStatuses = new Map()
30
34
 
31
35
  function getAfterEachHooks (testOrHook) {
32
36
  const hooks = []
@@ -44,10 +48,10 @@ function getTestProperties (test, testManagementTests) {
44
48
  const testSuite = getTestSuitePath(test.file, process.cwd())
45
49
  const testName = test.fullTitle()
46
50
 
47
- const { disabled: isDisabled, quarantined: isQuarantined } =
51
+ const { attempt_to_fix: isAttemptToFix, disabled: isDisabled, quarantined: isQuarantined } =
48
52
  testManagementTests?.mocha?.suites?.[testSuite]?.tests?.[testName]?.properties || {}
49
53
 
50
- return { isDisabled, isQuarantined }
54
+ return { isAttemptToFix, isDisabled, isQuarantined }
51
55
  }
52
56
 
53
57
  function isNewTest (test, knownTests) {
@@ -57,15 +61,18 @@ function isNewTest (test, knownTests) {
57
61
  return !testsForSuite.includes(testName)
58
62
  }
59
63
 
60
- function retryTest (test, earlyFlakeDetectionNumRetries) {
64
+ function retryTest (test, numRetries, modifyTestName, tags) {
61
65
  const originalTestName = test.title
62
66
  const suite = test.parent
63
- for (let retryIndex = 0; retryIndex < earlyFlakeDetectionNumRetries; retryIndex++) {
67
+ for (let retryIndex = 0; retryIndex < numRetries; retryIndex++) {
64
68
  const clonedTest = test.clone()
65
- clonedTest.title = addEfdStringToTestName(originalTestName, retryIndex + 1)
69
+ clonedTest.title = modifyTestName(originalTestName, retryIndex + 1)
66
70
  suite.addTest(clonedTest)
67
- clonedTest._ddIsNew = true
68
- clonedTest._ddIsEfdRetry = true
71
+ tags.forEach(tag => {
72
+ if (tag) {
73
+ clonedTest[tag] = true
74
+ }
75
+ })
69
76
  }
70
77
  }
71
78
 
@@ -102,7 +109,10 @@ function getIsLastRetry (test) {
102
109
  }
103
110
 
104
111
  function getTestFullName (test) {
105
- return `mocha.${getTestSuitePath(test.file, process.cwd())}.${removeEfdStringFromTestName(test.fullTitle())}`
112
+ const testName = removeEfdStringFromTestName(
113
+ removeAttemptToFixStringFromTestName(test.fullTitle())
114
+ )
115
+ return `mocha.${getTestSuitePath(test.file, process.cwd())}.${testName}`
106
116
  }
107
117
 
108
118
  function getTestStatus (test) {
@@ -195,12 +205,15 @@ function getOnTestHandler (isMain) {
195
205
  title,
196
206
  _ddIsNew: isNew,
197
207
  _ddIsEfdRetry: isEfdRetry,
208
+ _ddIsAttemptToFix: isAttemptToFix,
198
209
  _ddIsDisabled: isDisabled,
199
210
  _ddIsQuarantined: isQuarantined
200
211
  } = test
201
212
 
213
+ const testName = removeEfdStringFromTestName(removeAttemptToFixStringFromTestName(test.fullTitle()))
214
+
202
215
  const testInfo = {
203
- testName: test.fullTitle(),
216
+ testName,
204
217
  testSuiteAbsolutePath,
205
218
  title,
206
219
  testStartLine
@@ -212,6 +225,7 @@ function getOnTestHandler (isMain) {
212
225
 
213
226
  testInfo.isNew = isNew
214
227
  testInfo.isEfdRetry = isEfdRetry
228
+ testInfo.isAttemptToFix = isAttemptToFix
215
229
  testInfo.isDisabled = isDisabled
216
230
  testInfo.isQuarantined = isQuarantined
217
231
  // We want to store the result of the new tests
@@ -224,7 +238,7 @@ function getOnTestHandler (isMain) {
224
238
  }
225
239
  }
226
240
 
227
- if (isDisabled) {
241
+ if (!isAttemptToFix && isDisabled) {
228
242
  test.pending = true
229
243
  }
230
244
 
@@ -234,7 +248,7 @@ function getOnTestHandler (isMain) {
234
248
  }
235
249
  }
236
250
 
237
- function getOnTestEndHandler () {
251
+ function getOnTestEndHandler (config) {
238
252
  return async function (test) {
239
253
  const asyncResource = getTestAsyncResource(test)
240
254
  const status = getTestStatus(test)
@@ -249,13 +263,40 @@ function getOnTestEndHandler () {
249
263
  })
250
264
  }
251
265
 
266
+ let hasFailedAllRetries = false
267
+ let attemptToFixPassed = false
268
+
269
+ const testName = getTestFullName(test)
270
+
271
+ if (!testsStatuses.get(testName)) {
272
+ testsStatuses.set(testName, [status])
273
+ } else {
274
+ testsStatuses.get(testName).push(status)
275
+ }
276
+ const testStatuses = testsStatuses.get(testName)
277
+
278
+ const isLastAttempt = testStatuses.length === config.testManagementAttemptToFixRetries + 1
279
+
280
+ if (test._ddIsAttemptToFix && isLastAttempt) {
281
+ if (testStatuses.every(status => status === 'fail')) {
282
+ hasFailedAllRetries = true
283
+ } else if (testStatuses.every(status => status === 'pass')) {
284
+ attemptToFixPassed = true
285
+ }
286
+ }
287
+
288
+ const isAttemptToFixRetry = test._ddIsAttemptToFix && testStatuses.length > 1
289
+
252
290
  // if there are afterEach to be run, we don't finish the test yet
253
291
  if (asyncResource && !getAfterEachHooks(test).length) {
254
292
  asyncResource.runInAsyncScope(() => {
255
293
  testFinishCh.publish({
256
294
  status,
257
295
  hasBeenRetried: isMochaRetry(test),
258
- isLastRetry: getIsLastRetry(test)
296
+ isLastRetry: getIsLastRetry(test),
297
+ hasFailedAllRetries,
298
+ attemptToFixPassed,
299
+ isAttemptToFixRetry
259
300
  })
260
301
  })
261
302
  }
@@ -374,33 +415,50 @@ function getOnPendingHandler () {
374
415
  }
375
416
  }
376
417
 
377
- // Hook to add retries to tests if EFD is enabled
418
+ // Hook to add retries to tests if Test Management or EFD is enabled
378
419
  function getRunTestsWrapper (runTests, config) {
379
- return function (suite, fn) {
420
+ return function (suite) {
421
+ if (config.isTestManagementTestsEnabled) {
422
+ suite.tests.forEach((test) => {
423
+ const { isAttemptToFix, isDisabled, isQuarantined } = getTestProperties(test, config.testManagementTests)
424
+ if (isAttemptToFix && !test.isPending()) {
425
+ test._ddIsAttemptToFix = true
426
+ test._ddIsDisabled = isDisabled
427
+ test._ddIsQuarantined = isQuarantined
428
+ // This is needed to know afterwards which ones have been retried to ignore its result
429
+ testsAttemptToFix.add(test)
430
+ retryTest(
431
+ test,
432
+ config.testManagementAttemptToFixRetries,
433
+ addAttemptToFixStringToTestName,
434
+ ['_ddIsAttemptToFix', isDisabled && '_ddIsDisabled', isQuarantined && '_ddIsQuarantined']
435
+ )
436
+ } else if (isDisabled) {
437
+ test._ddIsDisabled = true
438
+ } else if (isQuarantined) {
439
+ testsQuarantined.add(test)
440
+ test._ddIsQuarantined = true
441
+ }
442
+ })
443
+ }
444
+
380
445
  if (config.isKnownTestsEnabled) {
381
446
  // by the time we reach `this.on('test')`, it is too late. We need to add retries here
382
447
  suite.tests.forEach(test => {
383
448
  if (!test.isPending() && isNewTest(test, config.knownTests)) {
384
449
  test._ddIsNew = true
385
450
  if (config.isEarlyFlakeDetectionEnabled) {
386
- retryTest(test, config.earlyFlakeDetectionNumRetries)
451
+ retryTest(
452
+ test,
453
+ config.earlyFlakeDetectionNumRetries,
454
+ addEfdStringToTestName,
455
+ ['_ddIsNew', '_ddIsEfdRetry']
456
+ )
387
457
  }
388
458
  }
389
459
  })
390
460
  }
391
461
 
392
- if (config.isTestManagementTestsEnabled) {
393
- suite.tests.forEach(test => {
394
- const { isDisabled, isQuarantined } = getTestProperties(test, config.testManagementTests)
395
- if (isDisabled) {
396
- test._ddIsDisabled = true
397
- } else if (isQuarantined) {
398
- testsQuarantined.add(test)
399
- test._ddIsQuarantined = true
400
- }
401
- })
402
- }
403
-
404
462
  return runTests.apply(this, arguments)
405
463
  }
406
464
  }
@@ -408,7 +466,6 @@ function getRunTestsWrapper (runTests, config) {
408
466
  module.exports = {
409
467
  isNewTest,
410
468
  getTestProperties,
411
- retryTest,
412
469
  getSuitesByTestFile,
413
470
  isMochaRetry,
414
471
  getTestFullName,
@@ -427,5 +484,7 @@ module.exports = {
427
484
  testFileToSuiteAr,
428
485
  getRunTestsWrapper,
429
486
  newTests,
430
- testsQuarantined
487
+ testsQuarantined,
488
+ testsAttemptToFix,
489
+ testsStatuses
431
490
  }
@@ -36,6 +36,8 @@ addHook({
36
36
  }
37
37
  if (this.options._ddIsTestManagementTestsEnabled) {
38
38
  config.isTestManagementTestsEnabled = true
39
+ // TODO: attempt to fix does not work in parallel mode yet
40
+ // config.testManagementAttemptToFixRetries = this.options._ddTestManagementAttemptToFixRetries
39
41
  config.testManagementTests = this.options._ddTestManagementTests
40
42
  delete this.options._ddIsTestManagementTestsEnabled
41
43
  delete this.options._ddTestManagementTests
@@ -64,7 +66,7 @@ addHook({
64
66
  })
65
67
  this.on('test', getOnTestHandler(false))
66
68
 
67
- this.on('test end', getOnTestEndHandler())
69
+ this.on('test end', getOnTestEndHandler(config))
68
70
 
69
71
  // If the hook passes, 'hook end' will be emitted. Otherwise, 'fail' will be emitted
70
72
  this.on('hook end', getOnHookEndHandler())
@@ -22,6 +22,7 @@ const testToAr = new WeakMap()
22
22
  const testSuiteToAr = new Map()
23
23
  const testSuiteToTestStatuses = new Map()
24
24
  const testSuiteToErrors = new Map()
25
+ const testsToTestStatuses = new Map()
25
26
  const testSessionAsyncResource = new AsyncResource('bound-anonymous-fn')
26
27
 
27
28
  let applyRepeatEachIndex = null
@@ -43,7 +44,9 @@ let isFlakyTestRetriesEnabled = false
43
44
  let flakyTestRetriesCount = 0
44
45
  let knownTests = {}
45
46
  let isTestManagementTestsEnabled = false
47
+ let testManagementAttemptToFixRetries = 0
46
48
  let testManagementTests = {}
49
+ const quarantinedOrDisabledTestsAttemptToFix = []
47
50
  let rootDir = ''
48
51
  const MINIMUM_SUPPORTED_VERSION_RANGE_EFD = '>=1.38.0'
49
52
 
@@ -51,10 +54,9 @@ function getTestProperties (test) {
51
54
  const testName = getTestFullname(test)
52
55
  const testSuite = getTestSuitePath(test._requireFile, rootDir)
53
56
 
54
- const { disabled, quarantined } =
57
+ const { attempt_to_fix: attemptToFix, disabled, quarantined } =
55
58
  testManagementTests?.playwright?.suites?.[testSuite]?.tests?.[testName]?.properties || {}
56
-
57
- return { disabled, quarantined }
59
+ return { attemptToFix, disabled, quarantined }
58
60
  }
59
61
 
60
62
  function isNewTest (test) {
@@ -73,16 +75,19 @@ function getSuiteType (test, type) {
73
75
  }
74
76
 
75
77
  // Copy of Suite#_deepClone but with a function to filter tests
76
- function deepCloneSuite (suite, filterTest) {
78
+ function deepCloneSuite (suite, filterTest, tags = []) {
77
79
  const copy = suite._clone()
78
80
  for (const entry of suite._entries) {
79
81
  if (entry.constructor.name === 'Suite') {
80
- copy._addSuite(deepCloneSuite(entry, filterTest))
82
+ copy._addSuite(deepCloneSuite(entry, filterTest, tags))
81
83
  } else {
82
84
  if (filterTest(entry)) {
83
85
  const copiedTest = entry._clone()
84
- copiedTest._ddIsNew = true
85
- copiedTest._ddIsEfdRetry = true
86
+ tags.forEach(tag => {
87
+ if (tag) {
88
+ copiedTest[tag] = true
89
+ }
90
+ })
86
91
  copy._addTest(copiedTest)
87
92
  }
88
93
  }
@@ -276,6 +281,11 @@ function testBeginHandler (test, browserName) {
276
281
  })
277
282
  }
278
283
 
284
+ // We disable retries by default if attemptToFix is true
285
+ if (getTestProperties(test).attemptToFix) {
286
+ test.retries = 0
287
+ }
288
+
279
289
  const testAsyncResource = new AsyncResource('bound-anonymous-fn')
280
290
  testToAr.set(test, testAsyncResource)
281
291
  testAsyncResource.runInAsyncScope(() => {
@@ -288,7 +298,6 @@ function testBeginHandler (test, browserName) {
288
298
  })
289
299
  })
290
300
  }
291
-
292
301
  function testEndHandler (test, annotations, testStatus, error, isTimeout) {
293
302
  let annotationTags
294
303
  if (annotations.length) {
@@ -305,6 +314,27 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout) {
305
314
  return
306
315
  }
307
316
 
317
+ const testFullName = getTestFullname(test)
318
+ const testFqn = `${testSuiteAbsolutePath} ${testFullName}`
319
+ const testStatuses = testsToTestStatuses.get(testFqn) || []
320
+
321
+ if (testStatuses.length === 0) {
322
+ testsToTestStatuses.set(testFqn, [testStatus])
323
+ } else {
324
+ testStatuses.push(testStatus)
325
+ }
326
+
327
+ let hasFailedAllRetries = false
328
+ let hasPassedAttemptToFixRetries = false
329
+
330
+ if (testStatuses.length === testManagementAttemptToFixRetries + 1) {
331
+ if (testStatuses.every(status => status === 'fail')) {
332
+ hasFailedAllRetries = true
333
+ } else if (testStatuses.every(status => status === 'pass')) {
334
+ hasPassedAttemptToFixRetries = true
335
+ }
336
+ }
337
+
308
338
  const testResult = results[results.length - 1]
309
339
  const testAsyncResource = testToAr.get(test)
310
340
  testAsyncResource.runInAsyncScope(() => {
@@ -315,8 +345,12 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout) {
315
345
  error,
316
346
  extraTags: annotationTags,
317
347
  isNew: test._ddIsNew,
348
+ isAttemptToFix: test._ddIsAttemptToFix,
349
+ isAttemptToFixRetry: test._ddIsAttemptToFixRetry,
318
350
  isQuarantined: test._ddIsQuarantined,
319
- isEfdRetry: test._ddIsEfdRetry
351
+ isEfdRetry: test._ddIsEfdRetry,
352
+ hasFailedAllRetries,
353
+ hasPassedAttemptToFixRetries
320
354
  })
321
355
  })
322
356
 
@@ -338,7 +372,6 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout) {
338
372
  // Last test, we finish the suite
339
373
  if (!remainingTestsByFile[testSuiteAbsolutePath].length) {
340
374
  const testStatuses = testSuiteToTestStatuses.get(testSuiteAbsolutePath)
341
-
342
375
  let testSuiteStatus = 'pass'
343
376
  if (testStatuses.some(status => status === 'fail')) {
344
377
  testSuiteStatus = 'fail'
@@ -445,6 +478,7 @@ function runnerHook (runnerExport, playwrightVersion) {
445
478
  isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
446
479
  flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
447
480
  isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
481
+ testManagementAttemptToFixRetries = libraryConfig.testManagementAttemptToFixRetries
448
482
  }
449
483
  } catch (e) {
450
484
  isEarlyFlakeDetectionEnabled = false
@@ -485,7 +519,10 @@ function runnerHook (runnerExport, playwrightVersion) {
485
519
 
486
520
  const projects = getProjectsFromRunner(this)
487
521
 
488
- if (isFlakyTestRetriesEnabled && flakyTestRetriesCount > 0) {
522
+ const shouldSetRetries = isFlakyTestRetriesEnabled &&
523
+ flakyTestRetriesCount > 0 &&
524
+ !isTestManagementTestsEnabled
525
+ if (shouldSetRetries) {
489
526
  projects.forEach(project => {
490
527
  if (project.retries === 0) { // Only if it hasn't been set by the user
491
528
  project.retries = flakyTestRetriesCount
@@ -493,7 +530,7 @@ function runnerHook (runnerExport, playwrightVersion) {
493
530
  })
494
531
  }
495
532
 
496
- const runAllTestsReturn = await runAllTests.apply(this, arguments)
533
+ let runAllTestsReturn = await runAllTests.apply(this, arguments)
497
534
 
498
535
  Object.values(remainingTestsByFile).forEach(tests => {
499
536
  // `tests` should normally be empty, but if it isn't,
@@ -508,6 +545,26 @@ function runnerHook (runnerExport, playwrightVersion) {
508
545
 
509
546
  const sessionStatus = runAllTestsReturn.status || runAllTestsReturn
510
547
 
548
+ if (isTestManagementTestsEnabled && sessionStatus === 'failed') {
549
+ let totalFailedTestCount = 0
550
+ let totalAttemptToFixFailedTestCount = 0
551
+
552
+ for (const testStatuses of testsToTestStatuses.values()) {
553
+ totalFailedTestCount += testStatuses.filter(status => status === 'fail').length
554
+ }
555
+
556
+ for (const test of quarantinedOrDisabledTestsAttemptToFix) {
557
+ const fullname = getTestFullname(test)
558
+ const fqn = `${test._requireFile} ${fullname}`
559
+ const testStatuses = testsToTestStatuses.get(fqn)
560
+ totalAttemptToFixFailedTestCount += testStatuses.filter(status => status === 'fail').length
561
+ }
562
+
563
+ if (totalFailedTestCount === totalAttemptToFixFailedTestCount) {
564
+ runAllTestsReturn = 'passed'
565
+ }
566
+ }
567
+
511
568
  const flushWait = new Promise(resolve => {
512
569
  onDone = resolve
513
570
  })
@@ -608,9 +665,28 @@ addHook({
608
665
  const testProperties = getTestProperties(test)
609
666
  if (testProperties.disabled) {
610
667
  test._ddIsDisabled = true
611
- test.expectedStatus = 'skipped'
612
668
  } else if (testProperties.quarantined) {
613
669
  test._ddIsQuarantined = true
670
+ }
671
+ if (testProperties.attemptToFix) {
672
+ test._ddIsAttemptToFix = true
673
+ const fileSuite = getSuiteType(test, 'file')
674
+ const projectSuite = getSuiteType(test, 'project')
675
+ const isAttemptToFix = test => getTestProperties(test).attemptToFix
676
+ for (let repeatEachIndex = 1; repeatEachIndex <= testManagementAttemptToFixRetries; repeatEachIndex++) {
677
+ const copyFileSuite = deepCloneSuite(fileSuite, isAttemptToFix, [
678
+ testProperties.disabled && '_ddIsDisabled',
679
+ testProperties.quarantined && '_ddIsQuarantined',
680
+ '_ddIsAttemptToFix',
681
+ '_ddIsAttemptToFixRetry'
682
+ ])
683
+ applyRepeatEachIndex(projectSuite._fullProject, copyFileSuite, repeatEachIndex + 1)
684
+ projectSuite._addSuite(copyFileSuite)
685
+ }
686
+ if (testProperties.disabled || testProperties.quarantined) {
687
+ quarantinedOrDisabledTestsAttemptToFix.push(test)
688
+ }
689
+ } else if (testProperties.disabled || testProperties.quarantined) {
614
690
  test.expectedStatus = 'skipped'
615
691
  }
616
692
  }
@@ -619,18 +695,22 @@ addHook({
619
695
  if (isKnownTestsEnabled) {
620
696
  const newTests = allTests.filter(isNewTest)
621
697
 
622
- newTests.forEach(newTest => {
698
+ for (const newTest of newTests) {
699
+ // No need to filter out attempt to fix tests here because attempt to fix tests are never new
623
700
  newTest._ddIsNew = true
624
701
  if (isEarlyFlakeDetectionEnabled && newTest.expectedStatus !== 'skipped') {
625
702
  const fileSuite = getSuiteType(newTest, 'file')
626
703
  const projectSuite = getSuiteType(newTest, 'project')
627
- for (let repeatEachIndex = 0; repeatEachIndex < earlyFlakeDetectionNumRetries; repeatEachIndex++) {
628
- const copyFileSuite = deepCloneSuite(fileSuite, isNewTest)
704
+ for (let repeatEachIndex = 1; repeatEachIndex <= earlyFlakeDetectionNumRetries; repeatEachIndex++) {
705
+ const copyFileSuite = deepCloneSuite(fileSuite, isNewTest, [
706
+ '_ddIsNew',
707
+ '_ddIsEfdRetry'
708
+ ])
629
709
  applyRepeatEachIndex(projectSuite._fullProject, copyFileSuite, repeatEachIndex + 1)
630
710
  projectSuite._addSuite(copyFileSuite)
631
711
  }
632
712
  }
633
- })
713
+ }
634
714
  }
635
715
 
636
716
  return rootSuite
@@ -5,6 +5,7 @@ const pathToRegExp = require('path-to-regexp')
5
5
  const shimmer = require('../../datadog-shimmer')
6
6
  const { addHook, channel } = require('./helpers/instrument')
7
7
 
8
+ // TODO: Move this function to a shared file between Express and Router
8
9
  function createWrapRouterMethod (name) {
9
10
  const enterChannel = channel(`apm:${name}:middleware:enter`)
10
11
  const exitChannel = channel(`apm:${name}:middleware:exit`)
@@ -16,7 +16,7 @@ addHook({ name: 'tedious', versions: ['>=1.0.0'] }, tedious => {
16
16
  return makeRequest.apply(this, arguments)
17
17
  }
18
18
 
19
- const queryOrProcedure = getQueryOrProcedure(request)
19
+ const [queryOrProcedure, queryParent, queryField] = getQueryOrProcedure(request)
20
20
 
21
21
  if (!queryOrProcedure) {
22
22
  return makeRequest.apply(this, arguments)
@@ -28,7 +28,9 @@ addHook({ name: 'tedious', versions: ['>=1.0.0'] }, tedious => {
28
28
  const connectionConfig = this.config
29
29
 
30
30
  return asyncResource.runInAsyncScope(() => {
31
- startCh.publish({ queryOrProcedure, connectionConfig })
31
+ const payload = { queryOrProcedure, connectionConfig }
32
+ startCh.publish(payload)
33
+ queryParent[queryField] = payload.sql
32
34
 
33
35
  const cb = callbackResource.bind(request.callback, request)
34
36
  request.callback = asyncResource.bind(function (error) {
@@ -53,14 +55,15 @@ addHook({ name: 'tedious', versions: ['>=1.0.0'] }, tedious => {
53
55
  return tedious
54
56
  })
55
57
 
58
+ // returns [queryOrProcedure, parentObjectToSet, propertyNameToSet]
56
59
  function getQueryOrProcedure (request) {
57
- if (!request.parameters) return
58
-
59
- const statement = request.parametersByName.statement || request.parametersByName.stmt
60
-
61
- if (!statement) {
62
- return request.sqlTextOrProcedure
60
+ if (!request.parameters) return [null]
61
+
62
+ if (request.parametersByName.statement) {
63
+ return [request.parametersByName.statement.value, request.parametersByName.statement, 'value']
64
+ } else if (request.parametersByName.stmt) {
65
+ return [request.parametersByName.stmt.value, request.parametersByName.stmt, 'value']
66
+ } else {
67
+ return [request.sqlTextOrProcedure, request, 'sqlTextOrProcedure']
63
68
  }
64
-
65
- return statement.value
66
69
  }