dd-trace 5.116.0 → 5.118.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.
- package/ci/diagnose.js +162 -21
- package/ci/init.js +0 -1
- package/ci/runbook.md +187 -180
- package/ci/test-optimization-validation/approval-artifacts.js +3 -1
- package/ci/test-optimization-validation/approval.js +52 -15
- package/ci/test-optimization-validation/ci-command-candidate.js +26 -32
- package/ci/test-optimization-validation/ci-discovery.js +1 -1
- package/ci/test-optimization-validation/ci-package-scripts.js +141 -0
- package/ci/test-optimization-validation/ci-remediation.js +112 -39
- package/ci/test-optimization-validation/cli.js +516 -681
- package/ci/test-optimization-validation/command-blocker.js +188 -33
- package/ci/test-optimization-validation/command-output-policy.js +3 -26
- package/ci/test-optimization-validation/command-runner.js +189 -245
- package/ci/test-optimization-validation/environment.js +90 -0
- package/ci/test-optimization-validation/executable.js +159 -388
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +119 -0
- package/ci/test-optimization-validation/framework-adapters/cypress.js +107 -0
- package/ci/test-optimization-validation/framework-adapters/playwright.js +181 -0
- package/ci/test-optimization-validation/generated-files.js +75 -13
- package/ci/test-optimization-validation/generated-test-contract.js +283 -0
- package/ci/test-optimization-validation/generated-verifier.js +49 -16
- package/ci/test-optimization-validation/literal-environment.js +57 -0
- package/ci/test-optimization-validation/manifest-loader.js +2 -22
- package/ci/test-optimization-validation/manifest-scaffold.js +816 -515
- package/ci/test-optimization-validation/manifest-schema.js +494 -702
- package/ci/test-optimization-validation/offline-fixtures.js +4 -1
- package/ci/test-optimization-validation/plan-writer.js +243 -984
- package/ci/test-optimization-validation/preflight-runner.js +49 -55
- package/ci/test-optimization-validation/redaction.js +2 -1
- package/ci/test-optimization-validation/report-writer.js +391 -1357
- package/ci/test-optimization-validation/result-semantics.js +86 -0
- package/ci/test-optimization-validation/runner-command.js +249 -0
- package/ci/test-optimization-validation/runner-contract.js +664 -0
- package/ci/test-optimization-validation/scenarios/auto-test-retries.js +29 -3
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +240 -543
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +450 -670
- package/ci/test-optimization-validation/scenarios/early-flake-detection.js +4 -3
- package/ci/test-optimization-validation/scenarios/helpers.js +67 -9
- package/ci/test-optimization-validation/scenarios/test-management.js +4 -3
- package/ci/test-optimization-validation/source-text.js +87 -0
- package/ci/test-optimization-validation/test-output.js +12 -22
- package/ext/tags.d.ts +1 -0
- package/index.d.ts +7 -0
- package/package.json +3 -3
- package/packages/datadog-instrumentations/src/cucumber.js +11 -12
- package/packages/datadog-instrumentations/src/helpers/channel.js +74 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/openai-agents.js +31 -0
- package/packages/datadog-instrumentations/src/jest.js +18 -17
- package/packages/datadog-instrumentations/src/mocha/main.js +79 -30
- package/packages/datadog-instrumentations/src/openai-agents.js +159 -0
- package/packages/datadog-instrumentations/src/playwright.js +3 -24
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1 -1
- package/packages/datadog-instrumentations/src/vitest-main.js +14 -26
- package/packages/datadog-instrumentations/src/vitest-util.js +0 -7
- package/packages/datadog-instrumentations/src/vitest-worker.js +5 -8
- package/packages/datadog-plugin-cucumber/src/index.js +2 -1
- package/packages/datadog-plugin-mocha/src/index.js +2 -1
- package/packages/datadog-plugin-openai-agents/src/index.js +74 -0
- package/packages/datadog-plugin-openai-agents/src/integration.js +503 -0
- package/packages/datadog-plugin-openai-agents/src/processor.js +108 -0
- package/packages/datadog-plugin-openai-agents/src/util.js +60 -0
- package/packages/datadog-plugin-playwright/src/index.js +4 -1
- package/packages/datadog-plugin-vitest/src/index.js +5 -5
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +48 -49
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +161 -33
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +6 -4
- package/packages/dd-trace/src/config/generated-config-types.d.ts +2 -0
- package/packages/dd-trace/src/config/supported-configurations.json +7 -0
- package/packages/dd-trace/src/encode/span-stats.js +7 -1
- package/packages/dd-trace/src/exporter.js +16 -9
- package/packages/dd-trace/src/llmobs/index.js +9 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +104 -22
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +5 -9
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +20 -0
- package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +321 -0
- package/packages/dd-trace/src/llmobs/sdk.js +2 -1
- package/packages/dd-trace/src/llmobs/span_processor.js +7 -2
- package/packages/dd-trace/src/llmobs/tagger.js +23 -3
- package/packages/dd-trace/src/llmobs/util.js +56 -3
- package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +3 -6
- package/packages/dd-trace/src/openfeature/configuration_source.js +5 -8
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +5 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +9 -3
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/outbound.js +4 -1
- package/packages/dd-trace/src/profiling/profilers/space.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/wall.js +1 -0
- package/packages/dd-trace/src/span_processor.js +1 -1
- package/packages/dd-trace/src/span_stats.js +22 -4
- package/ci/test-optimization-validation/command-suitability.js +0 -471
- package/ci/test-optimization-validation/init-probe-preload.js +0 -163
- package/ci/test-optimization-validation/init-probe.js +0 -246
- package/ci/test-optimization-validation/late-initialization.js +0 -63
- package/ci/test-optimization-validation/local-command.js +0 -163
- package/ci/test-optimization-validation/setup-runner.js +0 -97
- package/ci/test-optimization-validation-manifest.schema.json +0 -1
|
@@ -46,6 +46,10 @@ const {
|
|
|
46
46
|
addCoverageBackfillUntestedFiles,
|
|
47
47
|
getCoverageBackfillFiles,
|
|
48
48
|
} = require('./jest/coverage-backfill')
|
|
49
|
+
const {
|
|
50
|
+
getChannelPromise,
|
|
51
|
+
publishWithCompletion,
|
|
52
|
+
} = require('./helpers/channel')
|
|
49
53
|
const { addHook, channel } = require('./helpers/instrument')
|
|
50
54
|
|
|
51
55
|
const testSessionStartCh = channel('ci:jest:session:start')
|
|
@@ -1971,6 +1975,9 @@ function shouldFinishBailTestSession (globalConfig, results) {
|
|
|
1971
1975
|
return !!globalConfig?.bail && getNumBailFailures(results) >= globalConfig.bail
|
|
1972
1976
|
}
|
|
1973
1977
|
|
|
1978
|
+
/**
|
|
1979
|
+
* @param {Record<string, unknown> & { onDone?: () => void }} payload
|
|
1980
|
+
*/
|
|
1974
1981
|
async function waitForTestSessionFinish (payload) {
|
|
1975
1982
|
if (!testSessionFinishCh.hasSubscribers || hasFinishedTestSession) return
|
|
1976
1983
|
|
|
@@ -1978,8 +1985,9 @@ async function waitForTestSessionFinish (payload) {
|
|
|
1978
1985
|
|
|
1979
1986
|
let timeoutId
|
|
1980
1987
|
|
|
1988
|
+
let onDone
|
|
1981
1989
|
const flushPromise = new Promise((resolve) => {
|
|
1982
|
-
|
|
1990
|
+
onDone = () => {
|
|
1983
1991
|
clearTimeout(timeoutId)
|
|
1984
1992
|
resolve()
|
|
1985
1993
|
}
|
|
@@ -1992,7 +2000,7 @@ async function waitForTestSessionFinish (payload) {
|
|
|
1992
2000
|
timeoutId.unref?.()
|
|
1993
2001
|
})
|
|
1994
2002
|
|
|
1995
|
-
testSessionFinishCh
|
|
2003
|
+
publishWithCompletion(testSessionFinishCh, payload, onDone)
|
|
1996
2004
|
|
|
1997
2005
|
const waitingResult = await Promise.race([flushPromise, timeoutPromise])
|
|
1998
2006
|
|
|
@@ -2157,12 +2165,6 @@ function getWrappedScheduleTests (scheduleTests, frameworkVersion) {
|
|
|
2157
2165
|
}
|
|
2158
2166
|
}
|
|
2159
2167
|
|
|
2160
|
-
function getChannelPromise (channelToPublishTo, payload = {}) {
|
|
2161
|
-
return new Promise(resolve => {
|
|
2162
|
-
channelToPublishTo.publish({ ...payload, onDone: resolve })
|
|
2163
|
-
})
|
|
2164
|
-
}
|
|
2165
|
-
|
|
2166
2168
|
function searchSourceWrapper (searchSourcePackage, frameworkVersion) {
|
|
2167
2169
|
const SearchSource = searchSourcePackage.default ?? searchSourcePackage
|
|
2168
2170
|
|
|
@@ -2517,9 +2519,7 @@ function getCliWrapper (isNewJestVersion) {
|
|
|
2517
2519
|
|
|
2518
2520
|
if (codeCoverageReportCh.hasSubscribers) {
|
|
2519
2521
|
const rootDir = result.globalConfig?.rootDir || process.cwd()
|
|
2520
|
-
await
|
|
2521
|
-
codeCoverageReportCh.publish({ rootDir, onDone: resolve })
|
|
2522
|
-
})
|
|
2522
|
+
await getChannelPromise(codeCoverageReportCh, { rootDir })
|
|
2523
2523
|
}
|
|
2524
2524
|
|
|
2525
2525
|
logSessionSummary(ignoredFailuresSummary, getAttemptToFixExecutionsFromJestResults(result))
|
|
@@ -2537,6 +2537,10 @@ function shouldWaitForTestSuiteFinish (environment) {
|
|
|
2537
2537
|
return isJestWorker && environment.globalConfig?.workerIdleMemoryLimit !== undefined
|
|
2538
2538
|
}
|
|
2539
2539
|
|
|
2540
|
+
/**
|
|
2541
|
+
* @param {Record<string, unknown>} payload
|
|
2542
|
+
* @param {boolean} waitForFinish
|
|
2543
|
+
*/
|
|
2540
2544
|
function publishTestSuiteFinish (payload, waitForFinish) {
|
|
2541
2545
|
if (!testSuiteFinishCh.hasSubscribers) return
|
|
2542
2546
|
|
|
@@ -2545,12 +2549,9 @@ function publishTestSuiteFinish (payload, waitForFinish) {
|
|
|
2545
2549
|
return
|
|
2546
2550
|
}
|
|
2547
2551
|
|
|
2548
|
-
return
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
waitForFinish,
|
|
2552
|
-
onDone: resolve,
|
|
2553
|
-
})
|
|
2552
|
+
return getChannelPromise(testSuiteFinishCh, {
|
|
2553
|
+
...payload,
|
|
2554
|
+
waitForFinish,
|
|
2554
2555
|
})
|
|
2555
2556
|
}
|
|
2556
2557
|
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
const { createCoverageMap } = require('../../../../vendor/dist/istanbul-lib-coverage')
|
|
4
4
|
const satisfies = require('../../../../vendor/dist/semifies')
|
|
5
5
|
const { DD_MAJOR } = require('../../../../version')
|
|
6
|
+
const {
|
|
7
|
+
getRunStoresPromise,
|
|
8
|
+
publishWithCompletion,
|
|
9
|
+
runStoresWithCompletion,
|
|
10
|
+
} = require('../helpers/channel')
|
|
6
11
|
const { addHook, channel } = require('../helpers/instrument')
|
|
7
12
|
const shimmer = require('../../../datadog-shimmer')
|
|
8
13
|
const { isMarkedAsUnskippable } = require('../../../datadog-plugin-jest/src/util')
|
|
@@ -240,6 +245,41 @@ function resetSuiteSkippingRunState () {
|
|
|
240
245
|
writeCoverageBackfillToCache({})
|
|
241
246
|
}
|
|
242
247
|
|
|
248
|
+
/**
|
|
249
|
+
* @param {((failures: number) => void) | undefined} callback
|
|
250
|
+
* @returns {{ onRunDone: (failures: number) => void, onFlushDone: () => void }}
|
|
251
|
+
*/
|
|
252
|
+
function getRunCompletionCallbacks (callback) {
|
|
253
|
+
let failures
|
|
254
|
+
let hasRunFinished = false
|
|
255
|
+
let hasFlushFinished = false
|
|
256
|
+
let hasCompleted = false
|
|
257
|
+
const onDone = callback || (() => {})
|
|
258
|
+
|
|
259
|
+
const completeIfReady = () => {
|
|
260
|
+
if (hasCompleted || !hasRunFinished || !hasFlushFinished) return
|
|
261
|
+
|
|
262
|
+
hasCompleted = true
|
|
263
|
+
onDone(failures)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
onRunDone: (runFailures) => {
|
|
268
|
+
if (hasRunFinished) return
|
|
269
|
+
|
|
270
|
+
failures = runFailures
|
|
271
|
+
hasRunFinished = true
|
|
272
|
+
completeIfReady()
|
|
273
|
+
},
|
|
274
|
+
onFlushDone: () => {
|
|
275
|
+
if (hasFlushFinished) return
|
|
276
|
+
|
|
277
|
+
hasFlushFinished = true
|
|
278
|
+
completeIfReady()
|
|
279
|
+
},
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
243
283
|
function getOnStartHandler (frameworkVersion) {
|
|
244
284
|
return function () {
|
|
245
285
|
const processArgv = process.argv.slice(2).join(' ')
|
|
@@ -251,7 +291,12 @@ function getOnStartHandler (frameworkVersion) {
|
|
|
251
291
|
}
|
|
252
292
|
}
|
|
253
293
|
|
|
254
|
-
|
|
294
|
+
/**
|
|
295
|
+
* @param {boolean} isParallel
|
|
296
|
+
* @param {() => void} onDone
|
|
297
|
+
* @returns {() => void}
|
|
298
|
+
*/
|
|
299
|
+
function getOnEndHandler (isParallel, onDone) {
|
|
255
300
|
return function () {
|
|
256
301
|
let status = 'pass'
|
|
257
302
|
let error
|
|
@@ -337,7 +382,7 @@ function getOnEndHandler (isParallel) {
|
|
|
337
382
|
global.__coverage__ = fromCoverageMapToCoverage(originalCoverageMap)
|
|
338
383
|
}
|
|
339
384
|
|
|
340
|
-
testSessionFinishCh
|
|
385
|
+
publishWithCompletion(testSessionFinishCh, {
|
|
341
386
|
status,
|
|
342
387
|
isSuitesSkipped,
|
|
343
388
|
testCodeCoverageLinesTotal,
|
|
@@ -350,19 +395,13 @@ function getOnEndHandler (isParallel) {
|
|
|
350
395
|
isEarlyFlakeDetectionFaulty: config.isEarlyFlakeDetectionFaulty,
|
|
351
396
|
isTestManagementEnabled: config.isTestManagementTestsEnabled,
|
|
352
397
|
isParallel,
|
|
353
|
-
})
|
|
398
|
+
}, onDone)
|
|
354
399
|
|
|
355
400
|
logTestOptimizationSummary({ attemptToFixExecutions, newTestsWithDynamicNames })
|
|
356
401
|
loggedAttemptToFixTests.clear()
|
|
357
402
|
}
|
|
358
403
|
}
|
|
359
404
|
|
|
360
|
-
function getRunStoresPromise (channelToPublishTo, ctx) {
|
|
361
|
-
return new Promise(resolve => {
|
|
362
|
-
channelToPublishTo.runStores({ ...ctx, onDone: resolve }, () => {})
|
|
363
|
-
})
|
|
364
|
-
}
|
|
365
|
-
|
|
366
405
|
function applyKnownTestsResponse ({ err, knownTests }) {
|
|
367
406
|
if (err) {
|
|
368
407
|
config.knownTests = []
|
|
@@ -395,13 +434,14 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
|
|
|
395
434
|
let skippableSuitesResponse
|
|
396
435
|
resetSuiteSkippingRunState()
|
|
397
436
|
|
|
398
|
-
const onReceivedSkippableSuites = ({
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
437
|
+
const onReceivedSkippableSuites = (response) => {
|
|
438
|
+
const {
|
|
439
|
+
err,
|
|
440
|
+
skippableSuites,
|
|
441
|
+
itrCorrelationId: responseItrCorrelationId,
|
|
442
|
+
skippableSuitesCoverage: responseSkippableSuitesCoverage,
|
|
443
|
+
} = response || {}
|
|
444
|
+
if (!response || err) {
|
|
405
445
|
suitesToSkip = []
|
|
406
446
|
skippableSuitesCoverage = {}
|
|
407
447
|
} else {
|
|
@@ -443,12 +483,12 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
|
|
|
443
483
|
return
|
|
444
484
|
}
|
|
445
485
|
|
|
446
|
-
ctx
|
|
447
|
-
skippableSuitesCh.runStores(ctx, () => {})
|
|
486
|
+
runStoresWithCompletion(skippableSuitesCh, ctx, onReceivedSkippableSuites)
|
|
448
487
|
}
|
|
449
488
|
|
|
450
|
-
const onReceivedImpactedTests = (
|
|
451
|
-
|
|
489
|
+
const onReceivedImpactedTests = (response) => {
|
|
490
|
+
const { err, modifiedFiles: receivedModifiedFiles } = response || {}
|
|
491
|
+
if (!response || err) {
|
|
452
492
|
config.modifiedFiles = []
|
|
453
493
|
config.isImpactedTestsEnabled = false
|
|
454
494
|
} else {
|
|
@@ -465,8 +505,7 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
|
|
|
465
505
|
|
|
466
506
|
const continueAfterTestRequests = () => {
|
|
467
507
|
if (config.isImpactedTestsEnabled) {
|
|
468
|
-
ctx
|
|
469
|
-
modifiedFilesCh.runStores(ctx, () => {})
|
|
508
|
+
runStoresWithCompletion(modifiedFilesCh, ctx, onReceivedImpactedTests)
|
|
470
509
|
} else if (config.isSuitesSkippingEnabled) {
|
|
471
510
|
requestSkippableSuites()
|
|
472
511
|
} else {
|
|
@@ -476,8 +515,14 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
|
|
|
476
515
|
}
|
|
477
516
|
}
|
|
478
517
|
|
|
479
|
-
const onReceivedConfiguration = (
|
|
480
|
-
|
|
518
|
+
const onReceivedConfiguration = (response) => {
|
|
519
|
+
const {
|
|
520
|
+
err,
|
|
521
|
+
isTestDynamicInstrumentationEnabled,
|
|
522
|
+
libraryConfig,
|
|
523
|
+
repositoryRoot,
|
|
524
|
+
} = response || {}
|
|
525
|
+
if (!response || err || !skippableSuitesCh.hasSubscribers || !knownTestsCh.hasSubscribers) {
|
|
481
526
|
return mochaGlobalRunCh.runStores(ctx, () => {
|
|
482
527
|
onFinishRequest()
|
|
483
528
|
})
|
|
@@ -526,9 +571,7 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
|
|
|
526
571
|
})
|
|
527
572
|
}
|
|
528
573
|
|
|
529
|
-
ctx
|
|
530
|
-
|
|
531
|
-
libraryConfigurationCh.runStores(ctx, () => {})
|
|
574
|
+
runStoresWithCompletion(libraryConfigurationCh, ctx, onReceivedConfiguration)
|
|
532
575
|
}
|
|
533
576
|
|
|
534
577
|
// In this hook we delay the execution with options.delay to grab library configuration,
|
|
@@ -641,6 +684,9 @@ addHook({
|
|
|
641
684
|
return run.apply(this, args)
|
|
642
685
|
}
|
|
643
686
|
|
|
687
|
+
const { onRunDone, onFlushDone } = getRunCompletionCallbacks(args[0])
|
|
688
|
+
args[0] = onRunDone
|
|
689
|
+
|
|
644
690
|
const { suitesByTestFile, numSuitesByTestFile } = getSuitesByTestFile(this.suite)
|
|
645
691
|
// Root-level tests (direct children of root, no describe wrapper) keyed by file.
|
|
646
692
|
// Populated during the root 'suite' event so the normal finish path can include them
|
|
@@ -752,7 +798,7 @@ addHook({
|
|
|
752
798
|
finishRootSuiteForFile(test.file)
|
|
753
799
|
}
|
|
754
800
|
|
|
755
|
-
const onEnd = getOnEndHandler(false)
|
|
801
|
+
const onEnd = getOnEndHandler(false, onFlushDone)
|
|
756
802
|
|
|
757
803
|
this.once('start', getOnStartHandler(frameworkVersion))
|
|
758
804
|
|
|
@@ -1041,8 +1087,11 @@ addHook({
|
|
|
1041
1087
|
return run.apply(this, arguments)
|
|
1042
1088
|
}
|
|
1043
1089
|
|
|
1090
|
+
const { onRunDone, onFlushDone } = getRunCompletionCallbacks(cb)
|
|
1091
|
+
arguments[0] = onRunDone
|
|
1092
|
+
|
|
1044
1093
|
this.once('start', getOnStartHandler(frameworkVersion))
|
|
1045
|
-
this.once('end', getOnEndHandler(true))
|
|
1094
|
+
this.once('end', getOnEndHandler(true, onFlushDone))
|
|
1046
1095
|
|
|
1047
1096
|
// Populate unskippable suites before config is fetched (matches serial mode at Mocha.prototype.run)
|
|
1048
1097
|
for (const filePath of files) {
|
|
@@ -1082,7 +1131,7 @@ addHook({
|
|
|
1082
1131
|
skippedSuites = skippedFiles
|
|
1083
1132
|
skippedSuitesCoverage = getSkippedSuitesCoverageForRun()
|
|
1084
1133
|
writeCoverageBackfillToCache(skippedSuitesCoverage, getCoverageRootDir())
|
|
1085
|
-
run.apply(this, [
|
|
1134
|
+
run.apply(this, [onRunDone, { files: filteredFiles }])
|
|
1086
1135
|
} else {
|
|
1087
1136
|
run.apply(this, arguments)
|
|
1088
1137
|
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { channel, tracingChannel } = require('dc-polyfill')
|
|
4
|
+
const shimmer = require('../../datadog-shimmer')
|
|
5
|
+
const { addHook, getHooks } = require('./helpers/instrument')
|
|
6
|
+
|
|
7
|
+
// `WeakSet` keyed by module exports — replaces the underscored
|
|
8
|
+
// `mod._datadogPatched` flag while keeping dedupe semantics. Mods are kept
|
|
9
|
+
// alive by `require.cache` anyway, so this doesn't add lifetime to anything.
|
|
10
|
+
const patchedMods = new WeakSet()
|
|
11
|
+
const modelBaseURLs = new WeakMap()
|
|
12
|
+
const chatCompletionsModelConstructorCh =
|
|
13
|
+
tracingChannel('orchestrion:@openai/agents-openai:OpenAIChatCompletionsModel_constructor')
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Capture the client base URL before the SDK stores the client in a #private field.
|
|
17
|
+
*
|
|
18
|
+
* @param {{ arguments?: [{ baseURL?: string }], self?: object }} ctx
|
|
19
|
+
*/
|
|
20
|
+
function captureChatCompletionsModelBaseURL (ctx) {
|
|
21
|
+
const baseURL = ctx.arguments?.[0]?.baseURL
|
|
22
|
+
if (ctx.self && typeof baseURL === 'string') modelBaseURLs.set(ctx.self, baseURL)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
chatCompletionsModelConstructorCh.end.subscribe(captureChatCompletionsModelBaseURL)
|
|
26
|
+
|
|
27
|
+
for (const hook of getHooks('@openai/agents-openai')) {
|
|
28
|
+
addHook(hook, moduleExports => moduleExports)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Plugin subscribes to this and registers its TracingProcessor when
|
|
32
|
+
// `@openai/agents` loads. Publishing from here keeps this file free of
|
|
33
|
+
// any cross-package import from the plugin.
|
|
34
|
+
const agentsCoreLoadedCh = channel('apm:openai-agents:agents-core:loaded')
|
|
35
|
+
|
|
36
|
+
// Plugin uses addBind on this channel so that legacyStorage.run(store, fn) wraps
|
|
37
|
+
// the model call — including async iterator advancement for streaming responses.
|
|
38
|
+
// This ensures the active dd-trace span is visible to the openai plugin when it
|
|
39
|
+
// creates its openai.request span, correctly parenting it under the agent span.
|
|
40
|
+
const modelStartCh = channel('apm:openai-agents:model:start')
|
|
41
|
+
|
|
42
|
+
// Tool.invoke runs inside agents-core's public function-span context. Bind the
|
|
43
|
+
// corresponding dd-trace tool span around that public invocation boundary so
|
|
44
|
+
// spans created by user tool code inherit it.
|
|
45
|
+
const toolStartCh = channel('apm:openai-agents:tool:start')
|
|
46
|
+
|
|
47
|
+
// Reference to the loaded @openai/agents module, captured in the first hook
|
|
48
|
+
// so that wrapResponseMethod can call getCurrentSpan() without an additional
|
|
49
|
+
// require (and without triggering n/no-missing-require on agents-core internals).
|
|
50
|
+
let agentsMod
|
|
51
|
+
|
|
52
|
+
// @openai/agents >=0.8.0 moved addTraceProcessor / getCurrentSpan out of the
|
|
53
|
+
// top-level re-exports. The new public surface uses getGlobalTraceProvider():
|
|
54
|
+
// provider.registerProcessor(processor) (replaces addTraceProcessor)
|
|
55
|
+
// provider.getCurrentSpan() (replaces getCurrentSpan)
|
|
56
|
+
// Both APIs are tried so this file works across the full supported version range.
|
|
57
|
+
// The plugin subscriber (index.js) handles processor registration via the channel.
|
|
58
|
+
function getCurrentSpan () {
|
|
59
|
+
if (typeof agentsMod?.getCurrentSpan === 'function') {
|
|
60
|
+
return agentsMod.getCurrentSpan()
|
|
61
|
+
}
|
|
62
|
+
if (typeof agentsMod?.getGlobalTraceProvider === 'function') {
|
|
63
|
+
return agentsMod.getGlobalTraceProvider().getCurrentSpan()
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function getCurrentSpanId () {
|
|
68
|
+
return getCurrentSpan()?.spanId
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
addHook({ name: '@openai/agents', versions: ['>=0.7.0'] }, (mod) => {
|
|
72
|
+
if (patchedMods.has(mod)) return mod
|
|
73
|
+
if (typeof mod?.addTraceProcessor !== 'function' && typeof mod?.getGlobalTraceProvider !== 'function') return mod
|
|
74
|
+
patchedMods.add(mod)
|
|
75
|
+
agentsMod = mod
|
|
76
|
+
if (typeof mod.tool === 'function') {
|
|
77
|
+
shimmer.wrap(mod, 'tool', wrapToolFactory, { replaceGetter: true })
|
|
78
|
+
}
|
|
79
|
+
agentsCoreLoadedCh.publish({ mod })
|
|
80
|
+
return mod
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
function wrapToolFactory (original) {
|
|
84
|
+
return function (...args) {
|
|
85
|
+
const tool = original.apply(this, args)
|
|
86
|
+
if (typeof tool?.invoke === 'function') {
|
|
87
|
+
shimmer.wrap(tool, 'invoke', wrapToolInvoke)
|
|
88
|
+
}
|
|
89
|
+
return tool
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function wrapToolInvoke (original) {
|
|
94
|
+
return function (...args) {
|
|
95
|
+
const agentsCoreSpan = getCurrentSpan()
|
|
96
|
+
return toolStartCh.runStores({ agentsCoreSpan }, () => original.apply(this, args))
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function wrapResponseMethod (original) {
|
|
101
|
+
return function (...args) {
|
|
102
|
+
const agentsCoreSpanId = getCurrentSpanId()
|
|
103
|
+
const baseURL = getClientBaseURL(this)
|
|
104
|
+
return modelStartCh.runStores({ agentsCoreSpanId, baseURL }, () => original.apply(this, args))
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function wrapStreamedResponseMethod (original) {
|
|
109
|
+
return function (...args) {
|
|
110
|
+
const agentsCoreSpanId = getCurrentSpanId()
|
|
111
|
+
const baseURL = getClientBaseURL(this)
|
|
112
|
+
const context = { agentsCoreSpanId, baseURL }
|
|
113
|
+
const iterator = modelStartCh.runStores(context, () => original.apply(this, args))
|
|
114
|
+
return wrapAsyncIterator(iterator, context)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function getClientBaseURL (model) {
|
|
119
|
+
return model?.client?.baseURL ?? model?._client?.baseURL ?? modelBaseURLs.get(model)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function wrapAsyncIterator (iterator, context) {
|
|
123
|
+
if (!iterator || typeof iterator !== 'object') return iterator
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
next () {
|
|
127
|
+
return modelStartCh.runStores(context, () => iterator.next.apply(iterator, arguments))
|
|
128
|
+
},
|
|
129
|
+
throw () {
|
|
130
|
+
if (typeof iterator.throw !== 'function') return Promise.reject(arguments[0])
|
|
131
|
+
return modelStartCh.runStores(context, () => iterator.throw.apply(iterator, arguments))
|
|
132
|
+
},
|
|
133
|
+
return () {
|
|
134
|
+
if (typeof iterator.return !== 'function') return Promise.resolve({ done: true, value: arguments[0] })
|
|
135
|
+
return modelStartCh.runStores(context, () => iterator.return.apply(iterator, arguments))
|
|
136
|
+
},
|
|
137
|
+
[Symbol.asyncIterator] () {
|
|
138
|
+
return this
|
|
139
|
+
},
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
addHook({ name: '@openai/agents-openai', versions: ['>=0.7.0'] }, (mod) => {
|
|
144
|
+
if (patchedMods.has(mod)) return mod
|
|
145
|
+
const responseProto = mod?.OpenAIResponsesModel?.prototype
|
|
146
|
+
const chatCompletionsProto = mod?.OpenAIChatCompletionsModel?.prototype
|
|
147
|
+
if (!responseProto && !chatCompletionsProto) return mod
|
|
148
|
+
|
|
149
|
+
patchedMods.add(mod)
|
|
150
|
+
for (const proto of [responseProto, chatCompletionsProto]) {
|
|
151
|
+
if (typeof proto?.getResponse === 'function') {
|
|
152
|
+
shimmer.wrap(proto, 'getResponse', wrapResponseMethod)
|
|
153
|
+
}
|
|
154
|
+
if (typeof proto?.getStreamedResponse === 'function') {
|
|
155
|
+
shimmer.wrap(proto, 'getStreamedResponse', wrapStreamedResponseMethod)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return mod
|
|
159
|
+
})
|
|
@@ -29,6 +29,7 @@ const {
|
|
|
29
29
|
RUM_TEST_EXECUTION_ID_COOKIE_NAME: RUM_COOKIE_NAME,
|
|
30
30
|
} = require('../../dd-trace/src/ci-visibility/rum')
|
|
31
31
|
const { DD_MAJOR } = require('../../../version')
|
|
32
|
+
const { getChannelPromise } = require('./helpers/channel')
|
|
32
33
|
const { addHook, channel, tracingChannel } = require('./helpers/instrument')
|
|
33
34
|
|
|
34
35
|
const testStartCh = channel('ci:playwright:test:start')
|
|
@@ -614,12 +615,6 @@ function getTestByTestId (dispatcher, testId) {
|
|
|
614
615
|
}
|
|
615
616
|
}
|
|
616
617
|
|
|
617
|
-
function getChannelPromise (channelToPublishTo, params) {
|
|
618
|
-
return new Promise(resolve => {
|
|
619
|
-
channelToPublishTo.publish({ onDone: resolve, ...params })
|
|
620
|
-
})
|
|
621
|
-
}
|
|
622
|
-
|
|
623
618
|
// Inspired by https://github.com/microsoft/playwright/blob/2b77ed4d7aafa85a600caa0b0d101b72c8437eeb/packages/playwright/src/reporters/base.ts#L293
|
|
624
619
|
// We can't use test.outcome() directly because it's set on follow up handlers:
|
|
625
620
|
// our `testEndHandler` is called before the outcome is set.
|
|
@@ -1206,8 +1201,6 @@ function dispatcherHookNew (dispatcherExport, runWrapper) {
|
|
|
1206
1201
|
function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
1207
1202
|
// Config parameter is only available from >=1.55.0
|
|
1208
1203
|
return async function (config) {
|
|
1209
|
-
let onDone
|
|
1210
|
-
|
|
1211
1204
|
rootDir = getRootDir(this, config)
|
|
1212
1205
|
const projects = getProjectsFromRunner(this, config)
|
|
1213
1206
|
const isFailureScreenshotEnabled = isFailureScreenshotCaptureEnabled(projects)
|
|
@@ -1379,17 +1372,12 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
1379
1372
|
logTestOptimizationSummary({ attemptToFixExecutions, newTestsWithDynamicNames })
|
|
1380
1373
|
loggedAttemptToFixTests.clear()
|
|
1381
1374
|
|
|
1382
|
-
|
|
1383
|
-
onDone = resolve
|
|
1384
|
-
})
|
|
1385
|
-
testSessionFinishCh.publish({
|
|
1375
|
+
await getChannelPromise(testSessionFinishCh, {
|
|
1386
1376
|
status: preventedToFail ? 'pass' : STATUS_TO_TEST_STATUS[sessionStatus],
|
|
1387
1377
|
isEarlyFlakeDetectionEnabled,
|
|
1388
1378
|
isEarlyFlakeDetectionFaulty,
|
|
1389
1379
|
isTestManagementTestsEnabled,
|
|
1390
|
-
onDone,
|
|
1391
1380
|
})
|
|
1392
|
-
await flushWait
|
|
1393
1381
|
|
|
1394
1382
|
startedSuites = []
|
|
1395
1383
|
remainingTestsByFile = {}
|
|
@@ -2112,12 +2100,6 @@ function instrumentWorkerMainMethods (workerMain) {
|
|
|
2112
2100
|
annotationTags = parseAnnotations(annotations)
|
|
2113
2101
|
}
|
|
2114
2102
|
|
|
2115
|
-
let onDone
|
|
2116
|
-
|
|
2117
|
-
const flushPromise = new Promise(resolve => {
|
|
2118
|
-
onDone = resolve
|
|
2119
|
-
})
|
|
2120
|
-
|
|
2121
2103
|
// Wait for the properties to be received, but do not block the worker forever if IPC fails.
|
|
2122
2104
|
const ddPropertiesTimeoutPromise = new Promise(resolve => {
|
|
2123
2105
|
const ddPropertiesTimeout = realSetTimeout(() => {
|
|
@@ -2143,7 +2125,7 @@ function instrumentWorkerMainMethods (workerMain) {
|
|
|
2143
2125
|
testStatus: STATUS_TO_TEST_STATUS[status],
|
|
2144
2126
|
})
|
|
2145
2127
|
|
|
2146
|
-
testFinishCh
|
|
2128
|
+
await getChannelPromise(testFinishCh, {
|
|
2147
2129
|
testStatus: STATUS_TO_TEST_STATUS[status],
|
|
2148
2130
|
steps: steps.filter(step => step.testId === testId),
|
|
2149
2131
|
error,
|
|
@@ -2161,14 +2143,11 @@ function instrumentWorkerMainMethods (workerMain) {
|
|
|
2161
2143
|
hasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
|
|
2162
2144
|
isAtrRetry: test._ddIsAtrRetry,
|
|
2163
2145
|
isModified: test._ddIsModified,
|
|
2164
|
-
onDone,
|
|
2165
2146
|
finalStatus,
|
|
2166
2147
|
earlyFlakeAbortReason: test._ddEarlyFlakeAbortReason,
|
|
2167
2148
|
...testCtx.currentStore,
|
|
2168
2149
|
})
|
|
2169
2150
|
|
|
2170
|
-
await flushPromise
|
|
2171
|
-
|
|
2172
2151
|
return res
|
|
2173
2152
|
})
|
|
2174
2153
|
|
|
@@ -419,7 +419,7 @@ function createMainProcessReporter (reporterState) {
|
|
|
419
419
|
testSuiteFinishCh.publish({
|
|
420
420
|
status: getDatadogStatus(testSuiteResult),
|
|
421
421
|
deferFlush: true,
|
|
422
|
-
|
|
422
|
+
onDone: noop,
|
|
423
423
|
...testSuiteCtx.currentStore,
|
|
424
424
|
})
|
|
425
425
|
testSuiteContexts.delete(testModuleId)
|
|
@@ -16,6 +16,7 @@ const {
|
|
|
16
16
|
getTestSuitePath,
|
|
17
17
|
isModifiedTest,
|
|
18
18
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
19
|
+
const { getChannelPromise } = require('./helpers/channel')
|
|
19
20
|
const { addHook } = require('./helpers/instrument')
|
|
20
21
|
const noWorkerInit = require('./vitest-main-no-worker-init')
|
|
21
22
|
const {
|
|
@@ -38,7 +39,6 @@ const {
|
|
|
38
39
|
workerReportLogsCh,
|
|
39
40
|
codeCoverageReportCh,
|
|
40
41
|
findExportByName,
|
|
41
|
-
getChannelPromise,
|
|
42
42
|
getTypeTasks,
|
|
43
43
|
getWorkspaceProject,
|
|
44
44
|
setProvidedContext,
|
|
@@ -433,7 +433,8 @@ async function runMainProcessSetup (ctx, frameworkVersion, testSpecifications, s
|
|
|
433
433
|
err,
|
|
434
434
|
libraryConfig,
|
|
435
435
|
requestErrorTags: receivedRequestErrorTags = {},
|
|
436
|
-
} = await getChannelPromise(libraryConfigurationCh,
|
|
436
|
+
} = await getChannelPromise(libraryConfigurationCh, {
|
|
437
|
+
frameworkVersion,
|
|
437
438
|
isVitestNoWorkerInitActive: shouldInstallNoWorkerInit,
|
|
438
439
|
})
|
|
439
440
|
requestErrorTags = receivedRequestErrorTags
|
|
@@ -450,7 +451,7 @@ async function runMainProcessSetup (ctx, frameworkVersion, testSpecifications, s
|
|
|
450
451
|
resetMainProcessProvidedContext(ctx)
|
|
451
452
|
|
|
452
453
|
if (testSessionConfigurationCh.hasSubscribers) {
|
|
453
|
-
testSessionConfiguration = await getChannelPromise(testSessionConfigurationCh, frameworkVersion)
|
|
454
|
+
testSessionConfiguration = await getChannelPromise(testSessionConfigurationCh, { frameworkVersion }) || {}
|
|
454
455
|
const {
|
|
455
456
|
testSessionId,
|
|
456
457
|
testModuleId,
|
|
@@ -494,7 +495,7 @@ async function runMainProcessSetup (ctx, frameworkVersion, testSpecifications, s
|
|
|
494
495
|
|
|
495
496
|
if (isKnownTestsEnabled) {
|
|
496
497
|
const currentKnownTestsResponse = knownTestsResponse || await getChannelPromise(knownTestsCh)
|
|
497
|
-
if (currentKnownTestsResponse.err) {
|
|
498
|
+
if (!currentKnownTestsResponse || currentKnownTestsResponse.err) {
|
|
498
499
|
isEarlyFlakeDetectionEnabled = false
|
|
499
500
|
} else {
|
|
500
501
|
knownTests = currentKnownTestsResponse.knownTests
|
|
@@ -538,12 +539,13 @@ async function runMainProcessSetup (ctx, frameworkVersion, testSpecifications, s
|
|
|
538
539
|
}
|
|
539
540
|
|
|
540
541
|
if (isTestManagementTestsEnabled) {
|
|
541
|
-
const
|
|
542
|
+
const testManagementResponse =
|
|
542
543
|
testManagementTestsResponse || await getChannelPromise(testManagementTestsCh)
|
|
543
|
-
if (err) {
|
|
544
|
+
if (!testManagementResponse || testManagementResponse.err) {
|
|
544
545
|
isTestManagementTestsEnabled = false
|
|
545
546
|
log.error('Could not get test management tests.')
|
|
546
547
|
} else {
|
|
548
|
+
const { testManagementTests: receivedTestManagementTests } = testManagementResponse
|
|
547
549
|
testManagementTests = receivedTestManagementTests
|
|
548
550
|
testManagementTestsBySuite = getTestManagementTestsBySuite(receivedTestManagementTests)
|
|
549
551
|
shouldSendTestProperties = true
|
|
@@ -558,8 +560,7 @@ async function runMainProcessSetup (ctx, frameworkVersion, testSpecifications, s
|
|
|
558
560
|
|
|
559
561
|
if (isImpactedTestsEnabled) {
|
|
560
562
|
const modifiedFilesResponse = await getChannelPromise(modifiedFilesCh)
|
|
561
|
-
|
|
562
|
-
if (err) {
|
|
563
|
+
if (!modifiedFilesResponse || modifiedFilesResponse.err) {
|
|
563
564
|
log.error('Could not get modified tests.')
|
|
564
565
|
} else {
|
|
565
566
|
modifiedFiles = modifiedFilesResponse.modifiedFiles
|
|
@@ -812,18 +813,13 @@ function getFinishWrapper (exitOrClose) {
|
|
|
812
813
|
return exitOrClose.apply(this, arguments)
|
|
813
814
|
}
|
|
814
815
|
|
|
815
|
-
let onFinish
|
|
816
|
-
|
|
817
|
-
const flushPromise = new Promise(resolve => {
|
|
818
|
-
onFinish = resolve
|
|
819
|
-
})
|
|
820
816
|
const failedSuites = this.state.getFailedFilepaths()
|
|
821
817
|
let error
|
|
822
818
|
if (failedSuites.length) {
|
|
823
819
|
error = new Error(`Test suites failed: ${failedSuites.length}.`)
|
|
824
820
|
}
|
|
825
821
|
|
|
826
|
-
testSessionFinishCh
|
|
822
|
+
const flushPromise = getChannelPromise(testSessionFinishCh, {
|
|
827
823
|
status: getSessionStatus(this.state),
|
|
828
824
|
testCodeCoverageLinesTotal,
|
|
829
825
|
error,
|
|
@@ -833,7 +829,6 @@ function getFinishWrapper (exitOrClose) {
|
|
|
833
829
|
requestErrorTags,
|
|
834
830
|
vitestPool,
|
|
835
831
|
isVitestNoWorkerInitActive,
|
|
836
|
-
onFinish,
|
|
837
832
|
})
|
|
838
833
|
|
|
839
834
|
logTestOptimizationSummary({ attemptToFixExecutions, newTestsWithDynamicNames })
|
|
@@ -842,9 +837,7 @@ function getFinishWrapper (exitOrClose) {
|
|
|
842
837
|
|
|
843
838
|
// If coverage was generated, publish coverage report channel for upload
|
|
844
839
|
if (coverageRootDir && codeCoverageReportCh.hasSubscribers) {
|
|
845
|
-
await
|
|
846
|
-
codeCoverageReportCh.publish({ rootDir: coverageRootDir, onDone: resolve })
|
|
847
|
-
})
|
|
840
|
+
await getChannelPromise(codeCoverageReportCh, { rootDir: coverageRootDir })
|
|
848
841
|
}
|
|
849
842
|
|
|
850
843
|
return exitOrClose.apply(this, arguments)
|
|
@@ -1213,16 +1206,11 @@ async function reportTypecheckFile (file, sessionConfiguration, frameworkVersion
|
|
|
1213
1206
|
testSuiteErrorCh.runStores(testSuiteCtx, () => {})
|
|
1214
1207
|
}
|
|
1215
1208
|
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
onFinish = resolve
|
|
1219
|
-
})
|
|
1220
|
-
testSuiteFinishCh.publish({
|
|
1209
|
+
await getChannelPromise(testSuiteFinishCh, {
|
|
1210
|
+
frameworkVersion,
|
|
1221
1211
|
status: getTypecheckTaskStatus(file),
|
|
1222
|
-
onFinish,
|
|
1223
1212
|
...testSuiteCtx.currentStore,
|
|
1224
1213
|
})
|
|
1225
|
-
await onFinishPromise
|
|
1226
1214
|
}
|
|
1227
1215
|
|
|
1228
1216
|
async function reportTypecheckResults (result, frameworkVersion, ctx, typechecker) {
|
|
@@ -1234,7 +1222,7 @@ async function reportTypecheckResults (result, frameworkVersion, ctx, typechecke
|
|
|
1234
1222
|
}
|
|
1235
1223
|
const providedContext = getMainProcessProvidedContext(ctx)
|
|
1236
1224
|
const sessionConfiguration = testSessionConfigurationCh.hasSubscribers
|
|
1237
|
-
? await getChannelPromise(testSessionConfigurationCh, frameworkVersion)
|
|
1225
|
+
? await getChannelPromise(testSessionConfigurationCh, { frameworkVersion }) || {}
|
|
1238
1226
|
: {}
|
|
1239
1227
|
|
|
1240
1228
|
await Promise.all(result.files.map(file => reportTypecheckFile(
|