dd-trace 5.69.0 → 5.71.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/index.d.ts +17 -0
- package/initialize.mjs +7 -1
- package/package.json +2 -2
- package/packages/datadog-instrumentations/src/anthropic.js +115 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/jest.js +86 -84
- package/packages/datadog-instrumentations/src/mocha/utils.js +5 -21
- package/packages/datadog-instrumentations/src/playwright.js +5 -2
- package/packages/datadog-plugin-anthropic/src/index.js +17 -0
- package/packages/datadog-plugin-anthropic/src/tracing.js +30 -0
- package/packages/dd-trace/src/appsec/reporter.js +70 -21
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +1 -1
- package/packages/dd-trace/src/config.js +2 -0
- package/packages/dd-trace/src/config_defaults.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/anthropic.js +282 -0
- package/packages/dd-trace/src/llmobs/tagger.js +35 -0
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/test.js +0 -30
- package/packages/dd-trace/src/remote_config/capabilities.js +1 -0
- package/packages/dd-trace/src/remote_config/index.js +2 -0
- package/packages/dd-trace/src/supported-configurations.json +1 -0
- package/register.js +9 -1
package/index.d.ts
CHANGED
|
@@ -168,6 +168,7 @@ interface Plugins {
|
|
|
168
168
|
"aerospike": tracer.plugins.aerospike;
|
|
169
169
|
"amqp10": tracer.plugins.amqp10;
|
|
170
170
|
"amqplib": tracer.plugins.amqplib;
|
|
171
|
+
"anthropic": tracer.plugins.anthropic;
|
|
171
172
|
"apollo": tracer.plugins.apollo;
|
|
172
173
|
"avsc": tracer.plugins.avsc;
|
|
173
174
|
"aws-sdk": tracer.plugins.aws_sdk;
|
|
@@ -785,6 +786,8 @@ declare namespace tracer {
|
|
|
785
786
|
|
|
786
787
|
/** Whether to enable request body collection on RASP event
|
|
787
788
|
* @default false
|
|
789
|
+
*
|
|
790
|
+
* @deprecated Use UI and Remote Configuration to enable extended data collection
|
|
788
791
|
*/
|
|
789
792
|
bodyCollection?: boolean
|
|
790
793
|
},
|
|
@@ -809,20 +812,28 @@ declare namespace tracer {
|
|
|
809
812
|
},
|
|
810
813
|
/**
|
|
811
814
|
* Configuration for extended headers collection tied to security events
|
|
815
|
+
*
|
|
816
|
+
* @deprecated Use UI and Remote Configuration to enable extended data collection
|
|
812
817
|
*/
|
|
813
818
|
extendedHeadersCollection?: {
|
|
814
819
|
/** Whether to enable extended headers collection
|
|
815
820
|
* @default false
|
|
821
|
+
*
|
|
822
|
+
* @deprecated Use UI and Remote Configuration to enable extended data collection
|
|
816
823
|
*/
|
|
817
824
|
enabled: boolean,
|
|
818
825
|
|
|
819
826
|
/** Whether to redact collected headers
|
|
820
827
|
* @default true
|
|
828
|
+
*
|
|
829
|
+
* @deprecated Use UI and Remote Configuration to enable extended data collection
|
|
821
830
|
*/
|
|
822
831
|
redaction: boolean,
|
|
823
832
|
|
|
824
833
|
/** Specifies the maximum number of headers collected.
|
|
825
834
|
* @default 50
|
|
835
|
+
*
|
|
836
|
+
* @deprecated Use UI and Remote Configuration to enable extended data collection
|
|
826
837
|
*/
|
|
827
838
|
maxHeaders: number,
|
|
828
839
|
}
|
|
@@ -1530,6 +1541,12 @@ declare namespace tracer {
|
|
|
1530
1541
|
*/
|
|
1531
1542
|
interface amqplib extends Instrumentation {}
|
|
1532
1543
|
|
|
1544
|
+
/**
|
|
1545
|
+
* This plugin automatically instruments the
|
|
1546
|
+
* [anthropic](https://www.npmjs.com/package/@anthropic-ai/sdk) module.
|
|
1547
|
+
*/
|
|
1548
|
+
interface anthropic extends Instrumentation {}
|
|
1549
|
+
|
|
1533
1550
|
/**
|
|
1534
1551
|
* Currently this plugin automatically instruments
|
|
1535
1552
|
* [@apollo/gateway](https://github.com/apollographql/federation) for module versions >= v2.3.0.
|
package/initialize.mjs
CHANGED
|
@@ -36,7 +36,13 @@ ${result.source}`
|
|
|
36
36
|
const [NODE_MAJOR, NODE_MINOR] = process.versions.node.split('.').map(Number)
|
|
37
37
|
|
|
38
38
|
const brokenLoaders = NODE_MAJOR === 18 && NODE_MINOR === 0
|
|
39
|
-
const iitmExclusions = [
|
|
39
|
+
const iitmExclusions = [
|
|
40
|
+
/langsmith/,
|
|
41
|
+
/openai\/_shims/,
|
|
42
|
+
/openai\/resources\/chat\/completions\/messages/,
|
|
43
|
+
/openai\/agents-core\/dist\/shims/,
|
|
44
|
+
/@anthropic-ai\/sdk\/_shims/
|
|
45
|
+
]
|
|
40
46
|
|
|
41
47
|
export async function load (url, context, nextLoad) {
|
|
42
48
|
const iitmExclusionsMatch = iitmExclusions.some((exclusion) => exclusion.test(url))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.71.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"test:integration:playwright": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/playwright/*.spec.js\"",
|
|
62
62
|
"test:integration:selenium": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/selenium/*.spec.js\"",
|
|
63
63
|
"test:integration:vitest": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/vitest/*.spec.js\"",
|
|
64
|
-
"test:integration:testopt": "mocha --timeout
|
|
64
|
+
"test:integration:testopt": "mocha --timeout 90000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/ci-visibility/*.spec.js\"",
|
|
65
65
|
"test:integration:profiler": "mocha --timeout 180000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/profiler/*.spec.js\"",
|
|
66
66
|
"test:integration:plugins": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
|
|
67
67
|
"test:unit:plugins": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-instrumentations/test/@($(echo $PLUGINS)).spec.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/**/*.spec.js\" --exclude \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { addHook } = require('./helpers/instrument')
|
|
4
|
+
const shimmer = require('../../datadog-shimmer')
|
|
5
|
+
const { channel, tracingChannel } = require('dc-polyfill')
|
|
6
|
+
|
|
7
|
+
const anthropicTracingChannel = tracingChannel('apm:anthropic:request')
|
|
8
|
+
const onStreamedChunkCh = channel('apm:anthropic:request:chunk')
|
|
9
|
+
|
|
10
|
+
function wrapStreamIterator (iterator, ctx) {
|
|
11
|
+
return function () {
|
|
12
|
+
const itr = iterator.apply(this, arguments)
|
|
13
|
+
shimmer.wrap(itr, 'next', next => function () {
|
|
14
|
+
return next.apply(this, arguments)
|
|
15
|
+
.then(res => {
|
|
16
|
+
const { done, value: chunk } = res
|
|
17
|
+
onStreamedChunkCh.publish({ ctx, chunk, done })
|
|
18
|
+
|
|
19
|
+
if (done) {
|
|
20
|
+
finish(ctx)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return res
|
|
24
|
+
})
|
|
25
|
+
.catch(error => {
|
|
26
|
+
finish(ctx, null, error)
|
|
27
|
+
throw error
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
return itr
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function wrapCreate (create) {
|
|
36
|
+
return function () {
|
|
37
|
+
if (!anthropicTracingChannel.start.hasSubscribers) {
|
|
38
|
+
return create.apply(this, arguments)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const options = arguments[0]
|
|
42
|
+
const stream = options.stream
|
|
43
|
+
|
|
44
|
+
const ctx = { options, resource: 'create' }
|
|
45
|
+
|
|
46
|
+
return anthropicTracingChannel.start.runStores(ctx, () => {
|
|
47
|
+
let apiPromise
|
|
48
|
+
try {
|
|
49
|
+
apiPromise = create.apply(this, arguments)
|
|
50
|
+
} catch (error) {
|
|
51
|
+
finish(ctx, null, error)
|
|
52
|
+
throw error
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
shimmer.wrap(apiPromise, 'parse', parse => function () {
|
|
56
|
+
return parse.apply(this, arguments)
|
|
57
|
+
.then(response => {
|
|
58
|
+
if (stream) {
|
|
59
|
+
shimmer.wrap(response, Symbol.asyncIterator, iterator => wrapStreamIterator(iterator, ctx))
|
|
60
|
+
} else {
|
|
61
|
+
finish(ctx, response, null)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return response
|
|
65
|
+
}).catch(error => {
|
|
66
|
+
finish(ctx, null, error)
|
|
67
|
+
throw error
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
anthropicTracingChannel.end.publish(ctx)
|
|
72
|
+
|
|
73
|
+
return apiPromise
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function finish (ctx, result, error) {
|
|
79
|
+
if (error) {
|
|
80
|
+
ctx.error = error
|
|
81
|
+
anthropicTracingChannel.error.publish(ctx)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// streamed responses are handled and set separately
|
|
85
|
+
ctx.result ??= result
|
|
86
|
+
|
|
87
|
+
anthropicTracingChannel.asyncEnd.publish(ctx)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const extensions = ['js', 'mjs']
|
|
91
|
+
for (const extension of extensions) {
|
|
92
|
+
addHook({
|
|
93
|
+
name: '@anthropic-ai/sdk',
|
|
94
|
+
file: `resources/messages.${extension}`,
|
|
95
|
+
versions: ['>=0.14.0 <0.33.0']
|
|
96
|
+
}, exports => {
|
|
97
|
+
const Messages = exports.Messages
|
|
98
|
+
|
|
99
|
+
shimmer.wrap(Messages.prototype, 'create', wrapCreate)
|
|
100
|
+
|
|
101
|
+
return exports
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
addHook({
|
|
105
|
+
name: '@anthropic-ai/sdk',
|
|
106
|
+
file: `resources/messages/messages.${extension}`,
|
|
107
|
+
versions: ['>=0.33.0']
|
|
108
|
+
}, exports => {
|
|
109
|
+
const Messages = exports.Messages
|
|
110
|
+
|
|
111
|
+
shimmer.wrap(Messages.prototype, 'create', wrapCreate)
|
|
112
|
+
|
|
113
|
+
return exports
|
|
114
|
+
})
|
|
115
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
|
+
'@anthropic-ai/sdk': { esmFirst: true, fn: () => require('../anthropic') },
|
|
4
5
|
'@apollo/server': () => require('../apollo-server'),
|
|
5
6
|
'@apollo/gateway': () => require('../apollo'),
|
|
6
7
|
'apollo-server-core': () => require('../apollo-server-core'),
|
|
@@ -11,12 +11,8 @@ const {
|
|
|
11
11
|
getTestLineStart,
|
|
12
12
|
getTestSuitePath,
|
|
13
13
|
getTestParametersString,
|
|
14
|
-
addEfdStringToTestName,
|
|
15
|
-
removeEfdStringFromTestName,
|
|
16
14
|
getIsFaultyEarlyFlakeDetection,
|
|
17
15
|
JEST_WORKER_LOGS_PAYLOAD_CODE,
|
|
18
|
-
addAttemptToFixStringToTestName,
|
|
19
|
-
removeAttemptToFixStringFromTestName,
|
|
20
16
|
getTestEndLine,
|
|
21
17
|
isModifiedTest
|
|
22
18
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
@@ -92,6 +88,7 @@ const newTestsTestStatuses = new Map()
|
|
|
92
88
|
const attemptToFixRetriedTestsStatuses = new Map()
|
|
93
89
|
const wrappedWorkers = new WeakSet()
|
|
94
90
|
const testSuiteMockedFiles = new Map()
|
|
91
|
+
const testsToBeRetried = new Set()
|
|
95
92
|
|
|
96
93
|
const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
|
|
97
94
|
|
|
@@ -146,6 +143,8 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
146
143
|
|
|
147
144
|
const repositoryRoot = this.testEnvironmentOptions._ddRepositoryRoot
|
|
148
145
|
|
|
146
|
+
// TODO: could we grab testPath from `this.getVmContext().expect.getState()` instead?
|
|
147
|
+
// so we don't rely on context being passed (some custom test environment do not pass it)
|
|
149
148
|
if (repositoryRoot) {
|
|
150
149
|
this.testSourceFile = getTestSuitePath(context.testPath, repositoryRoot)
|
|
151
150
|
this.repositoryRoot = repositoryRoot
|
|
@@ -209,19 +208,31 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
209
208
|
}
|
|
210
209
|
}
|
|
211
210
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
211
|
+
/**
|
|
212
|
+
* Jest snapshot counter issue during test retries
|
|
213
|
+
*
|
|
214
|
+
* Problem:
|
|
215
|
+
* - Jest tracks snapshot calls using an internal counter per test name
|
|
216
|
+
* - Each `toMatchSnapshot()` call increments this counter
|
|
217
|
+
* - When a test is retried, it keeps the same name but the counter continues from where it left off
|
|
218
|
+
*
|
|
219
|
+
* Example Issue:
|
|
220
|
+
* Original test run creates: `exports["test can do multiple snapshots 1"] = "hello"`
|
|
221
|
+
* Retried test expects: `exports["test can do multiple snapshots 2"] = "hello"`
|
|
222
|
+
*
|
|
223
|
+
* This mismatch causes snapshot tests to fail on retry because Jest is looking
|
|
224
|
+
* for the wrong snapshot number. The solution is to reset the snapshot state.
|
|
225
|
+
*/
|
|
226
|
+
resetSnapshotState () {
|
|
217
227
|
try {
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
228
|
+
const expectGlobal = this.getVmContext().expect
|
|
229
|
+
const { snapshotState: { _counters: counters } } = expectGlobal.getState()
|
|
230
|
+
if (counters) {
|
|
231
|
+
counters.clear()
|
|
232
|
+
}
|
|
233
|
+
} catch (e) {
|
|
234
|
+
log.warn('Error resetting snapshot state', e)
|
|
222
235
|
}
|
|
223
|
-
this.hasSnapshotTests = hasSnapshotTests
|
|
224
|
-
return hasSnapshotTests
|
|
225
236
|
}
|
|
226
237
|
|
|
227
238
|
// This function returns an array if the known tests are valid and null otherwise.
|
|
@@ -293,18 +304,15 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
293
304
|
}
|
|
294
305
|
|
|
295
306
|
// Generic function to handle test retries
|
|
296
|
-
retryTest (
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
return
|
|
303
|
-
}
|
|
304
|
-
|
|
307
|
+
retryTest ({
|
|
308
|
+
jestEvent,
|
|
309
|
+
retryCount,
|
|
310
|
+
retryType
|
|
311
|
+
}) {
|
|
312
|
+
const { testName, fn, timeout } = jestEvent
|
|
305
313
|
for (let retryIndex = 0; retryIndex < retryCount; retryIndex++) {
|
|
306
314
|
if (this.global.test) {
|
|
307
|
-
this.global.test(
|
|
315
|
+
this.global.test(testName, fn, timeout)
|
|
308
316
|
} else {
|
|
309
317
|
log.error('%s could not retry test because global.test is undefined', retryType)
|
|
310
318
|
}
|
|
@@ -314,8 +322,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
314
322
|
// At the `add_test` event we don't have the test object yet, so we can't use it
|
|
315
323
|
getTestNameFromAddTestEvent (event, state) {
|
|
316
324
|
const describeSuffix = getJestTestName(state.currentDescribeBlock)
|
|
317
|
-
|
|
318
|
-
return removeAttemptToFixStringFromTestName(removeEfdStringFromTestName(fullTestName))
|
|
325
|
+
return describeSuffix ? `${describeSuffix} ${event.testName}` : event.testName
|
|
319
326
|
}
|
|
320
327
|
|
|
321
328
|
async handleTestEvent (event, state) {
|
|
@@ -337,25 +344,27 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
337
344
|
})
|
|
338
345
|
}
|
|
339
346
|
if (event.name === 'test_start') {
|
|
347
|
+
const testName = getJestTestName(event.test)
|
|
348
|
+
if (testsToBeRetried.has(testName)) {
|
|
349
|
+
// This is needed because we're trying tests with the same name
|
|
350
|
+
this.resetSnapshotState()
|
|
351
|
+
}
|
|
352
|
+
|
|
340
353
|
let isNewTest = false
|
|
341
354
|
let numEfdRetry = null
|
|
342
355
|
let numOfAttemptsToFixRetries = null
|
|
343
356
|
const testParameters = getTestParametersString(this.nameToParams, event.test.name)
|
|
344
|
-
// Async resource for this test is created here
|
|
345
|
-
// It is used later on by the test_done handler
|
|
346
|
-
const testName = getJestTestName(event.test)
|
|
347
|
-
const originalTestName = removeEfdStringFromTestName(removeAttemptToFixStringFromTestName(testName))
|
|
348
357
|
|
|
349
358
|
let isAttemptToFix = false
|
|
350
359
|
let isDisabled = false
|
|
351
360
|
let isQuarantined = false
|
|
352
361
|
if (this.isTestManagementTestsEnabled) {
|
|
353
|
-
isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(
|
|
354
|
-
isDisabled = this.testManagementTestsForThisSuite?.disabled?.includes(
|
|
355
|
-
isQuarantined = this.testManagementTestsForThisSuite?.quarantined?.includes(
|
|
362
|
+
isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(testName)
|
|
363
|
+
isDisabled = this.testManagementTestsForThisSuite?.disabled?.includes(testName)
|
|
364
|
+
isQuarantined = this.testManagementTestsForThisSuite?.quarantined?.includes(testName)
|
|
356
365
|
if (isAttemptToFix) {
|
|
357
|
-
numOfAttemptsToFixRetries = retriedTestsToNumAttempts.get(
|
|
358
|
-
retriedTestsToNumAttempts.set(
|
|
366
|
+
numOfAttemptsToFixRetries = retriedTestsToNumAttempts.get(testName)
|
|
367
|
+
retriedTestsToNumAttempts.set(testName, numOfAttemptsToFixRetries + 1)
|
|
359
368
|
} else if (isDisabled) {
|
|
360
369
|
event.test.mode = 'skip'
|
|
361
370
|
}
|
|
@@ -375,17 +384,17 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
375
384
|
}
|
|
376
385
|
|
|
377
386
|
if (this.isKnownTestsEnabled) {
|
|
378
|
-
isNewTest = retriedTestsToNumAttempts.has(
|
|
387
|
+
isNewTest = retriedTestsToNumAttempts.has(testName)
|
|
379
388
|
}
|
|
380
389
|
|
|
381
390
|
if (this.isEarlyFlakeDetectionEnabled && (isNewTest || isModified)) {
|
|
382
|
-
numEfdRetry = retriedTestsToNumAttempts.get(
|
|
383
|
-
retriedTestsToNumAttempts.set(
|
|
391
|
+
numEfdRetry = retriedTestsToNumAttempts.get(testName)
|
|
392
|
+
retriedTestsToNumAttempts.set(testName, numEfdRetry + 1)
|
|
384
393
|
}
|
|
385
394
|
|
|
386
395
|
const isJestRetry = event.test?.invocations > 1
|
|
387
396
|
const ctx = {
|
|
388
|
-
name:
|
|
397
|
+
name: testName,
|
|
389
398
|
suite: this.testSuite,
|
|
390
399
|
testSourceFile: this.testSourceFile,
|
|
391
400
|
displayName: this.displayName,
|
|
@@ -431,24 +440,22 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
431
440
|
}
|
|
432
441
|
|
|
433
442
|
if (event.name === 'add_test') {
|
|
434
|
-
const originalTestName = this.getTestNameFromAddTestEvent(event, state)
|
|
435
|
-
|
|
436
443
|
if (event.failing) {
|
|
437
444
|
return
|
|
438
445
|
}
|
|
439
446
|
|
|
447
|
+
const testFullName = this.getTestNameFromAddTestEvent(event, state)
|
|
440
448
|
const isSkipped = event.mode === 'todo' || event.mode === 'skip'
|
|
441
449
|
if (this.isTestManagementTestsEnabled) {
|
|
442
|
-
const isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(
|
|
443
|
-
if (isAttemptToFix && !isSkipped && !retriedTestsToNumAttempts.has(
|
|
444
|
-
retriedTestsToNumAttempts.set(
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
'Test Management (Attempt to Fix)'
|
|
450
|
-
|
|
451
|
-
)
|
|
450
|
+
const isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(testFullName)
|
|
451
|
+
if (isAttemptToFix && !isSkipped && !retriedTestsToNumAttempts.has(testFullName)) {
|
|
452
|
+
retriedTestsToNumAttempts.set(testFullName, 0)
|
|
453
|
+
testsToBeRetried.add(testFullName)
|
|
454
|
+
this.retryTest({
|
|
455
|
+
jestEvent: event,
|
|
456
|
+
retryCount: testManagementAttemptToFixRetries,
|
|
457
|
+
retryType: 'Test Management (Attempt to Fix)'
|
|
458
|
+
})
|
|
452
459
|
}
|
|
453
460
|
}
|
|
454
461
|
if (this.isImpactedTestsEnabled) {
|
|
@@ -461,29 +468,27 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
461
468
|
this.modifiedTestsForThisSuite,
|
|
462
469
|
'jest'
|
|
463
470
|
)
|
|
464
|
-
if (isModified && !retriedTestsToNumAttempts.has(
|
|
465
|
-
retriedTestsToNumAttempts.set(
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
'
|
|
471
|
-
|
|
472
|
-
)
|
|
471
|
+
if (isModified && !retriedTestsToNumAttempts.has(testFullName) && this.isEarlyFlakeDetectionEnabled) {
|
|
472
|
+
retriedTestsToNumAttempts.set(testFullName, 0)
|
|
473
|
+
testsToBeRetried.add(testFullName)
|
|
474
|
+
this.retryTest({
|
|
475
|
+
jestEvent: event,
|
|
476
|
+
retryCount: earlyFlakeDetectionNumRetries,
|
|
477
|
+
retryType: 'Impacted tests'
|
|
478
|
+
})
|
|
473
479
|
}
|
|
474
480
|
}
|
|
475
481
|
if (this.isKnownTestsEnabled) {
|
|
476
|
-
const isNew = !this.knownTestsForThisSuite.includes(
|
|
477
|
-
if (isNew && !isSkipped && !retriedTestsToNumAttempts.has(
|
|
478
|
-
retriedTestsToNumAttempts.set(
|
|
482
|
+
const isNew = !this.knownTestsForThisSuite.includes(testFullName)
|
|
483
|
+
if (isNew && !isSkipped && !retriedTestsToNumAttempts.has(testFullName)) {
|
|
484
|
+
retriedTestsToNumAttempts.set(testFullName, 0)
|
|
479
485
|
if (this.isEarlyFlakeDetectionEnabled) {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
'Early flake detection'
|
|
485
|
-
|
|
486
|
-
)
|
|
486
|
+
testsToBeRetried.add(testFullName)
|
|
487
|
+
this.retryTest({
|
|
488
|
+
jestEvent: event,
|
|
489
|
+
retryCount: earlyFlakeDetectionNumRetries,
|
|
490
|
+
retryType: 'Early flake detection'
|
|
491
|
+
})
|
|
487
492
|
}
|
|
488
493
|
}
|
|
489
494
|
}
|
|
@@ -502,15 +507,14 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
502
507
|
let isAttemptToFix = false
|
|
503
508
|
if (this.isTestManagementTestsEnabled) {
|
|
504
509
|
const testName = getJestTestName(event.test)
|
|
505
|
-
|
|
506
|
-
isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(originalTestName)
|
|
510
|
+
isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(testName)
|
|
507
511
|
if (isAttemptToFix) {
|
|
508
|
-
if (attemptToFixRetriedTestsStatuses.has(
|
|
509
|
-
attemptToFixRetriedTestsStatuses.get(
|
|
512
|
+
if (attemptToFixRetriedTestsStatuses.has(testName)) {
|
|
513
|
+
attemptToFixRetriedTestsStatuses.get(testName).push(status)
|
|
510
514
|
} else {
|
|
511
|
-
attemptToFixRetriedTestsStatuses.set(
|
|
515
|
+
attemptToFixRetriedTestsStatuses.set(testName, [status])
|
|
512
516
|
}
|
|
513
|
-
const testStatuses = attemptToFixRetriedTestsStatuses.get(
|
|
517
|
+
const testStatuses = attemptToFixRetriedTestsStatuses.get(testName)
|
|
514
518
|
// Check if this is the last attempt to fix.
|
|
515
519
|
// If it is, we'll set the failedAllTests flag to true if all the tests failed
|
|
516
520
|
// If all tests passed, we'll set the attemptToFixPassed flag to true
|
|
@@ -531,14 +535,13 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
531
535
|
// We'll store the test statuses of the retries
|
|
532
536
|
if (this.isKnownTestsEnabled) {
|
|
533
537
|
const testName = getJestTestName(event.test)
|
|
534
|
-
const
|
|
535
|
-
const isNewTest = retriedTestsToNumAttempts.has(originalTestName)
|
|
538
|
+
const isNewTest = retriedTestsToNumAttempts.has(testName)
|
|
536
539
|
if (isNewTest) {
|
|
537
|
-
if (newTestsTestStatuses.has(
|
|
538
|
-
newTestsTestStatuses.get(
|
|
540
|
+
if (newTestsTestStatuses.has(testName)) {
|
|
541
|
+
newTestsTestStatuses.get(testName).push(status)
|
|
539
542
|
isEfdRetry = true
|
|
540
543
|
} else {
|
|
541
|
-
newTestsTestStatuses.set(
|
|
544
|
+
newTestsTestStatuses.set(testName, [status])
|
|
542
545
|
}
|
|
543
546
|
}
|
|
544
547
|
}
|
|
@@ -951,13 +954,12 @@ function getCliWrapper (isNewJestVersion) {
|
|
|
951
954
|
|
|
952
955
|
for (const { testName, testSuiteAbsolutePath } of failedTests) {
|
|
953
956
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, result.globalConfig.rootDir)
|
|
954
|
-
const originalName = removeAttemptToFixStringFromTestName(testName)
|
|
955
957
|
const testManagementTest = testManagementTests
|
|
956
958
|
?.jest
|
|
957
959
|
?.suites
|
|
958
960
|
?.[testSuite]
|
|
959
961
|
?.tests
|
|
960
|
-
?.[
|
|
962
|
+
?.[testName]
|
|
961
963
|
?.properties
|
|
962
964
|
// This uses `attempt_to_fix` because this is always the main process and it's not formatted in camelCase
|
|
963
965
|
if (testManagementTest?.attempt_to_fix && (testManagementTest?.quarantined || testManagementTest?.disabled)) {
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
getTestSuitePath,
|
|
5
|
-
removeEfdStringFromTestName,
|
|
6
|
-
addEfdStringToTestName,
|
|
7
|
-
addAttemptToFixStringToTestName,
|
|
8
|
-
removeAttemptToFixStringFromTestName
|
|
9
|
-
} = require('../../../dd-trace/src/plugins/util/test')
|
|
3
|
+
const { getTestSuitePath } = require('../../../dd-trace/src/plugins/util/test')
|
|
10
4
|
const { channel } = require('../helpers/instrument')
|
|
11
5
|
const shimmer = require('../../../datadog-shimmer')
|
|
12
6
|
|
|
@@ -60,17 +54,15 @@ function isNewTest (test, knownTests) {
|
|
|
60
54
|
return false
|
|
61
55
|
}
|
|
62
56
|
const testSuite = getTestSuitePath(test.file, process.cwd())
|
|
63
|
-
const testName =
|
|
57
|
+
const testName = test.fullTitle()
|
|
64
58
|
const testsForSuite = knownTests.mocha?.[testSuite] || []
|
|
65
59
|
return !testsForSuite.includes(testName)
|
|
66
60
|
}
|
|
67
61
|
|
|
68
|
-
function retryTest (test, numRetries,
|
|
69
|
-
const originalTestName = test.title
|
|
62
|
+
function retryTest (test, numRetries, tags) {
|
|
70
63
|
const suite = test.parent
|
|
71
64
|
for (let retryIndex = 0; retryIndex < numRetries; retryIndex++) {
|
|
72
65
|
const clonedTest = test.clone()
|
|
73
|
-
clonedTest.title = modifyTestName(originalTestName, retryIndex + 1)
|
|
74
66
|
suite.addTest(clonedTest)
|
|
75
67
|
tags.forEach(tag => {
|
|
76
68
|
if (tag) {
|
|
@@ -113,10 +105,7 @@ function getIsLastRetry (test) {
|
|
|
113
105
|
}
|
|
114
106
|
|
|
115
107
|
function getTestFullName (test) {
|
|
116
|
-
|
|
117
|
-
removeAttemptToFixStringFromTestName(test.fullTitle())
|
|
118
|
-
)
|
|
119
|
-
return `mocha.${getTestSuitePath(test.file, process.cwd())}.${testName}`
|
|
108
|
+
return `mocha.${getTestSuitePath(test.file, process.cwd())}.${test.fullTitle()}`
|
|
120
109
|
}
|
|
121
110
|
|
|
122
111
|
function getTestStatus (test) {
|
|
@@ -216,10 +205,8 @@ function getOnTestHandler (isMain) {
|
|
|
216
205
|
_ddIsModified: isModified
|
|
217
206
|
} = test
|
|
218
207
|
|
|
219
|
-
const testName = removeEfdStringFromTestName(removeAttemptToFixStringFromTestName(test.fullTitle()))
|
|
220
|
-
|
|
221
208
|
const testInfo = {
|
|
222
|
-
testName,
|
|
209
|
+
testName: test.fullTitle(),
|
|
223
210
|
testSuiteAbsolutePath,
|
|
224
211
|
title,
|
|
225
212
|
testStartLine
|
|
@@ -445,7 +432,6 @@ function getRunTestsWrapper (runTests, config) {
|
|
|
445
432
|
retryTest(
|
|
446
433
|
test,
|
|
447
434
|
config.testManagementAttemptToFixRetries,
|
|
448
|
-
addAttemptToFixStringToTestName,
|
|
449
435
|
['_ddIsAttemptToFix', isDisabled && '_ddIsDisabled', isQuarantined && '_ddIsQuarantined']
|
|
450
436
|
)
|
|
451
437
|
} else if (isDisabled) {
|
|
@@ -469,7 +455,6 @@ function getRunTestsWrapper (runTests, config) {
|
|
|
469
455
|
retryTest(
|
|
470
456
|
test,
|
|
471
457
|
config.earlyFlakeDetectionNumRetries,
|
|
472
|
-
addEfdStringToTestName,
|
|
473
458
|
['_ddIsModified', '_ddIsEfdRetry']
|
|
474
459
|
)
|
|
475
460
|
}
|
|
@@ -488,7 +473,6 @@ function getRunTestsWrapper (runTests, config) {
|
|
|
488
473
|
retryTest(
|
|
489
474
|
test,
|
|
490
475
|
config.earlyFlakeDetectionNumRetries,
|
|
491
|
-
addEfdStringToTestName,
|
|
492
476
|
['_ddIsNew', '_ddIsEfdRetry']
|
|
493
477
|
)
|
|
494
478
|
}
|
|
@@ -772,7 +772,10 @@ addHook({
|
|
|
772
772
|
if (!isKnownTestsEnabled && !isTestManagementTestsEnabled && !isImpactedTestsEnabled) {
|
|
773
773
|
return oldCreateRootSuite.apply(this, arguments)
|
|
774
774
|
}
|
|
775
|
-
|
|
775
|
+
|
|
776
|
+
const createRootSuiteReturnValue = await oldCreateRootSuite.apply(this, arguments)
|
|
777
|
+
// From v1.56.0 on, createRootSuite returns `{ rootSuite, topLevelProjects }`
|
|
778
|
+
const rootSuite = createRootSuiteReturnValue.rootSuite || createRootSuiteReturnValue
|
|
776
779
|
|
|
777
780
|
const allTests = rootSuite.allTests()
|
|
778
781
|
|
|
@@ -861,7 +864,7 @@ addHook({
|
|
|
861
864
|
}
|
|
862
865
|
}
|
|
863
866
|
|
|
864
|
-
return
|
|
867
|
+
return createRootSuiteReturnValue
|
|
865
868
|
}
|
|
866
869
|
|
|
867
870
|
// We need to proxy the createRootSuite function because the function is not configurable
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const CompositePlugin = require('../../dd-trace/src/plugins/composite')
|
|
4
|
+
const AnthropicTracingPlugin = require('./tracing')
|
|
5
|
+
const AnthropicLLMObsPlugin = require('../../dd-trace/src/llmobs/plugins/anthropic')
|
|
6
|
+
|
|
7
|
+
class AnthropicPlugin extends CompositePlugin {
|
|
8
|
+
static id = 'anthropic'
|
|
9
|
+
static get plugins () {
|
|
10
|
+
return {
|
|
11
|
+
llmobs: AnthropicLLMObsPlugin,
|
|
12
|
+
tracing: AnthropicTracingPlugin
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = AnthropicPlugin
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
|
|
4
|
+
|
|
5
|
+
class AnthropicTracingPlugin extends TracingPlugin {
|
|
6
|
+
static id = 'anthropic'
|
|
7
|
+
static operation = 'request'
|
|
8
|
+
static system = 'anthropic'
|
|
9
|
+
static prefix = 'tracing:apm:anthropic:request'
|
|
10
|
+
|
|
11
|
+
bindStart (ctx) {
|
|
12
|
+
const { resource, options } = ctx
|
|
13
|
+
|
|
14
|
+
this.startSpan('anthropic.request', {
|
|
15
|
+
meta: {
|
|
16
|
+
'resource.name': `Messages.${resource}`,
|
|
17
|
+
'anthropic.request.model': options.model
|
|
18
|
+
}
|
|
19
|
+
}, ctx)
|
|
20
|
+
|
|
21
|
+
return ctx.currentStore
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
asyncEnd (ctx) {
|
|
25
|
+
const span = ctx.currentStore?.span
|
|
26
|
+
span?.finish()
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = AnthropicTracingPlugin
|