diff --git a/integration-tests/cucumber/cucumber.spec.js b/integration-tests/cucumber/cucumber.spec.js index f5702980222..79295467ae7 100644 --- a/integration-tests/cucumber/cucumber.spec.js +++ b/integration-tests/cucumber/cucumber.spec.js @@ -35,7 +35,8 @@ const { CUCUMBER_IS_PARALLEL, TEST_SUITE, TEST_CODE_OWNERS, - TEST_SESSION_NAME + TEST_SESSION_NAME, + TEST_LEVEL_EVENT_TYPES } = require('../../packages/dd-trace/src/plugins/util/test') const isOldNode = semver.satisfies(process.version, '<=16') @@ -115,6 +116,13 @@ versions.forEach(version => { const receiverPromise = receiver .gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), payloads => { + const metadataDicts = payloads.flatMap(({ payload }) => payload.metadata) + metadataDicts.forEach(metadata => { + for (const testLevel of TEST_LEVEL_EVENT_TYPES) { + assert.equal(metadata[testLevel][TEST_SESSION_NAME], 'my-test-session') + } + }) + const events = payloads.flatMap(({ payload }) => payload.events) const testSessionEvent = events.find(event => event.type === 'test_session_end') @@ -131,14 +139,12 @@ versions.forEach(version => { assert.equal(testSessionEventContent.meta[CUCUMBER_IS_PARALLEL], 'true') } - assert.equal(testSessionEventContent.meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(testSessionEventContent.test_session_id) assert.exists(testSessionEventContent.meta[TEST_COMMAND]) assert.exists(testSessionEventContent.meta[TEST_TOOLCHAIN]) assert.equal(testSessionEventContent.resource.startsWith('test_session.'), true) assert.equal(testSessionEventContent.meta[TEST_STATUS], 'fail') - assert.equal(testModuleEventContent.meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(testModuleEventContent.test_session_id) assert.exists(testModuleEventContent.test_module_id) assert.exists(testModuleEventContent.meta[TEST_COMMAND]) @@ -168,7 +174,6 @@ versions.forEach(version => { test_session_id: testSessionId } }) => { - assert.equal(meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(meta[TEST_COMMAND]) assert.exists(meta[TEST_MODULE]) assert.exists(testSuiteId) @@ -201,7 +206,6 @@ versions.forEach(version => { test_session_id: testSessionId } }) => { - assert.equal(meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(meta[TEST_COMMAND]) assert.exists(meta[TEST_MODULE]) assert.exists(testSuiteId) diff --git a/integration-tests/cypress/cypress.spec.js b/integration-tests/cypress/cypress.spec.js index 64f0b401f97..6144618f6c3 100644 --- a/integration-tests/cypress/cypress.spec.js +++ b/integration-tests/cypress/cypress.spec.js @@ -34,7 +34,8 @@ const { TEST_EARLY_FLAKE_ENABLED, TEST_SUITE, TEST_CODE_OWNERS, - TEST_SESSION_NAME + TEST_SESSION_NAME, + TEST_LEVEL_EVENT_TYPES } = require('../../packages/dd-trace/src/plugins/util/test') const { ERROR_MESSAGE } = require('../../packages/dd-trace/src/constants') const { NODE_MAJOR } = require('../../version') @@ -227,6 +228,13 @@ moduleTypes.forEach(({ it('can run and report tests', (done) => { const receiverPromise = receiver .gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), payloads => { + const metadataDicts = payloads.flatMap(({ payload }) => payload.metadata) + + metadataDicts.forEach(metadata => { + for (const testLevel of TEST_LEVEL_EVENT_TYPES) { + assert.equal(metadata[testLevel][TEST_SESSION_NAME], 'my-test-session') + } + }) const events = payloads.flatMap(({ payload }) => payload.events) const testSessionEvent = events.find(event => event.type === 'test_session_end') @@ -237,14 +245,12 @@ moduleTypes.forEach(({ const { content: testSessionEventContent } = testSessionEvent const { content: testModuleEventContent } = testModuleEvent - assert.equal(testSessionEventContent.meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(testSessionEventContent.test_session_id) assert.exists(testSessionEventContent.meta[TEST_COMMAND]) assert.exists(testSessionEventContent.meta[TEST_TOOLCHAIN]) assert.equal(testSessionEventContent.resource.startsWith('test_session.'), true) assert.equal(testSessionEventContent.meta[TEST_STATUS], 'fail') - assert.equal(testModuleEventContent.meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(testModuleEventContent.test_session_id) assert.exists(testModuleEventContent.test_module_id) assert.exists(testModuleEventContent.meta[TEST_COMMAND]) @@ -276,7 +282,6 @@ moduleTypes.forEach(({ test_session_id: testSessionId } }) => { - assert.equal(meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(meta[TEST_COMMAND]) assert.exists(meta[TEST_MODULE]) assert.exists(testSuiteId) @@ -306,7 +311,6 @@ moduleTypes.forEach(({ test_session_id: testSessionId } }) => { - assert.equal(meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(meta[TEST_COMMAND]) assert.exists(meta[TEST_MODULE]) assert.exists(testSuiteId) diff --git a/integration-tests/jest/jest.spec.js b/integration-tests/jest/jest.spec.js index d275a63c8dd..fac07b540f2 100644 --- a/integration-tests/jest/jest.spec.js +++ b/integration-tests/jest/jest.spec.js @@ -32,7 +32,8 @@ const { TEST_EARLY_FLAKE_ABORT_REASON, TEST_SOURCE_START, TEST_CODE_OWNERS, - TEST_SESSION_NAME + TEST_SESSION_NAME, + TEST_LEVEL_EVENT_TYPES } = require('../../packages/dd-trace/src/plugins/util/test') const { ERROR_MESSAGE } = require('../../packages/dd-trace/src/constants') @@ -134,6 +135,14 @@ describe('jest CommonJS', () => { receiver.setInfoResponse({ endpoints: ['/evp_proxy/v4'] }) } receiver.gatherPayloadsMaxTimeout(({ url }) => url.endsWith('citestcycle'), (payloads) => { + const metadataDicts = payloads.flatMap(({ payload }) => payload.metadata) + + metadataDicts.forEach(metadata => { + for (const testLevel of TEST_LEVEL_EVENT_TYPES) { + assert.equal(metadata[testLevel][TEST_SESSION_NAME], 'my-test-session') + } + }) + const events = payloads.flatMap(({ payload }) => payload.events) const sessionEventContent = events.find(event => event.type === 'test_session_end').content const moduleEventContent = events.find(event => event.type === 'test_module_end').content @@ -150,14 +159,11 @@ describe('jest CommonJS', () => { ) assert.equal(suites.length, 2) assert.exists(sessionEventContent) - assert.equal(sessionEventContent.meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(moduleEventContent) - assert.equal(moduleEventContent.meta[TEST_SESSION_NAME], 'my-test-session') assert.include(testOutput, expectedStdout) tests.forEach(testEvent => { - assert.equal(testEvent.meta[TEST_SESSION_NAME], 'my-test-session') assert.equal(testEvent.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/test/ci-visibility-test'), true) assert.exists(testEvent.metrics[TEST_SOURCE_START]) // Can read DD_TAGS @@ -166,7 +172,6 @@ describe('jest CommonJS', () => { }) suites.forEach(testSuite => { - assert.equal(testSuite.meta[TEST_SESSION_NAME], 'my-test-session') assert.isTrue(testSuite.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/test/ci-visibility-test')) assert.equal(testSuite.metrics[TEST_SOURCE_START], 1) }) @@ -445,21 +450,19 @@ describe('jest CommonJS', () => { }) receiver.gatherPayloads(({ url }) => url === '/api/v2/citestcycle', 5000).then(eventsRequests => { + const metadataDicts = eventsRequests.flatMap(({ payload }) => payload.metadata) + + // it propagates test session name to the test and test suite events in parallel mode + metadataDicts.forEach(metadata => { + for (const testLevel of TEST_LEVEL_EVENT_TYPES) { + assert.equal(metadata[testLevel][TEST_SESSION_NAME], 'my-test-session') + } + }) + const events = eventsRequests.map(({ payload }) => payload) .flatMap(({ events }) => events) const eventTypes = events.map(event => event.type) - assert.includeMembers(eventTypes, ['test', 'test_suite_end', 'test_module_end', 'test_session_end']) - const tests = events.filter(event => event.type === 'test').map(event => event.content) - const testSuites = events.filter(event => event.type === 'test_suite_end').map(event => event.content) - - // it propagates test session name to the test and test suite events in parallel mode - tests.forEach(testEvent => { - assert.equal(testEvent.meta[TEST_SESSION_NAME], 'my-test-session') - }) - testSuites.forEach(testSuite => { - assert.equal(testSuite.meta[TEST_SESSION_NAME], 'my-test-session') - }) done() }).catch(done) diff --git a/integration-tests/mocha/mocha.spec.js b/integration-tests/mocha/mocha.spec.js index dab0c639b75..af3a1e827d1 100644 --- a/integration-tests/mocha/mocha.spec.js +++ b/integration-tests/mocha/mocha.spec.js @@ -33,7 +33,8 @@ const { MOCHA_IS_PARALLEL, TEST_SOURCE_START, TEST_CODE_OWNERS, - TEST_SESSION_NAME + TEST_SESSION_NAME, + TEST_LEVEL_EVENT_TYPES } = require('../../packages/dd-trace/src/plugins/util/test') const { ERROR_MESSAGE } = require('../../packages/dd-trace/src/constants') @@ -133,6 +134,14 @@ describe('mocha CommonJS', function () { receiver.setInfoResponse({ endpoints: ['/evp_proxy/v4'] }) } receiver.gatherPayloadsMaxTimeout(({ url }) => url.endsWith('citestcycle'), (payloads) => { + const metadataDicts = payloads.flatMap(({ payload }) => payload.metadata) + + metadataDicts.forEach(metadata => { + for (const testLevel of TEST_LEVEL_EVENT_TYPES) { + assert.equal(metadata[testLevel][TEST_SESSION_NAME], 'my-test-session') + } + }) + const events = payloads.flatMap(({ payload }) => payload.events) const sessionEventContent = events.find(event => event.type === 'test_session_end').content const moduleEventContent = events.find(event => event.type === 'test_module_end').content @@ -149,15 +158,12 @@ describe('mocha CommonJS', function () { ) assert.equal(suites.length, 2) assert.exists(sessionEventContent) - assert.equal(sessionEventContent.meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(moduleEventContent) - assert.equal(moduleEventContent.meta[TEST_SESSION_NAME], 'my-test-session') assert.include(testOutput, expectedStdout) assert.include(testOutput, extraStdout) tests.forEach(testEvent => { - assert.equal(testEvent.meta[TEST_SESSION_NAME], 'my-test-session') assert.equal(testEvent.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/test/ci-visibility-test'), true) assert.exists(testEvent.metrics[TEST_SOURCE_START]) // Can read DD_TAGS @@ -166,7 +172,6 @@ describe('mocha CommonJS', function () { }) suites.forEach(testSuite => { - assert.equal(testSuite.meta[TEST_SESSION_NAME], 'my-test-session') assert.isTrue(testSuite.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/test/ci-visibility-test')) assert.equal(testSuite.metrics[TEST_SOURCE_START], 1) }) @@ -317,6 +322,14 @@ describe('mocha CommonJS', function () { it('works with parallel mode', (done) => { const eventsPromise = receiver .gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), (payloads) => { + const metadataDicts = payloads.flatMap(({ payload }) => payload.metadata) + + metadataDicts.forEach(metadata => { + for (const testLevel of TEST_LEVEL_EVENT_TYPES) { + assert.equal(metadata[testLevel][TEST_SESSION_NAME], 'my-test-session') + } + }) + const events = payloads.flatMap(({ payload }) => payload.events) const sessionEventContent = events.find(event => event.type === 'test_session_end').content const moduleEventContent = events.find(event => event.type === 'test_module_end').content @@ -334,7 +347,6 @@ describe('mocha CommonJS', function () { test_module_id: testModuleId, test_session_id: testSessionId }) => { - assert.equal(meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(meta[TEST_COMMAND]) assert.exists(meta[TEST_MODULE]) assert.exists(testSuiteId) @@ -349,7 +361,6 @@ describe('mocha CommonJS', function () { test_module_id: testModuleId, test_session_id: testSessionId }) => { - assert.equal(meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(meta[TEST_COMMAND]) assert.exists(meta[TEST_MODULE]) assert.exists(testSuiteId) diff --git a/integration-tests/playwright/playwright.spec.js b/integration-tests/playwright/playwright.spec.js index da7355f6848..683969dce6b 100644 --- a/integration-tests/playwright/playwright.spec.js +++ b/integration-tests/playwright/playwright.spec.js @@ -23,7 +23,8 @@ const { TEST_EARLY_FLAKE_ENABLED, TEST_SUITE, TEST_CODE_OWNERS, - TEST_SESSION_NAME + TEST_SESSION_NAME, + TEST_LEVEL_EVENT_TYPES } = require('../../packages/dd-trace/src/plugins/util/test') const { ERROR_MESSAGE } = require('../../packages/dd-trace/src/constants') @@ -72,6 +73,14 @@ versions.forEach((version) => { const reportUrl = reportMethod === 'agentless' ? '/api/v2/citestcycle' : '/evp_proxy/v2/api/v2/citestcycle' receiver.gatherPayloadsMaxTimeout(({ url }) => url === reportUrl, payloads => { + const metadataDicts = payloads.flatMap(({ payload }) => payload.metadata) + + metadataDicts.forEach(metadata => { + for (const testLevel of TEST_LEVEL_EVENT_TYPES) { + assert.equal(metadata[testLevel][TEST_SESSION_NAME], 'my-test-session') + } + }) + const events = payloads.flatMap(({ payload }) => payload.events) const testSessionEvent = events.find(event => event.type === 'test_session_end') @@ -81,10 +90,8 @@ versions.forEach((version) => { const stepEvents = events.filter(event => event.type === 'span') - assert.equal(testSessionEvent.content.meta[TEST_SESSION_NAME], 'my-test-session') assert.include(testSessionEvent.content.resource, 'test_session.playwright test') assert.equal(testSessionEvent.content.meta[TEST_STATUS], 'fail') - assert.equal(testModuleEvent.content.meta[TEST_SESSION_NAME], 'my-test-session') assert.include(testModuleEvent.content.resource, 'test_module.playwright test') assert.equal(testModuleEvent.content.meta[TEST_STATUS], 'fail') assert.equal(testSessionEvent.content.meta[TEST_TYPE], 'browser') @@ -106,7 +113,6 @@ versions.forEach((version) => { ]) testSuiteEvents.forEach(testSuiteEvent => { - assert.equal(testSuiteEvent.content.meta[TEST_SESSION_NAME], 'my-test-session') if (testSuiteEvent.content.meta[TEST_STATUS] === 'fail') { assert.exists(testSuiteEvent.content.meta[ERROR_MESSAGE]) } @@ -130,7 +136,6 @@ versions.forEach((version) => { ]) testEvents.forEach(testEvent => { - assert.equal(testEvent.content.meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(testEvent.content.metrics[TEST_SOURCE_START]) assert.equal( testEvent.content.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/playwright-tests/'), true diff --git a/integration-tests/vitest/vitest.spec.js b/integration-tests/vitest/vitest.spec.js index 4afbc118155..05a5fe42061 100644 --- a/integration-tests/vitest/vitest.spec.js +++ b/integration-tests/vitest/vitest.spec.js @@ -17,6 +17,7 @@ const { TEST_CODE_COVERAGE_LINES_PCT, TEST_SESSION_NAME, TEST_COMMAND, + TEST_LEVEL_EVENT_TYPES, TEST_SOURCE_FILE, TEST_SOURCE_START } = require('../../packages/dd-trace/src/plugins/util/test') @@ -54,6 +55,14 @@ versions.forEach((version) => { it('can run and report tests', (done) => { receiver.gatherPayloadsMaxTimeout(({ url }) => url === '/api/v2/citestcycle', payloads => { + const metadataDicts = payloads.flatMap(({ payload }) => payload.metadata) + + metadataDicts.forEach(metadata => { + for (const testLevel of TEST_LEVEL_EVENT_TYPES) { + assert.equal(metadata[testLevel][TEST_SESSION_NAME], 'my-test-session') + } + }) + const events = payloads.flatMap(({ payload }) => payload.events) const testSessionEvent = events.find(event => event.type === 'test_session_end') @@ -61,10 +70,8 @@ versions.forEach((version) => { const testSuiteEvents = events.filter(event => event.type === 'test_suite_end') const testEvents = events.filter(event => event.type === 'test') - assert.equal(testSessionEvent.content.meta[TEST_SESSION_NAME], 'my-test-session') assert.include(testSessionEvent.content.resource, 'test_session.vitest run') assert.equal(testSessionEvent.content.meta[TEST_STATUS], 'fail') - assert.equal(testModuleEvent.content.meta[TEST_SESSION_NAME], 'my-test-session') assert.include(testModuleEvent.content.resource, 'test_module.vitest run') assert.equal(testModuleEvent.content.meta[TEST_STATUS], 'fail') assert.equal(testSessionEvent.content.meta[TEST_TYPE], 'test') @@ -137,12 +144,10 @@ versions.forEach((version) => { ) testEvents.forEach(test => { - assert.equal(test.content.meta[TEST_SESSION_NAME], 'my-test-session') assert.equal(test.content.meta[TEST_COMMAND], 'vitest run') }) testSuiteEvents.forEach(testSuite => { - assert.equal(testSuite.content.meta[TEST_SESSION_NAME], 'my-test-session') assert.equal(testSuite.content.meta[TEST_COMMAND], 'vitest run') assert.isTrue( testSuite.content.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/vitest-tests/test-visibility') diff --git a/packages/datadog-plugin-cucumber/src/index.js b/packages/datadog-plugin-cucumber/src/index.js index 95e14ef9c6b..7aaf264d763 100644 --- a/packages/datadog-plugin-cucumber/src/index.js +++ b/packages/datadog-plugin-cucumber/src/index.js @@ -25,8 +25,7 @@ const { TEST_MODULE, TEST_MODULE_ID, TEST_SUITE, - CUCUMBER_IS_PARALLEL, - TEST_SESSION_NAME + CUCUMBER_IS_PARALLEL } = require('../../dd-trace/src/plugins/util/test') const { RESOURCE_NAME } = require('../../../ext/tags') const { COMPONENT, ERROR_MESSAGE } = require('../../dd-trace/src/constants') @@ -52,8 +51,7 @@ function getTestSuiteTags (testSuiteSpan) { [TEST_SUITE_ID]: testSuiteSpan.context().toSpanId(), [TEST_SESSION_ID]: testSuiteSpan.context().toTraceId(), [TEST_COMMAND]: testSuiteSpan.context()._tags[TEST_COMMAND], - [TEST_MODULE]: 'cucumber', - [TEST_SESSION_NAME]: testSuiteSpan.context()._tags[TEST_SESSION_NAME] + [TEST_MODULE]: 'cucumber' } if (testSuiteSpan.context()._parentId) { suiteTags[TEST_MODULE_ID] = testSuiteSpan.context()._parentId.toString(10) @@ -144,9 +142,6 @@ class CucumberPlugin extends CiPlugin { if (itrCorrelationId) { testSuiteMetadata[ITR_CORRELATION_ID] = itrCorrelationId } - if (this.testSessionName) { - testSuiteMetadata[TEST_SESSION_NAME] = this.testSessionName - } if (testSourceFile) { testSuiteMetadata[TEST_SOURCE_FILE] = testSourceFile testSuiteMetadata[TEST_SOURCE_START] = 1 diff --git a/packages/datadog-plugin-cypress/src/cypress-plugin.js b/packages/datadog-plugin-cypress/src/cypress-plugin.js index 87c83067b0c..630d613f772 100644 --- a/packages/datadog-plugin-cypress/src/cypress-plugin.js +++ b/packages/datadog-plugin-cypress/src/cypress-plugin.js @@ -30,7 +30,8 @@ const { TEST_IS_RETRY, TEST_EARLY_FLAKE_ENABLED, getTestSessionName, - TEST_SESSION_NAME + TEST_SESSION_NAME, + TEST_LEVEL_EVENT_TYPES } = require('../../dd-trace/src/plugins/util/test') const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util') const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants') @@ -267,7 +268,6 @@ class CypressPlugin { childOf: this.testModuleSpan, tags: { [COMPONENT]: TEST_FRAMEWORK_NAME, - [TEST_SESSION_NAME]: this.testSessionName, ...this.testEnvironmentMetadata, ...testSuiteSpanMetadata } @@ -278,8 +278,7 @@ class CypressPlugin { const testSuiteTags = { [TEST_COMMAND]: this.command, [TEST_COMMAND]: this.command, - [TEST_MODULE]: TEST_FRAMEWORK_NAME, - [TEST_SESSION_NAME]: this.testSessionName + [TEST_MODULE]: TEST_FRAMEWORK_NAME } if (this.testSuiteSpan) { testSuiteTags[TEST_SUITE_ID] = this.testSuiteSpan.context().toSpanId() @@ -403,13 +402,22 @@ class CypressPlugin { testSessionSpanMetadata[TEST_EARLY_FLAKE_ENABLED] = 'true' } - this.testSessionName = getTestSessionName(this.tracer._tracer._config, this.command, this.testEnvironmentMetadata) + const testSessionName = getTestSessionName(this.tracer._tracer._config, this.command, this.testEnvironmentMetadata) + + if (this.tracer._tracer._exporter?.setMetadataTags) { + const metadataTags = {} + for (const testLevel of TEST_LEVEL_EVENT_TYPES) { + metadataTags[testLevel] = { + [TEST_SESSION_NAME]: testSessionName + } + } + this.tracer._tracer._exporter.setMetadataTags(metadataTags) + } this.testSessionSpan = this.tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_session`, { childOf, tags: { [COMPONENT]: TEST_FRAMEWORK_NAME, - [TEST_SESSION_NAME]: this.testSessionName, ...this.testEnvironmentMetadata, ...testSessionSpanMetadata } @@ -420,7 +428,6 @@ class CypressPlugin { childOf: this.testSessionSpan, tags: { [COMPONENT]: TEST_FRAMEWORK_NAME, - [TEST_SESSION_NAME]: this.testSessionName, ...this.testEnvironmentMetadata, ...testModuleSpanMetadata } diff --git a/packages/datadog-plugin-jest/src/index.js b/packages/datadog-plugin-jest/src/index.js index c1e61468a7f..4362094b0be 100644 --- a/packages/datadog-plugin-jest/src/index.js +++ b/packages/datadog-plugin-jest/src/index.js @@ -22,8 +22,7 @@ const { TEST_EARLY_FLAKE_ABORT_REASON, JEST_DISPLAY_NAME, TEST_IS_RUM_ACTIVE, - TEST_BROWSER_DRIVER, - TEST_SESSION_NAME + TEST_BROWSER_DRIVER } = require('../../dd-trace/src/plugins/util/test') const { COMPONENT } = require('../../dd-trace/src/constants') const id = require('../../dd-trace/src/id') @@ -150,7 +149,6 @@ class JestPlugin extends CiPlugin { config._ddTestSessionId = this.testSessionSpan.context().toTraceId() config._ddTestModuleId = this.testModuleSpan.context().toSpanId() config._ddTestCommand = this.testSessionSpan.context()._tags[TEST_COMMAND] - config._ddTestSessionName = this.testSessionName config._ddItrCorrelationId = this.itrCorrelationId config._ddIsEarlyFlakeDetectionEnabled = !!this.libraryConfig?.isEarlyFlakeDetectionEnabled config._ddEarlyFlakeDetectionNumRetries = this.libraryConfig?.earlyFlakeDetectionNumRetries ?? 0 @@ -170,7 +168,6 @@ class JestPlugin extends CiPlugin { const { _ddTestSessionId: testSessionId, _ddTestCommand: testCommand, - _ddTestSessionName: testSessionName, _ddTestModuleId: testModuleId, _ddItrCorrelationId: itrCorrelationId, _ddForcedToRun, @@ -205,9 +202,6 @@ class JestPlugin extends CiPlugin { if (displayName) { testSuiteMetadata[JEST_DISPLAY_NAME] = displayName } - if (testSessionName) { - testSuiteMetadata[TEST_SESSION_NAME] = testSessionName - } if (testSourceFile) { testSuiteMetadata[TEST_SOURCE_FILE] = testSourceFile // Test suite is the whole test file, so we can use the first line as the start diff --git a/packages/datadog-plugin-mocha/src/index.js b/packages/datadog-plugin-mocha/src/index.js index 54add82ac8c..82483a3a49f 100644 --- a/packages/datadog-plugin-mocha/src/index.js +++ b/packages/datadog-plugin-mocha/src/index.js @@ -29,8 +29,7 @@ const { TEST_SUITE, MOCHA_IS_PARALLEL, TEST_IS_RUM_ACTIVE, - TEST_BROWSER_DRIVER, - TEST_SESSION_NAME + TEST_BROWSER_DRIVER } = require('../../dd-trace/src/plugins/util/test') const { COMPONENT } = require('../../dd-trace/src/constants') const { @@ -53,8 +52,7 @@ function getTestSuiteLevelVisibilityTags (testSuiteSpan) { [TEST_SUITE_ID]: testSuiteSpanContext.toSpanId(), [TEST_SESSION_ID]: testSuiteSpanContext.toTraceId(), [TEST_COMMAND]: testSuiteSpanContext._tags[TEST_COMMAND], - [TEST_MODULE]: 'mocha', - [TEST_SESSION_NAME]: testSuiteSpanContext._tags[TEST_SESSION_NAME] + [TEST_MODULE]: 'mocha' } if (testSuiteSpanContext._parentId) { suiteTags[TEST_MODULE_ID] = testSuiteSpanContext._parentId.toString(10) @@ -126,9 +124,6 @@ class MochaPlugin extends CiPlugin { testSuiteMetadata[TEST_ITR_FORCED_RUN] = 'true' this.telemetry.count(TELEMETRY_ITR_FORCED_TO_RUN, { testLevel: 'suite' }) } - if (this.testSessionName) { - testSuiteMetadata[TEST_SESSION_NAME] = this.testSessionName - } if (this.repositoryRoot !== this.sourceRoot && !!this.repositoryRoot) { testSuiteMetadata[TEST_SOURCE_FILE] = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot) } else { diff --git a/packages/datadog-plugin-playwright/src/index.js b/packages/datadog-plugin-playwright/src/index.js index 08f8b4b07b6..941f779ff54 100644 --- a/packages/datadog-plugin-playwright/src/index.js +++ b/packages/datadog-plugin-playwright/src/index.js @@ -15,8 +15,7 @@ const { TEST_IS_NEW, TEST_IS_RETRY, TEST_EARLY_FLAKE_ENABLED, - TELEMETRY_TEST_SESSION, - TEST_SESSION_NAME + TELEMETRY_TEST_SESSION } = require('../../dd-trace/src/plugins/util/test') const { RESOURCE_NAME } = require('../../../ext/tags') const { COMPONENT } = require('../../dd-trace/src/constants') @@ -78,9 +77,6 @@ class PlaywrightPlugin extends CiPlugin { testSuite, 'playwright' ) - if (this.testSessionName) { - testSuiteMetadata[TEST_SESSION_NAME] = this.testSessionName - } if (testSourceFile) { testSuiteMetadata[TEST_SOURCE_FILE] = testSourceFile testSuiteMetadata[TEST_SOURCE_START] = 1 diff --git a/packages/datadog-plugin-vitest/src/index.js b/packages/datadog-plugin-vitest/src/index.js index 610a1d8be9e..149f3e5882f 100644 --- a/packages/datadog-plugin-vitest/src/index.js +++ b/packages/datadog-plugin-vitest/src/index.js @@ -6,10 +6,12 @@ const { finishAllTraceSpans, getTestSuitePath, getTestSuiteCommonTags, + getTestSessionName, TEST_SOURCE_FILE, TEST_IS_RETRY, TEST_CODE_COVERAGE_LINES_PCT, TEST_CODE_OWNERS, + TEST_LEVEL_EVENT_TYPES, TEST_SESSION_NAME, TEST_SOURCE_START } = require('../../dd-trace/src/plugins/util/test') @@ -123,20 +125,32 @@ class VitestPlugin extends CiPlugin { }) this.addSub('ci:vitest:test-suite:start', ({ testSuiteAbsolutePath, frameworkVersion }) => { + this.command = process.env.DD_CIVISIBILITY_TEST_COMMAND this.frameworkVersion = frameworkVersion const testSessionSpanContext = this.tracer.extract('text_map', { 'x-datadog-trace-id': process.env.DD_CIVISIBILITY_TEST_SESSION_ID, 'x-datadog-parent-id': process.env.DD_CIVISIBILITY_TEST_MODULE_ID }) + // test suites run in a different process, so they also need to init the metadata dictionary + const testSessionName = getTestSessionName(this.config, this.command, this.testEnvironmentMetadata) + const metadataTags = {} + for (const testLevel of TEST_LEVEL_EVENT_TYPES) { + metadataTags[testLevel] = { + [TEST_SESSION_NAME]: testSessionName + } + } + if (this.tracer._exporter.setMetadataTags) { + this.tracer._exporter.setMetadataTags(metadataTags) + } + const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot) const testSuiteMetadata = getTestSuiteCommonTags( - process.env.DD_CIVISIBILITY_TEST_COMMAND, + this.command, this.frameworkVersion, testSuite, 'vitest' ) - testSuiteMetadata[TEST_SESSION_NAME] = process.env.DD_CIVISIBILITY_TEST_SESSION_NAME testSuiteMetadata[TEST_SOURCE_FILE] = testSuite testSuiteMetadata[TEST_SOURCE_START] = 1 diff --git a/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js b/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js index 3934ec0d5b2..466c5230b22 100644 --- a/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +++ b/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js @@ -72,6 +72,10 @@ class Writer extends BaseWriter { done() }) } + + setMetadataTags (tags) { + this._encoder.setMetadataTags(tags) + } } module.exports = Writer diff --git a/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js b/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js index 4ec092d4905..9dabd34f7f3 100644 --- a/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +++ b/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js @@ -291,6 +291,19 @@ class CiVisibilityExporter extends AgentInfoExporter { _getApiUrl () { return this._url } + + // By the time setMetadataTags is called, the agent info request might not have finished + setMetadataTags (tags) { + if (this._writer?.setMetadataTags) { + this._writer.setMetadataTags(tags) + } else { + this._canUseCiVisProtocolPromise.then(() => { + if (this._writer?.setMetadataTags) { + this._writer.setMetadataTags(tags) + } + }) + } + } } module.exports = CiVisibilityExporter diff --git a/packages/dd-trace/src/encode/agentless-ci-visibility.js b/packages/dd-trace/src/encode/agentless-ci-visibility.js index 7b78c0ea3ce..dea15182323 100644 --- a/packages/dd-trace/src/encode/agentless-ci-visibility.js +++ b/packages/dd-trace/src/encode/agentless-ci-visibility.js @@ -43,9 +43,15 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder { // length of `payload.events` when calling `makePayload` this._eventCount = 0 + this.metadataTags = {} + this.reset() } + setMetadataTags (tags) { + this.metadataTags = tags + } + _encodeTestSuite (bytes, content) { let keysLength = TEST_SUITE_KEYS_LENGTH const itrCorrelationId = content.meta[ITR_CORRELATION_ID] @@ -277,6 +283,10 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder { } _encode (bytes, trace) { + if (this._isReset) { + this._encodePayloadStart(bytes) + this._isReset = false + } const startTime = Date.now() const rawEvents = trace.map(formatSpan) @@ -330,7 +340,8 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder { '*': { language: 'javascript', library_version: ddTraceVersion - } + }, + ...this.metadataTags }, events: [] } @@ -349,6 +360,22 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder { this._encodeMapPrefix(bytes, Object.keys(payload.metadata).length) this._encodeString(bytes, '*') this._encodeMap(bytes, payload.metadata['*']) + if (payload.metadata.test) { + this._encodeString(bytes, 'test') + this._encodeMap(bytes, payload.metadata.test) + } + if (payload.metadata.test_suite_end) { + this._encodeString(bytes, 'test_suite_end') + this._encodeMap(bytes, payload.metadata.test_suite_end) + } + if (payload.metadata.test_module_end) { + this._encodeString(bytes, 'test_module_end') + this._encodeMap(bytes, payload.metadata.test_module_end) + } + if (payload.metadata.test_session_end) { + this._encodeString(bytes, 'test_session_end') + this._encodeMap(bytes, payload.metadata.test_session_end) + } this._encodeString(bytes, 'events') // Get offset of the events list to update the length of the array when calling `makePayload` this._eventsOffset = bytes.length @@ -359,7 +386,7 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder { reset () { this._reset() this._eventCount = 0 - this._encodePayloadStart(this._traceBytes) + this._isReset = true } } diff --git a/packages/dd-trace/src/plugins/ci_plugin.js b/packages/dd-trace/src/plugins/ci_plugin.js index 76a31b19766..b86d20d5760 100644 --- a/packages/dd-trace/src/plugins/ci_plugin.js +++ b/packages/dd-trace/src/plugins/ci_plugin.js @@ -20,6 +20,7 @@ const { TEST_SKIPPED_BY_ITR, ITR_CORRELATION_ID, TEST_SOURCE_FILE, + TEST_LEVEL_EVENT_TYPES, TEST_SUITE } = require('./util/test') const Plugin = require('./plugin') @@ -78,13 +79,23 @@ module.exports = class CiPlugin extends Plugin { // only for playwright this.rootDir = rootDir - this.testSessionName = getTestSessionName(this.config, this.command, this.testEnvironmentMetadata) + const testSessionName = getTestSessionName(this.config, this.command, this.testEnvironmentMetadata) + + const metadataTags = {} + for (const testLevel of TEST_LEVEL_EVENT_TYPES) { + metadataTags[testLevel] = { + [TEST_SESSION_NAME]: testSessionName + } + } + // tracer might not be initialized correctly + if (this.tracer._exporter.setMetadataTags) { + this.tracer._exporter.setMetadataTags(metadataTags) + } this.testSessionSpan = this.tracer.startSpan(`${this.constructor.id}.test_session`, { childOf, tags: { [COMPONENT]: this.constructor.id, - [TEST_SESSION_NAME]: this.testSessionName, ...this.testEnvironmentMetadata, ...testSessionSpanMetadata } @@ -94,7 +105,6 @@ module.exports = class CiPlugin extends Plugin { childOf: this.testSessionSpan, tags: { [COMPONENT]: this.constructor.id, - [TEST_SESSION_NAME]: this.testSessionName, ...this.testEnvironmentMetadata, ...testModuleSpanMetadata } @@ -105,7 +115,6 @@ module.exports = class CiPlugin extends Plugin { process.env.DD_CIVISIBILITY_TEST_SESSION_ID = this.testSessionSpan.context().toTraceId() process.env.DD_CIVISIBILITY_TEST_MODULE_ID = this.testModuleSpan.context().toSpanId() process.env.DD_CIVISIBILITY_TEST_COMMAND = this.command - process.env.DD_CIVISIBILITY_TEST_SESSION_NAME = this.testSessionName } this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'module') @@ -230,11 +239,6 @@ module.exports = class CiPlugin extends Plugin { ...extraTags } - // this.testSessionName might be empty for parallel workers - if (this.testSessionName) { - testTags[TEST_SESSION_NAME] = this.testSessionName - } - const codeOwners = this.getCodeOwners(testTags) if (codeOwners) { testTags[TEST_CODE_OWNERS] = codeOwners @@ -250,8 +254,7 @@ module.exports = class CiPlugin extends Plugin { [TEST_SUITE_ID]: testSuiteSpan.context().toSpanId(), [TEST_SESSION_ID]: testSuiteSpan.context().toTraceId(), [TEST_COMMAND]: testSuiteSpan.context()._tags[TEST_COMMAND], - [TEST_MODULE]: this.constructor.id, - [TEST_SESSION_NAME]: testSuiteSpan.context()._tags[TEST_SESSION_NAME] + [TEST_MODULE]: this.constructor.id } if (testSuiteSpan.context()._parentId) { suiteTags[TEST_MODULE_ID] = testSuiteSpan.context()._parentId.toString(10) diff --git a/packages/dd-trace/src/plugins/util/test.js b/packages/dd-trace/src/plugins/util/test.js index edd4291a974..6d2e367c09c 100644 --- a/packages/dd-trace/src/plugins/util/test.js +++ b/packages/dd-trace/src/plugins/util/test.js @@ -99,6 +99,13 @@ const MOCHA_WORKER_TRACE_PAYLOAD_CODE = 80 const EFD_STRING = "Retried by Datadog's Early Flake Detection" const EFD_TEST_NAME_REGEX = new RegExp(EFD_STRING + ' \\(#\\d+\\): ', 'g') +const TEST_LEVEL_EVENT_TYPES = [ + 'test', + 'test_suite_end', + 'test_module_end', + 'test_session_end' +] + module.exports = { TEST_CODE_OWNERS, TEST_SESSION_NAME, @@ -173,7 +180,8 @@ module.exports = { TEST_BROWSER_DRIVER_VERSION, TEST_BROWSER_NAME, TEST_BROWSER_VERSION, - getTestSessionName + getTestSessionName, + TEST_LEVEL_EVENT_TYPES } // Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19