Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
aab4e8b
feat(llmobs): accept image_parts on messages (#9684)
joizddog Aug 5, 2026
0cd6b91
test(test-optimization): stabilize output file limit test (#9702)
BridgeAR Aug 5, 2026
90eafa0
fix(datastreams): start a new pathway for a message that carries no c…
BridgeAR Aug 5, 2026
fa4ec0c
fix(kafka): preserve application headers on native produce path (#9595)
BridgeAR Aug 5, 2026
48c1867
feat(http): configure server error statuses (#9638)
BridgeAR Aug 5, 2026
4b90b30
feat(mysql,mysql2): trace pool connection acquisition (#8920)
BridgeAR Aug 6, 2026
aa8adf7
chore(deps): bump mocha (#9710)
dependabot[bot] Aug 6, 2026
3ffe36e
fix(test-optimization): defer Vitest EFD faultiness detection (#9700)
juan-fernandez Aug 6, 2026
6b6a999
ci: update one-pipeline to 1.2.0 (#9718)
gh-worker-campaigns-3e9aa4[bot] Aug 6, 2026
ce247b2
refactor(test-optimization): move Jest test utilities (#9713)
juan-fernandez Aug 6, 2026
a935494
chore(deps): bump openai (#9725)
dependabot[bot] Aug 7, 2026
3f38bf1
chore(deps): bump the testing-and-build group across 1 directory with…
dependabot[bot] Aug 7, 2026
db49baa
chore(deps): bump the test-versions group across 1 directory with 6 u…
dependabot[bot] Aug 7, 2026
75301dc
feat(instrumentation): add awaited context callbacks to conditional b…
juan-fernandez Aug 7, 2026
267cdaf
feat(test-optimization): optimize WebdriverIO Jasmine tests (#9668)
juan-fernandez Aug 7, 2026
02361b6
fix(test-optimization): escape Vitest browser provided context (#9730)
juan-fernandez Aug 7, 2026
8302908
test(test-optimization): avoid duplicate WebdriverIO fixture copy (#9…
juan-fernandez Aug 7, 2026
379014a
feat(appsec): RFC-1103 normalized HTTP route tag for Express (#8857)
christophe-papazian Aug 7, 2026
fe54caa
test(aws-sdk): stabilize DynamoDB trace assertions (#9675)
BridgeAR Aug 7, 2026
700dc43
fix(openai-agents): preserve structural span ancestry (#9674)
crysmags Aug 7, 2026
38266e7
feat(llmobs): resolve and propagate agent attribution (#9175)
yahya-mouman Aug 7, 2026
9d90f8d
v6.10.0
sabrenner Aug 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@
/packages/dd-trace/test/git_metadata_tagger.spec.js @DataDog/ci-app-libraries
/packages/dd-trace/test/plugins/util/git.spec.js @DataDog/ci-app-libraries
/packages/dd-trace/test/plugins/util/ci-env/ @DataDog/ci-app-libraries
/packages/dd-trace/test/plugins/util/fixtures/jest/ @DataDog/ci-app-libraries
/packages/dd-trace/test/plugins/util/jest.spec.js @DataDog/ci-app-libraries
/packages/dd-trace/test/plugins/util/test.spec.js @DataDog/ci-app-libraries
/packages/dd-trace/test/plugins/util/test-environment.spec.js @DataDog/ci-app-libraries
/packages/dd-trace/src/encode/agentless-ci-visibility.js @DataDog/ci-app-libraries
Expand All @@ -124,6 +126,7 @@
/packages/dd-trace/src/plugins/util/ci.js @DataDog/ci-app-libraries
/packages/dd-trace/src/plugins/util/env.js @DataDog/ci-app-libraries
/packages/dd-trace/src/plugins/util/git-cache.js @DataDog/ci-app-libraries
/packages/dd-trace/src/plugins/util/jest.js @DataDog/ci-app-libraries
/packages/dd-trace/src/plugins/util/tags.js @DataDog/ci-app-libraries
/packages/dd-trace/src/plugins/util/test.js @DataDog/ci-app-libraries
/packages/dd-trace/src/plugins/util/user-provided-git.js @DataDog/ci-app-libraries
Expand Down Expand Up @@ -257,6 +260,8 @@
/packages/dd-trace/test/remote_config/ @DataDog/apm-sdk-capabilities-js
/packages/dd-trace/src/baggage.js @DataDog/apm-sdk-capabilities-js
/packages/dd-trace/test/baggage.spec.js @DataDog/apm-sdk-capabilities-js
/packages/dd-trace/src/carrier.js @DataDog/apm-sdk-capabilities-js
/packages/dd-trace/test/carrier.spec.js @DataDog/apm-sdk-capabilities-js
/packages/dd-trace/src/sampler.js @DataDog/apm-sdk-capabilities-js
/packages/dd-trace/test/sampler.spec.js @DataDog/apm-sdk-capabilities-js
/packages/dd-trace/src/priority_sampler.js @DataDog/apm-sdk-capabilities-js
Expand Down
29 changes: 29 additions & 0 deletions .github/workflows/instrumentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: ./.github/actions/instrumentations/test

instrumentation-confluentinc-kafka-javascript:
runs-on: ubuntu-latest
permissions:
id-token: write
env:
PLUGINS: confluentinc-kafka-javascript
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: ./.github/actions/instrumentations/test

instrumentation-connect:
runs-on: ubuntu-latest
permissions:
Expand Down Expand Up @@ -376,6 +386,25 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: ./.github/actions/instrumentations/test

instrumentation-mysql:
runs-on: ubuntu-latest
permissions:
id-token: write
services:
mysql:
image: mariadb@sha256:22edfe1c78349f8cae88bbb5c2873b7e6c515be767c91c691308ce57445806f3 # 10.4
env:
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
MYSQL_DATABASE: "db"
ports:
- 3306:3306
env:
PLUGINS: mysql
SERVICES: mysql
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: ./.github/actions/instrumentations/test

instrumentation-mysql2:
runs-on: ubuntu-latest
permissions:
Expand Down
13 changes: 0 additions & 13 deletions .github/workflows/test-optimization.yml
Original file line number Diff line number Diff line change
Expand Up @@ -299,19 +299,6 @@ jobs:
with:
flags: test-optimization-jest-${{ matrix.version }}-${{ matrix.jest-version }}

# Unit tests for `packages/datadog-plugin-jest`. The `integration-jest` matrix above only runs
# `integration-tests/jest/*.spec.js`, so this is the only CI invocation whose glob expands to
# cover `packages/datadog-plugin-jest/test/util.spec.js`.
jest:
runs-on: ubuntu-latest
permissions:
id-token: write
env:
PLUGINS: jest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: ./.github/actions/plugins/test

dependency-helpers:
runs-on: ubuntu-latest
steps:
Expand Down
2 changes: 1 addition & 1 deletion .gitlab/one-pipeline.locked.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# DO NOT EDIT THIS FILE MANUALLY
# This file is auto-generated by automation.
include:
- remote: https://gitlab-templates.ddbuild.io/libdatadog/include/versions/1.1.0/one-pipeline.yml
- remote: https://gitlab-templates.ddbuild.io/libdatadog/include/versions/1.2.0/one-pipeline.yml
122 changes: 112 additions & 10 deletions ci/vitest-no-worker-init-setup.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@ import { afterEach, beforeAll, beforeEach, inject } from 'vitest'
// Instrumentation-less setup for DD_EXPERIMENTAL_TEST_OPT_VITEST_NO_WORKER_INIT.
// It applies Test Optimization execution changes without initializing dd-trace and also supports Browser Mode.
const VITEST_NO_WORKER_INIT_ACTIVE_ENV = 'DD_TEST_OPT_VITEST_NO_WORKER_INIT_ACTIVE'
const SERIALIZED_CONTEXT_PREFIX = '\u0000dd-vitest-context:'
const providedContext = getProvidedContext()
const isNoWorkerInitActive = providedContext.isActive ?? getIsNoWorkerInitActive()
const attemptToFixTests = providedContext.attemptToFixTests || {}
const attemptToFixRetries = providedContext.attemptToFixRetries || 0
const disabledTests = providedContext.disabledTests || {}
const efdSuiteAdmissionBrowserCommand = providedContext.efdSuiteAdmissionBrowserCommand
const earlyFlakeDetectionRetryPolicy = providedContext.earlyFlakeDetectionRetryPolicy || {
durationRetryCounts: [],
schedulingRetryCount: 0,
}
const earlyFlakeDetectionRetries = earlyFlakeDetectionRetryPolicy.schedulingRetryCount
const isEfdSuiteAdmissionEnabled = providedContext.isEfdSuiteAdmissionEnabled === true
const isEarlyFlakeDetectionEnabled = providedContext.isEarlyFlakeDetectionEnabled === true
const knownTests = providedContext.knownTests || {}
const modifiedFiles = providedContext.modifiedFiles || {}
Expand All @@ -34,6 +37,7 @@ const earlyFlakeDetectionStartByTask = new WeakMap()
const nextAttemptIndexByTask = new WeakMap()
const retryAttemptIndexByTask = new WeakMap()
const usedRumTestExecutionIds = new Set()
let browserCommands
let now
let timeOrigin
// Use an unfaked monotonic clock in Node and Vitest's parent frame in Browser Mode.
Expand All @@ -48,18 +52,42 @@ if (typeof globalThis.process?.uptime === 'function') {
timeOrigin = Number.isFinite(clock?.timeOrigin) ? clock.timeOrigin : Date.now() - now()
}

/**
* Requests permission from the Vitest main process to schedule Browser Mode EFD retries for one suite.
*
* @param {string} testSuite
* @param {boolean} hasNewTest
* @returns {Promise<boolean>}
*/
async function requestBrowserEfdSuiteAdmission (testSuite, hasNewTest) {
try {
if (!browserCommands) {
const vitestBrowser = await import('@vitest/browser/context')
browserCommands = vitestBrowser.commands
}
return await browserCommands[efdSuiteAdmissionBrowserCommand](testSuite, hasNewTest) === true
} catch (error) {
// Browser Mode setup runs without dd-trace, so the tracer logger is unavailable.
globalThis.console?.error('Datadog Test Optimization could not request Vitest EFD suite admission.', error)
return false
}
}

if (isNoWorkerInitActive) {
// eslint-disable-next-line no-empty-pattern
beforeAll(function ({}, suite) {
beforeAll(async function ({}, suite) {
suite ||= arguments[0]
applyExecutionChanges(suite)
const efdSuiteCandidate = getEarlyFlakeDetectionSuiteCandidate(suite)
const isEfdSuiteAdmissionAllowed = !efdSuiteCandidate || !isEfdSuiteAdmissionEnabled ||
await requestBrowserEfdSuiteAdmission(efdSuiteCandidate.testSuite, efdSuiteCandidate.hasNewTest)
applyExecutionChanges(suite, isEfdSuiteAdmissionAllowed)
})

beforeEach(function ({ onTestFinished, task, skip }) {
const testSuite = getTestSuite(task)
const testName = getTestName(task)
const isAttemptToFixTest = attemptToFixTests[testSuite]?.[testName]
const isEarlyFlakeDetectionTestAttempt = isEarlyFlakeDetectionTest(testSuite, testName)
const isEarlyFlakeDetectionTestAttempt = isEarlyFlakeDetectionTask(task)
const isQuarantinedTest = quarantinedTests[testSuite]?.[testName] && !isAttemptToFixTest
const attemptIndex = getNextAttemptIndex(task)
const attemptStart = now()
Expand Down Expand Up @@ -108,12 +136,12 @@ if (isNoWorkerInitActive) {
})
}

function applyExecutionChanges (suite) {
function applyExecutionChanges (suite, isEfdSuiteAdmissionAllowed) {
const tasks = suite?.tasks
if (tasks) {
for (const task of tasks) {
if (task.type === 'suite') {
applyExecutionChanges(task)
applyExecutionChanges(task, isEfdSuiteAdmissionAllowed)
continue
}

Expand All @@ -125,7 +153,7 @@ function applyExecutionChanges (suite) {
task.meta.__ddTestOptAtfRetries = attemptToFixRetries
} else if (disabledTests[testSuite]?.[testName]) {
task.mode = 'skip'
} else if (isEarlyFlakeDetectionTest(testSuite, testName)) {
} else if (isEfdSuiteAdmissionAllowed && isEarlyFlakeDetectionTest(testSuite, testName)) {
task.retry = 0
task.repeats = earlyFlakeDetectionRetries
task.meta.__ddTestOptEfdRetries = earlyFlakeDetectionRetries
Expand Down Expand Up @@ -333,7 +361,7 @@ function recordTestOptimizationStatus (task, attemptIndex = task.result?.repeatC

if (attemptToFixTests[testSuite]?.[testName]) {
recordAttemptToFixStatus(task, attemptIndex, onlyIfNewErrors)
} else if (isEarlyFlakeDetectionTest(testSuite, testName)) {
} else if (isEarlyFlakeDetectionTask(task)) {
recordEarlyFlakeDetectionStatus(task, attemptIndex, onlyIfNewErrors)
} else if (task.repeats > 0) {
recordManualRepeatStatus(task, attemptIndex)
Expand Down Expand Up @@ -510,7 +538,7 @@ function getFinalAttemptIndex (task) {
if (attemptToFixTests[testSuite]?.[testName]) {
return getAttemptToFixRetryCount(task)
}
if (isEarlyFlakeDetectionTest(testSuite, testName)) {
if (isEarlyFlakeDetectionTask(task)) {
return getEarlyFlakeDetectionRetryCountForTask(task)
}

Expand Down Expand Up @@ -671,6 +699,64 @@ function isEarlyFlakeDetectionTest (testSuite, testName) {
return !testsForSuite.includes(testName)
}

/**
* Returns whether Datadog admitted this task for EFD retries.
*
* @param {object} task
* @returns {boolean}
*/
function isEarlyFlakeDetectionTask (task) {
return task.meta.__ddTestOptEfdRetries !== undefined
}

/**
* Returns suite admission data when a collected suite contains an EFD candidate.
*
* @param {object} suite
* @returns {{ hasNewTest: boolean, testSuite: string }|undefined}
*/
function getEarlyFlakeDetectionSuiteCandidate (suite) {
const tasks = suite?.tasks
if (!tasks) return

let candidate
for (const task of tasks) {
if (task.type === 'suite') {
const nestedCandidate = getEarlyFlakeDetectionSuiteCandidate(task)
if (nestedCandidate) {
candidate ||= nestedCandidate
candidate.hasNewTest ||= nestedCandidate.hasNewTest
}
continue
}

if (task.mode === 'skip' || task.mode === 'todo') continue

const testSuite = getTestSuite(task)
const testName = getTestName(task)
if (attemptToFixTests[testSuite]?.[testName] || disabledTests[testSuite]?.[testName]) continue
if (!isEarlyFlakeDetectionTest(testSuite, testName)) continue

candidate ||= {
hasNewTest: false,
testSuite,
}
candidate.hasNewTest ||= isNewTest(testSuite, testName)
}
return candidate
}

/**
* Returns whether a collected test is absent from the known-tests response.
*
* @param {string} testSuite
* @param {string} testName
* @returns {boolean}
*/
function isNewTest (testSuite, testName) {
return !(knownTests[testSuite] || []).includes(testName)
}

function isModifiedTest (testSuite) {
return modifiedFiles[testSuite]?.length > 0
}
Expand Down Expand Up @@ -712,12 +798,28 @@ function getIsNoWorkerInitActive () {

function getProvidedContext () {
try {
return inject('_ddVitestWorkerSetup') || {}
return parseProvidedContextValue(inject('_ddVitestWorkerSetup'))
} catch {
try {
return globalThis.__vitest_worker__.providedContext._ddVitestWorkerSetup || {}
return parseProvidedContextValue(globalThis.__vitest_worker__.providedContext._ddVitestWorkerSetup)
} catch {
return {}
}
}
}

/**
* Restore context serialized to keep Vitest's inline browser bootstrap script valid.
*
* @param {object|string|undefined} value
* @returns {object}
*/
function parseProvidedContextValue (value) {
if (typeof value !== 'string' || !value.startsWith(SERIALIZED_CONTEXT_PREFIX)) return value || {}

try {
return JSON.parse(value.slice(SERIALIZED_CONTEXT_PREFIX.length))
} catch {
return {}
}
}
25 changes: 25 additions & 0 deletions docs/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,31 @@ llmobs.trace({ name: 'name', kind: 'llm' }, (span, cb) => {
cb(new Error('boom'))
})

// messages carrying image parts, inline and by attachment key
llmobs.annotate({
inputData: [{
content: 'what is in this image',
imageParts: [{ mimeType: 'image/png', content: 'iVBORw0KGgo=' }]
}],
outputData: [{
content: 'a pixel',
imageParts: [{ mimeType: 'image/jpeg', attachmentKey: 'key-123' }]
}]
})

// an image part carries exactly one of content or attachmentKey
type ImagePart = import('..').llmobs.ImagePart
const inlineImagePart: ImagePart = { mimeType: 'image/png', content: 'iVBORw0KGgo=' }
const keyedImagePart: ImagePart = { mimeType: 'image/jpeg', attachmentKey: 'key-123' }
// @ts-expect-error An image part must carry either content or attachmentKey.
const emptyImagePart: ImagePart = { mimeType: 'image/png' }
// @ts-expect-error An image part must not carry both content and attachmentKey.
const overspecifiedImagePart: ImagePart = {
mimeType: 'image/png',
content: 'iVBORw0KGgo=',
attachmentKey: 'key-123'
}

// wrap a function
llmobs.wrap({ kind: 'llm' }, function myLLM() { })()
llmobs.wrap({ kind: 'llm', name: 'myLLM', modelName: 'myModel', modelProvider: 'myProvider' }, function myFunction() { })()
Expand Down
26 changes: 26 additions & 0 deletions eslint-rules/carrier-fields-policy.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export const carrierFieldsFilePatterns = ['packages/*/src/**/*.{js,mjs,cjs}']

const strictCarrierFieldsFilePatterns = [
'packages/dd-trace/src/datastreams/pathway.js',
'packages/dd-trace/src/opentracing/propagation/text_map.js',
]

export const carrierFieldsConfig = [
{
name: 'dd-trace/propagation/managed-header-fields',
files: carrierFieldsFilePatterns,
ignores: ['packages/dd-trace/src/carrier.js'],
rules: {
'eslint-rules/eslint-carrier-fields': 'error',
},
},
{
name: 'dd-trace/propagation/carrier-fields',
files: strictCarrierFieldsFilePatterns,
rules: {
'eslint-rules/eslint-carrier-fields': ['error', {
strictCarrierIdentifiers: true,
}],
},
},
]
Loading
Loading