dd-trace 6.4.0 → 6.6.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.electron.md +7 -0
- package/README.md +17 -0
- package/ci/diagnose.js +2100 -0
- package/ci/init.js +23 -2
- package/ci/runbook.md +198 -0
- package/ci/test-optimization-validation/approval-artifacts.js +143 -0
- package/ci/test-optimization-validation/approval.js +299 -0
- package/ci/test-optimization-validation/artifact-id.js +20 -0
- package/ci/test-optimization-validation/bounded-json.js +121 -0
- package/ci/test-optimization-validation/ci-command-candidate.js +83 -0
- package/ci/test-optimization-validation/ci-discovery.js +181 -0
- package/ci/test-optimization-validation/ci-remediation.js +210 -0
- package/ci/test-optimization-validation/cli.js +903 -0
- package/ci/test-optimization-validation/command-blocker.js +81 -0
- package/ci/test-optimization-validation/command-output-policy.js +206 -0
- package/ci/test-optimization-validation/command-runner.js +707 -0
- package/ci/test-optimization-validation/command-suitability.js +471 -0
- package/ci/test-optimization-validation/executable-approval.js +48 -0
- package/ci/test-optimization-validation/executable.js +480 -0
- package/ci/test-optimization-validation/generated-file-policy.js +63 -0
- package/ci/test-optimization-validation/generated-files.js +351 -0
- package/ci/test-optimization-validation/generated-verifier.js +196 -0
- package/ci/test-optimization-validation/init-probe-preload.js +163 -0
- package/ci/test-optimization-validation/init-probe.js +246 -0
- package/ci/test-optimization-validation/late-initialization.js +63 -0
- package/ci/test-optimization-validation/local-command.js +163 -0
- package/ci/test-optimization-validation/manifest-loader.js +133 -0
- package/ci/test-optimization-validation/manifest-scaffold.js +738 -0
- package/ci/test-optimization-validation/manifest-schema.js +862 -0
- package/ci/test-optimization-validation/offline-fixtures.js +327 -0
- package/ci/test-optimization-validation/offline-output.js +406 -0
- package/ci/test-optimization-validation/payload-normalizer.js +72 -0
- package/ci/test-optimization-validation/plan-writer.js +1120 -0
- package/ci/test-optimization-validation/preflight-runner.js +114 -0
- package/ci/test-optimization-validation/redaction.js +331 -0
- package/ci/test-optimization-validation/report-writer.js +1508 -0
- package/ci/test-optimization-validation/safe-files.js +203 -0
- package/ci/test-optimization-validation/scenarios/auto-test-retries.js +159 -0
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +657 -0
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +780 -0
- package/ci/test-optimization-validation/scenarios/early-flake-detection.js +141 -0
- package/ci/test-optimization-validation/scenarios/helpers.js +539 -0
- package/ci/test-optimization-validation/scenarios/test-management.js +218 -0
- package/ci/test-optimization-validation/setup-runner.js +97 -0
- package/ci/test-optimization-validation/static-diagnosis.js +159 -0
- package/ci/test-optimization-validation/test-output.js +129 -0
- package/ci/test-optimization-validation-manifest.schema.json +1 -0
- package/ci/validate-test-optimization.js +3 -0
- package/ext/exporters.js +1 -0
- package/index.d.ts +121 -8
- package/init.js +18 -0
- package/initialize.mjs +1 -1
- package/loader-hook.mjs +28 -18
- package/openfeature.d.ts +1 -0
- package/openfeature.js +4 -0
- package/package.json +11 -8
- package/packages/datadog-instrumentations/src/ai.js +8 -2
- package/packages/datadog-instrumentations/src/child_process.js +1 -1
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +1 -1
- package/packages/datadog-instrumentations/src/cucumber-worker-threads.js +9 -3
- package/packages/datadog-instrumentations/src/cucumber.js +31 -19
- package/packages/datadog-instrumentations/src/cypress-config.js +398 -52
- package/packages/datadog-instrumentations/src/fastify.js +7 -11
- package/packages/datadog-instrumentations/src/helpers/channel.js +74 -0
- package/packages/datadog-instrumentations/src/helpers/hook.js +27 -6
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +8 -8
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +28 -2
- package/packages/datadog-instrumentations/src/jest.js +44 -17
- package/packages/datadog-instrumentations/src/mariadb.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +79 -32
- package/packages/datadog-instrumentations/src/mocha/utils.js +0 -4
- package/packages/datadog-instrumentations/src/mongodb.js +3 -3
- package/packages/datadog-instrumentations/src/mongoose.js +3 -3
- package/packages/datadog-instrumentations/src/mquery.js +2 -2
- package/packages/datadog-instrumentations/src/mysql.js +1 -1
- package/packages/datadog-instrumentations/src/next.js +24 -0
- package/packages/datadog-instrumentations/src/nyc.js +0 -2
- package/packages/datadog-instrumentations/src/oracledb.js +2 -2
- package/packages/datadog-instrumentations/src/pg.js +2 -2
- package/packages/datadog-instrumentations/src/playwright.js +162 -46
- package/packages/datadog-instrumentations/src/process.js +3 -0
- package/packages/datadog-instrumentations/src/router.js +18 -8
- package/packages/datadog-instrumentations/src/selenium.js +52 -26
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +3 -3
- package/packages/datadog-instrumentations/src/vitest-main.js +14 -28
- package/packages/datadog-instrumentations/src/vitest-util.js +0 -7
- package/packages/datadog-instrumentations/src/vitest-worker.js +5 -8
- package/packages/datadog-instrumentations/src/ws.js +2 -1
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +2 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +2 -1
- package/packages/datadog-plugin-aws-sdk/src/util.js +2 -2
- package/packages/datadog-plugin-cucumber/src/index.js +3 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -57
- package/packages/datadog-plugin-cypress/src/index.js +6 -1
- package/packages/datadog-plugin-cypress/src/support.js +92 -26
- package/packages/datadog-plugin-electron/src/ipc.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +87 -45
- package/packages/datadog-plugin-graphql/src/validate.js +2 -2
- package/packages/datadog-plugin-http2/src/client.js +1 -1
- package/packages/datadog-plugin-langchain/src/handlers/embedding.js +2 -1
- package/packages/datadog-plugin-langchain/src/handlers/language_models.js +2 -1
- package/packages/datadog-plugin-langchain/src/tokens.js +2 -2
- package/packages/datadog-plugin-mocha/src/index.js +2 -1
- package/packages/datadog-plugin-playwright/src/index.js +181 -65
- package/packages/datadog-plugin-router/src/index.js +11 -2
- package/packages/datadog-plugin-selenium/src/index.js +5 -36
- package/packages/datadog-plugin-vitest/src/index.js +5 -5
- package/packages/datadog-plugin-ws/src/close.js +2 -1
- package/packages/datadog-plugin-ws/src/producer.js +2 -1
- package/packages/datadog-plugin-ws/src/receiver.js +2 -1
- package/packages/datadog-plugin-ws/src/server.js +2 -1
- package/packages/dd-trace/src/aiguard/sdk.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -2
- package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +3 -1
- package/packages/dd-trace/src/azure_metadata.js +2 -1
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +93 -28
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/index.js +160 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +288 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/payload-projection.js +84 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +369 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +60 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +41 -3
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +7 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +1 -1
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +6 -2
- package/packages/dd-trace/src/ci-visibility/rum.js +7 -0
- package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +4 -0
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +1 -1
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +137 -9
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +168 -8
- package/packages/dd-trace/src/ci-visibility/test-screenshot.js +90 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +16 -0
- package/packages/dd-trace/src/config/helper.js +1 -0
- package/packages/dd-trace/src/config/index.js +10 -0
- package/packages/dd-trace/src/config/supported-configurations.json +66 -0
- package/packages/dd-trace/src/exporter.js +2 -0
- package/packages/dd-trace/src/exporters/common/client-library-headers.js +21 -0
- package/packages/dd-trace/src/exporters/common/request.js +59 -30
- package/packages/dd-trace/src/exporters/common/url.js +15 -1
- package/packages/dd-trace/src/feature-registry.js +10 -3
- package/packages/dd-trace/src/llmobs/experiments/client.js +113 -0
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +154 -0
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +283 -0
- package/packages/dd-trace/src/llmobs/experiments/index.js +152 -0
- package/packages/dd-trace/src/llmobs/experiments/noop.js +30 -0
- package/packages/dd-trace/src/llmobs/experiments/result.js +34 -0
- package/packages/dd-trace/src/llmobs/noop.js +6 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +20 -3
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +2 -2
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +8 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +48 -14
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +43 -0
- package/packages/dd-trace/src/llmobs/sdk.js +17 -0
- package/packages/dd-trace/src/llmobs/tagger.js +68 -0
- package/packages/dd-trace/src/llmobs/telemetry.js +2 -1
- package/packages/dd-trace/src/llmobs/util.js +32 -0
- package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +322 -0
- package/packages/dd-trace/src/openfeature/configuration_source.js +90 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +14 -23
- package/packages/dd-trace/src/openfeature/index.js +0 -2
- package/packages/dd-trace/src/openfeature/noop.js +1 -28
- package/packages/dd-trace/src/openfeature/register.js +16 -27
- package/packages/dd-trace/src/openfeature/remote_config.js +15 -18
- package/packages/dd-trace/src/openfeature/require-provider.js +18 -0
- package/packages/dd-trace/src/opentelemetry/span-ending-hook.js +10 -0
- package/packages/dd-trace/src/opentelemetry/span.js +5 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +20 -6
- package/packages/dd-trace/src/plugins/util/git.js +3 -2
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +29 -5
- package/packages/dd-trace/src/plugins/util/test.js +56 -4
- 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/proxy.js +84 -7
- package/packages/dd-trace/src/span_processor.js +5 -0
- package/packages/dd-trace/src/standalone/index.js +0 -14
- package/packages/dd-trace/src/telemetry/send-data.js +2 -2
- package/packages/dd-trace/src/util.js +26 -0
- 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/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-instrumentations/src/helpers/require-optional-peer.js +0 -17
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const crypto = require('node:crypto')
|
|
4
|
+
const fs = require('node:fs')
|
|
5
|
+
const os = require('node:os')
|
|
6
|
+
const path = require('node:path')
|
|
7
|
+
|
|
8
|
+
const { getArtifactId } = require('./artifact-id')
|
|
9
|
+
const { createFileSafely, ensureSafeDirectory } = require('./safe-files')
|
|
10
|
+
|
|
11
|
+
const MAX_FIXTURE_FILE_BYTES = 1024 * 1024
|
|
12
|
+
const OFFLINE_FIXTURE_NONCE_PATTERN = /^[a-f0-9]{32}$/
|
|
13
|
+
const DEFAULT_SETTINGS = {
|
|
14
|
+
code_coverage: false,
|
|
15
|
+
tests_skipping: false,
|
|
16
|
+
itr_enabled: false,
|
|
17
|
+
require_git: false,
|
|
18
|
+
early_flake_detection: {
|
|
19
|
+
enabled: false,
|
|
20
|
+
slow_test_retries: {
|
|
21
|
+
'5s': 3,
|
|
22
|
+
},
|
|
23
|
+
faulty_session_threshold: 100,
|
|
24
|
+
},
|
|
25
|
+
flaky_test_retries_enabled: false,
|
|
26
|
+
di_enabled: false,
|
|
27
|
+
known_tests_enabled: false,
|
|
28
|
+
test_management: {
|
|
29
|
+
enabled: false,
|
|
30
|
+
},
|
|
31
|
+
impacted_tests_enabled: false,
|
|
32
|
+
coverage_report_upload_enabled: false,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Creates one validator-controlled cache fixture outside the repository.
|
|
37
|
+
*
|
|
38
|
+
* @param {object} input fixture inputs
|
|
39
|
+
* @param {string} input.approvedPlanSha256 approved execution-plan digest
|
|
40
|
+
* @param {string} input.offlineFixtureNonce random fixture-root nonce from the approved plan
|
|
41
|
+
* @param {object} input.framework framework manifest entry
|
|
42
|
+
* @param {string} input.repositoryRoot repository checkout root
|
|
43
|
+
* @param {string} input.scenarioName unique scenario execution name
|
|
44
|
+
* @param {object} [input.settings] scenario settings overrides
|
|
45
|
+
* @param {object} [input.knownTests] known-tests fixture contents
|
|
46
|
+
* @param {object[]} [input.skippableTests] skippable-tests fixture contents
|
|
47
|
+
* @param {object} [input.testManagementTests] managed-tests fixture contents
|
|
48
|
+
* @returns {{manifestPath: string, root: string, files: object[]}} fixture details
|
|
49
|
+
*/
|
|
50
|
+
function createOfflineFixture ({
|
|
51
|
+
approvedPlanSha256,
|
|
52
|
+
offlineFixtureNonce,
|
|
53
|
+
framework,
|
|
54
|
+
repositoryRoot,
|
|
55
|
+
scenarioName,
|
|
56
|
+
settings,
|
|
57
|
+
knownTests = {},
|
|
58
|
+
skippableTests = [],
|
|
59
|
+
testManagementTests = {},
|
|
60
|
+
}) {
|
|
61
|
+
if (!/^[a-f0-9]{64}$/.test(approvedPlanSha256 || '')) {
|
|
62
|
+
throw new Error('Offline validation requires an approved plan digest before creating fixtures.')
|
|
63
|
+
}
|
|
64
|
+
if (!OFFLINE_FIXTURE_NONCE_PATTERN.test(offlineFixtureNonce || '')) {
|
|
65
|
+
throw new Error('Offline validation requires the fixture nonce from the approved execution plan.')
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const { base, root } = getOfflineFixturePaths({ offlineFixtureNonce, framework, scenarioName })
|
|
69
|
+
if (isPathInside(path.resolve(repositoryRoot), base)) {
|
|
70
|
+
throw new Error('Offline validation fixtures must be outside the repository checkout.')
|
|
71
|
+
}
|
|
72
|
+
ensurePrivateDirectory(base)
|
|
73
|
+
if (fs.existsSync(root)) {
|
|
74
|
+
throw new Error(`Offline validation fixture already exists and will not be replaced: ${root}`)
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
fs.mkdirSync(root, { recursive: true, mode: 0o700 })
|
|
78
|
+
ensureSafeDirectory(base, root, 'offline validation fixture directory')
|
|
79
|
+
|
|
80
|
+
const testOptimizationRoot = path.join(root, '.testoptimization')
|
|
81
|
+
const cacheRoot = path.join(testOptimizationRoot, 'cache', 'http')
|
|
82
|
+
fs.mkdirSync(cacheRoot, { recursive: true, mode: 0o700 })
|
|
83
|
+
ensureSafeDirectory(root, cacheRoot, 'offline validation cache directory')
|
|
84
|
+
|
|
85
|
+
const manifestPath = path.join(testOptimizationRoot, 'manifest.txt')
|
|
86
|
+
const fixtureFiles = [
|
|
87
|
+
[manifestPath, '1\n'],
|
|
88
|
+
[path.join(cacheRoot, 'settings.json'), JSON.stringify({
|
|
89
|
+
data: { attributes: mergeSettings(settings) },
|
|
90
|
+
})],
|
|
91
|
+
[path.join(cacheRoot, 'known_tests.json'), JSON.stringify({
|
|
92
|
+
data: { attributes: { tests: knownTests } },
|
|
93
|
+
})],
|
|
94
|
+
[path.join(cacheRoot, 'skippable_tests.json'), JSON.stringify({
|
|
95
|
+
data: skippableTests,
|
|
96
|
+
meta: { correlation_id: 'dd-test-optimization-validation' },
|
|
97
|
+
})],
|
|
98
|
+
[path.join(cacheRoot, 'test_management.json'), JSON.stringify({
|
|
99
|
+
data: { attributes: { modules: testManagementTests } },
|
|
100
|
+
})],
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
for (const [filename, content] of fixtureFiles) {
|
|
104
|
+
if (Buffer.byteLength(content) > MAX_FIXTURE_FILE_BYTES) {
|
|
105
|
+
throw new Error(`Offline validation fixture exceeds ${MAX_FIXTURE_FILE_BYTES} bytes: ${filename}`)
|
|
106
|
+
}
|
|
107
|
+
createFileSafely(root, filename, content, 'offline validation fixture')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
manifestPath,
|
|
112
|
+
root,
|
|
113
|
+
files: fixtureFiles.map(([filename, content]) => ({ filename, bytes: Buffer.byteLength(content) })),
|
|
114
|
+
}
|
|
115
|
+
} catch (error) {
|
|
116
|
+
fs.rmSync(root, { recursive: true, force: true })
|
|
117
|
+
removeEmptyParents(path.dirname(root), base)
|
|
118
|
+
throw error
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Returns random, validator-controlled fixture paths bound to an approved execution plan.
|
|
124
|
+
*
|
|
125
|
+
* @param {object} input fixture path inputs
|
|
126
|
+
* @param {string} input.offlineFixtureNonce random fixture-root nonce from the approved plan
|
|
127
|
+
* @param {object} input.framework framework manifest entry
|
|
128
|
+
* @param {string} input.scenarioName scenario execution name
|
|
129
|
+
* @returns {{base: string, root: string}} fixture base and scenario root
|
|
130
|
+
*/
|
|
131
|
+
function getOfflineFixturePaths ({ offlineFixtureNonce, framework, scenarioName }) {
|
|
132
|
+
if (!OFFLINE_FIXTURE_NONCE_PATTERN.test(offlineFixtureNonce || '')) {
|
|
133
|
+
throw new Error('Invalid offline validation fixture nonce.')
|
|
134
|
+
}
|
|
135
|
+
const base = path.join(fs.realpathSync(os.tmpdir()), `dd-test-optimization-validation-${offlineFixtureNonce}`)
|
|
136
|
+
return {
|
|
137
|
+
base,
|
|
138
|
+
root: path.join(base, getArtifactId(framework.id), getArtifactId(scenarioName)),
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Returns the cache executions selected by a validator scenario selection.
|
|
144
|
+
*
|
|
145
|
+
* @param {string|null|undefined} requestedScenario selected validation scenario
|
|
146
|
+
* @returns {string[]} cache execution names
|
|
147
|
+
*/
|
|
148
|
+
function getOfflineScenarioNames (requestedScenario) {
|
|
149
|
+
const scenarios = new Set(['basic-reporting', 'basic-reporting-debug'])
|
|
150
|
+
if (!requestedScenario || requestedScenario === 'ci-wiring') scenarios.add('ci-wiring')
|
|
151
|
+
const advanced = requestedScenario
|
|
152
|
+
? requestedScenario === 'basic-reporting' || requestedScenario === 'ci-wiring'
|
|
153
|
+
? []
|
|
154
|
+
: [requestedScenario]
|
|
155
|
+
: ['efd', 'atr', 'test-management']
|
|
156
|
+
for (const scenario of advanced) {
|
|
157
|
+
scenarios.add(`${scenario}-baseline`)
|
|
158
|
+
scenarios.add(scenario)
|
|
159
|
+
scenarios.add(`${scenario}-debug`)
|
|
160
|
+
}
|
|
161
|
+
return [...scenarios]
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Hashes the validator-controlled inputs used to build one scenario fixture.
|
|
166
|
+
*
|
|
167
|
+
* @param {object} framework framework manifest entry
|
|
168
|
+
* @param {string} scenarioName cache execution name
|
|
169
|
+
* @returns {string} SHA-256 fixture recipe digest
|
|
170
|
+
*/
|
|
171
|
+
function getFixtureRecipeDigest (framework, scenarioName) {
|
|
172
|
+
const generatedScenarioId = {
|
|
173
|
+
atr: 'atr-fail-once',
|
|
174
|
+
efd: 'basic-pass',
|
|
175
|
+
'test-management': 'test-management-target',
|
|
176
|
+
}[scenarioName.replace(/-(?:baseline|debug)$/, '')]
|
|
177
|
+
const recipe = {
|
|
178
|
+
version: 1,
|
|
179
|
+
framework: framework.framework,
|
|
180
|
+
scenarioName,
|
|
181
|
+
defaultSettings: DEFAULT_SETTINGS,
|
|
182
|
+
settingsOverrides: getFixtureSettingsOverrides(scenarioName),
|
|
183
|
+
testIdentities: framework.generatedTestStrategy?.scenarios?.find(scenario => {
|
|
184
|
+
return scenario.id === generatedScenarioId
|
|
185
|
+
})?.testIdentities || [],
|
|
186
|
+
dynamicTestManagementIdentity: scenarioName.startsWith('test-management'),
|
|
187
|
+
}
|
|
188
|
+
return crypto.createHash('sha256').update(JSON.stringify(recipe)).digest('hex')
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Returns stable fixture recipe hashes included directly in the approval scope.
|
|
193
|
+
*
|
|
194
|
+
* @param {object} input recipe selection
|
|
195
|
+
* @param {object[]} input.frameworks normalized manifest framework entries
|
|
196
|
+
* @param {string[]} [input.selectedFrameworkIds] selected framework ids
|
|
197
|
+
* @param {string|null} [input.requestedScenario] selected validation scenario
|
|
198
|
+
* @returns {object[]} fixture recipe identities and hashes
|
|
199
|
+
*/
|
|
200
|
+
function getFixtureRecipeDigests ({ frameworks, selectedFrameworkIds = [], requestedScenario = null }) {
|
|
201
|
+
const selected = new Set(selectedFrameworkIds)
|
|
202
|
+
const entries = []
|
|
203
|
+
for (const framework of frameworks) {
|
|
204
|
+
if (framework.status !== 'runnable' || (selected.size > 0 && !selected.has(framework.id))) continue
|
|
205
|
+
for (const scenarioName of getOfflineScenarioNames(requestedScenario)) {
|
|
206
|
+
entries.push({
|
|
207
|
+
frameworkId: framework.id,
|
|
208
|
+
scenarioName,
|
|
209
|
+
sha256: getFixtureRecipeDigest(framework, scenarioName),
|
|
210
|
+
})
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return entries
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function getFixtureSettingsOverrides (scenarioName) {
|
|
217
|
+
if (scenarioName === 'atr' || scenarioName === 'atr-debug') {
|
|
218
|
+
return { flaky_test_retries_enabled: true }
|
|
219
|
+
}
|
|
220
|
+
if (scenarioName === 'efd' || scenarioName === 'efd-debug') {
|
|
221
|
+
return {
|
|
222
|
+
early_flake_detection: {
|
|
223
|
+
enabled: true,
|
|
224
|
+
slow_test_retries: { '5s': 3 },
|
|
225
|
+
faulty_session_threshold: 100,
|
|
226
|
+
},
|
|
227
|
+
known_tests_enabled: true,
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (scenarioName === 'test-management' || scenarioName === 'test-management-debug') {
|
|
231
|
+
return { test_management: { enabled: true, attempt_to_fix_retries: 2 } }
|
|
232
|
+
}
|
|
233
|
+
return {}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Removes a scenario fixture after its command has exited and output has been read.
|
|
238
|
+
*
|
|
239
|
+
* @param {string} fixtureRoot scenario fixture root
|
|
240
|
+
*/
|
|
241
|
+
function cleanupOfflineFixture (fixtureRoot) {
|
|
242
|
+
const parent = path.dirname(path.dirname(fixtureRoot))
|
|
243
|
+
ensureSafeDirectory(parent, fixtureRoot, 'offline validation fixture cleanup')
|
|
244
|
+
fs.rmSync(fixtureRoot, { recursive: true })
|
|
245
|
+
removeEmptyParents(path.dirname(fixtureRoot), parent)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function mergeSettings (settings = {}) {
|
|
249
|
+
return {
|
|
250
|
+
...DEFAULT_SETTINGS,
|
|
251
|
+
...settings,
|
|
252
|
+
early_flake_detection: {
|
|
253
|
+
...DEFAULT_SETTINGS.early_flake_detection,
|
|
254
|
+
...settings.early_flake_detection,
|
|
255
|
+
},
|
|
256
|
+
test_management: {
|
|
257
|
+
...DEFAULT_SETTINGS.test_management,
|
|
258
|
+
...settings.test_management,
|
|
259
|
+
},
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Creates or verifies a private validator-owned fixture directory.
|
|
265
|
+
*
|
|
266
|
+
* @param {string} directory fixture base directory
|
|
267
|
+
* @returns {void}
|
|
268
|
+
*/
|
|
269
|
+
function ensurePrivateDirectory (directory) {
|
|
270
|
+
try {
|
|
271
|
+
fs.mkdirSync(directory, { mode: 0o700 })
|
|
272
|
+
} catch (error) {
|
|
273
|
+
if (error.code !== 'EEXIST') throw error
|
|
274
|
+
}
|
|
275
|
+
const stat = fs.lstatSync(directory)
|
|
276
|
+
const supportsPosixPermissions = typeof process.getuid === 'function'
|
|
277
|
+
const ownerMismatch = supportsPosixPermissions && stat.uid !== process.getuid()
|
|
278
|
+
const permissionsMismatch = supportsPosixPermissions && (stat.mode & 0o077) !== 0
|
|
279
|
+
if (!stat.isDirectory() || stat.isSymbolicLink() || ownerMismatch || permissionsMismatch) {
|
|
280
|
+
throw new Error(`Offline validation fixture base is not a regular directory: ${directory}`)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Removes empty directories up to and including the fixture base.
|
|
286
|
+
*
|
|
287
|
+
* @param {string} directory first candidate directory
|
|
288
|
+
* @param {string} stop fixture base directory
|
|
289
|
+
* @returns {void}
|
|
290
|
+
*/
|
|
291
|
+
function removeEmptyParents (directory, stop) {
|
|
292
|
+
let current = directory
|
|
293
|
+
while (current !== stop && current.startsWith(`${stop}${path.sep}`)) {
|
|
294
|
+
try {
|
|
295
|
+
fs.rmdirSync(current)
|
|
296
|
+
} catch {
|
|
297
|
+
return
|
|
298
|
+
}
|
|
299
|
+
current = path.dirname(current)
|
|
300
|
+
}
|
|
301
|
+
try {
|
|
302
|
+
fs.rmdirSync(stop)
|
|
303
|
+
} catch {}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Checks lexical path containment.
|
|
308
|
+
*
|
|
309
|
+
* @param {string} root candidate parent
|
|
310
|
+
* @param {string} filename candidate child
|
|
311
|
+
* @returns {boolean} whether the child is inside the parent
|
|
312
|
+
*/
|
|
313
|
+
function isPathInside (root, filename) {
|
|
314
|
+
const relative = path.relative(root, filename)
|
|
315
|
+
return relative === '' || (relative && !relative.startsWith('..') && !path.isAbsolute(relative))
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
module.exports = {
|
|
319
|
+
cleanupOfflineFixture,
|
|
320
|
+
createOfflineFixture,
|
|
321
|
+
DEFAULT_SETTINGS,
|
|
322
|
+
getFixtureRecipeDigest,
|
|
323
|
+
getFixtureRecipeDigests,
|
|
324
|
+
getOfflineFixturePaths,
|
|
325
|
+
getOfflineScenarioNames,
|
|
326
|
+
MAX_FIXTURE_FILE_BYTES,
|
|
327
|
+
}
|