diff --git a/packages/datadog-plugin-mongodb-core/test/core.spec.js b/packages/datadog-plugin-mongodb-core/test/core.spec.js index 8fcf8e93855..39975e5e082 100644 --- a/packages/datadog-plugin-mongodb-core/test/core.spec.js +++ b/packages/datadog-plugin-mongodb-core/test/core.spec.js @@ -1,7 +1,7 @@ 'use strict' const assert = require('node:assert/strict') -const { inspect } = require('node:util') +const { inspect, promisify } = require('node:util') const { after, afterEach, before, beforeEach, describe, it } = require('mocha') const ddpv = require('mocha/package.json').version @@ -14,20 +14,22 @@ const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../../dd-trace/src/c const MongodbCorePlugin = require('../../datadog-plugin-mongodb-core/src/query') const { expectedSchema, rawExpectedSchema } = require('./naming') +const traceTimeoutMs = 2_000 + const withTopologies = fn => { - withVersions('mongodb-core', ['mongodb-core', 'mongodb'], '<4', (version, moduleName) => { + withVersions('mongodb-core', ['mongodb-core', 'mongodb'], '<4', (version, moduleName, resolvedVersion) => { describe('using the server topology', () => { fn(() => { const { CoreServer, Server } = require(`../../../versions/${moduleName}@${version}`).get() return CoreServer || Server - }) + }, resolvedVersion) }) // TODO: use semver.subset when we can update semver if (moduleName === 'mongodb-core' && !semver.intersects(version, '<3.2')) { describe('using the unified topology', () => { - fn(() => require(`../../../versions/${moduleName}@${version}`).get().Topology) + fn(() => require(`../../../versions/${moduleName}@${version}`).get().Topology, resolvedVersion) }) } }) @@ -41,7 +43,14 @@ describe('Plugin', () => { let injectCommentSpy describe('mongodb-core (core)', () => { - withTopologies(getServer => { + withTopologies((getServer, resolvedVersion) => { + /** + * @param {Promise} promise + */ + const expectCommandCompletion = promise => semver.satisfies(resolvedVersion, '<2.1') + ? promise + : assert.rejects(promise) + const next = (cursor, cb = () => {}) => { return cursor._next ? cursor._next(cb) @@ -89,63 +98,67 @@ describe('Plugin', () => { }) describe('server', () => { - it('should do automatic instrumentation', done => { - agent - .assertFirstTraceSpan({ - name: expectedSchema.outbound.opName, - service: expectedSchema.outbound.serviceName, - resource: `insert test.${collection}`, - type: 'mongodb', - meta: { - 'span.kind': 'client', - 'db.name': `test.${collection}`, - 'out.host': '127.0.0.1', - component: 'mongodb', - '_dd.integration': 'mongodb', - }, - }) - .then(done) - .catch(done) + it('should do automatic instrumentation', async () => { + const tracePromise = agent.assertFirstTraceSpan({ + name: expectedSchema.outbound.opName, + service: expectedSchema.outbound.serviceName, + resource: `insert test.${collection}`, + type: 'mongodb', + meta: { + 'span.kind': 'client', + 'db.name': `test.${collection}`, + 'out.host': '127.0.0.1', + component: 'mongodb', + '_dd.integration': 'mongodb', + }, + }, { timeoutMs: traceTimeoutMs }) - server.insert(`test.${collection}`, [{ a: 1 }], {}, () => {}) + await Promise.all([ + tracePromise, + promisify(server.insert.bind(server))(`test.${collection}`, [{ a: 1 }], {}), + ]) }) - it('should use the correct resource name for arbitrary commands', done => { - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - const resource = `planCacheListPlans test.${collection}` - - assert.strictEqual(span.resource, resource) - }) - .then(done) - .catch(done) - - server.command(`test.${collection}`, { - planCacheListPlans: `test.${collection}`, - query: {}, - }, () => {}) + it('should use the correct resource name for arbitrary commands', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + const resource = `planCacheListPlans test.${collection}` + + assert.strictEqual(span.resource, resource) + }, { timeoutMs: traceTimeoutMs }) + + await Promise.all([ + tracePromise, + expectCommandCompletion( + promisify(server.command.bind(server))(`test.${collection}`, { + planCacheListPlans: `test.${collection}`, + query: {}, + }) + ), + ]) }) - it('should sanitize buffers as values and not as objects', done => { - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - const resource = `find test.${collection}` - const query = '{"_id":"?"}' - - assert.strictEqual(span.resource, resource) - assert.strictEqual(span.meta['mongodb.query'], query) - }) - .then(done) - .catch(done) - - server.command(`test.${collection}`, { - find: `test.${collection}`, - query: { - _id: Buffer.from('1234'), - }, - }, () => {}) + it('should sanitize buffers as values and not as objects', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + const resource = `find test.${collection}` + const query = '{"_id":"?"}' + + assert.strictEqual(span.resource, resource) + assert.strictEqual(span.meta['mongodb.query'], query) + }, { timeoutMs: traceTimeoutMs }) + + await Promise.all([ + tracePromise, + expectCommandCompletion( + promisify(server.command.bind(server))(`test.${collection}`, { + find: `test.${collection}`, + query: { + _id: Buffer.from('1234'), + }, + }) + ), + ]) }) it('should serialize BigInt without erroring', done => { @@ -183,50 +196,54 @@ describe('Plugin', () => { .catch(done) }) - it('should stringify BSON objects', done => { + it('should stringify BSON objects', async () => { const BSON = require('../../../versions/bson@4.0.0').get() const id = '123456781234567812345678' - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - const resource = `find test.${collection}` - const query = `{"_id":"${id}"}` - - assert.strictEqual(span.resource, resource) - assert.strictEqual(span.meta['mongodb.query'], query) - }) - .then(done) - .catch(done) - - server.command(`test.${collection}`, { - find: `test.${collection}`, - query: { - _id: new BSON.ObjectID(id), - }, - }, () => {}) + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + const resource = `find test.${collection}` + const query = `{"_id":"${id}"}` + + assert.strictEqual(span.resource, resource) + assert.strictEqual(span.meta['mongodb.query'], query) + }, { timeoutMs: traceTimeoutMs }) + + await Promise.all([ + tracePromise, + expectCommandCompletion( + promisify(server.command.bind(server))(`test.${collection}`, { + find: `test.${collection}`, + query: { + _id: new BSON.ObjectID(id), + }, + }) + ), + ]) }) - it('should skip functions when sanitizing', done => { - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - const resource = `find test.${collection}` - const query = '{"_id":"1234"}' - - assert.strictEqual(span.resource, resource) - assert.strictEqual(span.meta['mongodb.query'], query) - }) - .then(done) - .catch(done) - - server.command(`test.${collection}`, { - find: `test.${collection}`, - query: { - _id: '1234', - foo: () => {}, - }, - }, () => {}) + it('should skip functions when sanitizing', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + const resource = `find test.${collection}` + const query = '{"_id":"1234"}' + + assert.strictEqual(span.resource, resource) + assert.strictEqual(span.meta['mongodb.query'], query) + }, { timeoutMs: traceTimeoutMs }) + + await Promise.all([ + tracePromise, + expectCommandCompletion( + promisify(server.command.bind(server))(`test.${collection}`, { + find: `test.${collection}`, + query: { + _id: '1234', + foo: () => {}, + }, + }) + ), + ]) }) it('should run the callback in the parent context', done => { @@ -267,49 +284,48 @@ describe('Plugin', () => { }) describe('cursor', () => { - it('should do automatic instrumentation', done => { - let cursor - - Promise.all([ + it('should do automatic instrumentation', async () => { + const tracePromise = Promise.all([ agent .assertSomeTraces(traces => { assert.strictEqual(traces[0][0].resource, `find test.${collection}`) - }), + }, { timeoutMs: traceTimeoutMs }), agent .assertSomeTraces(traces => { assert.strictEqual(traces[0][0].resource, `getMore test.${collection}`) - }), + }, { timeoutMs: traceTimeoutMs }), agent .assertSomeTraces(traces => { assert.strictEqual(traces[0][0].resource, `killCursors test.${collection}`) - }), + }, { timeoutMs: traceTimeoutMs }), ]) - .then(() => done()) - .catch(done) - server.insert(`test.${collection}`, [{ a: 1 }, { a: 2 }, { a: 3 }], {}, () => { - cursor = server.cursor(`test.${collection}`, { + const operationPromise = (async () => { + await promisify(server.insert.bind(server))(`test.${collection}`, [{ a: 1 }, { a: 2 }, { a: 3 }], {}) + + const cursor = server.cursor(`test.${collection}`, { find: `test.${collection}`, query: {}, batchSize: 1, }, { batchSize: 1 }) - next(cursor, () => next(cursor, () => cursor.kill(() => {}))) - }) + await promisify(next)(cursor) + await promisify(next)(cursor) + await promisify(cursor.kill.bind(cursor))() + })() + + await Promise.all([tracePromise, operationPromise]) }) - it('should sanitize the query as the resource', done => { - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - const resource = `find test.${collection}` - const query = '{"foo":1,"bar":{"baz":[1,2,3]}}' + it('should sanitize the query as the resource', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + const resource = `find test.${collection}` + const query = '{"foo":1,"bar":{"baz":[1,2,3]}}' - assert.strictEqual(span.resource, resource) - assert.strictEqual(span.meta['mongodb.query'], query) - }) - .then(done) - .catch(done) + assert.strictEqual(span.resource, resource) + assert.strictEqual(span.meta['mongodb.query'], query) + }, { timeoutMs: traceTimeoutMs }) const cursor = server.cursor(`test.${collection}`, { find: `test.${collection}`, @@ -321,7 +337,10 @@ describe('Plugin', () => { }, }) - next(cursor) + await Promise.all([ + tracePromise, + promisify(next)(cursor), + ]) }) it('should run the callback in the parent context', done => { @@ -390,16 +409,16 @@ describe('Plugin', () => { server.connect() }) - it('should be configured with the correct values', done => { - agent - .assertSomeTraces(traces => { - assert.strictEqual(traces[0][0].name, expectedSchema.outbound.opName) - assert.strictEqual(traces[0][0].service, 'custom') - }) - .then(done) - .catch(done) + it('should be configured with the correct values', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + assert.strictEqual(traces[0][0].name, expectedSchema.outbound.opName) + assert.strictEqual(traces[0][0].service, 'custom') + }, { timeoutMs: traceTimeoutMs }) - server.insert(`test.${collection}`, [{ a: 1 }], () => {}) + await Promise.all([ + tracePromise, + promisify(server.insert.bind(server))(`test.${collection}`, [{ a: 1 }]), + ]) }) withNamingSchema( @@ -447,17 +466,17 @@ describe('Plugin', () => { injectCommentSpy?.restore() }) - it('DBM propagation should not inject comment', done => { - agent - .assertSomeTraces(traces => { - assert.strictEqual(injectCommentSpy.called, true) - assert.strictEqual(injectCommentSpy.getCall(0).args[1], undefined) - assert.strictEqual(injectCommentSpy.getCall(0).returnValue, undefined) - }) - .then(done) - .catch(done) + it('DBM propagation should not inject comment', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + assert.strictEqual(injectCommentSpy.called, true) + assert.strictEqual(injectCommentSpy.getCall(0).args[1], undefined) + assert.strictEqual(injectCommentSpy.getCall(0).returnValue, undefined) + }, { timeoutMs: traceTimeoutMs }) - server.insert(`test.${collection}`, [{ a: 1 }], () => {}) + await Promise.all([ + tracePromise, + promisify(server.insert.bind(server))(`test.${collection}`, [{ a: 1 }]), + ]) }) }) @@ -491,36 +510,38 @@ describe('Plugin', () => { injectCommentSpy?.restore() }) - it('DBM propagation should not inject comment', done => { - agent - .assertSomeTraces(traces => { - assert.strictEqual(injectCommentSpy.called, true) - const comment = injectCommentSpy.getCall(0).returnValue - assert.strictEqual(comment, undefined) - }) - .then(done) - .catch(done) + it('DBM propagation should not inject comment', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + assert.strictEqual(injectCommentSpy.called, true) + const comment = injectCommentSpy.getCall(0).returnValue + assert.strictEqual(comment, undefined) + }, { timeoutMs: traceTimeoutMs }) - server.insert(`test.${collection}`, [{ a: 1 }], () => {}) + await Promise.all([ + tracePromise, + promisify(server.insert.bind(server))(`test.${collection}`, [{ a: 1 }]), + ]) }) - it('DBM propagation should not alter existing comment', done => { - agent - .assertSomeTraces(traces => { - assert.strictEqual(injectCommentSpy.called, true) - const comment = injectCommentSpy.getCall(0).returnValue - assert.strictEqual(comment, 'test comment') - }) - .then(done) - .catch(done) + it('DBM propagation should not alter existing comment', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + assert.strictEqual(injectCommentSpy.called, true) + const comment = injectCommentSpy.getCall(0).returnValue + assert.strictEqual(comment, 'test comment') + }, { timeoutMs: traceTimeoutMs }) - server.command(`test.${collection}`, { - find: `test.${collection}`, - query: { - _id: Buffer.from('1234'), - }, - comment: 'test comment', - }, () => {}) + await Promise.all([ + tracePromise, + expectCommandCompletion( + promisify(server.command.bind(server))(`test.${collection}`, { + find: `test.${collection}`, + query: { + _id: Buffer.from('1234'), + }, + comment: 'test comment', + }) + ), + ]) }) }) @@ -560,21 +581,21 @@ describe('Plugin', () => { injectCommentSpy?.restore() }) - it('DBM propagation should inject full mode comment with traceparent', done => { - agent - .assertFirstTraceSpan(span => { - const traceId = span.meta['_dd.p.tid'] + span.trace_id.toString(16).padStart(16, '0') - const spanId = span.span_id.toString(16).padStart(16, '0') + it('DBM propagation should inject full mode comment with traceparent', async () => { + const tracePromise = agent.assertFirstTraceSpan(span => { + const traceId = span.meta['_dd.p.tid'] + span.trace_id.toString(16).padStart(16, '0') + const spanId = span.span_id.toString(16).padStart(16, '0') - assert.strictEqual(injectCommentSpy.called, true) - const comment = injectCommentSpy.getCall(0).returnValue - assert.ok(comment.includes(`traceparent='00-${traceId}-${spanId}-01'`), `Got: ${inspect(comment)}`) - assert.strictEqual(span.meta['_dd.dbm_trace_injected'], 'true') - }) - .then(done) - .catch(done) + assert.strictEqual(injectCommentSpy.called, true) + const comment = injectCommentSpy.getCall(0).returnValue + assert.ok(comment.includes(`traceparent='00-${traceId}-${spanId}-01'`), `Got: ${inspect(comment)}`) + assert.strictEqual(span.meta['_dd.dbm_trace_injected'], 'true') + }, { timeoutMs: traceTimeoutMs }) - server.insert(`test.${collection}`, [{ a: 1 }], () => {}) + await Promise.all([ + tracePromise, + promisify(server.insert.bind(server))(`test.${collection}`, [{ a: 1 }]), + ]) }) }) @@ -608,87 +629,91 @@ describe('Plugin', () => { injectCommentSpy?.restore() }) - it('DBM propagation should inject service mode as comment', done => { - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - - assert.strictEqual(injectCommentSpy.called, true) - const comment = injectCommentSpy.getCall(0).returnValue - assert.strictEqual(comment, - `dddb='${encodeURIComponent(span.meta['db.name'])}',` + - 'dddbs=\'test-mongodb\',' + - 'dde=\'tester\',' + - `ddh='${encodeURIComponent(span.meta['out.host'])}',` + - `ddps='${encodeURIComponent(span.meta.service)}',` + - `ddpv='${ddpv}',` + - `ddprs='${encodeURIComponent(span.meta['peer.service'])}'` - ) - }) - .then(done) - .catch(done) - - server.insert(`test.${collection}`, [{ a: 1 }], () => {}) - }) - - it('DBM propagation should inject service mode after eixsting str comment', done => { - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - - assert.strictEqual(injectCommentSpy.called, true) - const comment = injectCommentSpy.getCall(0).returnValue - assert.strictEqual(comment, - 'test comment,' + - `dddb='${encodeURIComponent(span.meta['db.name'])}',` + - 'dddbs=\'test-mongodb\',' + - 'dde=\'tester\',' + - `ddh='${encodeURIComponent(span.meta['out.host'])}',` + - `ddps='${encodeURIComponent(span.meta.service)}',` + - `ddpv='${ddpv}',` + - `ddprs='${encodeURIComponent(span.meta['peer.service'])}'` - ) - }) - .then(done) - .catch(done) - - server.command(`test.${collection}`, { - find: `test.${collection}`, - query: { - _id: Buffer.from('1234'), - }, - comment: 'test comment', - }, () => {}) - }) - - it('DBM propagation should inject service mode after existing array comment', done => { - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - - assert.strictEqual(injectCommentSpy.called, true) - const comment = injectCommentSpy.getCall(0).returnValue - assert.deepStrictEqual(comment, [ - 'test comment', - `dddb='${encodeURIComponent(span.meta['db.name'])}',` + - 'dddbs=\'test-mongodb\',' + - 'dde=\'tester\',' + - `ddh='${encodeURIComponent(span.meta['out.host'])}',` + - `ddps='${encodeURIComponent(span.meta.service)}',` + - `ddpv='${ddpv}',` + - `ddprs='${encodeURIComponent(span.meta['peer.service'])}'`, - ]) - }) - .then(done) - .catch(done) + it('DBM propagation should inject service mode as comment', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + + assert.strictEqual(injectCommentSpy.called, true) + const comment = injectCommentSpy.getCall(0).returnValue + assert.strictEqual(comment, + `dddb='${encodeURIComponent(span.meta['db.name'])}',` + + 'dddbs=\'test-mongodb\',' + + 'dde=\'tester\',' + + `ddh='${encodeURIComponent(span.meta['out.host'])}',` + + `ddps='${encodeURIComponent(span.meta.service)}',` + + `ddpv='${ddpv}',` + + `ddprs='${encodeURIComponent(span.meta['peer.service'])}'` + ) + }, { timeoutMs: traceTimeoutMs }) + + await Promise.all([ + tracePromise, + promisify(server.insert.bind(server))(`test.${collection}`, [{ a: 1 }]), + ]) + }) + + it('DBM propagation should inject service mode after eixsting str comment', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + + assert.strictEqual(injectCommentSpy.called, true) + const comment = injectCommentSpy.getCall(0).returnValue + assert.strictEqual(comment, + 'test comment,' + + `dddb='${encodeURIComponent(span.meta['db.name'])}',` + + 'dddbs=\'test-mongodb\',' + + 'dde=\'tester\',' + + `ddh='${encodeURIComponent(span.meta['out.host'])}',` + + `ddps='${encodeURIComponent(span.meta.service)}',` + + `ddpv='${ddpv}',` + + `ddprs='${encodeURIComponent(span.meta['peer.service'])}'` + ) + }, { timeoutMs: traceTimeoutMs }) + + await Promise.all([ + tracePromise, + expectCommandCompletion( + promisify(server.command.bind(server))(`test.${collection}`, { + find: `test.${collection}`, + query: { + _id: Buffer.from('1234'), + }, + comment: 'test comment', + }) + ), + ]) + }) + + it('DBM propagation should inject service mode after existing array comment', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + + assert.strictEqual(injectCommentSpy.called, true) + const comment = injectCommentSpy.getCall(0).returnValue + assert.deepStrictEqual(comment, [ + 'test comment', + `dddb='${encodeURIComponent(span.meta['db.name'])}',` + + 'dddbs=\'test-mongodb\',' + + 'dde=\'tester\',' + + `ddh='${encodeURIComponent(span.meta['out.host'])}',` + + `ddps='${encodeURIComponent(span.meta.service)}',` + + `ddpv='${ddpv}',` + + `ddprs='${encodeURIComponent(span.meta['peer.service'])}'`, + ]) + }, { timeoutMs: traceTimeoutMs }) - server.command(`test.${collection}`, { - find: `test.${collection}`, - query: { - _id: Buffer.from('1234'), - }, - comment: ['test comment'], - }, () => {}) + await Promise.all([ + tracePromise, + expectCommandCompletion( + promisify(server.command.bind(server))(`test.${collection}`, { + find: `test.${collection}`, + query: { + _id: Buffer.from('1234'), + }, + comment: ['test comment'], + }) + ), + ]) }) }) @@ -722,29 +747,29 @@ describe('Plugin', () => { injectCommentSpy?.restore() }) - it('DBM propagation should inject full mode with traceparent as comment', done => { - agent - .assertFirstTraceSpan(span => { - const traceId = span.meta['_dd.p.tid'] + span.trace_id.toString(16).padStart(16, '0') - const spanId = span.span_id.toString(16).padStart(16, '0') - - assert.strictEqual(injectCommentSpy.called, true) - const comment = injectCommentSpy.getCall(0).returnValue - assert.strictEqual(comment, - `dddb='${encodeURIComponent(span.meta['db.name'])}',` + - 'dddbs=\'test-mongodb\',' + - 'dde=\'tester\',' + - `ddh='${encodeURIComponent(span.meta['out.host'])}',` + - `ddps='${encodeURIComponent(span.meta.service)}',` + - `ddpv='${ddpv}',` + - `ddprs='${encodeURIComponent(span.meta['peer.service'])}',` + - `traceparent='00-${traceId}-${spanId}-01'` - ) - }) - .then(done) - .catch(done) - - server.insert(`test.${collection}`, [{ a: 1 }], () => {}) + it('DBM propagation should inject full mode with traceparent as comment', async () => { + const tracePromise = agent.assertFirstTraceSpan(span => { + const traceId = span.meta['_dd.p.tid'] + span.trace_id.toString(16).padStart(16, '0') + const spanId = span.span_id.toString(16).padStart(16, '0') + + assert.strictEqual(injectCommentSpy.called, true) + const comment = injectCommentSpy.getCall(0).returnValue + assert.strictEqual(comment, + `dddb='${encodeURIComponent(span.meta['db.name'])}',` + + 'dddbs=\'test-mongodb\',' + + 'dde=\'tester\',' + + `ddh='${encodeURIComponent(span.meta['out.host'])}',` + + `ddps='${encodeURIComponent(span.meta.service)}',` + + `ddpv='${ddpv}',` + + `ddprs='${encodeURIComponent(span.meta['peer.service'])}',` + + `traceparent='00-${traceId}-${spanId}-01'` + ) + }, { timeoutMs: traceTimeoutMs }) + + await Promise.all([ + tracePromise, + promisify(server.insert.bind(server))(`test.${collection}`, [{ a: 1 }]), + ]) }) }) @@ -780,24 +805,24 @@ describe('Plugin', () => { it( 'DBM propagation should inject full mode with traceparent as comment and the rejected sampling decision', - done => { - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - const traceId = span.meta['_dd.p.tid'] + span.trace_id.toString(16).padStart(16, '0') - const spanId = span.span_id.toString(16).padStart(16, '0') - - assert.strictEqual(injectCommentSpy.called, true) - const comment = injectCommentSpy.getCall(0).returnValue - assert.match( - comment, - new RegExp(String.raw`traceparent='00-${traceId}-${spanId}-00'`) - ) - }) - .then(done) - .catch(done) + async () => { + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + const traceId = span.meta['_dd.p.tid'] + span.trace_id.toString(16).padStart(16, '0') + const spanId = span.span_id.toString(16).padStart(16, '0') - server.insert(`test.${collection}`, [{ a: 1 }], () => {}) + assert.strictEqual(injectCommentSpy.called, true) + const comment = injectCommentSpy.getCall(0).returnValue + assert.match( + comment, + new RegExp(String.raw`traceparent='00-${traceId}-${spanId}-00'`) + ) + }, { timeoutMs: traceTimeoutMs }) + + await Promise.all([ + tracePromise, + promisify(server.insert.bind(server))(`test.${collection}`, [{ a: 1 }]), + ]) }) }) }) diff --git a/packages/datadog-plugin-mongodb-core/test/mongodb.spec.js b/packages/datadog-plugin-mongodb-core/test/mongodb.spec.js index c489d6d63cc..dbed63d2e5b 100644 --- a/packages/datadog-plugin-mongodb-core/test/mongodb.spec.js +++ b/packages/datadog-plugin-mongodb-core/test/mongodb.spec.js @@ -17,6 +17,10 @@ const traceTimeoutMs = 2_000 const withTopologies = fn => { withVersions('mongodb-core', 'mongodb', '>=2', (version, moduleName, resolvedVersion) => { + const getBSON = () => semver.satisfies(resolvedVersion, '>=5') + ? require(`../../../versions/${moduleName}@${version}`).get() + : require('../../../versions/bson@4.0.0').get() + describe('using the default topology', () => { fn(async () => { // Different warnings for different versions of mongodb-core @@ -42,7 +46,7 @@ const withTopologies = fn => { await client.connect() return client - }, version) + }, version, getBSON) }) // unified topology is now the only topology and thus the default since 4.x @@ -56,7 +60,7 @@ const withTopologies = fn => { await client.connect() return client - }) + }, version, getBSON) }) } }) @@ -74,14 +78,14 @@ describe('Plugin', () => { let usesDelete describe('mongodb-core', () => { - withTopologies((createClient, version) => { + withTopologies((createClient, version, getBSON) => { beforeEach(() => { id = require('../../dd-trace/src/id') tracer = require('../../dd-trace') usesDelete = version ? semver.intersects(version, '>=4') : false collectionName = id().toString() - BSON = require('../../../versions/bson@4.0.0').get() + BSON = getBSON() }) afterEach(() => { @@ -391,134 +395,139 @@ describe('Plugin', () => { }, { spanResourceMatch: usesDelete ? /^delete test\./ : /^remove test\./ }) }) - it('should use the correct resource name for arbitrary commands', done => { - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - const resource = 'planCacheListPlans test.$cmd' - const query = '{}' + it('should use the correct resource name for arbitrary commands', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + const resource = 'planCacheListPlans test.$cmd' + const query = '{}' - assert.strictEqual(span.resource, resource) - assert.strictEqual(span.meta['mongodb.query'], query) - }) - .then(done) - .catch(done) + assert.strictEqual(span.resource, resource) + assert.strictEqual(span.meta['mongodb.query'], query) + }, { timeoutMs: traceTimeoutMs }) - db.command({ - planCacheListPlans: `test.${collectionName}`, - query: {}, - }, () => {}) + const operationPromise = new Promise((resolve, reject) => { + const promise = db.command({ + planCacheListPlans: `test.${collectionName}`, + query: {}, + }, error => error ? reject(error) : resolve()) + promise?.then(resolve, reject) + }) + + await Promise.all([ + tracePromise, + assert.rejects(operationPromise), + ]) }) - it('should sanitize buffers as values and not as objects', done => { - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - const resource = `find test.${collectionName}` - const query = '{"_id":"?"}' + it('should sanitize buffers as values and not as objects', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + const resource = `find test.${collectionName}` + const query = '{"_id":"?"}' - assert.strictEqual(span.resource, resource) - assert.strictEqual(span.meta['mongodb.query'], query) - }) - .then(done) - .catch(done) + assert.strictEqual(span.resource, resource) + assert.strictEqual(span.meta['mongodb.query'], query) + }, { timeoutMs: traceTimeoutMs }) - collection.find({ - _id: Buffer.from('1234'), - }).toArray() + await Promise.all([ + tracePromise, + collection.find({ + _id: Buffer.from('1234'), + }).toArray(), + ]) }) - it('should sanitize BSON binary', done => { - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - const resource = `find test.${collectionName}` - const query = '{"_bin":"?"}' + it('should sanitize BSON binary', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + const resource = `find test.${collectionName}` + const query = '{"_bin":"?"}' - assert.strictEqual(span.resource, resource) - assert.strictEqual(span.meta['mongodb.query'], query) - }) - .then(done) - .catch(done) + assert.strictEqual(span.resource, resource) + assert.strictEqual(span.meta['mongodb.query'], query) + }, { timeoutMs: traceTimeoutMs }) - collection.find({ - _bin: new BSON.Binary(), - }).toArray() + await Promise.all([ + tracePromise, + collection.find({ + _bin: new BSON.Binary(), + }).toArray(), + ]) }) - it('should stringify BSON primitives', done => { + it('should stringify BSON primitives', async () => { const id = '123456781234567812345678' - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - const resource = `find test.${collectionName}` - const query = `{"_id":"${id}"}` + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + const resource = `find test.${collectionName}` + const query = `{"_id":"${id}"}` - assert.strictEqual(span.resource, resource) - assert.strictEqual(span.meta['mongodb.query'], query) - }) - .then(done) - .catch(done) + assert.strictEqual(span.resource, resource) + assert.strictEqual(span.meta['mongodb.query'], query) + }, { timeoutMs: traceTimeoutMs }) - collection.find({ - _id: new BSON.ObjectID(id), - }).toArray() + await Promise.all([ + tracePromise, + collection.find({ + _id: new BSON.ObjectId(id), + }).toArray(), + ]) }) - it('should stringify BSON objects', done => { - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - const resource = `find test.${collectionName}` - const query = '{"_time":{"$timestamp":"0"}}' + it('should stringify BSON objects', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + const resource = `find test.${collectionName}` + const query = '{"_time":{"$timestamp":"0"}}' - assert.strictEqual(span.resource, resource) - assert.strictEqual(span.meta['mongodb.query'], query) - }) - .then(done) - .catch(done) + assert.strictEqual(span.resource, resource) + assert.strictEqual(span.meta['mongodb.query'], query) + }, { timeoutMs: traceTimeoutMs }) - collection.find({ - _time: new BSON.Timestamp(), - }).toArray() + await Promise.all([ + tracePromise, + collection.find({ + _time: new BSON.Timestamp(), + }).toArray(), + ]) }) - it('should stringify BSON internal types', done => { - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - const resource = `find test.${collectionName}` - const query = '{"_id":"?"}' + it('should stringify BSON internal types', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + const resource = `find test.${collectionName}` + const query = '{"_id":"?"}' - assert.strictEqual(span.resource, resource) - assert.strictEqual(span.meta['mongodb.query'], query) - }) - .then(done) - .catch(done) + assert.strictEqual(span.resource, resource) + assert.strictEqual(span.meta['mongodb.query'], query) + }, { timeoutMs: traceTimeoutMs }) - collection.find({ - _id: new BSON.MinKey(), - }).toArray() + await Promise.all([ + tracePromise, + collection.find({ + _id: new BSON.MinKey(), + }).toArray(), + ]) }) - it('should collapse beyond max depth', done => { + it('should collapse beyond max depth', async () => { let nested = { a: 1 } for (let i = 0; i < 12; i++) { nested = { a: nested } } - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - assert.strictEqual(span.resource, `find test.${collectionName}`) - // 10 levels of `{"a":` then `"?"`, then 10 closing braces. - assert.strictEqual(span.meta['mongodb.query'], `${'{"a":'.repeat(10)}"?"${'}'.repeat(10)}`) - }) - .then(done) - .catch(done) + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + assert.strictEqual(span.resource, `find test.${collectionName}`) + // 10 levels of `{"a":` then `"?"`, then 10 closing braces. + assert.strictEqual(span.meta['mongodb.query'], `${'{"a":'.repeat(10)}"?"${'}'.repeat(10)}`) + }, { timeoutMs: traceTimeoutMs }) - collection.find(nested).toArray().catch(() => {}) + await Promise.all([ + tracePromise, + collection.find(nested).toArray(), + ]) }) it('should collapse cyclic queries to ?', done => { @@ -538,62 +547,62 @@ describe('Plugin', () => { collection.find(cyclic).toArray().catch(() => {}) }) - it('should skip functions when sanitizing', done => { - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - const resource = `find test.${collectionName}` - const query = '{"_id":"1234"}' + it('should skip functions when sanitizing', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + const resource = `find test.${collectionName}` + const query = '{"_id":"1234"}' - assert.strictEqual(span.resource, resource) - assert.strictEqual(span.meta['mongodb.query'], query) - }) - .then(done) - .catch(done) + assert.strictEqual(span.resource, resource) + assert.strictEqual(span.meta['mongodb.query'], query) + }, { timeoutMs: traceTimeoutMs }) - collection.find({ - _id: '1234', - foo: () => {}, - }).toArray() + await Promise.all([ + tracePromise, + collection.find({ + _id: '1234', + foo: () => {}, + }).toArray(), + ]) }) - it('should log the aggregate pipeline in mongodb.query', done => { - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - const resource = 'aggregate test.$cmd' - const query = '[{"$match":{"_id":"1234"}},{"$project":{"_id":1}}]' + it('should log the aggregate pipeline in mongodb.query', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + const resource = 'aggregate test.$cmd' + const query = '[{"$match":{"_id":"1234"}},{"$project":{"_id":1}}]' - assert.strictEqual(span.resource, resource) - assert.strictEqual(span.meta['mongodb.query'], query) - }) - .then(done) - .catch(done) + assert.strictEqual(span.resource, resource) + assert.strictEqual(span.meta['mongodb.query'], query) + }, { timeoutMs: traceTimeoutMs }) - collection.aggregate([ - { $match: { _id: '1234' } }, - { $project: { _id: 1 } }, - ]).toArray() + await Promise.all([ + tracePromise, + collection.aggregate([ + { $match: { _id: '1234' } }, + { $project: { _id: 1 } }, + ]).toArray(), + ]) }) - it('should use the toJSON method of objects if it exists', done => { + it('should use the toJSON method of objects if it exists', async () => { const id = '123456781234567812345678' - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - const resource = `find test.${collectionName}` - const query = `{"_id":"${id}"}` + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + const resource = `find test.${collectionName}` + const query = `{"_id":"${id}"}` - assert.strictEqual(span.resource, resource) - assert.strictEqual(span.meta['mongodb.query'], query) - }) - .then(done) - .catch(done) + assert.strictEqual(span.resource, resource) + assert.strictEqual(span.meta['mongodb.query'], query) + }, { timeoutMs: traceTimeoutMs }) - collection.find({ - _id: { toJSON: () => id }, - }).toArray() + await Promise.all([ + tracePromise, + collection.find({ + _id: { toJSON: () => id }, + }).toArray(), + ]) }) it('should run the callback in the parent context', done => { @@ -646,20 +655,20 @@ describe('Plugin', () => { collection.insertOne({ a: 1 }, {}, () => {}) }) - it('should include sanitized query in resource when configured', done => { - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - const resource = `find test.${collectionName} {"_bin":"?"}` + it('should include sanitized query in resource when configured', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + const resource = `find test.${collectionName} {"_bin":"?"}` - assert.strictEqual(span.resource, resource) - }) - .then(done) - .catch(done) + assert.strictEqual(span.resource, resource) + }, { timeoutMs: traceTimeoutMs }) - collection.find({ - _bin: new BSON.Binary(), - }).toArray() + await Promise.all([ + tracePromise, + collection.find({ + _bin: new BSON.Binary(), + }).toArray(), + ]) }) it('should sanitize query in resource when configured and doing a multi statement update', async () => { @@ -794,29 +803,29 @@ describe('Plugin', () => { injectCommentSpy?.restore() }) - it('DBM propagation should inject service mode as comment', done => { - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - - assert.strictEqual(injectCommentSpy.called, true) - const comment = injectCommentSpy.getCall(0).returnValue - assert.strictEqual(comment, - `dddb='${encodeURIComponent(span.meta['db.name'])}',` + - 'dddbs=\'test-mongodb\',' + - 'dde=\'tester\',' + - `ddh='${encodeURIComponent(span.meta['out.host'])}',` + - `ddps='${encodeURIComponent(span.meta.service)}',` + - `ddpv='${ddpv}',` + - `ddprs='${encodeURIComponent(span.meta['peer.service'])}'` - ) - }) - .then(done) - .catch(done) - - collection.find({ - _id: Buffer.from('1234'), - }).toArray() + it('DBM propagation should inject service mode as comment', async () => { + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + + assert.strictEqual(injectCommentSpy.called, true) + const comment = injectCommentSpy.getCall(0).returnValue + assert.strictEqual(comment, + `dddb='${encodeURIComponent(span.meta['db.name'])}',` + + 'dddbs=\'test-mongodb\',' + + 'dde=\'tester\',' + + `ddh='${encodeURIComponent(span.meta['out.host'])}',` + + `ddps='${encodeURIComponent(span.meta.service)}',` + + `ddpv='${ddpv}',` + + `ddprs='${encodeURIComponent(span.meta['peer.service'])}'` + ) + }, { timeoutMs: traceTimeoutMs }) + + await Promise.all([ + tracePromise, + collection.find({ + _id: Buffer.from('1234'), + }).toArray(), + ]) }) }) @@ -841,31 +850,31 @@ describe('Plugin', () => { injectCommentSpy?.restore() }) - it('DBM propagation should inject full mode with traceparent as comment', done => { - agent - .assertFirstTraceSpan(span => { - const traceId = span.meta['_dd.p.tid'] + span.trace_id.toString(16).padStart(16, '0') - const spanId = span.span_id.toString(16).padStart(16, '0') - - assert.strictEqual(injectCommentSpy.called, true) - const comment = injectCommentSpy.getCall(0).returnValue - assert.strictEqual(comment, - `dddb='${encodeURIComponent(span.meta['db.name'])}',` + - 'dddbs=\'test-mongodb\',' + - 'dde=\'tester\',' + - `ddh='${encodeURIComponent(span.meta['out.host'])}',` + - `ddps='${encodeURIComponent(span.meta.service)}',` + - `ddpv='${ddpv}',` + - `ddprs='${encodeURIComponent(span.meta['peer.service'])}',` + - `traceparent='00-${traceId}-${spanId}-01'` - ) - }) - .then(done) - .catch(done) - - collection.find({ - _id: Buffer.from('1234'), - }).toArray() + it('DBM propagation should inject full mode with traceparent as comment', async () => { + const tracePromise = agent.assertFirstTraceSpan(span => { + const traceId = span.meta['_dd.p.tid'] + span.trace_id.toString(16).padStart(16, '0') + const spanId = span.span_id.toString(16).padStart(16, '0') + + assert.strictEqual(injectCommentSpy.called, true) + const comment = injectCommentSpy.getCall(0).returnValue + assert.strictEqual(comment, + `dddb='${encodeURIComponent(span.meta['db.name'])}',` + + 'dddbs=\'test-mongodb\',' + + 'dde=\'tester\',' + + `ddh='${encodeURIComponent(span.meta['out.host'])}',` + + `ddps='${encodeURIComponent(span.meta.service)}',` + + `ddpv='${ddpv}',` + + `ddprs='${encodeURIComponent(span.meta['peer.service'])}',` + + `traceparent='00-${traceId}-${spanId}-01'` + ) + }, { timeoutMs: traceTimeoutMs }) + + await Promise.all([ + tracePromise, + collection.find({ + _id: Buffer.from('1234'), + }).toArray(), + ]) }) }) @@ -892,26 +901,26 @@ describe('Plugin', () => { it( 'DBM propagation should inject full mode with traceparent as comment and the rejected sampling decision', - done => { - agent - .assertSomeTraces(traces => { - const span = traces[0][0] - const traceId = span.meta['_dd.p.tid'] + span.trace_id.toString(16).padStart(16, '0') - const spanId = span.span_id.toString(16).padStart(16, '0') - - assert.strictEqual(injectCommentSpy.called, true) - const comment = injectCommentSpy.getCall(0).returnValue - assert.match( - comment, - new RegExp(String.raw`traceparent='00-${traceId}-${spanId}-00'`) - ) - }) - .then(done) - .catch(done) + async () => { + const tracePromise = agent.assertSomeTraces(traces => { + const span = traces[0][0] + const traceId = span.meta['_dd.p.tid'] + span.trace_id.toString(16).padStart(16, '0') + const spanId = span.span_id.toString(16).padStart(16, '0') - collection.find({ - _id: Buffer.from('1234'), - }).toArray() + assert.strictEqual(injectCommentSpy.called, true) + const comment = injectCommentSpy.getCall(0).returnValue + assert.match( + comment, + new RegExp(String.raw`traceparent='00-${traceId}-${spanId}-00'`) + ) + }, { timeoutMs: traceTimeoutMs }) + + await Promise.all([ + tracePromise, + collection.find({ + _id: Buffer.from('1234'), + }).toArray(), + ]) }) })