Skip to content

Commit

Permalink
[Test Optimization] Fix DI setup for jest workers (#5110)
Browse files Browse the repository at this point in the history
  • Loading branch information
juan-fernandez authored Jan 16, 2025
1 parent 26722b3 commit 803ac98
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 7 deletions.
73 changes: 73 additions & 0 deletions integration-tests/jest/jest.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,79 @@ describe('jest CommonJS', () => {
done()
}).catch(done)
})

it('can work with Dynamic Instrumentation', (done) => {
receiver.setSettings({
flaky_test_retries_enabled: true,
di_enabled: true
})
let snapshotIdByTest, snapshotIdByLog
let spanIdByTest, spanIdByLog, traceIdByTest, traceIdByLog
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)
const retriedTests = tests.filter(test => test.meta[TEST_IS_RETRY] === 'true')

assert.equal(retriedTests.length, 2)
const [retriedTest] = retriedTests

assert.propertyVal(retriedTest.meta, DI_ERROR_DEBUG_INFO_CAPTURED, 'true')

assert.isTrue(
retriedTest.meta[`${DI_DEBUG_ERROR_PREFIX}.0.${DI_DEBUG_ERROR_FILE_SUFFIX}`]
.endsWith('ci-visibility/dynamic-instrumentation/dependency.js')
)
assert.equal(retriedTest.metrics[`${DI_DEBUG_ERROR_PREFIX}.0.${DI_DEBUG_ERROR_LINE_SUFFIX}`], 4)

const snapshotIdKey = `${DI_DEBUG_ERROR_PREFIX}.0.${DI_DEBUG_ERROR_SNAPSHOT_ID_SUFFIX}`
assert.exists(retriedTest.meta[snapshotIdKey])

snapshotIdByTest = retriedTest.meta[snapshotIdKey]
spanIdByTest = retriedTest.span_id.toString()
traceIdByTest = retriedTest.trace_id.toString()

const notRetriedTest = tests.find(test => test.meta[TEST_NAME].includes('is not retried'))

assert.notProperty(notRetriedTest.meta, DI_ERROR_DEBUG_INFO_CAPTURED)
})

const logsPromise = receiver
.gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/logs'), (payloads) => {
const [{ logMessage: [diLog] }] = payloads
assert.deepInclude(diLog, {
ddsource: 'dd_debugger',
level: 'error'
})
assert.equal(diLog.debugger.snapshot.language, 'javascript')
spanIdByLog = diLog.dd.span_id
traceIdByLog = diLog.dd.trace_id
snapshotIdByLog = diLog.debugger.snapshot.id
})

childProcess = exec(runTestsWithCoverageCommand,
{
cwd,
env: {
...getCiVisAgentlessConfig(receiver.port),
TESTS_TO_RUN: 'dynamic-instrumentation/test-',
DD_TEST_DYNAMIC_INSTRUMENTATION_ENABLED: 'true',
RUN_IN_PARALLEL: true
},
stdio: 'inherit'
}
)

childProcess.on('exit', () => {
Promise.all([eventsPromise, logsPromise]).then(() => {
assert.equal(snapshotIdByTest, snapshotIdByLog)
assert.equal(spanIdByTest, spanIdByLog)
assert.equal(traceIdByTest, traceIdByLog)
done()
}).catch(done)
})
})
})

it('reports timeout error message', (done) => {
Expand Down
10 changes: 9 additions & 1 deletion packages/datadog-instrumentations/src/jest.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ const {
getTestParametersString,
addEfdStringToTestName,
removeEfdStringFromTestName,
getIsFaultyEarlyFlakeDetection
getIsFaultyEarlyFlakeDetection,
JEST_WORKER_LOGS_PAYLOAD_CODE
} = require('../../dd-trace/src/plugins/util/test')
const {
getFormattedJestTestParameters,
Expand All @@ -30,6 +31,7 @@ const testSuiteFinishCh = channel('ci:jest:test-suite:finish')

const workerReportTraceCh = channel('ci:jest:worker-report:trace')
const workerReportCoverageCh = channel('ci:jest:worker-report:coverage')
const workerReportLogsCh = channel('ci:jest:worker-report:logs')

const testSuiteCodeCoverageCh = channel('ci:jest:test-suite:code-coverage')

Expand Down Expand Up @@ -979,6 +981,12 @@ addHook({
})
return
}
if (code === JEST_WORKER_LOGS_PAYLOAD_CODE) { // datadog logs payload
sessionAsyncResource.runInAsyncScope(() => {
workerReportLogsCh.publish(data)
})
return
}
return _onMessage.apply(this, arguments)
})
return childProcessWorker
Expand Down
6 changes: 6 additions & 0 deletions packages/datadog-plugin-jest/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,12 @@ class JestPlugin extends CiPlugin {
})
})

this.addSub('ci:jest:worker-report:logs', (logsPayloads) => {
JSON.parse(logsPayloads).forEach(({ testConfiguration, logMessage }) => {
this.tracer._exporter.exportDiLogs(testConfiguration, logMessage)
})
})

