diff --git a/integration-tests/cucumber/cucumber.spec.js b/integration-tests/cucumber/cucumber.spec.js index ebda279f8c6..3dfef057fd1 100644 --- a/integration-tests/cucumber/cucumber.spec.js +++ b/integration-tests/cucumber/cucumber.spec.js @@ -42,7 +42,8 @@ const { DI_DEBUG_ERROR_FILE_SUFFIX, DI_DEBUG_ERROR_SNAPSHOT_ID_SUFFIX, DI_DEBUG_ERROR_LINE_SUFFIX, - TEST_RETRY_REASON + TEST_RETRY_REASON, + DD_TEST_IS_USER_PROVIDED_SERVICE } = require('../../packages/dd-trace/src/plugins/util/test') const { DD_HOST_CPU_COUNT } = require('../../packages/dd-trace/src/plugins/util/env') @@ -206,6 +207,7 @@ versions.forEach(version => { assert.equal(testModuleId.toString(10), testModuleEventContent.test_module_id.toString(10)) assert.equal(testSessionId.toString(10), testSessionEventContent.test_session_id.toString(10)) assert.equal(meta[TEST_SOURCE_FILE].startsWith('ci-visibility/features'), true) + assert.equal(meta[DD_TEST_IS_USER_PROVIDED_SERVICE], 'false') // Can read DD_TAGS assert.propertyVal(meta, 'test.customtag', 'customvalue') assert.propertyVal(meta, 'test.customtag2', 'customvalue2') @@ -228,7 +230,8 @@ versions.forEach(version => { env: { ...envVars, DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2', - DD_TEST_SESSION_NAME: 'my-test-session' + DD_TEST_SESSION_NAME: 'my-test-session', + DD_SERVICE: undefined }, stdio: 'pipe' } @@ -1996,5 +1999,35 @@ versions.forEach(version => { }) }) }) + + it('sets _dd.test.is_user_provided_service to true if DD_SERVICE is used', (done) => { + const eventsPromise = receiver + .gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), (payloads) => { + const events = payloads.flatMap(({ payload }) => payload.events) + + const tests = events.filter(event => event.type === 'test').map(event => event.content) + tests.forEach(test => { + assert.equal(test.meta[DD_TEST_IS_USER_PROVIDED_SERVICE], 'true') + }) + }) + + childProcess = exec( + runTestsCommand, + { + cwd, + env: { + ...getCiVisAgentlessConfig(receiver.port), + DD_SERVICE: 'my-service' + }, + stdio: 'pipe' + } + ) + + childProcess.on('exit', () => { + eventsPromise.then(() => { + done() + }).catch(done) + }) + }) }) }) diff --git a/integration-tests/cypress/cypress.spec.js b/integration-tests/cypress/cypress.spec.js index 7bec90d898b..a2dd81a74f5 100644 --- a/integration-tests/cypress/cypress.spec.js +++ b/integration-tests/cypress/cypress.spec.js @@ -37,7 +37,8 @@ const { TEST_CODE_OWNERS, TEST_SESSION_NAME, TEST_LEVEL_EVENT_TYPES, - TEST_RETRY_REASON + TEST_RETRY_REASON, + DD_TEST_IS_USER_PROVIDED_SERVICE } = require('../../packages/dd-trace/src/plugins/util/test') const { DD_HOST_CPU_COUNT } = require('../../packages/dd-trace/src/plugins/util/env') const { ERROR_MESSAGE } = require('../../packages/dd-trace/src/constants') @@ -326,6 +327,7 @@ moduleTypes.forEach(({ assert.equal(testSessionId.toString(10), testSessionEventContent.test_session_id.toString(10)) assert.equal(meta[TEST_SOURCE_FILE].startsWith('cypress/e2e/'), true) // Can read DD_TAGS + assert.propertyVal(meta, DD_TEST_IS_USER_PROVIDED_SERVICE, 'false') assert.propertyVal(meta, 'test.customtag', 'customvalue') assert.propertyVal(meta, 'test.customtag2', 'customvalue2') assert.exists(metrics[DD_HOST_CPU_COUNT]) @@ -345,7 +347,8 @@ moduleTypes.forEach(({ ...restEnvVars, CYPRESS_BASE_URL: `http://localhost:${webAppPort}`, DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2', - DD_TEST_SESSION_NAME: 'my-test-session' + DD_TEST_SESSION_NAME: 'my-test-session', + DD_SERVICE: undefined }, stdio: 'pipe' } @@ -1691,5 +1694,42 @@ moduleTypes.forEach(({ }) }) } + + it('sets _dd.test.is_user_provided_service to true if DD_SERVICE is used', (done) => { + const receiverPromise = receiver + .gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), payloads => { + const events = payloads.flatMap(({ payload }) => payload.events) + + const testEvents = events.filter(event => event.type === 'test') + + testEvents.forEach(({ content: { meta } }) => { + assert.propertyVal(meta, DD_TEST_IS_USER_PROVIDED_SERVICE, 'true') + }) + }, 25000) + + const { + NODE_OPTIONS, // NODE_OPTIONS dd-trace config does not work with cypress + ...restEnvVars + } = getCiVisEvpProxyConfig(receiver.port) + + childProcess = exec( + testCommand, + { + cwd, + env: { + ...restEnvVars, + CYPRESS_BASE_URL: `http://localhost:${webAppPort}`, + DD_SERVICE: 'my-service' + }, + stdio: 'pipe' + } + ) + + childProcess.on('exit', () => { + receiverPromise.then(() => { + done() + }).catch(done) + }) + }) }) }) diff --git a/integration-tests/jest/jest.spec.js b/integration-tests/jest/jest.spec.js index 784ea393e5a..35413ea7e60 100644 --- a/integration-tests/jest/jest.spec.js +++ b/integration-tests/jest/jest.spec.js @@ -39,7 +39,8 @@ const { DI_DEBUG_ERROR_PREFIX, DI_DEBUG_ERROR_FILE_SUFFIX, DI_DEBUG_ERROR_SNAPSHOT_ID_SUFFIX, - DI_DEBUG_ERROR_LINE_SUFFIX + DI_DEBUG_ERROR_LINE_SUFFIX, + DD_TEST_IS_USER_PROVIDED_SERVICE } = require('../../packages/dd-trace/src/plugins/util/test') const { DD_HOST_CPU_COUNT } = require('../../packages/dd-trace/src/plugins/util/env') const { ERROR_MESSAGE } = require('../../packages/dd-trace/src/constants') @@ -179,6 +180,7 @@ describe('jest CommonJS', () => { tests.forEach(testEvent => { assert.equal(testEvent.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/test/ci-visibility-test'), true) assert.exists(testEvent.metrics[TEST_SOURCE_START]) + assert.equal(testEvent.meta[DD_TEST_IS_USER_PROVIDED_SERVICE], 'false') // Can read DD_TAGS assert.propertyVal(testEvent.meta, 'test.customtag', 'customvalue') assert.propertyVal(testEvent.meta, 'test.customtag2', 'customvalue2') @@ -199,7 +201,8 @@ describe('jest CommonJS', () => { env: { ...envVars, DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2', - DD_TEST_SESSION_NAME: 'my-test-session' + DD_TEST_SESSION_NAME: 'my-test-session', + DD_SERVICE: undefined }, stdio: 'pipe' }) @@ -2905,4 +2908,34 @@ describe('jest CommonJS', () => { }) }) }) + + it('sets _dd.test.is_user_provided_service to true if DD_SERVICE is used', (done) => { + const eventsPromise = receiver + .gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), (payloads) => { + const events = payloads.flatMap(({ payload }) => payload.events) + const tests = events.filter(event => event.type === 'test').map(event => event.content) + tests.forEach(test => { + assert.equal(test.meta[DD_TEST_IS_USER_PROVIDED_SERVICE], 'true') + }) + }) + + childProcess = exec( + runTestsWithCoverageCommand, + { + cwd, + env: { + ...getCiVisEvpProxyConfig(receiver.port), + TESTS_TO_RUN: 'test/ci-visibility-test', + DD_SERVICE: 'my-service' + }, + stdio: 'inherit' + } + ) + + childProcess.on('exit', () => { + eventsPromise.then(() => { + done() + }).catch(done) + }) + }) }) diff --git a/integration-tests/mocha/mocha.spec.js b/integration-tests/mocha/mocha.spec.js index 21e7670d077..86d9491b3f0 100644 --- a/integration-tests/mocha/mocha.spec.js +++ b/integration-tests/mocha/mocha.spec.js @@ -41,7 +41,8 @@ const { DI_DEBUG_ERROR_FILE_SUFFIX, DI_DEBUG_ERROR_SNAPSHOT_ID_SUFFIX, DI_DEBUG_ERROR_LINE_SUFFIX, - TEST_RETRY_REASON + TEST_RETRY_REASON, + DD_TEST_IS_USER_PROVIDED_SERVICE } = require('../../packages/dd-trace/src/plugins/util/test') const { DD_HOST_CPU_COUNT } = require('../../packages/dd-trace/src/plugins/util/env') const { ERROR_MESSAGE } = require('../../packages/dd-trace/src/constants') @@ -174,6 +175,7 @@ describe('mocha CommonJS', function () { tests.forEach(testEvent => { assert.equal(testEvent.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/test/ci-visibility-test'), true) assert.exists(testEvent.metrics[TEST_SOURCE_START]) + assert.equal(testEvent.meta[DD_TEST_IS_USER_PROVIDED_SERVICE], 'false') // Can read DD_TAGS assert.propertyVal(testEvent.meta, 'test.customtag', 'customvalue') assert.propertyVal(testEvent.meta, 'test.customtag2', 'customvalue2') @@ -194,7 +196,8 @@ describe('mocha CommonJS', function () { env: { ...envVars, DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2', - DD_TEST_SESSION_NAME: 'my-test-session' + DD_TEST_SESSION_NAME: 'my-test-session', + DD_SERVICE: undefined }, stdio: 'pipe' }) @@ -2520,4 +2523,38 @@ describe('mocha CommonJS', function () { }) }) }) + + it('sets _dd.test.is_user_provided_service to true if DD_SERVICE is used', (done) => { + const eventsPromise = receiver + .gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), (payloads) => { + const events = payloads.flatMap(({ payload }) => payload.events) + + const tests = events.filter(event => event.type === 'test').map(event => event.content) + tests.forEach(test => { + assert.equal(test.meta[DD_TEST_IS_USER_PROVIDED_SERVICE], 'true') + }) + }) + + childProcess = exec( + runTestsWithCoverageCommand, + { + cwd, + env: { + ...getCiVisAgentlessConfig(receiver.port), + TESTS_TO_RUN: JSON.stringify([ + './test/ci-visibility-test.js', + './test/ci-visibility-test-2.js' + ]), + DD_SERVICE: 'my-service' + }, + stdio: 'inherit' + } + ) + + childProcess.on('exit', () => { + eventsPromise.then(() => { + done() + }).catch(done) + }) + }) }) diff --git a/integration-tests/playwright/playwright.spec.js b/integration-tests/playwright/playwright.spec.js index 691a09b4d13..03ff3accd0d 100644 --- a/integration-tests/playwright/playwright.spec.js +++ b/integration-tests/playwright/playwright.spec.js @@ -25,7 +25,8 @@ const { TEST_CODE_OWNERS, TEST_SESSION_NAME, TEST_LEVEL_EVENT_TYPES, - TEST_RETRY_REASON + TEST_RETRY_REASON, + DD_TEST_IS_USER_PROVIDED_SERVICE } = require('../../packages/dd-trace/src/plugins/util/test') const { DD_HOST_CPU_COUNT } = require('../../packages/dd-trace/src/plugins/util/env') const { ERROR_MESSAGE } = require('../../packages/dd-trace/src/constants') @@ -147,6 +148,7 @@ versions.forEach((version) => { assert.equal( testEvent.content.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/playwright-tests/'), true ) + assert.equal(testEvent.content.meta[DD_TEST_IS_USER_PROVIDED_SERVICE], 'false') // Can read DD_TAGS assert.propertyVal(testEvent.content.meta, 'test.customtag', 'customvalue') assert.propertyVal(testEvent.content.meta, 'test.customtag2', 'customvalue2') @@ -176,7 +178,8 @@ versions.forEach((version) => { ...envVars, PW_BASE_URL: `http://localhost:${webAppPort}`, DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2', - DD_TEST_SESSION_NAME: 'my-test-session' + DD_TEST_SESSION_NAME: 'my-test-session', + DD_SERVICE: undefined }, stdio: 'pipe' } @@ -848,5 +851,34 @@ versions.forEach((version) => { }) }) } + + it('sets _dd.test.is_user_provided_service to true if DD_SERVICE is used', (done) => { + const receiverPromise = receiver + .gatherPayloadsMaxTimeout(({ url }) => url === '/api/v2/citestcycle', (payloads) => { + const events = payloads.flatMap(({ payload }) => payload.events) + + const tests = events.filter(event => event.type === 'test').map(event => event.content) + tests.forEach(test => { + assert.equal(test.meta[DD_TEST_IS_USER_PROVIDED_SERVICE], 'true') + }) + }) + + childProcess = exec( + './node_modules/.bin/playwright test -c playwright.config.js', + { + cwd, + env: { + ...getCiVisAgentlessConfig(receiver.port), + PW_BASE_URL: `http://localhost:${webAppPort}`, + DD_SERVICE: 'my-service' + }, + stdio: 'pipe' + } + ) + + childProcess.on('exit', () => { + receiverPromise.then(() => done()).catch(done) + }) + }) }) }) diff --git a/integration-tests/vitest/vitest.spec.js b/integration-tests/vitest/vitest.spec.js index eb53b395202..56f060ce509 100644 --- a/integration-tests/vitest/vitest.spec.js +++ b/integration-tests/vitest/vitest.spec.js @@ -30,7 +30,8 @@ const { DI_DEBUG_ERROR_FILE_SUFFIX, DI_DEBUG_ERROR_SNAPSHOT_ID_SUFFIX, DI_DEBUG_ERROR_LINE_SUFFIX, - TEST_RETRY_REASON + TEST_RETRY_REASON, + DD_TEST_IS_USER_PROVIDED_SERVICE } = require('../../packages/dd-trace/src/plugins/util/test') const { DD_HOST_CPU_COUNT } = require('../../packages/dd-trace/src/plugins/util/env') @@ -160,6 +161,7 @@ versions.forEach((version) => { testEvents.forEach(test => { assert.equal(test.content.meta[TEST_COMMAND], 'vitest run') assert.exists(test.content.metrics[DD_HOST_CPU_COUNT]) + assert.equal(test.content.meta[DD_TEST_IS_USER_PROVIDED_SERVICE], 'false') }) testSuiteEvents.forEach(testSuite => { @@ -180,7 +182,8 @@ versions.forEach((version) => { env: { ...getCiVisAgentlessConfig(receiver.port), NODE_OPTIONS: '--import dd-trace/register.js -r dd-trace/ci/init', // ESM requires more flags - DD_TEST_SESSION_NAME: 'my-test-session' + DD_TEST_SESSION_NAME: 'my-test-session', + DD_SERVICE: undefined }, stdio: 'pipe' } @@ -1298,5 +1301,38 @@ versions.forEach((version) => { }) }) }) + + it('sets _dd.test.is_user_provided_service to true if DD_SERVICE is used', (done) => { + const eventsPromise = receiver + .gatherPayloadsMaxTimeout(({ url }) => url === '/api/v2/citestcycle', payloads => { + const events = payloads.flatMap(({ payload }) => payload.events) + + const tests = events.filter(event => event.type === 'test').map(test => test.content) + tests.forEach(test => { + assert.equal(test.meta[DD_TEST_IS_USER_PROVIDED_SERVICE], 'true') + }) + }) + + childProcess = exec( + './node_modules/.bin/vitest run', + { + cwd, + env: { + ...getCiVisAgentlessConfig(receiver.port), + TEST_DIR: 'ci-visibility/vitest-tests/early-flake-detection*', + NODE_OPTIONS: '--import dd-trace/register.js -r dd-trace/ci/init', + DD_SERVICE: 'my-service' + }, + stdio: 'pipe' + } + ) + + childProcess.on('exit', (exitCode) => { + eventsPromise.then(() => { + assert.equal(exitCode, 1) + done() + }).catch(done) + }) + }) }) }) diff --git a/packages/datadog-plugin-cypress/src/cypress-plugin.js b/packages/datadog-plugin-cypress/src/cypress-plugin.js index 31d4d282f64..67487e47dbb 100644 --- a/packages/datadog-plugin-cypress/src/cypress-plugin.js +++ b/packages/datadog-plugin-cypress/src/cypress-plugin.js @@ -32,7 +32,8 @@ const { getTestSessionName, TEST_SESSION_NAME, TEST_LEVEL_EVENT_TYPES, - TEST_RETRY_REASON + TEST_RETRY_REASON, + DD_TEST_IS_USER_PROVIDED_SERVICE } = require('../../dd-trace/src/plugins/util/test') const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util') const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants') @@ -222,6 +223,10 @@ class CypressPlugin { this.tracer = tracer this.cypressConfig = cypressConfig + // we have to do it here because the tracer is not initialized in the constructor + this.testEnvironmentMetadata[DD_TEST_IS_USER_PROVIDED_SERVICE] = + tracer._tracer._config.isServiceUserProvided ? 'true' : 'false' + this.libraryConfigurationPromise = getLibraryConfiguration(this.tracer, this.testConfiguration) .then((libraryConfigurationResponse) => { if (libraryConfigurationResponse.err) { diff --git a/packages/dd-trace/src/config.js b/packages/dd-trace/src/config.js index 2beff234924..7e4299a0d74 100644 --- a/packages/dd-trace/src/config.js +++ b/packages/dd-trace/src/config.js @@ -518,6 +518,7 @@ class Config { this._setValue(defaults, 'ciVisAgentlessLogSubmissionEnabled', false) this._setValue(defaults, 'legacyBaggageEnabled', true) this._setValue(defaults, 'isTestDynamicInstrumentationEnabled', false) + this._setValue(defaults, 'isServiceUserProvided', false) this._setValue(defaults, 'logInjection', false) this._setValue(defaults, 'lookup', undefined) this._setValue(defaults, 'inferredProxyServicesEnabled', false) @@ -1156,6 +1157,7 @@ class Config { this._setString(calc, 'ciVisibilityTestSessionName', DD_TEST_SESSION_NAME) this._setBoolean(calc, 'ciVisAgentlessLogSubmissionEnabled', isTrue(DD_AGENTLESS_LOG_SUBMISSION_ENABLED)) this._setBoolean(calc, 'isTestDynamicInstrumentationEnabled', isTrue(DD_TEST_DYNAMIC_INSTRUMENTATION_ENABLED)) + this._setBoolean(calc, 'isServiceUserProvided', !!this._env.service) } this._setString(calc, 'dogstatsd.hostname', this._getHostname()) this._setBoolean(calc, 'isGitUploadEnabled', diff --git a/packages/dd-trace/src/plugin_manager.js b/packages/dd-trace/src/plugin_manager.js index c1b326dc767..7b200217fb2 100644 --- a/packages/dd-trace/src/plugin_manager.js +++ b/packages/dd-trace/src/plugin_manager.js @@ -143,6 +143,7 @@ module.exports = class PluginManager { ciVisibilityTestSessionName, ciVisAgentlessLogSubmissionEnabled, isTestDynamicInstrumentationEnabled, + isServiceUserProvided, middlewareTracingEnabled } = this._tracerConfig @@ -155,7 +156,8 @@ module.exports = class PluginManager { headers: headerTags || [], ciVisibilityTestSessionName, ciVisAgentlessLogSubmissionEnabled, - isTestDynamicInstrumentationEnabled + isTestDynamicInstrumentationEnabled, + isServiceUserProvided } if (logInjection !== undefined) { diff --git a/packages/dd-trace/src/plugins/util/test.js b/packages/dd-trace/src/plugins/util/test.js index 2d8ce1a1d33..407676d5c57 100644 --- a/packages/dd-trace/src/plugins/util/test.js +++ b/packages/dd-trace/src/plugins/util/test.js @@ -108,6 +108,8 @@ const TEST_LEVEL_EVENT_TYPES = [ 'test_session_end' ] +const DD_TEST_IS_USER_PROVIDED_SERVICE = '_dd.test.is_user_provided_service' + // Dynamic instrumentation - Test optimization integration tags const DI_ERROR_DEBUG_INFO_CAPTURED = 'error.debug_info_captured' const DI_DEBUG_ERROR_PREFIX = '_dd.debug.error' @@ -199,7 +201,8 @@ module.exports = { DI_DEBUG_ERROR_SNAPSHOT_ID_SUFFIX, DI_DEBUG_ERROR_FILE_SUFFIX, DI_DEBUG_ERROR_LINE_SUFFIX, - getFormattedError + getFormattedError, + DD_TEST_IS_USER_PROVIDED_SERVICE } // Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19 @@ -275,6 +278,7 @@ function getTestEnvironmentMetadata (testFramework, config) { const metadata = { [TEST_FRAMEWORK]: testFramework, + [DD_TEST_IS_USER_PROVIDED_SERVICE]: (config && config.isServiceUserProvided) ? 'true' : 'false', ...gitMetadata, ...ciMetadata, ...userProvidedGitMetadata,