Skip to content

Commit 9fc9971

Browse files
[test optimization] Do not enable Test Optimization plugins when isCiVisibility is false (#6404)
1 parent 0b52fec commit 9fc9971

File tree

13 files changed

+313
-23
lines changed

13 files changed

+313
-23
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Feature: Sum calculation
2+
As a developer
3+
I want to test sum functionality
4+
So that I can verify basic math operations
5+
6+
Scenario: Add two numbers
7+
Given I have two numbers 1 and 2
8+
When I add them together
9+
Then the result should be 3
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use strict'
2+
3+
const tracer = require('dd-trace')
4+
const { Given, When, Then } = require('@cucumber/cucumber')
5+
const { expect } = require('chai')
6+
7+
let num1, num2, result
8+
9+
Given('I have two numbers {int} and {int}', function (first, second) {
10+
tracer.startSpan('custom').finish()
11+
num1 = first
12+
num2 = second
13+
})
14+
15+
When('I add them together', function () {
16+
result = num1 + num2
17+
})
18+
19+
Then('the result should be {int}', function (expected) {
20+
expect(result).to.equal(expected)
21+
})
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
'use strict'
2+
3+
const { once } = require('node:events')
4+
const assert = require('node:assert')
5+
const { exec } = require('child_process')
6+
7+
const { createSandbox, getCiVisAgentlessConfig } = require('../helpers')
8+
const { FakeCiVisIntake } = require('../ci-visibility-intake')
9+
const { NODE_MAJOR } = require('../../version')
10+
11+
// no playwright because it has no programmatic API
12+
// no cypress because it's not a proper dd-trace plugin
13+
const testFrameworks = [
14+
{
15+
testFramework: 'mocha',
16+
command: 'node ./ci-visibility/test-optimization-wrong-init/run-mocha.js',
17+
expectedOutput: '1 passing'
18+
},
19+
{
20+
testFramework: 'jest',
21+
command: 'node ./ci-visibility/test-optimization-wrong-init/run-jest.js',
22+
expectedOutput: 'PASS ci-visibility/test-optimization-wrong-init/sum-wrong-init-test.js'
23+
},
24+
{
25+
testFramework: 'vitest',
26+
command: 'node ./ci-visibility/test-optimization-wrong-init/run-vitest.mjs',
27+
expectedOutput: '1 passed',
28+
extraTestContext: {
29+
TEST_DIR: 'ci-visibility/test-optimization-wrong-init/vitest-sum-wrong-init*',
30+
NODE_OPTIONS: '--import dd-trace/register.js'
31+
}
32+
},
33+
{
34+
testFramework: 'cucumber',
35+
command: './node_modules/.bin/cucumber-js ci-visibility/test-optimization-wrong-init-cucumber/*.feature',
36+
expectedOutput: '1 passed',
37+
extraTestContext: {
38+
NODE_OPTIONS: '-r dd-trace/init'
39+
}
40+
}
41+
]
42+
43+
testFrameworks.forEach(({ testFramework, command, expectedOutput, extraTestContext }) => {
44+
describe(`test optimization wrong init for ${testFramework}`, () => {
45+
let sandbox, cwd, receiver, childProcess, processOutput
46+
47+
// cucumber does not support Node.js@18 anymore
48+
if (NODE_MAJOR <= 18 && testFramework === 'cucumber') return
49+
50+
before(async () => {
51+
const testFrameworks = ['jest', 'mocha', 'vitest']
52+
53+
// Remove once we drop support for Node.js@18
54+
if (NODE_MAJOR > 18) {
55+
testFrameworks.push('@cucumber/cucumber')
56+
}
57+
58+
sandbox = await createSandbox(testFrameworks, true)
59+
cwd = sandbox.folder
60+
})
61+
62+
after(async () => {
63+
await sandbox.remove()
64+
})
65+
66+
beforeEach(async function () {
67+
processOutput = ''
68+
receiver = await new FakeCiVisIntake().start()
69+
})
70+
71+
afterEach(async () => {
72+
childProcess.kill()
73+
await receiver.stop()
74+
})
75+
76+
it('does not initialize test optimization plugins if Test Optimization mode is not enabled', async () => {
77+
const eventsPromise = receiver
78+
.gatherPayloadsMaxTimeout(({ url }) => url === '/v0.4/traces', (tracesRequests) => {
79+
const spans = tracesRequests.flatMap(trace => trace.payload).flatMap(request => request)
80+
const includesTestOptimizationSpans = spans.some(span => span.name.includes(testFramework))
81+
if (spans.length === 0) {
82+
throw new Error('No spans were sent')
83+
}
84+
if (includesTestOptimizationSpans) {
85+
throw new Error('Test Optimization spans should not be sent')
86+
}
87+
}, 10000)
88+
89+
const envVars = getCiVisAgentlessConfig(receiver.port)
90+
91+
const {
92+
NODE_OPTIONS, // we don't want to initialize dd-trace in Test Optimization mode
93+
...restEnvVars
94+
} = envVars
95+
96+
childProcess = exec(command,
97+
{
98+
cwd,
99+
env: {
100+
...process.env,
101+
...restEnvVars,
102+
DD_TRACE_DISABLED_INSTRUMENTATIONS: 'child_process',
103+
DD_TRACE_DEBUG: '1',
104+
...extraTestContext
105+
},
106+
stdio: 'pipe'
107+
}
108+
)
109+
110+
childProcess.stderr.on('data', (chunk) => {
111+
processOutput += chunk.toString()
112+
})
113+
114+
childProcess.stdout.on('data', (chunk) => {
115+
processOutput += chunk.toString()
116+
})
117+
118+
await Promise.all([
119+
once(childProcess, 'exit'),
120+
eventsPromise
121+
])
122+
123+
assert.ok(
124+
processOutput.includes(
125+
`Plugin "${testFramework}" is not initialized because Test Optimization mode is not enabled.`
126+
)
127+
)
128+
assert.ok(processOutput.includes(expectedOutput))
129+
})
130+
})
131+
})
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
'use strict'
2+
3+
require('dd-trace').init({
4+
service: 'sum-service-tests',
5+
})
6+
7+
const { runCLI } = require('jest')
8+
9+
async function main () {
10+
const projectRoot = process.cwd()
11+
12+
await runCLI(
13+
{
14+
testMatch: ['**/sum-wrong-init-test.js'],
15+
},
16+
[projectRoot]
17+
)
18+
}
19+
20+
main()
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'use strict'
2+
3+
require('dd-trace').init({
4+
service: 'sum-service-tests',
5+
})
6+
7+
const Mocha = require('mocha')
8+
9+
async function main () {
10+
const mocha = new Mocha()
11+
12+
mocha.addFile(require.resolve('./sum-wrong-init-test.js'))
13+
14+
await new Promise((resolve, reject) => {
15+
mocha.run(failures => {
16+
if (failures > 0) {
17+
reject(new Error(`${failures} test(s) failed.`))
18+
} else {
19+
resolve()
20+
}
21+
})
22+
})
23+
}
24+
25+
main().catch(() => {
26+
process.exit(1)
27+
})
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import tracer from 'dd-trace'
2+
import { startVitest } from 'vitest/node'
3+
4+
// The tracer needs to be initialized both in the main process and in the worker process.
5+
// This is normally taken care of by using NODE_OPTIONS, but we can't set
6+
// flushInterval through an env var
7+
tracer.init({
8+
flushInterval: 0
9+
})
10+
11+
async function main () {
12+
return startVitest(
13+
'test',
14+
[],
15+
{ watch: false },
16+
{},
17+
{}
18+
)
19+
}
20+
21+
main().catch(() => {
22+
process.exit(1)
23+
})
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
'use strict'
2+
3+
const assert = require('node:assert')
4+
const tracer = require('dd-trace')
5+
6+
tracer.trace('sum.test', { resource: 'sum.test.js' }, () => {
7+
describe('sum', () => {
8+
it('should return the sum of two numbers', () => {
9+
assert.equal(1 + 2, 3)
10+
})
11+
})
12+
})
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import tracer from 'dd-trace'
2+
import { describe, it, expect } from 'vitest'
3+
4+
// The tracer needs to be initialized both in the main process and in the worker process.
5+
// This is normally taken care of by using NODE_OPTIONS, but we can't set
6+
// flushInterval through an env var
7+
tracer.init({
8+
flushInterval: 0
9+
})
10+
11+
tracer.trace('sum.test', { resource: 'sum.test.js' }, () => {
12+
describe('sum', () => {
13+
it('should return the sum of two numbers', async () => {
14+
expect(1 + 2).toBe(3)
15+
})
16+
})
17+
})

packages/datadog-instrumentations/src/cucumber.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,9 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
599599
// Handles EFD in both the main process and the worker process.
600600
function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = false, isWorker = false) {
601601
return async function () {
602+
if (!testSuiteFinishCh.hasSubscribers) {
603+
return runTestCaseFunction.apply(this, arguments)
604+
}
602605
const pickle = isNewerCucumberVersion
603606
? arguments[0].pickle
604607
: this.eventDataCollector.getPickle(arguments[0])
@@ -754,6 +757,9 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
754757

755758
function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion) {
756759
return function (worker, message) {
760+
if (!testSuiteFinishCh.hasSubscribers) {
761+
return parseWorkerMessageFunction.apply(this, arguments)
762+
}
757763
// If the message is an array, it's a dd-trace message, so we need to stop cucumber processing,
758764
// or cucumber will throw an error
759765
// TODO: identify the message better

packages/datadog-plugin-cucumber/test/index.spec.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ describe('Plugin', function () {
7070
.get('/')
7171
.reply(200, 'OK')
7272

73-
return agent.load(['cucumber', 'http']).then(() => {
73+
return agent.load(
74+
['cucumber', 'http'], { service: 'test' }, { isCiVisibility: true }).then(() => {
7475
Cucumber = proxyquire(`../../../versions/@cucumber/cucumber@${version}`, {}).get()
7576
})
7677
})

0 commit comments

Comments
 (0)