dd-trace 5.11.0 → 5.13.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/init.js +7 -0
- package/ext/exporters.d.ts +2 -1
- package/ext/exporters.js +2 -1
- package/index.d.ts +14 -6
- package/package.json +4 -4
- package/packages/datadog-esbuild/index.js +8 -2
- package/packages/datadog-instrumentations/src/aws-sdk.js +4 -1
- package/packages/datadog-instrumentations/src/cucumber.js +182 -105
- package/packages/datadog-instrumentations/src/helpers/fetch.js +9 -4
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/lodash.js +31 -0
- package/packages/datadog-instrumentations/src/openai.js +149 -0
- package/packages/datadog-instrumentations/src/playwright.js +6 -1
- package/packages/datadog-plugin-aws-sdk/src/services/index.js +3 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sfn.js +7 -0
- package/packages/datadog-plugin-aws-sdk/src/services/states.js +7 -0
- package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +64 -0
- package/packages/datadog-plugin-cucumber/src/index.js +83 -11
- package/packages/datadog-plugin-fetch/src/index.js +5 -2
- package/packages/datadog-plugin-openai/src/index.js +159 -32
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-base-analyzer.js +1 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-analyzer.js +4 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +3 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +1 -9
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +10 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +4 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +55 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/hardcoded-password-analyzer.js +13 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +8 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +6 -6
- package/packages/dd-trace/src/appsec/remote_config/index.js +5 -5
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +56 -0
- package/packages/dd-trace/src/ci-visibility/exporters/{jest-worker → test-worker}/writer.js +7 -0
- package/packages/dd-trace/src/config.js +7 -0
- package/packages/dd-trace/src/exporter.js +2 -1
- package/packages/dd-trace/src/plugins/database.js +20 -5
- package/packages/dd-trace/src/plugins/util/test.js +7 -0
- package/packages/dd-trace/src/proxy.js +26 -6
- package/packages/dd-trace/src/telemetry/index.js +9 -3
- package/packages/dd-trace/src/ci-visibility/exporters/jest-worker/index.js +0 -33
|
@@ -10,6 +10,98 @@ const startCh = channel('apm:openai:request:start')
|
|
|
10
10
|
const finishCh = channel('apm:openai:request:finish')
|
|
11
11
|
const errorCh = channel('apm:openai:request:error')
|
|
12
12
|
|
|
13
|
+
const V4_PACKAGE_SHIMS = [
|
|
14
|
+
{
|
|
15
|
+
file: 'resources/chat/completions.js',
|
|
16
|
+
targetClass: 'Completions',
|
|
17
|
+
baseResource: 'chat.completions',
|
|
18
|
+
methods: ['create']
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
file: 'resources/completions.js',
|
|
22
|
+
targetClass: 'Completions',
|
|
23
|
+
baseResource: 'completions',
|
|
24
|
+
methods: ['create']
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
file: 'resources/embeddings.js',
|
|
28
|
+
targetClass: 'Embeddings',
|
|
29
|
+
baseResource: 'embeddings',
|
|
30
|
+
methods: ['create']
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
file: 'resources/files.js',
|
|
34
|
+
targetClass: 'Files',
|
|
35
|
+
baseResource: 'files',
|
|
36
|
+
methods: ['create', 'del', 'list', 'retrieve']
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
file: 'resources/files.js',
|
|
40
|
+
targetClass: 'Files',
|
|
41
|
+
baseResource: 'files',
|
|
42
|
+
methods: ['retrieveContent'],
|
|
43
|
+
versions: ['>=4.0.0 <4.17.1']
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
file: 'resources/files.js',
|
|
47
|
+
targetClass: 'Files',
|
|
48
|
+
baseResource: 'files',
|
|
49
|
+
methods: ['content'], // replaced `retrieveContent` in v4.17.1
|
|
50
|
+
versions: ['>=4.17.1']
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
file: 'resources/images.js',
|
|
54
|
+
targetClass: 'Images',
|
|
55
|
+
baseResource: 'images',
|
|
56
|
+
methods: ['createVariation', 'edit', 'generate']
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
file: 'resources/fine-tuning/jobs/jobs.js',
|
|
60
|
+
targetClass: 'Jobs',
|
|
61
|
+
baseResource: 'fine_tuning.jobs',
|
|
62
|
+
methods: ['cancel', 'create', 'list', 'listEvents', 'retrieve'],
|
|
63
|
+
versions: ['>=4.34.0'] // file location changed in 4.34.0
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
file: 'resources/fine-tuning/jobs.js',
|
|
67
|
+
targetClass: 'Jobs',
|
|
68
|
+
baseResource: 'fine_tuning.jobs',
|
|
69
|
+
methods: ['cancel', 'create', 'list', 'listEvents', 'retrieve'],
|
|
70
|
+
versions: ['>=4.1.0 <4.34.0']
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
file: 'resources/fine-tunes.js', // deprecated after 4.1.0
|
|
74
|
+
targetClass: 'FineTunes',
|
|
75
|
+
baseResource: 'fine-tune',
|
|
76
|
+
methods: ['cancel', 'create', 'list', 'listEvents', 'retrieve'],
|
|
77
|
+
versions: ['>=4.0.0 <4.1.0']
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
file: 'resources/models.js',
|
|
81
|
+
targetClass: 'Models',
|
|
82
|
+
baseResource: 'models',
|
|
83
|
+
methods: ['del', 'list', 'retrieve']
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
file: 'resources/moderations.js',
|
|
87
|
+
targetClass: 'Moderations',
|
|
88
|
+
baseResource: 'moderations',
|
|
89
|
+
methods: ['create']
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
file: 'resources/audio/transcriptions.js',
|
|
93
|
+
targetClass: 'Transcriptions',
|
|
94
|
+
baseResource: 'audio.transcriptions',
|
|
95
|
+
methods: ['create']
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
file: 'resources/audio/translations.js',
|
|
99
|
+
targetClass: 'Translations',
|
|
100
|
+
baseResource: 'audio.translations',
|
|
101
|
+
methods: ['create']
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
|
|
13
105
|
addHook({ name: 'openai', file: 'dist/api.js', versions: ['>=3.0.0 <4'] }, exports => {
|
|
14
106
|
const methodNames = Object.getOwnPropertyNames(exports.OpenAIApi.prototype)
|
|
15
107
|
methodNames.shift() // remove leading 'constructor' method
|
|
@@ -48,3 +140,60 @@ addHook({ name: 'openai', file: 'dist/api.js', versions: ['>=3.0.0 <4'] }, expor
|
|
|
48
140
|
|
|
49
141
|
return exports
|
|
50
142
|
})
|
|
143
|
+
|
|
144
|
+
for (const shim of V4_PACKAGE_SHIMS) {
|
|
145
|
+
const { file, targetClass, baseResource, methods } = shim
|
|
146
|
+
addHook({ name: 'openai', file, versions: shim.versions || ['>=4'] }, exports => {
|
|
147
|
+
const targetPrototype = exports[targetClass].prototype
|
|
148
|
+
|
|
149
|
+
for (const methodName of methods) {
|
|
150
|
+
shimmer.wrap(targetPrototype, methodName, methodFn => function () {
|
|
151
|
+
if (!startCh.hasSubscribers) {
|
|
152
|
+
return methodFn.apply(this, arguments)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const client = this._client || this.client
|
|
156
|
+
|
|
157
|
+
startCh.publish({
|
|
158
|
+
methodName: `${baseResource}.${methodName}`,
|
|
159
|
+
args: arguments,
|
|
160
|
+
basePath: client.baseURL,
|
|
161
|
+
apiKey: client.apiKey
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
const apiProm = methodFn.apply(this, arguments)
|
|
165
|
+
|
|
166
|
+
// wrapping `parse` avoids problematic wrapping of `then` when trying to call
|
|
167
|
+
// `withResponse` in userland code after. This way, we can return the whole `APIPromise`
|
|
168
|
+
shimmer.wrap(apiProm, 'parse', origApiPromParse => function () {
|
|
169
|
+
return origApiPromParse.apply(this, arguments)
|
|
170
|
+
// the original response is wrapped in a promise, so we need to unwrap it
|
|
171
|
+
.then(body => Promise.all([this.responsePromise, body]))
|
|
172
|
+
.then(([{ response, options }, body]) => {
|
|
173
|
+
finishCh.publish({
|
|
174
|
+
headers: response.headers,
|
|
175
|
+
body,
|
|
176
|
+
path: response.url,
|
|
177
|
+
method: options.method
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
return body
|
|
181
|
+
})
|
|
182
|
+
.catch(err => {
|
|
183
|
+
errorCh.publish({ err })
|
|
184
|
+
|
|
185
|
+
throw err
|
|
186
|
+
})
|
|
187
|
+
.finally(() => {
|
|
188
|
+
// maybe we don't want to unwrap here in case the promise is re-used?
|
|
189
|
+
// other hand: we want to avoid resource leakage
|
|
190
|
+
shimmer.unwrap(apiProm, 'parse')
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
return apiProm
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
return exports
|
|
198
|
+
})
|
|
199
|
+
}
|
|
@@ -297,7 +297,12 @@ function dispatcherRunWrapper (run) {
|
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
function dispatcherRunWrapperNew (run) {
|
|
300
|
-
return function () {
|
|
300
|
+
return function (testGroups) {
|
|
301
|
+
if (!this._allTests) {
|
|
302
|
+
// Removed in https://github.com/microsoft/playwright/commit/1e52c37b254a441cccf332520f60225a5acc14c7
|
|
303
|
+
// Not available from >=1.44.0
|
|
304
|
+
this._allTests = testGroups.map(g => g.tests).flat()
|
|
305
|
+
}
|
|
301
306
|
remainingTestsByFile = getTestsBySuiteFromTestGroups(arguments[0])
|
|
302
307
|
return run.apply(this, arguments)
|
|
303
308
|
}
|
|
@@ -7,6 +7,9 @@ exports.kinesis = require('./kinesis')
|
|
|
7
7
|
exports.lambda = require('./lambda')
|
|
8
8
|
exports.redshift = require('./redshift')
|
|
9
9
|
exports.s3 = require('./s3')
|
|
10
|
+
exports.sfn = require('./sfn')
|
|
10
11
|
exports.sns = require('./sns')
|
|
11
12
|
exports.sqs = require('./sqs')
|
|
13
|
+
exports.states = require('./states')
|
|
14
|
+
exports.stepfunctions = require('./stepfunctions')
|
|
12
15
|
exports.default = require('./default')
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const log = require('../../../dd-trace/src/log')
|
|
3
|
+
const BaseAwsSdkPlugin = require('../base')
|
|
4
|
+
|
|
5
|
+
class Stepfunctions extends BaseAwsSdkPlugin {
|
|
6
|
+
static get id () { return 'stepfunctions' }
|
|
7
|
+
|
|
8
|
+
// This is the shape of StartExecutionInput, as defined in
|
|
9
|
+
// https://github.com/aws/aws-sdk-js/blob/master/apis/states-2016-11-23.normal.json
|
|
10
|
+
// "StartExecutionInput": {
|
|
11
|
+
// "type": "structure",
|
|
12
|
+
// "required": [
|
|
13
|
+
// "stateMachineArn"
|
|
14
|
+
// ],
|
|
15
|
+
// "members": {
|
|
16
|
+
// "stateMachineArn": {
|
|
17
|
+
// "shape": "Arn",
|
|
18
|
+
// },
|
|
19
|
+
// "name": {
|
|
20
|
+
// "shape": "Name",
|
|
21
|
+
// },
|
|
22
|
+
// "input": {
|
|
23
|
+
// "shape": "SensitiveData",
|
|
24
|
+
// },
|
|
25
|
+
// "traceHeader": {
|
|
26
|
+
// "shape": "TraceHeader",
|
|
27
|
+
// }
|
|
28
|
+
// }
|
|
29
|
+
|
|
30
|
+
generateTags (params, operation, response) {
|
|
31
|
+
if (!params) return {}
|
|
32
|
+
const tags = { 'resource.name': params.name ? `${operation} ${params.name}` : `${operation}` }
|
|
33
|
+
if (operation === 'startExecution' || operation === 'startSyncExecution') {
|
|
34
|
+
tags.statemachinearn = `${params.stateMachineArn}`
|
|
35
|
+
}
|
|
36
|
+
return tags
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
requestInject (span, request) {
|
|
40
|
+
const operation = request.operation
|
|
41
|
+
if (operation === 'startExecution' || operation === 'startSyncExecution') {
|
|
42
|
+
if (!request.params || !request.params.input) {
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const input = request.params.input
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const inputObj = JSON.parse(input)
|
|
50
|
+
if (inputObj && typeof inputObj === 'object') {
|
|
51
|
+
// We've parsed the input JSON string
|
|
52
|
+
inputObj._datadog = {}
|
|
53
|
+
this.tracer.inject(span, 'text_map', inputObj._datadog)
|
|
54
|
+
const newInput = JSON.stringify(inputObj)
|
|
55
|
+
request.params.input = newInput
|
|
56
|
+
}
|
|
57
|
+
} catch (e) {
|
|
58
|
+
log.info('Unable to treat input as JSON')
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = Stepfunctions
|
|
@@ -18,7 +18,14 @@ const {
|
|
|
18
18
|
TEST_SOURCE_FILE,
|
|
19
19
|
TEST_EARLY_FLAKE_ENABLED,
|
|
20
20
|
TEST_IS_NEW,
|
|
21
|
-
TEST_IS_RETRY
|
|
21
|
+
TEST_IS_RETRY,
|
|
22
|
+
TEST_SUITE_ID,
|
|
23
|
+
TEST_SESSION_ID,
|
|
24
|
+
TEST_COMMAND,
|
|
25
|
+
TEST_MODULE,
|
|
26
|
+
TEST_MODULE_ID,
|
|
27
|
+
TEST_SUITE,
|
|
28
|
+
CUCUMBER_IS_PARALLEL
|
|
22
29
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
23
30
|
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
24
31
|
const { COMPONENT, ERROR_MESSAGE } = require('../../dd-trace/src/constants')
|
|
@@ -32,6 +39,22 @@ const {
|
|
|
32
39
|
TELEMETRY_ITR_UNSKIPPABLE,
|
|
33
40
|
TELEMETRY_CODE_COVERAGE_NUM_FILES
|
|
34
41
|
} = require('../../dd-trace/src/ci-visibility/telemetry')
|
|
42
|
+
const id = require('../../dd-trace/src/id')
|
|
43
|
+
|
|
44
|
+
const isCucumberWorker = !!process.env.CUCUMBER_WORKER_ID
|
|
45
|
+
|
|
46
|
+
function getTestSuiteTags (testSuiteSpan) {
|
|
47
|
+
const suiteTags = {
|
|
48
|
+
[TEST_SUITE_ID]: testSuiteSpan.context().toSpanId(),
|
|
49
|
+
[TEST_SESSION_ID]: testSuiteSpan.context().toTraceId(),
|
|
50
|
+
[TEST_COMMAND]: testSuiteSpan.context()._tags[TEST_COMMAND],
|
|
51
|
+
[TEST_MODULE]: 'cucumber'
|
|
52
|
+
}
|
|
53
|
+
if (testSuiteSpan.context()._parentId) {
|
|
54
|
+
suiteTags[TEST_MODULE_ID] = testSuiteSpan.context()._parentId.toString(10)
|
|
55
|
+
}
|
|
56
|
+
return suiteTags
|
|
57
|
+
}
|
|
35
58
|
|
|
36
59
|
class CucumberPlugin extends CiPlugin {
|
|
37
60
|
static get id () {
|
|
@@ -43,6 +66,8 @@ class CucumberPlugin extends CiPlugin {
|
|
|
43
66
|
|
|
44
67
|
this.sourceRoot = process.cwd()
|
|
45
68
|
|
|
69
|
+
this.testSuiteSpanByPath = {}
|
|
70
|
+
|
|
46
71
|
this.addSub('ci:cucumber:session:finish', ({
|
|
47
72
|
status,
|
|
48
73
|
isSuitesSkipped,
|
|
@@ -50,7 +75,8 @@ class CucumberPlugin extends CiPlugin {
|
|
|
50
75
|
testCodeCoverageLinesTotal,
|
|
51
76
|
hasUnskippableSuites,
|
|
52
77
|
hasForcedToRunSuites,
|
|
53
|
-
isEarlyFlakeDetectionEnabled
|
|
78
|
+
isEarlyFlakeDetectionEnabled,
|
|
79
|
+
isParallel
|
|
54
80
|
}) => {
|
|
55
81
|
const { isSuitesSkippingEnabled, isCodeCoverageEnabled } = this.libraryConfig || {}
|
|
56
82
|
addIntelligentTestRunnerSpanTags(
|
|
@@ -70,6 +96,9 @@ class CucumberPlugin extends CiPlugin {
|
|
|
70
96
|
if (isEarlyFlakeDetectionEnabled) {
|
|
71
97
|
this.testSessionSpan.setTag(TEST_EARLY_FLAKE_ENABLED, 'true')
|
|
72
98
|
}
|
|
99
|
+
if (isParallel) {
|
|
100
|
+
this.testSessionSpan.setTag(CUCUMBER_IS_PARALLEL, 'true')
|
|
101
|
+
}
|
|
73
102
|
|
|
74
103
|
this.testSessionSpan.setTag(TEST_STATUS, status)
|
|
75
104
|
this.testModuleSpan.setTag(TEST_STATUS, status)
|
|
@@ -101,7 +130,7 @@ class CucumberPlugin extends CiPlugin {
|
|
|
101
130
|
if (itrCorrelationId) {
|
|
102
131
|
testSuiteMetadata[ITR_CORRELATION_ID] = itrCorrelationId
|
|
103
132
|
}
|
|
104
|
-
|
|
133
|
+
const testSuiteSpan = this.tracer.startSpan('cucumber.test_suite', {
|
|
105
134
|
childOf: this.testModuleSpan,
|
|
106
135
|
tags: {
|
|
107
136
|
[COMPONENT]: this.constructor.id,
|
|
@@ -109,25 +138,29 @@ class CucumberPlugin extends CiPlugin {
|
|
|
109
138
|
...testSuiteMetadata
|
|
110
139
|
}
|
|
111
140
|
})
|
|
141
|
+
this.testSuiteSpanByPath[testSuitePath] = testSuiteSpan
|
|
142
|
+
|
|
112
143
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite')
|
|
113
144
|
if (this.libraryConfig?.isCodeCoverageEnabled) {
|
|
114
145
|
this.telemetry.ciVisEvent(TELEMETRY_CODE_COVERAGE_STARTED, 'suite', { library: 'istanbul' })
|
|
115
146
|
}
|
|
116
147
|
})
|
|
117
148
|
|
|
118
|
-
this.addSub('ci:cucumber:test-suite:finish', status => {
|
|
119
|
-
this.
|
|
120
|
-
|
|
149
|
+
this.addSub('ci:cucumber:test-suite:finish', ({ status, testSuitePath }) => {
|
|
150
|
+
const testSuiteSpan = this.testSuiteSpanByPath[testSuitePath]
|
|
151
|
+
testSuiteSpan.setTag(TEST_STATUS, status)
|
|
152
|
+
testSuiteSpan.finish()
|
|
121
153
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
|
|
122
154
|
})
|
|
123
155
|
|
|
124
|
-
this.addSub('ci:cucumber:test-suite:code-coverage', ({ coverageFiles, suiteFile }) => {
|
|
156
|
+
this.addSub('ci:cucumber:test-suite:code-coverage', ({ coverageFiles, suiteFile, testSuitePath }) => {
|
|
125
157
|
if (!this.libraryConfig?.isCodeCoverageEnabled) {
|
|
126
158
|
return
|
|
127
159
|
}
|
|
128
160
|
if (!coverageFiles.length) {
|
|
129
161
|
this.telemetry.count(TELEMETRY_CODE_COVERAGE_EMPTY)
|
|
130
162
|
}
|
|
163
|
+
const testSuiteSpan = this.testSuiteSpanByPath[testSuitePath]
|
|
131
164
|
|
|
132
165
|
const relativeCoverageFiles = [...coverageFiles, suiteFile]
|
|
133
166
|
.map(filename => getTestSuitePath(filename, this.repositoryRoot))
|
|
@@ -135,8 +168,8 @@ class CucumberPlugin extends CiPlugin {
|
|
|
135
168
|
this.telemetry.distribution(TELEMETRY_CODE_COVERAGE_NUM_FILES, {}, relativeCoverageFiles.length)
|
|
136
169
|
|
|
137
170
|
const formattedCoverage = {
|
|
138
|
-
sessionId:
|
|
139
|
-
suiteId:
|
|
171
|
+
sessionId: testSuiteSpan.context()._traceId,
|
|
172
|
+
suiteId: testSuiteSpan.context()._spanId,
|
|
140
173
|
files: relativeCoverageFiles
|
|
141
174
|
}
|
|
142
175
|
|
|
@@ -144,7 +177,7 @@ class CucumberPlugin extends CiPlugin {
|
|
|
144
177
|
this.telemetry.ciVisEvent(TELEMETRY_CODE_COVERAGE_FINISHED, 'suite', { library: 'istanbul' })
|
|
145
178
|
})
|
|
146
179
|
|
|
147
|
-
this.addSub('ci:cucumber:test:start', ({ testName, testFileAbsolutePath, testSourceLine }) => {
|
|
180
|
+
this.addSub('ci:cucumber:test:start', ({ testName, testFileAbsolutePath, testSourceLine, isParallel }) => {
|
|
148
181
|
const store = storage.getStore()
|
|
149
182
|
const testSuite = getTestSuitePath(testFileAbsolutePath, this.sourceRoot)
|
|
150
183
|
const testSourceFile = getTestSuitePath(testFileAbsolutePath, this.repositoryRoot)
|
|
@@ -153,6 +186,10 @@ class CucumberPlugin extends CiPlugin {
|
|
|
153
186
|
[TEST_SOURCE_START]: testSourceLine,
|
|
154
187
|
[TEST_SOURCE_FILE]: testSourceFile
|
|
155
188
|
}
|
|
189
|
+
if (isParallel) {
|
|
190
|
+
extraTags[CUCUMBER_IS_PARALLEL] = 'true'
|
|
191
|
+
}
|
|
192
|
+
|
|
156
193
|
const testSpan = this.startTestSpan(testName, testSuite, extraTags)
|
|
157
194
|
|
|
158
195
|
this.enter(testSpan, store)
|
|
@@ -172,6 +209,36 @@ class CucumberPlugin extends CiPlugin {
|
|
|
172
209
|
this.enter(span, store)
|
|
173
210
|
})
|
|
174
211
|
|
|
212
|
+
this.addSub('ci:cucumber:worker-report:trace', (traces) => {
|
|
213
|
+
const formattedTraces = JSON.parse(traces).map(trace =>
|
|
214
|
+
trace.map(span => ({
|
|
215
|
+
...span,
|
|
216
|
+
span_id: id(span.span_id),
|
|
217
|
+
trace_id: id(span.trace_id),
|
|
218
|
+
parent_id: id(span.parent_id)
|
|
219
|
+
}))
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
// We have to update the test session, test module and test suite ids
|
|
223
|
+
// before we export them in the main process
|
|
224
|
+
formattedTraces.forEach(trace => {
|
|
225
|
+
trace.forEach(span => {
|
|
226
|
+
if (span.name === 'cucumber.test') {
|
|
227
|
+
const testSuite = span.meta[TEST_SUITE]
|
|
228
|
+
const testSuiteSpan = this.testSuiteSpanByPath[testSuite]
|
|
229
|
+
|
|
230
|
+
const testSuiteTags = getTestSuiteTags(testSuiteSpan)
|
|
231
|
+
span.meta = {
|
|
232
|
+
...span.meta,
|
|
233
|
+
...testSuiteTags
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
this.tracer._exporter.export(trace)
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
|
|
175
242
|
this.addSub('ci:cucumber:test:finish', ({ isStep, status, skipReason, errorMessage, isNew, isEfdRetry }) => {
|
|
176
243
|
const span = storage.getStore().span
|
|
177
244
|
const statusTag = isStep ? 'step.status' : TEST_STATUS
|
|
@@ -201,6 +268,10 @@ class CucumberPlugin extends CiPlugin {
|
|
|
201
268
|
{ hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] }
|
|
202
269
|
)
|
|
203
270
|
finishAllTraceSpans(span)
|
|
271
|
+
// If it's a worker, flushing is cheap, as it's just sending data to the main process
|
|
272
|
+
if (isCucumberWorker) {
|
|
273
|
+
this.tracer._exporter.flush()
|
|
274
|
+
}
|
|
204
275
|
}
|
|
205
276
|
})
|
|
206
277
|
|
|
@@ -213,10 +284,11 @@ class CucumberPlugin extends CiPlugin {
|
|
|
213
284
|
}
|
|
214
285
|
|
|
215
286
|
startTestSpan (testName, testSuite, extraTags) {
|
|
287
|
+
const testSuiteSpan = this.testSuiteSpanByPath[testSuite]
|
|
216
288
|
return super.startTestSpan(
|
|
217
289
|
testName,
|
|
218
290
|
testSuite,
|
|
219
|
-
|
|
291
|
+
testSuiteSpan,
|
|
220
292
|
extraTags
|
|
221
293
|
)
|
|
222
294
|
}
|
|
@@ -17,8 +17,11 @@ class FetchPlugin extends HttpClientPlugin {
|
|
|
17
17
|
|
|
18
18
|
const store = super.bindStart(ctx)
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
for (const name in headers) {
|
|
21
|
+
if (!req.headers.has(name)) {
|
|
22
|
+
req.headers.set(name, headers[name])
|
|
23
|
+
}
|
|
24
|
+
}
|
|
22
25
|
|
|
23
26
|
return store
|
|
24
27
|
}
|