Skip to content

Commit 06156ff

Browse files
authored
Changes for AI Integration Toolkit Project (#7178)
Changes to make it simpler to write integrations. Main changes are a test abstraction layer for setting up integration tests, which handles tracer / agent / module initialization. The other change being the ability to use assertFirstTraceSpan with the object API, while asserting basic types for tags that change between runs (mainly for remote ip tags and error stack traces).
1 parent a44e8f9 commit 06156ff

4 files changed

Lines changed: 83 additions & 6 deletions

File tree

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,10 @@ packages/datadog-plugin-azure-functions/test/integration-test/fixtures/node_modu
135135
__azurite_db_queue__.json
136136
__azurite_db_queue_extent__.json
137137
__queuestorage__/AzuriteConfig
138+
139+
140+
# ignore apm-instrumentation-toolkit analysis results dirs
141+
.analysis/
142+
143+
# ignore claude settings
144+
.claude/

eslint.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export default [
4848
'**/versions', // This is effectively a node_modules tree.
4949
'**/acmeair-nodejs', // We don't own this.
5050
'**/vendor', // Generally, we didn't author this code.
51+
'**/.analysis', // Ignore apm-instrumentation-toolkit analysis results
5152
'integration-tests/code-origin/typescript.js', // Generated
5253
'integration-tests/debugger/target-app/source-map-support/bundle.js', // Generated
5354
'integration-tests/debugger/target-app/source-map-support/hello/world.js', // Generated

integration-tests/helpers/index.js

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ const { DEBUG } = process.env
2424
// This is set by the setShouldKill function
2525
let shouldKill
2626

27+
// Symbol constants for dynamic value matching in assertObjectContains
28+
const ANY_STRING = Symbol('test.ANY_STRING')
29+
const ANY_NUMBER = Symbol('test.ANY_NUMBER')
30+
const ANY_VALUE = Symbol('test.ANY_VALUE')
31+
2732
/**
2833
* @param {string} filename
2934
* @param {string} cwd
@@ -635,9 +640,8 @@ function setShouldKill (value) {
635640
})
636641
}
637642

638-
// @ts-expect-error assert.partialDeepStrictEqual does not exist on older Node.js versions
639-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
640-
const assertObjectContains = assert.partialDeepStrictEqual || function assertObjectContains (actual, expected, msg) {
643+
// Implementation with optional matcher support (ANY_STRING, ANY_NUMBER, ANY_VALUE)
644+
function assertObjectContainsImpl (actual, expected, msg, useMatchers) {
641645
if (expected === null || typeof expected !== 'object') {
642646
assert.strictEqual(actual, expected, msg)
643647
return
@@ -652,7 +656,7 @@ const assertObjectContains = assert.partialDeepStrictEqual || function assertObj
652656
const actualItem = actual[i]
653657
try {
654658
if (expectedItem !== null && typeof expectedItem === 'object') {
655-
assertObjectContains(actualItem, expectedItem, msg)
659+
assertObjectContainsImpl(actualItem, expectedItem, msg, useMatchers)
656660
} else {
657661
assert.strictEqual(actualItem, expectedItem, msg)
658662
}
@@ -670,15 +674,40 @@ const assertObjectContains = assert.partialDeepStrictEqual || function assertObj
670674

671675
for (const [key, val] of Object.entries(expected)) {
672676
assert.ok(Object.hasOwn(actual, key), msg)
673-
if (val !== null && typeof val === 'object') {
674-
assertObjectContains(actual[key], val, msg)
677+
if (useMatchers && val === ANY_STRING) {
678+
assert.strictEqual(typeof actual[key], 'string', `Expected ${key} to be a string but got ${typeof actual[key]}`)
679+
} else if (useMatchers && val === ANY_NUMBER) {
680+
assert.strictEqual(typeof actual[key], 'number', `Expected ${key} to be a number but got ${typeof actual[key]}`)
681+
} else if (useMatchers && val === ANY_VALUE) {
682+
assert.ok(actual[key] !== undefined, `Expected ${key} to be present but it was undefined`)
683+
} else if (val !== null && typeof val === 'object') {
684+
assertObjectContainsImpl(actual[key], val, msg, useMatchers)
675685
} else {
676686
assert.ok(actual, msg)
677687
assert.strictEqual(actual[key], expected[key], msg)
678688
}
679689
}
680690
}
681691

692+
// Main assertObjectContains: tries partialDeepStrictEqual or strict first, falls back to matchers
693+
const assertObjectContains = function assertObjectContains (actual, expected, msg) {
694+
// @ts-expect-error assert.partialDeepStrictEqual does not exist on older Node.js versions
695+
// eslint-disable-next-line n/no-unsupported-features/node-builtins
696+
const assertionFn = assert.partialDeepStrictEqual ||
697+
((actual, expected, msg) => assertObjectContainsImpl(actual, expected, msg, false))
698+
699+
try {
700+
assertionFn(actual, expected, msg)
701+
} catch (originalError) {
702+
// First attempt failed, retry with matcher support
703+
try {
704+
assertObjectContainsImpl(actual, expected, msg, true)
705+
} catch {
706+
throw originalError
707+
}
708+
}
709+
}
710+
682711
/**
683712
* @param {string} actual
684713
* @param {string} [msg]
@@ -688,6 +717,9 @@ function assertUUID (actual, msg = 'not a valid UUID') {
688717
}
689718

690719
module.exports = {
720+
ANY_NUMBER,
721+
ANY_STRING,
722+
ANY_VALUE,
691723
FakeAgent,
692724
hookFile,
693725
assertObjectContains,
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use strict'
2+
3+
const { describe, before, after } = require('mocha')
4+
const agent = require('../../../plugins/agent')
5+
const { withVersions } = require('../../mocha')
6+
7+
function createIntegrationTestSuite (pluginName, packageName, options, testCallback) {
8+
describe('Plugin', () => {
9+
describe(pluginName, () => {
10+
withVersions(pluginName, packageName, version => {
11+
const meta = { agent, tracer: null, mod: null }
12+
13+
describe('without configuration', () => {
14+
before(async () => {
15+
const plugins = [pluginName, ...(options.additionalPlugins || [])]
16+
const pluginConfigs = [options.pluginConfig || {}, ...(options.additionalPlugins || []).map(() => ({}))]
17+
await agent.load(plugins, pluginConfigs)
18+
})
19+
20+
after(async () => {
21+
await agent.close({ ritmReset: false })
22+
})
23+
24+
before(async () => {
25+
meta.tracer = require('../../../../../dd-trace').init()
26+
const mod = require(`../../../../../../versions/${packageName}@${version}`)
27+
meta.mod = options.subModule ? mod.get(options.subModule) : mod.get()
28+
})
29+
30+
testCallback(meta)
31+
})
32+
})
33+
})
34+
})
35+
}
36+
37+
module.exports = { createIntegrationTestSuite }

0 commit comments

Comments
 (0)