this.addSub('ci:jest:test-suite:finish', ({ status, errorMessage, error }) => {
this.testSuiteSpan.setTag(TEST_STATUS, status)
if (error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ class TestVisDynamicInstrumentation {
start (config) {
if (this.worker) return

const { NODE_OPTIONS, ...envWithoutNodeOptions } = process.env

log.debug('Starting Test Visibility - Dynamic Instrumentation client...')

const rcChannel = new MessageChannel() // mock channel
Expand All @@ -66,7 +64,14 @@ class TestVisDynamicInstrumentation {
join(__dirname, 'worker', 'index.js'),
{
execArgv: [],
env: envWithoutNodeOptions,
// Not passing `NODE_OPTIONS` results in issues with yarn, which relies on NODE_OPTIONS
// for PnP support, hence why we deviate from the DI pattern here.
// To avoid infinite initialization loops, we're disabling DI and tracing in the worker.
env: {
...process.env,
DD_TRACE_ENABLED: 0,
DD_TEST_DYNAMIC_INSTRUMENTATION_ENABLED: 0
},
workerData: {
config: config.serialize(),
parentThreadId,
Expand All @@ -89,9 +94,11 @@ class TestVisDynamicInstrumentation {
log.debug('Test Visibility - Dynamic Instrumentation client is ready')
this._onReady()
})

this.worker.on('error', (err) => {
log.error('Test Visibility - Dynamic Instrumentation worker error', err)
})

this.worker.on('messageerror', (err) => {
log.error('Test Visibility - Dynamic Instrumentation worker messageerror', err)
})
Expand Down
21 changes: 18 additions & 3 deletions packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ const {
JEST_WORKER_COVERAGE_PAYLOAD_CODE,
JEST_WORKER_TRACE_PAYLOAD_CODE,
CUCUMBER_WORKER_TRACE_PAYLOAD_CODE,
MOCHA_WORKER_TRACE_PAYLOAD_CODE
MOCHA_WORKER_TRACE_PAYLOAD_CODE,
JEST_WORKER_LOGS_PAYLOAD_CODE
} = require('../../../plugins/util/test')

function getInterprocessTraceCode () {
Expand All @@ -29,18 +30,27 @@ function getInterprocessCoverageCode () {
return null
}

function getInterprocessLogsCode () {
if (process.env.JEST_WORKER_ID) {
return JEST_WORKER_LOGS_PAYLOAD_CODE
}
return null
}

/**
* Lightweight exporter whose writers only do simple JSON serialization
* of trace and coverage payloads, which they send to the test framework's main process.
* Currently used by Jest and Cucumber workers.
* of trace, coverage and logs payloads, which they send to the test framework's main process.
* Currently used by Jest, Cucumber and Mocha workers.
*/
class TestWorkerCiVisibilityExporter {
constructor () {
const interprocessTraceCode = getInterprocessTraceCode()
const interprocessCoverageCode = getInterprocessCoverageCode()
const interprocessLogsCode = getInterprocessLogsCode()

this._writer = new Writer(interprocessTraceCode)
this._coverageWriter = new Writer(interprocessCoverageCode)
this._logsWriter = new Writer(interprocessLogsCode)
}

export (payload) {
Expand All @@ -51,9 +61,14 @@ class TestWorkerCiVisibilityExporter {
this._coverageWriter.append(formattedCoverage)
}

exportDiLogs (testConfiguration, logMessage) {
this._logsWriter.append({ testConfiguration, logMessage })
}

flush () {
this._writer.flush()
this._coverageWriter.flush()
this._logsWriter.flush()
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/dd-trace/src/plugins/ci_plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ module.exports = class CiPlugin extends Plugin {
)

const activeTestSpanContext = this.activeTestSpan.context()

this.tracer._exporter.exportDiLogs(this.testEnvironmentMetadata, {
debugger: { snapshot },
dd: {
Expand Down
2 changes: 2 additions & 0 deletions packages/dd-trace/src/plugins/util/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ const TEST_BROWSER_VERSION = 'test.browser.version'
// jest worker variables
const JEST_WORKER_TRACE_PAYLOAD_CODE = 60
const JEST_WORKER_COVERAGE_PAYLOAD_CODE = 61
const JEST_WORKER_LOGS_PAYLOAD_CODE = 62

// cucumber worker variables
const CUCUMBER_WORKER_TRACE_PAYLOAD_CODE = 70
Expand Down Expand Up @@ -134,6 +135,7 @@ module.exports = {
LIBRARY_VERSION,
JEST_WORKER_TRACE_PAYLOAD_CODE,
JEST_WORKER_COVERAGE_PAYLOAD_CODE,
JEST_WORKER_LOGS_PAYLOAD_CODE,
CUCUMBER_WORKER_TRACE_PAYLOAD_CODE,
MOCHA_WORKER_TRACE_PAYLOAD_CODE,
TEST_SOURCE_START,
Expand Down

0 comments on commit 803ac98

Please sign in to comment.