From 037cb1fe90ac3be0418ef98f6bc72f7aaac4cb37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Antonio=20Fern=C3=A1ndez=20de=20Alba?= Date: Wed, 11 Sep 2024 17:03:14 +0200 Subject: [PATCH] =?UTF-8?q?Revert=20"Revert=20"[test=20visibility]=C2=A0Us?= =?UTF-8?q?e=20new=20metadata=20field=20in=20citestcycle=20=20(#46?= =?UTF-8?q?=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit fab4824ace1f2b87912004d173e182cbcaca97ee. --- integration-tests/cucumber/cucumber.spec.js | 14 ++++--- integration-tests/cypress/cypress.spec.js | 14 ++++--- integration-tests/jest/jest.spec.js | 38 +++++++++---------- integration-tests/mocha/mocha.spec.js | 28 +++++++++----- .../playwright/playwright.spec.js | 15 +++++--- integration-tests/vitest/vitest.spec.js | 15 +++++--- packages/datadog-plugin-cucumber/src/index.js | 9 +---- .../src/cypress-plugin.js | 21 ++++++---- packages/datadog-plugin-jest/src/index.js | 8 +--- packages/datadog-plugin-mocha/src/index.js | 9 +---- .../datadog-plugin-playwright/src/index.js | 6 +-- packages/datadog-plugin-vitest/src/index.js | 18 ++++++++- .../exporters/agentless/writer.js | 4 ++ .../exporters/ci-visibility-exporter.js | 13 +++++++ .../src/encode/agentless-ci-visibility.js | 31 ++++++++++++++- packages/dd-trace/src/plugins/ci_plugin.js | 27 +++++++------ packages/dd-trace/src/plugins/util/test.js | 10 ++++- 17 files changed, 181 insertions(+), 99 deletions(-) diff --git a/integration-tests/cucumber/cucumber.spec.js b/integration-tests/cucumber/cucumber.spec.js index 0fb5074f0d7..e74ca4a748c 100644 --- a/integration-tests/cucumber/cucumber.spec.js +++ b/integration-tests/cucumber/cucumber.spec.js @@ -34,7 +34,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]) @@ -167,7 +173,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) @@ -198,7 +203,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 b6fe70dad78..4bce94316a0 100644 --- a/integration-tests/cypress/cypress.spec.js +++ b/integration-tests/cypress/cypress.spec.js @@ -33,7 +33,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') @@ -226,6 +227,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') @@ -236,14 +244,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]) @@ -274,7 +280,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) @@ -302,7 +307,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 d90f8de2bcf..adf97d33a12 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 @@ -165,10 +171,6 @@ describe('jest CommonJS', () => { assert.propertyVal(testEvent.meta, 'test.customtag2', 'customvalue2') }) - suites.forEach(testSuite => { - assert.equal(testSuite.meta[TEST_SESSION_NAME], 'my-test-session') - }) - done() }) @@ -441,21 +443,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 5fb00645d48..ec5a2dd48ad 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 @@ -165,10 +171,6 @@ describe('mocha CommonJS', function () { assert.propertyVal(testEvent.meta, 'test.customtag2', 'customvalue2') }) - suites.forEach(testSuite => { - assert.equal(testSuite.meta[TEST_SESSION_NAME], 'my-test-session') - }) - done() }) @@ -313,6 +315,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 @@ -330,7 +340,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) @@ -345,7 +354,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 49c0d097f82..4b8b029e2ee 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]) } @@ -128,7 +134,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 ee079783316..9c446f840ec 100644 --- a/integration-tests/vitest/vitest.spec.js +++ b/integration-tests/vitest/vitest.spec.js @@ -16,7 +16,8 @@ const { TEST_CODE_OWNERS, TEST_CODE_COVERAGE_LINES_PCT, TEST_SESSION_NAME, - TEST_COMMAND + TEST_COMMAND, + TEST_LEVEL_EVENT_TYPES } = require('../../packages/dd-trace/src/plugins/util/test') const versions = ['1.6.0', 'latest'] @@ -52,6 +53,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') @@ -59,10 +68,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') @@ -135,12 +142,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') }) // TODO: check error messages diff --git a/packages/datadog-plugin-cucumber/src/index.js b/packages/datadog-plugin-cucumber/src/index.js index 2cd6c4d020b..98ed65cfbd4 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) @@ -136,9 +134,6 @@ class CucumberPlugin extends CiPlugin { if (itrCorrelationId) { testSuiteMetadata[ITR_CORRELATION_ID] = itrCorrelationId } - if (this.testSessionName) { - testSuiteMetadata[TEST_SESSION_NAME] = this.testSessionName - } const testSuiteSpan = this.tracer.startSpan('cucumber.test_suite', { childOf: this.testModuleSpan, tags: { diff --git a/packages/datadog-plugin-cypress/src/cypress-plugin.js b/packages/datadog-plugin-cypress/src/cypress-plugin.js index 27347fcebdb..908f048f6ae 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') @@ -256,7 +257,6 @@ class CypressPlugin { childOf: this.testModuleSpan, tags: { [COMPONENT]: TEST_FRAMEWORK_NAME, - [TEST_SESSION_NAME]: this.testSessionName, ...this.testEnvironmentMetadata, ...testSuiteSpanMetadata } @@ -267,8 +267,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() @@ -392,13 +391,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 } @@ -409,7 +417,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 c517f7560be..606fdcec538 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 @@ -164,7 +162,6 @@ class JestPlugin extends CiPlugin { const { _ddTestSessionId: testSessionId, _ddTestCommand: testCommand, - _ddTestSessionName: testSessionName, _ddTestModuleId: testModuleId, _ddItrCorrelationId: itrCorrelationId, _ddForcedToRun, @@ -199,9 +196,6 @@ class JestPlugin extends CiPlugin { if (displayName) { testSuiteMetadata[JEST_DISPLAY_NAME] = displayName } - if (testSessionName) { - testSuiteMetadata[TEST_SESSION_NAME] = testSessionName - } this.testSuiteSpan = this.tracer.startSpan('jest.test_suite', { childOf: testSessionSpanContext, diff --git a/packages/datadog-plugin-mocha/src/index.js b/packages/datadog-plugin-mocha/src/index.js index 5a0cadc4770..79b0d14c62f 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 - } const testSuiteSpan = this.tracer.startSpan('mocha.test_suite', { childOf: this.testModuleSpan, diff --git a/packages/datadog-plugin-playwright/src/index.js b/packages/datadog-plugin-playwright/src/index.js index 0e9bf4f6c57..482bd6f10b9 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') @@ -77,9 +76,6 @@ class PlaywrightPlugin extends CiPlugin { testSuite, 'playwright' ) - if (this.testSessionName) { - testSuiteMetadata[TEST_SESSION_NAME] = this.testSessionName - } const testSuiteSpan = this.tracer.startSpan('playwright.test_suite', { childOf: this.testModuleSpan, diff --git a/packages/datadog-plugin-vitest/src/index.js b/packages/datadog-plugin-vitest/src/index.js index fc10935745f..d058a723fbf 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 } = require('../../dd-trace/src/plugins/util/test') const { COMPONENT } = require('../../dd-trace/src/constants') @@ -121,20 +123,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 const testSuiteSpan = this.tracer.startSpan('vitest.test_suite', { childOf: testSessionSpanContext, 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 3805224eac9..cba2baf2a1d 100644 --- a/packages/dd-trace/src/plugins/ci_plugin.js +++ b/packages/dd-trace/src/plugins/ci_plugin.js @@ -19,7 +19,8 @@ const { TEST_STATUS, TEST_SKIPPED_BY_ITR, ITR_CORRELATION_ID, - TEST_SOURCE_FILE + TEST_SOURCE_FILE, + TEST_LEVEL_EVENT_TYPES } = require('./util/test') const Plugin = require('./plugin') const { COMPONENT } = require('../constants') @@ -77,13 +78,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 } @@ -93,7 +104,6 @@ module.exports = class CiPlugin extends Plugin { childOf: this.testSessionSpan, tags: { [COMPONENT]: this.constructor.id, - [TEST_SESSION_NAME]: this.testSessionName, ...this.testEnvironmentMetadata, ...testModuleSpanMetadata } @@ -104,7 +114,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') @@ -216,11 +225,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 { [TEST_SOURCE_FILE]: testSourceFile } = extraTags // We'll try with the test source file if available (it could be different from the test suite) let codeOwners = getCodeOwnersForFilename(testSourceFile, this.codeOwnersEntries) @@ -242,8 +246,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