dd-trace 5.109.0 → 5.111.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/README.md +13 -8
- package/ci/init.js +25 -5
- package/ci/vitest-no-worker-init-setup.mjs +430 -0
- package/ext/tags.js +2 -0
- package/index.d.ts +79 -24
- package/initialize.mjs +5 -6
- package/loader-hook.mjs +154 -47
- package/package.json +20 -16
- package/packages/datadog-esbuild/index.js +26 -0
- package/packages/datadog-esbuild/src/utils.js +46 -3
- package/packages/datadog-instrumentations/src/connect.js +4 -3
- package/packages/datadog-instrumentations/src/fastify.js +4 -12
- package/packages/datadog-instrumentations/src/fs.js +8 -6
- package/packages/datadog-instrumentations/src/graphql.js +26 -484
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
- package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
- package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +201 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +18 -2
- package/packages/datadog-instrumentations/src/koa.js +3 -2
- package/packages/datadog-instrumentations/src/mariadb.js +4 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
- package/packages/datadog-instrumentations/src/mongoose.js +14 -2
- package/packages/datadog-instrumentations/src/next.js +10 -10
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
- package/packages/datadog-instrumentations/src/playwright.js +1 -1
- package/packages/datadog-instrumentations/src/restify.js +4 -3
- package/packages/datadog-instrumentations/src/router.js +18 -5
- package/packages/datadog-instrumentations/src/selenium.js +1 -2
- package/packages/datadog-instrumentations/src/tedious.js +28 -0
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1293 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +1567 -0
- package/packages/datadog-instrumentations/src/vitest-util.js +249 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +753 -0
- package/packages/datadog-instrumentations/src/vitest.js +11 -1598
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +2 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +18 -10
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
- package/packages/datadog-plugin-aws-sdk/src/base.js +13 -9
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +83 -91
- package/packages/datadog-plugin-cucumber/src/index.js +1 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -5
- package/packages/datadog-plugin-cypress/src/support.js +45 -1
- package/packages/datadog-plugin-express/src/code_origin.js +1 -1
- package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +627 -11
- package/packages/datadog-plugin-graphql/src/index.js +20 -8
- package/packages/datadog-plugin-graphql/src/parse.js +24 -4
- package/packages/datadog-plugin-graphql/src/validate.js +16 -5
- package/packages/datadog-plugin-http/src/client.js +8 -3
- package/packages/datadog-plugin-http/src/index.js +6 -8
- package/packages/datadog-plugin-http2/src/client.js +5 -2
- package/packages/datadog-plugin-jest/src/index.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +1 -1
- package/packages/datadog-plugin-mysql/src/index.js +7 -2
- package/packages/datadog-plugin-openai/src/services.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
- package/packages/datadog-plugin-undici/src/index.js +5 -2
- package/packages/datadog-plugin-vitest/src/index.js +76 -64
- package/packages/datadog-shimmer/src/shimmer.js +37 -16
- package/packages/datadog-webpack/index.js +17 -1
- package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
- package/packages/dd-trace/src/aiguard/sdk.js +15 -3
- package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
- package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
- package/packages/dd-trace/src/appsec/channels.js +3 -1
- package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
- package/packages/dd-trace/src/appsec/graphql.js +5 -5
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +26 -19
- package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
- package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
- package/packages/dd-trace/src/appsec/index.js +20 -7
- package/packages/dd-trace/src/appsec/lambda.js +135 -0
- package/packages/dd-trace/src/appsec/reporter.js +50 -10
- package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +27 -1
- package/packages/dd-trace/src/appsec/waf/index.js +16 -4
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +21 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +55 -13
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
- package/packages/dd-trace/src/ci-visibility/requests/request.js +1 -17
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
- package/packages/dd-trace/src/config/defaults.js +25 -5
- package/packages/dd-trace/src/config/generated-config-types.d.ts +596 -58
- package/packages/dd-trace/src/config/helper.js +134 -33
- package/packages/dd-trace/src/config/index.js +39 -23
- package/packages/dd-trace/src/config/major-overrides.js +4 -2
- package/packages/dd-trace/src/config/parsers.js +19 -1
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +109 -57
- package/packages/dd-trace/src/constants.js +7 -0
- package/packages/dd-trace/src/datastreams/processor.js +11 -3
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -35
- package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
- package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
- package/packages/dd-trace/src/debugger/index.js +5 -0
- package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
- package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
- package/packages/dd-trace/src/exporters/agent/index.js +1 -1
- package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
- package/packages/dd-trace/src/exporters/common/request.js +1 -20
- package/packages/dd-trace/src/exporters/common/url.js +34 -0
- package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
- package/packages/dd-trace/src/guardrails/index.js +8 -5
- package/packages/dd-trace/src/index.js +6 -4
- package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
- package/packages/dd-trace/src/llmobs/index.js +17 -6
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +8 -6
- package/packages/dd-trace/src/llmobs/span_processor.js +5 -1
- package/packages/dd-trace/src/llmobs/tagger.js +58 -3
- package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
- package/packages/dd-trace/src/log/index.js +18 -20
- package/packages/dd-trace/src/openfeature/flagging_provider.js +2 -7
- package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
- package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +4 -3
- package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
- package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
- package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +44 -38
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
- package/packages/dd-trace/src/opentelemetry/span.js +14 -3
- package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
- package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
- package/packages/dd-trace/src/opentracing/span_context.js +17 -0
- package/packages/dd-trace/src/opentracing/tracer.js +6 -1
- package/packages/dd-trace/src/plugin_manager.js +14 -8
- package/packages/dd-trace/src/plugins/ci_plugin.js +21 -4
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
- package/packages/dd-trace/src/plugins/util/llm.js +6 -1
- package/packages/dd-trace/src/plugins/util/test.js +91 -9
- package/packages/dd-trace/src/plugins/util/url.js +68 -1
- package/packages/dd-trace/src/plugins/util/web.js +19 -36
- package/packages/dd-trace/src/priority_sampler.js +1 -13
- package/packages/dd-trace/src/profiling/config.js +129 -198
- package/packages/dd-trace/src/profiling/constants.js +4 -7
- package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
- package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
- package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
- package/packages/dd-trace/src/profiling/oom.js +71 -0
- package/packages/dd-trace/src/profiling/profiler.js +35 -23
- package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
- package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
- package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
- package/packages/dd-trace/src/proxy.js +8 -8
- package/packages/dd-trace/src/ritm.js +4 -2
- package/packages/dd-trace/src/serverless.js +1 -2
- package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
- package/packages/dd-trace/src/span_format.js +2 -1
- package/packages/dd-trace/src/span_processor.js +11 -6
- package/packages/dd-trace/src/span_stats.js +96 -78
- package/packages/dd-trace/src/startup-log.js +2 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
- package/packages/dd-trace/src/telemetry/index.js +2 -2
- package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
- package/packages/dd-trace/src/telemetry/send-data.js +20 -18
- package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
- package/packages/dd-trace/src/tracer.js +15 -1
- package/packages/dd-trace/src/util.js +14 -0
- package/register.js +58 -1
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/pprof-format/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/shell-quote/index.js +1 -1
- package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
- package/packages/dd-trace/src/appsec/api_security_sampler.js +0 -117
|
@@ -34,9 +34,9 @@ function uploadCoverageReport (
|
|
|
34
34
|
{ filePath, format, testEnvironmentMetadata, url, isEvpProxy, evpProxyPrefix },
|
|
35
35
|
callback
|
|
36
36
|
) {
|
|
37
|
-
const
|
|
37
|
+
const { DD_API_KEY } = getConfig()
|
|
38
38
|
|
|
39
|
-
if (!
|
|
39
|
+
if (!DD_API_KEY && !isEvpProxy) {
|
|
40
40
|
return callback(new Error('DD_API_KEY is required for coverage report upload'))
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -82,7 +82,7 @@ function uploadCoverageReport (
|
|
|
82
82
|
options.headers['X-Datadog-EVP-Subdomain'] = 'ci-intake'
|
|
83
83
|
} else {
|
|
84
84
|
options.path = '/api/v2/cicovreprt'
|
|
85
|
-
options.headers['dd-api-key'] =
|
|
85
|
+
options.headers['dd-api-key'] = DD_API_KEY
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
log.debug('Uploading coverage report %s to %s%s', filePath, url, options.path)
|
|
@@ -16,6 +16,7 @@ const {
|
|
|
16
16
|
} = require('../telemetry')
|
|
17
17
|
|
|
18
18
|
const { buildCacheKey, writeToCache, withCache } = require('../requests/fs-cache')
|
|
19
|
+
const { validateTestManagementTestsResponse } = require('../test-optimization-http-cache-schema')
|
|
19
20
|
|
|
20
21
|
// Calculate the number of tests from the test management tests response, which has a shape like:
|
|
21
22
|
// { module: { suites: { suite: { tests: { testName: { properties: {...} } } } } } }
|
|
@@ -39,6 +40,19 @@ function getNumFromTestManagementTests (testManagementTests) {
|
|
|
39
40
|
return totalNumTests
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
function parseJsonResponse (rawJson) {
|
|
44
|
+
return typeof rawJson === 'string' ? JSON.parse(rawJson) : rawJson
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function parseTestManagementTestsResponse (rawJson, options = {}) {
|
|
48
|
+
const parsedResponse = parseJsonResponse(rawJson)
|
|
49
|
+
if (options.validateRequiredFields) {
|
|
50
|
+
validateTestManagementTestsResponse(parsedResponse)
|
|
51
|
+
}
|
|
52
|
+
const { data: { attributes: { modules: testManagementTests } } } = parsedResponse
|
|
53
|
+
return testManagementTests
|
|
54
|
+
}
|
|
55
|
+
|
|
42
56
|
function getTestManagementTests ({
|
|
43
57
|
url,
|
|
44
58
|
isEvpProxy,
|
|
@@ -121,12 +135,12 @@ function fetchFromApi ({
|
|
|
121
135
|
options.path = `${evpProxyPrefix}/api/v2/test/libraries/test-management/tests`
|
|
122
136
|
options.headers['X-Datadog-EVP-Subdomain'] = 'api'
|
|
123
137
|
} else {
|
|
124
|
-
const {
|
|
125
|
-
if (!
|
|
138
|
+
const { DD_API_KEY } = getConfig()
|
|
139
|
+
if (!DD_API_KEY) {
|
|
126
140
|
return done(new Error('Test management tests were not fetched because Datadog API key is not defined.'))
|
|
127
141
|
}
|
|
128
142
|
|
|
129
|
-
options.headers['dd-api-key'] =
|
|
143
|
+
options.headers['dd-api-key'] = DD_API_KEY
|
|
130
144
|
}
|
|
131
145
|
|
|
132
146
|
const data = JSON.stringify({
|
|
@@ -155,7 +169,7 @@ function fetchFromApi ({
|
|
|
155
169
|
done(err)
|
|
156
170
|
} else {
|
|
157
171
|
try {
|
|
158
|
-
const
|
|
172
|
+
const testManagementTests = parseTestManagementTestsResponse(res)
|
|
159
173
|
|
|
160
174
|
const numTests = getNumFromTestManagementTests(testManagementTests)
|
|
161
175
|
|
|
@@ -174,4 +188,8 @@ function fetchFromApi ({
|
|
|
174
188
|
})
|
|
175
189
|
}
|
|
176
190
|
|
|
177
|
-
module.exports = {
|
|
191
|
+
module.exports = {
|
|
192
|
+
getNumFromTestManagementTests, // Exported for later use in the cache
|
|
193
|
+
getTestManagementTests,
|
|
194
|
+
parseTestManagementTestsResponse,
|
|
195
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const REQUIRED_SETTINGS_KEYS = [
|
|
4
|
+
'code_coverage',
|
|
5
|
+
'tests_skipping',
|
|
6
|
+
'itr_enabled',
|
|
7
|
+
'require_git',
|
|
8
|
+
'early_flake_detection',
|
|
9
|
+
'flaky_test_retries_enabled',
|
|
10
|
+
'di_enabled',
|
|
11
|
+
'known_tests_enabled',
|
|
12
|
+
'test_management',
|
|
13
|
+
'impacted_tests_enabled',
|
|
14
|
+
'coverage_report_upload_enabled',
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
function isObject (value) {
|
|
18
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function assertObject (value, message) {
|
|
22
|
+
if (!isObject(value)) {
|
|
23
|
+
throw new Error(message)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function assertHasKeys (value, keys, endpoint) {
|
|
28
|
+
for (const key of keys) {
|
|
29
|
+
if (!Object.hasOwn(value, key)) {
|
|
30
|
+
throw new Error(`Invalid ${endpoint} response: missing ${key}`)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getAttributes (response, endpoint) {
|
|
36
|
+
const attributes = response?.data?.attributes
|
|
37
|
+
assertObject(attributes, `Invalid ${endpoint} response: attributes must be an object`)
|
|
38
|
+
return attributes
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function validateSettingsResponse (response) {
|
|
42
|
+
const attributes = response?.data?.attributes ?? response
|
|
43
|
+
assertObject(attributes, 'Invalid settings response: attributes must be an object')
|
|
44
|
+
assertHasKeys(attributes, REQUIRED_SETTINGS_KEYS, 'settings')
|
|
45
|
+
assertObject(attributes.early_flake_detection, 'Invalid settings response: early_flake_detection must be an object')
|
|
46
|
+
assertObject(attributes.test_management, 'Invalid settings response: test_management must be an object')
|
|
47
|
+
assertHasKeys(attributes.early_flake_detection, ['enabled'], 'settings early_flake_detection')
|
|
48
|
+
assertHasKeys(attributes.test_management, ['enabled'], 'settings test_management')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function validateKnownTestsResponse (response) {
|
|
52
|
+
const attributes = getAttributes(response, 'known tests')
|
|
53
|
+
assertHasKeys(attributes, ['tests'], 'known tests')
|
|
54
|
+
|
|
55
|
+
const { tests } = attributes
|
|
56
|
+
if (tests === null) return
|
|
57
|
+
assertObject(tests, 'Invalid known tests response: tests must be an object or null')
|
|
58
|
+
|
|
59
|
+
for (const suites of Object.values(tests)) {
|
|
60
|
+
assertObject(suites, 'Invalid known tests response: module suites must be objects')
|
|
61
|
+
for (const testNames of Object.values(suites)) {
|
|
62
|
+
if (!Array.isArray(testNames)) {
|
|
63
|
+
throw new TypeError('Invalid known tests response: suite tests must be arrays')
|
|
64
|
+
}
|
|
65
|
+
for (const testName of testNames) {
|
|
66
|
+
if (typeof testName !== 'string') {
|
|
67
|
+
throw new TypeError('Invalid known tests response: test names must be strings')
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function validateSkippableTestsResponse (response) {
|
|
75
|
+
assertObject(response, 'Invalid skippable tests response: response must be an object')
|
|
76
|
+
if (!Array.isArray(response.data)) {
|
|
77
|
+
throw new TypeError('Invalid skippable tests response: data must be an array')
|
|
78
|
+
}
|
|
79
|
+
if (response.meta !== undefined) {
|
|
80
|
+
assertObject(response.meta, 'Invalid skippable tests response: meta must be an object')
|
|
81
|
+
}
|
|
82
|
+
if (response.meta?.coverage !== undefined) {
|
|
83
|
+
assertObject(response.meta.coverage, 'Invalid skippable tests response: meta.coverage must be an object')
|
|
84
|
+
}
|
|
85
|
+
if (response.meta?.correlation_id !== undefined && typeof response.meta.correlation_id !== 'string') {
|
|
86
|
+
throw new Error('Invalid skippable tests response: meta.correlation_id must be a string')
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (const item of response.data) {
|
|
90
|
+
assertObject(item, 'Invalid skippable tests response: data entries must be objects')
|
|
91
|
+
if (typeof item.type !== 'string') {
|
|
92
|
+
throw new TypeError('Invalid skippable tests response: data entry type must be a string')
|
|
93
|
+
}
|
|
94
|
+
assertObject(item.attributes, 'Invalid skippable tests response: data entry attributes must be an object')
|
|
95
|
+
if ((item.type === 'suite' || item.type === 'test') && typeof item.attributes.suite !== 'string') {
|
|
96
|
+
throw new Error('Invalid skippable tests response: data entry suite must be a string')
|
|
97
|
+
}
|
|
98
|
+
if (item.type === 'test' && typeof item.attributes.name !== 'string') {
|
|
99
|
+
throw new Error('Invalid skippable tests response: data entry name must be a string')
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function validateTestManagementTestsResponse (response) {
|
|
105
|
+
const attributes = getAttributes(response, 'test management tests')
|
|
106
|
+
assertHasKeys(attributes, ['modules'], 'test management tests')
|
|
107
|
+
assertObject(attributes.modules, 'Invalid test management tests response: modules must be an object')
|
|
108
|
+
|
|
109
|
+
for (const testModule of Object.values(attributes.modules)) {
|
|
110
|
+
assertObject(testModule, 'Invalid test management tests response: modules entries must be objects')
|
|
111
|
+
assertObject(testModule.suites, 'Invalid test management tests response: module suites must be objects')
|
|
112
|
+
for (const suite of Object.values(testModule.suites)) {
|
|
113
|
+
assertObject(suite, 'Invalid test management tests response: suites entries must be objects')
|
|
114
|
+
assertObject(suite.tests, 'Invalid test management tests response: suite tests must be objects')
|
|
115
|
+
for (const test of Object.values(suite.tests)) {
|
|
116
|
+
assertObject(test, 'Invalid test management tests response: tests entries must be objects')
|
|
117
|
+
assertObject(test.properties, 'Invalid test management tests response: test properties must be objects')
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = {
|
|
124
|
+
validateKnownTestsResponse,
|
|
125
|
+
validateSettingsResponse,
|
|
126
|
+
validateSkippableTestsResponse,
|
|
127
|
+
validateTestManagementTestsResponse,
|
|
128
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs')
|
|
4
|
+
const path = require('node:path')
|
|
5
|
+
|
|
6
|
+
const log = require('../log')
|
|
7
|
+
const { getNumFromKnownTests } = require('../plugins/util/test')
|
|
8
|
+
const { parseKnownTestsResponse } = require('./early-flake-detection/get-known-tests')
|
|
9
|
+
const { parseSkippableSuitesResponse } = require('./intelligent-test-runner/get-skippable-suites')
|
|
10
|
+
const { parseLibraryConfigurationResponse } = require('./requests/get-library-configuration')
|
|
11
|
+
const {
|
|
12
|
+
getNumFromTestManagementTests,
|
|
13
|
+
parseTestManagementTestsResponse,
|
|
14
|
+
} = require('./test-management/get-test-management-tests')
|
|
15
|
+
const {
|
|
16
|
+
incrementCountMetric,
|
|
17
|
+
distributionMetric,
|
|
18
|
+
TELEMETRY_GIT_REQUESTS_SETTINGS_RESPONSE,
|
|
19
|
+
TELEMETRY_KNOWN_TESTS_RESPONSE_TESTS,
|
|
20
|
+
TELEMETRY_KNOWN_TESTS_RESPONSE_BYTES,
|
|
21
|
+
TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_SUITES,
|
|
22
|
+
TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_TESTS,
|
|
23
|
+
TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES,
|
|
24
|
+
TELEMETRY_TEST_MANAGEMENT_TESTS_RESPONSE_TESTS,
|
|
25
|
+
TELEMETRY_TEST_MANAGEMENT_TESTS_RESPONSE_BYTES,
|
|
26
|
+
} = require('./telemetry')
|
|
27
|
+
|
|
28
|
+
const CACHE_MISS = Symbol('test optimization http cache miss')
|
|
29
|
+
|
|
30
|
+
const PLAN_FOLDER = '.testoptimization'
|
|
31
|
+
const MANIFEST_FILE_NAME = 'manifest.txt'
|
|
32
|
+
const SUPPORTED_MANIFEST_VERSION = '1'
|
|
33
|
+
const CACHE_FOLDER_NAME = 'cache'
|
|
34
|
+
const HTTP_CACHE_FOLDER_NAME = 'http'
|
|
35
|
+
|
|
36
|
+
const ENV_MANIFEST_FILE = 'TEST_OPTIMIZATION_MANIFEST_FILE'
|
|
37
|
+
const ENV_MANIFEST_FILE_ALIAS = 'DD_TEST_OPTIMIZATION_MANIFEST_FILE'
|
|
38
|
+
const ENV_RUNFILES_DIR = 'RUNFILES_DIR'
|
|
39
|
+
const ENV_RUNFILES_MANIFEST_FILE = 'RUNFILES_MANIFEST_FILE'
|
|
40
|
+
const ENV_TEST_SRCDIR = 'TEST_SRCDIR'
|
|
41
|
+
|
|
42
|
+
const SETTINGS_FILE_NAME = 'settings.json'
|
|
43
|
+
const KNOWN_TESTS_FILE_NAME = 'known_tests.json'
|
|
44
|
+
const SKIPPABLE_TESTS_FILE_NAME = 'skippable_tests.json'
|
|
45
|
+
const TEST_MANAGEMENT_FILE_NAME = 'test_management.json'
|
|
46
|
+
|
|
47
|
+
const RUNFILES_MANIFEST_SEPARATOR = ' '
|
|
48
|
+
|
|
49
|
+
function parseManifestVersion (content) {
|
|
50
|
+
// Supported just the number version or 'version=x'
|
|
51
|
+
const version = content.replace(/^\uFEFF/, '').trim()
|
|
52
|
+
const match = version.match(/^version=(.+)$/)
|
|
53
|
+
return match ? match[1].trim() : version
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
class TestOptimizationHttpCache {
|
|
57
|
+
constructor ({ cwd = process.cwd(), env } = {}) {
|
|
58
|
+
this._cwd = cwd
|
|
59
|
+
// This cache intentionally consumes env vars that are not tracer config keys. (??)
|
|
60
|
+
// eslint-disable-next-line eslint-rules/eslint-process-env
|
|
61
|
+
this._env = env ?? process.env
|
|
62
|
+
this._manifestPath = this._resolveManifestPath()
|
|
63
|
+
this._testOptimizationPath = undefined
|
|
64
|
+
this._httpCachePath = undefined
|
|
65
|
+
this._available = false
|
|
66
|
+
|
|
67
|
+
this._buildReader()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
isAvailable () {
|
|
71
|
+
return this._available
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
readSettings () {
|
|
75
|
+
const payload = this._readFile(SETTINGS_FILE_NAME)
|
|
76
|
+
if (payload === CACHE_MISS) {
|
|
77
|
+
this._disable()
|
|
78
|
+
return CACHE_MISS
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const settings = parseLibraryConfigurationResponse(payload, undefined, { validateRequiredFields: true })
|
|
83
|
+
incrementCountMetric(TELEMETRY_GIT_REQUESTS_SETTINGS_RESPONSE, settings)
|
|
84
|
+
return settings
|
|
85
|
+
} catch (err) {
|
|
86
|
+
this._logInvalidCacheFile(SETTINGS_FILE_NAME, err)
|
|
87
|
+
this._disable()
|
|
88
|
+
return CACHE_MISS
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
readKnownTests () {
|
|
93
|
+
const payload = this._readFile(KNOWN_TESTS_FILE_NAME)
|
|
94
|
+
if (payload === CACHE_MISS) return CACHE_MISS
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
const knownTests = parseKnownTestsResponse(payload, { validateRequiredFields: true })
|
|
98
|
+
distributionMetric(TELEMETRY_KNOWN_TESTS_RESPONSE_TESTS, {}, getNumFromKnownTests(knownTests))
|
|
99
|
+
distributionMetric(TELEMETRY_KNOWN_TESTS_RESPONSE_BYTES, {}, payload.length)
|
|
100
|
+
return knownTests
|
|
101
|
+
} catch (err) {
|
|
102
|
+
this._logInvalidCacheFile(KNOWN_TESTS_FILE_NAME, err)
|
|
103
|
+
return CACHE_MISS
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
readSkippableSuites (options = {}) {
|
|
108
|
+
const payload = this._readFile(SKIPPABLE_TESTS_FILE_NAME)
|
|
109
|
+
if (payload === CACHE_MISS) return CACHE_MISS
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const parsedResponse = JSON.parse(payload)
|
|
113
|
+
const result = parseSkippableSuitesResponse(parsedResponse, { ...options, validateRequiredFields: true })
|
|
114
|
+
const testLevel = options.testLevel || 'suite'
|
|
115
|
+
const skippableItems = parsedResponse.data.filter(({ type }) => type === testLevel)
|
|
116
|
+
incrementCountMetric(
|
|
117
|
+
testLevel === 'test'
|
|
118
|
+
? TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_TESTS
|
|
119
|
+
: TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_SUITES,
|
|
120
|
+
{},
|
|
121
|
+
skippableItems.length
|
|
122
|
+
)
|
|
123
|
+
distributionMetric(TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES, {}, payload.length)
|
|
124
|
+
return result
|
|
125
|
+
} catch (err) {
|
|
126
|
+
this._logInvalidCacheFile(SKIPPABLE_TESTS_FILE_NAME, err)
|
|
127
|
+
return CACHE_MISS
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
hasValidSkippableSuites (options = {}) {
|
|
132
|
+
const payload = this._readFile(SKIPPABLE_TESTS_FILE_NAME)
|
|
133
|
+
if (payload === CACHE_MISS) return false
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
parseSkippableSuitesResponse(JSON.parse(payload), { ...options, validateRequiredFields: true })
|
|
137
|
+
return true
|
|
138
|
+
} catch {
|
|
139
|
+
return false
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
readTestManagementTests () {
|
|
144
|
+
const payload = this._readFile(TEST_MANAGEMENT_FILE_NAME)
|
|
145
|
+
if (payload === CACHE_MISS) return CACHE_MISS
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
const testManagementTests = parseTestManagementTestsResponse(payload, { validateRequiredFields: true })
|
|
149
|
+
distributionMetric(
|
|
150
|
+
TELEMETRY_TEST_MANAGEMENT_TESTS_RESPONSE_TESTS,
|
|
151
|
+
{},
|
|
152
|
+
getNumFromTestManagementTests(testManagementTests)
|
|
153
|
+
)
|
|
154
|
+
distributionMetric(TELEMETRY_TEST_MANAGEMENT_TESTS_RESPONSE_BYTES, {}, payload.length)
|
|
155
|
+
return testManagementTests
|
|
156
|
+
} catch (err) {
|
|
157
|
+
this._logInvalidCacheFile(TEST_MANAGEMENT_FILE_NAME, err)
|
|
158
|
+
return CACHE_MISS
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
_buildReader () {
|
|
163
|
+
if (!this._manifestPath) {
|
|
164
|
+
log.debug('Test Optimization HTTP cache manifest not found')
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const version = this._readManifestVersion(this._manifestPath)
|
|
169
|
+
if (version !== SUPPORTED_MANIFEST_VERSION) {
|
|
170
|
+
log.debug('Unsupported Test Optimization HTTP cache manifest version %j at %s', version, this._manifestPath)
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
this._testOptimizationPath = path.dirname(this._manifestPath)
|
|
175
|
+
this._httpCachePath = path.join(
|
|
176
|
+
this._testOptimizationPath,
|
|
177
|
+
CACHE_FOLDER_NAME,
|
|
178
|
+
HTTP_CACHE_FOLDER_NAME
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
const settingsPath = path.join(this._httpCachePath, SETTINGS_FILE_NAME)
|
|
182
|
+
if (!fs.existsSync(settingsPath)) {
|
|
183
|
+
log.debug('Test Optimization HTTP cache settings file not found at %s', settingsPath)
|
|
184
|
+
return
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
log.debug('Test Optimization HTTP cache found at %s', this._httpCachePath)
|
|
188
|
+
this._available = true
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
_resolveManifestPath () {
|
|
192
|
+
const localManifest = path.join(this._cwd, PLAN_FOLDER, MANIFEST_FILE_NAME)
|
|
193
|
+
if (fs.existsSync(localManifest)) {
|
|
194
|
+
return localManifest
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const manifestFile = this._env[ENV_MANIFEST_FILE] || this._env[ENV_MANIFEST_FILE_ALIAS]
|
|
198
|
+
if (!manifestFile) return
|
|
199
|
+
|
|
200
|
+
const resolvedManifestPath = this._resolveRunfilePath(manifestFile)
|
|
201
|
+
if (fs.existsSync(resolvedManifestPath)) {
|
|
202
|
+
return resolvedManifestPath
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
_resolveRunfilePath (manifestFile) {
|
|
207
|
+
if (fs.existsSync(manifestFile)) {
|
|
208
|
+
return manifestFile
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const runfilesDir = this._env[ENV_RUNFILES_DIR]
|
|
212
|
+
if (runfilesDir) {
|
|
213
|
+
const candidate = path.join(runfilesDir, manifestFile)
|
|
214
|
+
if (fs.existsSync(candidate)) {
|
|
215
|
+
return candidate
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const runfilesManifestCandidate = this._resolveRunfilePathFromManifest(manifestFile)
|
|
220
|
+
if (runfilesManifestCandidate) {
|
|
221
|
+
return runfilesManifestCandidate
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const testSrcdir = this._env[ENV_TEST_SRCDIR]
|
|
225
|
+
if (testSrcdir) {
|
|
226
|
+
const candidate = path.join(testSrcdir, manifestFile)
|
|
227
|
+
if (fs.existsSync(candidate)) {
|
|
228
|
+
return candidate
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return manifestFile
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
_resolveRunfilePathFromManifest (manifestFile) {
|
|
236
|
+
const runfilesManifest = this._env[ENV_RUNFILES_MANIFEST_FILE]
|
|
237
|
+
if (!runfilesManifest || !fs.existsSync(runfilesManifest)) return
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
const lines = fs.readFileSync(runfilesManifest, 'utf8').split('\n')
|
|
241
|
+
for (const line of lines) {
|
|
242
|
+
const separatorIndex = line.indexOf(RUNFILES_MANIFEST_SEPARATOR)
|
|
243
|
+
if (separatorIndex <= 0) continue
|
|
244
|
+
if (line.slice(0, separatorIndex) !== manifestFile) continue
|
|
245
|
+
|
|
246
|
+
const resolvedPath = line.slice(separatorIndex + 1).trim()
|
|
247
|
+
if (resolvedPath) return resolvedPath
|
|
248
|
+
}
|
|
249
|
+
} catch (err) {
|
|
250
|
+
log.debug('Failed to resolve Test Optimization HTTP cache manifest from %s: %s', runfilesManifest, err.message)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
_readManifestVersion (manifestPath) {
|
|
255
|
+
try {
|
|
256
|
+
return parseManifestVersion(fs.readFileSync(manifestPath, 'utf8'))
|
|
257
|
+
} catch (err) {
|
|
258
|
+
log.debug('Failed to read Test Optimization HTTP cache manifest %s: %s', manifestPath, err.message)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
_readFile (fileName) {
|
|
263
|
+
if (!this.isAvailable()) return CACHE_MISS
|
|
264
|
+
|
|
265
|
+
const filePath = path.join(this._httpCachePath, fileName)
|
|
266
|
+
try {
|
|
267
|
+
log.debug('Reading Test Optimization HTTP cache file %s', filePath)
|
|
268
|
+
return fs.readFileSync(filePath, 'utf8')
|
|
269
|
+
} catch (err) {
|
|
270
|
+
log.debug('Test Optimization HTTP cache file %s could not be read: %s', filePath, err.message)
|
|
271
|
+
return CACHE_MISS
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
_logInvalidCacheFile (fileName, err) {
|
|
276
|
+
log.debug('Test Optimization HTTP cache file %s could not be parsed: %s', fileName, err.message)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
_disable () {
|
|
280
|
+
this._available = false
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
module.exports = {
|
|
285
|
+
CACHE_MISS,
|
|
286
|
+
TestOptimizationHttpCache,
|
|
287
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const dns = require('dns')
|
|
4
3
|
const util = require('util')
|
|
5
4
|
|
|
6
5
|
const { DD_MAJOR } = require('../../../../version')
|
|
@@ -51,7 +50,6 @@ const defaults = {
|
|
|
51
50
|
isServiceUserProvided: false,
|
|
52
51
|
plugins: true,
|
|
53
52
|
isCiVisibility: false,
|
|
54
|
-
lookup: dns.lookup,
|
|
55
53
|
logger: undefined,
|
|
56
54
|
}
|
|
57
55
|
|
|
@@ -218,11 +216,20 @@ for (const [canonicalName, entries] of Object.entries(supportedConfigurations))
|
|
|
218
216
|
)
|
|
219
217
|
}
|
|
220
218
|
for (const entry of entries) {
|
|
219
|
+
// A deprecated entry that only aliases a canonical option must not surface as
|
|
220
|
+
// its own Config property/default: the canonical entry already owns the value
|
|
221
|
+
// and the alias is resolved by helper.js. helper.js drops these from the shared
|
|
222
|
+
// supported-configurations object (after registering the deprecation), but that
|
|
223
|
+
// mutation is order-dependent on which module loads first. Skip here too so
|
|
224
|
+
// `defaults` is identical regardless of load order (it differed in v5, where
|
|
225
|
+
// major-overrides keeps these entries instead of deleting them outright).
|
|
226
|
+
if (entry.deprecated && entry.aliases) continue
|
|
221
227
|
if (entry.sensitive) {
|
|
222
228
|
sensitiveConfigurations.add(canonicalName)
|
|
223
229
|
}
|
|
224
|
-
const
|
|
225
|
-
|
|
230
|
+
const fullPropertyName = entry.namespace
|
|
231
|
+
? `${entry.namespace}.${canonicalName}`
|
|
232
|
+
: (entry.internalPropertyName ?? entry.configurationNames?.[0] ?? canonicalName)
|
|
226
233
|
const type = entry.type.toUpperCase()
|
|
227
234
|
|
|
228
235
|
let transformer = transformers[entry.transform]
|
|
@@ -285,7 +292,7 @@ for (const [canonicalName, entries] of Object.entries(supportedConfigurations))
|
|
|
285
292
|
|
|
286
293
|
// Replace the alias with the canonical property name.
|
|
287
294
|
for (const [fullPropertyName, alias] of fallbackConfigurations) {
|
|
288
|
-
if (configurationsTable[alias]
|
|
295
|
+
if (configurationsTable[alias]?.property) {
|
|
289
296
|
fallbackConfigurations.set(fullPropertyName, configurationsTable[alias].property)
|
|
290
297
|
}
|
|
291
298
|
}
|
|
@@ -347,3 +354,16 @@ module.exports = {
|
|
|
347
354
|
|
|
348
355
|
generateTelemetry,
|
|
349
356
|
}
|
|
357
|
+
|
|
358
|
+
// `dns` is instrumented, so requiring it pulls in the dns plugin, which loads
|
|
359
|
+
// `log`, whose bootstrap calls back into `require('./defaults')`. Resolve the
|
|
360
|
+
// `lookup` default last — after the exports above are fully built — so that the
|
|
361
|
+
// re-entrant require during that cascade sees the complete module instead of a
|
|
362
|
+
// half-initialized one.
|
|
363
|
+
defaults.lookup = require('dns').lookup
|
|
364
|
+
configWithOrigin.set('lookupdefault', {
|
|
365
|
+
name: 'lookup',
|
|
366
|
+
value: defaults.lookup,
|
|
367
|
+
origin: 'default',
|
|
368
|
+
seq_id: seqId++,
|
|
369
|
+
})
|