diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 5504e8528a..ba63f2c357 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -113,7 +113,7 @@ jobs: chown -R 1001:121 "/github/home/.npm" [ -e /__w/opentelemetry-js-contrib/opentelemetry-js-contrib/package-lock.json ] && chown 1001:121 /__w/opentelemetry-js-contrib/opentelemetry-js-contrib/package-lock.json - name: Bootstrap Dependencies - run: npx lerna bootstrap --no-ci --hoist --nohoist='zone.js' + run: npx lerna bootstrap --no-ci --hoist --nohoist='zone.js' --nohoist='mocha' --nohoist='ts-mocha' - name: Unit tests run: npm run test:ci:changed - name: Report Coverage diff --git a/packages/opentelemetry-test-utils/README.md b/packages/opentelemetry-test-utils/README.md index 90970142e6..db3b3e2e0b 100644 --- a/packages/opentelemetry-test-utils/README.md +++ b/packages/opentelemetry-test-utils/README.md @@ -2,6 +2,60 @@ This is a internal utils package used across the contrib packages. No guarantees are given to uses outside of [open-telemetry/opentelemetry-js-contrib](https://github.com/open-telemetry/opentelemetry-js-contrib/) repository. +## Instrumentation Testing +This package exports a mocha [root hook plugin](https://mochajs.org/#root-hook-plugins), which implements common boilerplate code a developer probably needs for writing instrumentation unit tests in node. + +This package: +- Initializes and registers a global trace provider for tests. +- Registers a global memory exporter which can be referenced in test to access span. +- Make sure there is only a single instance of an instrumentation class that is used across different `.spec.ts` files so patching is consistent, deterministic and idiomatic. +- Reset the memory exporter before each test, so spans do not leak from one test to another. +- Optionally - export the test traces to Jaeger for convenience while debugging and developing. + +By using this package, testing instrumentation code can be shorter, and good practices for writing tests are more easily applied. + +### Supporter Version +Since [root hook plugin](https://mochajs.org/#root-hook-plugins) are used, this package is compatible to mocha v8.0.0 and above. + +### Usage +1. Add dev dependency on this package: + +``` +npm install @opentelemetry/test-utils --save-dev +``` +2. [`require`](https://mochajs.org/#-require-module-r-module) this package in mocha invocation: + +As command line argument option to mocha: +```js + "scripts": { + "test": "mocha --require @opentelemetry/test-utils", + "test:jaeger": "OTEL_EXPORTER_JAEGER_AGENT_HOST=localhost mocha --require @opentelemetry/test-utils", + }, +`` + +Or by using config file / package.json config: +```js + "mocha": { + "require": [ "@opentelemetry/test-utils" ] + } +``` + +3. In your `.spec` file, import `registerInstrumentationTesting` and `getTestSpans` functions and use them to create instrumentation class instance and make assertions in the test: + +```js +import { getTestSpans, registerInstrumentationTesting } from '@opentelemetry/test-utils'; + +const instrumentation = registerInstrumentationTesting(new MyAwesomeInstrumentation()); + +it('some test', () => { + // your code that generate spans for this test + const spans: ReadableSpan[] = getTestSpans(); + // your code doing assertions with the spans array +}); +``` + +That's it - supper short and easy. + ## Useful links - For more information on OpenTelemetry, visit: diff --git a/packages/opentelemetry-test-utils/package.json b/packages/opentelemetry-test-utils/package.json index f8bc7c9638..3df4dc40d5 100644 --- a/packages/opentelemetry-test-utils/package.json +++ b/packages/opentelemetry-test-utils/package.json @@ -2,7 +2,8 @@ "name": "@opentelemetry/contrib-test-utils", "version": "0.25.0", "description": "Test utilities for opentelemetry components", - "main": "build/index.js", + "main": "build/src/index.js", + "types": "build/src/index.d.ts", "publishConfig": { "access": "public" }, @@ -43,6 +44,9 @@ }, "dependencies": { "@opentelemetry/core": "0.25.0", + "@opentelemetry/exporter-jaeger": "0.25.0", + "@opentelemetry/instrumentation": "0.25.0", + "@opentelemetry/sdk-trace-node": "0.25.0", "@opentelemetry/resources": "0.25.0", "@opentelemetry/sdk-trace-base": "0.25.0", "@opentelemetry/semantic-conventions": "0.25.0" diff --git a/packages/opentelemetry-test-utils/index.ts b/packages/opentelemetry-test-utils/src/index.ts similarity index 94% rename from packages/opentelemetry-test-utils/index.ts rename to packages/opentelemetry-test-utils/src/index.ts index 13355c6afa..9ed12bb749 100644 --- a/packages/opentelemetry-test-utils/index.ts +++ b/packages/opentelemetry-test-utils/src/index.ts @@ -16,3 +16,4 @@ export * from './resource-assertions'; export * from './test-utils'; +export * from './instrumentations'; diff --git a/packages/opentelemetry-test-utils/src/instrumentations/index.ts b/packages/opentelemetry-test-utils/src/instrumentations/index.ts new file mode 100644 index 0000000000..bd709db0cb --- /dev/null +++ b/packages/opentelemetry-test-utils/src/instrumentations/index.ts @@ -0,0 +1,56 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Resource } from '@opentelemetry/resources'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import { getInstrumentation } from './instrumentation-singelton'; +import { registerInstrumentationTestingProvider } from './otel-default-provider'; +import { resetMemoryExporter } from './otel-provider-api'; + +export * from './instrumentation-singelton'; +export * from './otel-provider-api'; +export * from './otel-default-provider'; + +export const mochaHooks = { + beforeAll(done: Function) { + // since we run mocha executable, process.argv[1] will look like this: + // ${root instrumentation package path}/node_modules/.bin/mocha + // this is not very robust, might need to refactor in the future + let serviceName = 'unknown_instrumentation'; + if (process.env.OTEL_SERVICE_NAME) { + serviceName = process.env.OTEL_SERVICE_NAME; + } else { + try { + serviceName = require(process.argv[1] + '/../../../package.json').name; + } catch { + // could not determine serviceName, continue regardless of this + } + } + const provider = registerInstrumentationTestingProvider({ + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: serviceName, + }), + }); + getInstrumentation()?.setTracerProvider(provider); + done(); + }, + + beforeEach(done: Function) { + resetMemoryExporter(); + // reset the config before each test, so that we don't leak state from one test to another + getInstrumentation()?.setConfig({}); + done(); + }, +}; diff --git a/packages/opentelemetry-test-utils/src/instrumentations/instrumentation-singelton.ts b/packages/opentelemetry-test-utils/src/instrumentations/instrumentation-singelton.ts new file mode 100644 index 0000000000..732fed16d2 --- /dev/null +++ b/packages/opentelemetry-test-utils/src/instrumentations/instrumentation-singelton.ts @@ -0,0 +1,42 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { InstrumentationBase } from '@opentelemetry/instrumentation'; + +const OTEL_TESTING_INSTRUMENTATION_SINGLETON = Symbol.for( + 'opentelemetry.testing.instrumentation_singleton' +); + +type OTelInstrumentationSingeltonGlobal = { + [OTEL_TESTING_INSTRUMENTATION_SINGLETON]?: InstrumentationBase; +}; +const _global = global as OTelInstrumentationSingeltonGlobal; + +export const getInstrumentation = (): + | T + | undefined => { + return _global[OTEL_TESTING_INSTRUMENTATION_SINGLETON] as T; +}; + +export const registerInstrumentationTesting = ( + instrumentation: T +): T => { + const existing = getInstrumentation(); + if (existing) { + return existing; + } + _global[OTEL_TESTING_INSTRUMENTATION_SINGLETON] = instrumentation; + return instrumentation; +}; diff --git a/packages/opentelemetry-test-utils/src/instrumentations/otel-default-provider.ts b/packages/opentelemetry-test-utils/src/instrumentations/otel-default-provider.ts new file mode 100644 index 0000000000..7882b72c9a --- /dev/null +++ b/packages/opentelemetry-test-utils/src/instrumentations/otel-default-provider.ts @@ -0,0 +1,48 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { JaegerExporter } from '@opentelemetry/exporter-jaeger'; +import { + NodeTracerProvider, + NodeTracerConfig, +} from '@opentelemetry/sdk-trace-node'; +import { + InMemorySpanExporter, + SimpleSpanProcessor, +} from '@opentelemetry/sdk-trace-base'; +import { + getTestMemoryExporter, + setTestMemoryExporter, +} from './otel-provider-api'; + +export const registerInstrumentationTestingProvider = ( + config?: NodeTracerConfig +): NodeTracerProvider => { + const otelTestingProvider = new NodeTracerProvider(config); + + setTestMemoryExporter(new InMemorySpanExporter()); + otelTestingProvider.addSpanProcessor( + new SimpleSpanProcessor(getTestMemoryExporter()!) + ); + + if (process.env.OTEL_EXPORTER_JAEGER_AGENT_HOST) { + otelTestingProvider.addSpanProcessor( + new SimpleSpanProcessor(new JaegerExporter()) + ); + } + + otelTestingProvider.register(); + return otelTestingProvider; +}; diff --git a/packages/opentelemetry-test-utils/src/instrumentations/otel-provider-api.ts b/packages/opentelemetry-test-utils/src/instrumentations/otel-provider-api.ts new file mode 100644 index 0000000000..05ad3d1a37 --- /dev/null +++ b/packages/opentelemetry-test-utils/src/instrumentations/otel-provider-api.ts @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + InMemorySpanExporter, + ReadableSpan, +} from '@opentelemetry/sdk-trace-base'; + +const OTEL_TESTING_MEMORY_EXPORTER = Symbol.for( + 'opentelemetry.testing.memory_exporter' +); + +type OTelProviderApiGlobal = { + [OTEL_TESTING_MEMORY_EXPORTER]?: InMemorySpanExporter; +}; +const _global = global as OTelProviderApiGlobal; + +export const getTestMemoryExporter = (): InMemorySpanExporter | undefined => { + return _global[OTEL_TESTING_MEMORY_EXPORTER]; +}; + +export const setTestMemoryExporter = (memoryExporter: InMemorySpanExporter) => { + _global[OTEL_TESTING_MEMORY_EXPORTER] = memoryExporter; +}; + +export const getTestSpans = (): ReadableSpan[] => { + return getTestMemoryExporter()!.getFinishedSpans(); +}; + +export const resetMemoryExporter = () => { + getTestMemoryExporter()?.reset(); +}; diff --git a/packages/opentelemetry-test-utils/resource-assertions.ts b/packages/opentelemetry-test-utils/src/resource-assertions.ts similarity index 100% rename from packages/opentelemetry-test-utils/resource-assertions.ts rename to packages/opentelemetry-test-utils/src/resource-assertions.ts diff --git a/packages/opentelemetry-test-utils/test-utils.ts b/packages/opentelemetry-test-utils/src/test-utils.ts similarity index 100% rename from packages/opentelemetry-test-utils/test-utils.ts rename to packages/opentelemetry-test-utils/src/test-utils.ts diff --git a/packages/opentelemetry-test-utils/tsconfig.json b/packages/opentelemetry-test-utils/tsconfig.json index 15ea27f288..31551191d7 100644 --- a/packages/opentelemetry-test-utils/tsconfig.json +++ b/packages/opentelemetry-test-utils/tsconfig.json @@ -5,6 +5,6 @@ "outDir": "build" }, "include": [ - "*.ts" + "src/**/*.ts" ] } diff --git a/plugins/node/opentelemetry-instrumentation-mongodb/package.json b/plugins/node/opentelemetry-instrumentation-mongodb/package.json index a55bcc7eed..f8182dbbb0 100644 --- a/plugins/node/opentelemetry-instrumentation-mongodb/package.json +++ b/plugins/node/opentelemetry-instrumentation-mongodb/package.json @@ -7,7 +7,7 @@ "repository": "open-telemetry/opentelemetry-js-contrib", "scripts": { "docker:start": "docker run -e MONGODB_DB=opentelemetry-tests -e MONGODB_PORT=27017 -e MONGODB_HOST=localhost -p 27017:27017 --rm mongo", - "test": "nyc ts-mocha --parallel -p tsconfig.json 'test/**/*.test.ts'", + "test": "nyc ts-mocha --parallel -p tsconfig.json --require '@opentelemetry/contrib-test-utils' 'test/**/*.test.ts'", "test-all-versions": "tav", "codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../", "tdd": "npm run test -- --watch-extensions ts --watch", @@ -49,6 +49,7 @@ }, "devDependencies": { "@opentelemetry/api": "1.0.2", + "@opentelemetry/contrib-test-utils": "^0.25.0", "@opentelemetry/context-async-hooks": "0.25.0", "@opentelemetry/sdk-trace-base": "0.25.0", "@opentelemetry/sdk-trace-node": "0.25.0", @@ -69,4 +70,4 @@ "@opentelemetry/semantic-conventions": "^0.25.0", "@types/mongodb": "3.6.20" } -} +} \ No newline at end of file diff --git a/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts b/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts index f88f3bea78..d86c8490be 100644 --- a/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts +++ b/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts @@ -17,19 +17,18 @@ // for testing locally "npm run docker:start" import { context, trace, SpanKind, Span } from '@opentelemetry/api'; -import { BasicTracerProvider } from '@opentelemetry/sdk-trace-base'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; import { MongoDBInstrumentation, MongoDBInstrumentationConfig } from '../src'; import { MongoResponseHookInformation } from '../src/types'; +import { + registerInstrumentationTesting, + getTestSpans, + resetMemoryExporter, +} from '@opentelemetry/contrib-test-utils'; -const instrumentation = new MongoDBInstrumentation(); -instrumentation.enable(); -instrumentation.disable(); +const instrumentation = registerInstrumentationTesting( + new MongoDBInstrumentation() +); import * as mongodb from 'mongodb'; import { assertSpans, accessCollection } from './utils'; @@ -38,7 +37,6 @@ import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; describe('MongoDBInstrumentation', () => { function create(config: MongoDBInstrumentationConfig = {}) { instrumentation.setConfig(config); - instrumentation.enable(); } // For these tests, mongo must be running. Add RUN_MONGODB_TESTS to run // these tests. @@ -58,16 +56,8 @@ describe('MongoDBInstrumentation', () => { let client: mongodb.MongoClient; let collection: mongodb.Collection; - const provider = new BasicTracerProvider(); - const contextManager = new AsyncHooksContextManager().enable(); - const memoryExporter = new InMemorySpanExporter(); - const spanProcessor = new SimpleSpanProcessor(memoryExporter); before(done => { - instrumentation.enable(); - instrumentation.setTracerProvider(provider); - provider.addSpanProcessor(spanProcessor); - context.setGlobalContextManager(contextManager); shouldTest = true; accessCollection(URL, DB_NAME, COLLECTION_NAME) .then(result => { @@ -83,10 +73,6 @@ describe('MongoDBInstrumentation', () => { done(); }); }); - after(() => { - contextManager.disable(); - instrumentation.disable(); - }); beforeEach(function mongoBeforeEach(done) { // Skipping all tests in beforeEach() is a workaround. Mocha does not work @@ -95,16 +81,15 @@ describe('MongoDBInstrumentation', () => { if (!shouldTest) { this.skip(); } - memoryExporter.reset(); // Non traced insertion of basic data to perform tests const insertData = [{ a: 1 }, { a: 2 }, { a: 3 }]; collection.insertMany(insertData, (err, result) => { + resetMemoryExporter(); done(); }); }); afterEach(done => { - memoryExporter.reset(); if (shouldTest) { return collection.deleteMany({}, done); } @@ -119,53 +104,39 @@ describe('MongoDBInstrumentation', () => { /** Should intercept query */ describe('Instrumenting query operations', () => { - beforeEach(() => { - memoryExporter.reset(); - }); it('should create a child span for insert', done => { const insertData = [{ a: 1 }, { a: 2 }, { a: 3 }]; - const span = provider.getTracer('default').startSpan('insertRootSpan'); + const span = trace.getTracer('default').startSpan('insertRootSpan'); context.with(trace.setSpan(context.active(), span), () => { collection.insertMany(insertData, (err, result) => { span.end(); assert.ifError(err); - assertSpans( - memoryExporter.getFinishedSpans(), - 'mongodb.insert', - SpanKind.CLIENT - ); + assertSpans(getTestSpans(), 'mongodb.insert', SpanKind.CLIENT); done(); }); }); }); it('should create a child span for update', done => { - const span = provider.getTracer('default').startSpan('updateRootSpan'); + const span = trace.getTracer('default').startSpan('updateRootSpan'); context.with(trace.setSpan(context.active(), span), () => { collection.updateOne({ a: 2 }, { $set: { b: 1 } }, (err, result) => { span.end(); + console.log(getTestSpans()); assert.ifError(err); - assertSpans( - memoryExporter.getFinishedSpans(), - 'mongodb.update', - SpanKind.CLIENT - ); + assertSpans(getTestSpans(), 'mongodb.update', SpanKind.CLIENT); done(); }); }); }); it('should create a child span for remove', done => { - const span = provider.getTracer('default').startSpan('removeRootSpan'); + const span = trace.getTracer('default').startSpan('removeRootSpan'); context.with(trace.setSpan(context.active(), span), () => { collection.deleteOne({ a: 3 }, (err, result) => { span.end(); assert.ifError(err); - assertSpans( - memoryExporter.getFinishedSpans(), - 'mongodb.remove', - SpanKind.CLIENT - ); + assertSpans(getTestSpans(), 'mongodb.remove', SpanKind.CLIENT); done(); }); }); @@ -174,27 +145,19 @@ describe('MongoDBInstrumentation', () => { /** Should intercept cursor */ describe('Instrumenting cursor operations', () => { - beforeEach(() => { - memoryExporter.reset(); - }); - it('should create a child span for find', done => { - const span = provider.getTracer('default').startSpan('findRootSpan'); + const span = trace.getTracer('default').startSpan('findRootSpan'); context.with(trace.setSpan(context.active(), span), () => { collection.find({ a: 1 }).toArray((err, result) => { span.end(); assert.ifError(err); - assertSpans( - memoryExporter.getFinishedSpans(), - 'mongodb.find', - SpanKind.CLIENT - ); + assertSpans(getTestSpans(), 'mongodb.find', SpanKind.CLIENT); done(); }); }); }); it('should create a child span for cursor operations', done => { - const span = provider.getTracer('default').startSpan('findRootSpan'); + const span = trace.getTracer('default').startSpan('findRootSpan'); context.with(trace.setSpan(context.active(), span), () => { const cursor = collection.find().batchSize(1); cursor.next().then(firstElement => { @@ -204,19 +167,17 @@ describe('MongoDBInstrumentation', () => { assert(secondElement !== null); // assert that we correctly got the first as a find assertSpans( - memoryExporter - .getFinishedSpans() - .filter( - span => span.name.includes('mongodb.getMore') === false - ), + getTestSpans().filter( + span => span.name.includes('mongodb.getMore') === false + ), 'mongodb.find', SpanKind.CLIENT ); // assert that we correctly got the first as a find assertSpans( - memoryExporter - .getFinishedSpans() - .filter(span => span.name.includes('mongodb.find') === false), + getTestSpans().filter( + span => span.name.includes('mongodb.find') === false + ), 'mongodb.getMore', SpanKind.CLIENT ); @@ -229,21 +190,13 @@ describe('MongoDBInstrumentation', () => { /** Should intercept command */ describe('Instrumenting command operations', () => { - beforeEach(() => { - memoryExporter.reset(); - }); - it('should create a child span for create index', done => { - const span = provider.getTracer('default').startSpan('indexRootSpan'); + const span = trace.getTracer('default').startSpan('indexRootSpan'); context.with(trace.setSpan(context.active(), span), () => { collection.createIndex({ a: 1 }, (err, result) => { span.end(); assert.ifError(err); - assertSpans( - memoryExporter.getFinishedSpans(), - 'mongodb.createIndexes', - SpanKind.CLIENT - ); + assertSpans(getTestSpans(), 'mongodb.createIndexes', SpanKind.CLIENT); done(); }); }); @@ -256,18 +209,17 @@ describe('MongoDBInstrumentation', () => { const object = { [key]: value }; beforeEach(() => { - memoryExporter.reset(); create({ enhancedDatabaseReporting: false, }); }); it('should properly collect db statement (hide attribute values)', done => { - const span = provider.getTracer('default').startSpan('insertRootSpan'); + const span = trace.getTracer('default').startSpan('insertRootSpan'); context.with(trace.setSpan(context.active(), span), () => { collection.insertOne(object).then(() => { span.end(); - const spans = memoryExporter.getFinishedSpans(); + const spans = getTestSpans(); const operationName = 'mongodb.insert'; assertSpans(spans, operationName, SpanKind.CLIENT, false, false); const mongoSpan = spans.find(s => s.name === operationName); @@ -288,7 +240,6 @@ describe('MongoDBInstrumentation', () => { describe('with a valid function', () => { beforeEach(() => { - memoryExporter.reset(); create({ dbStatementSerializer: (commandObj: Record) => { return JSON.stringify(commandObj); @@ -297,11 +248,11 @@ describe('MongoDBInstrumentation', () => { }); it('should properly collect db statement', done => { - const span = provider.getTracer('default').startSpan('insertRootSpan'); + const span = trace.getTracer('default').startSpan('insertRootSpan'); context.with(trace.setSpan(context.active(), span), () => { collection.insertOne(object).then(() => { span.end(); - const spans = memoryExporter.getFinishedSpans(); + const spans = getTestSpans(); const operationName = 'mongodb.insert'; assertSpans(spans, operationName, SpanKind.CLIENT, false, true); const mongoSpan = spans.find(s => s.name === operationName); @@ -317,7 +268,6 @@ describe('MongoDBInstrumentation', () => { describe('with an invalid function', () => { beforeEach(() => { - memoryExporter.reset(); create({ enhancedDatabaseReporting: true, dbStatementSerializer: (_commandObj: Record) => { @@ -327,11 +277,11 @@ describe('MongoDBInstrumentation', () => { }); it('should not do any harm when throwing an exception', done => { - const span = provider.getTracer('default').startSpan('insertRootSpan'); + const span = trace.getTracer('default').startSpan('insertRootSpan'); context.with(trace.setSpan(context.active(), span), () => { collection.insertOne(object).then(() => { span.end(); - const spans = memoryExporter.getFinishedSpans(); + const spans = getTestSpans(); assertSpans(spans, 'mongodb.insert', SpanKind.CLIENT); done(); }); @@ -342,9 +292,6 @@ describe('MongoDBInstrumentation', () => { describe('when specifying a responseHook configuration', () => { const dataAttributeName = 'mongodb_data'; - beforeEach(() => { - memoryExporter.reset(); - }); describe('with a valid function', () => { beforeEach(() => { @@ -360,12 +307,12 @@ describe('MongoDBInstrumentation', () => { it('should attach response hook data to the resulting span for insert function', done => { const insertData = [{ a: 1 }, { a: 2 }, { a: 3 }]; - const span = provider.getTracer('default').startSpan('insertRootSpan'); + const span = trace.getTracer('default').startSpan('insertRootSpan'); context.with(trace.setSpan(context.active(), span), () => { collection.insertMany(insertData, (err, result) => { span.end(); assert.ifError(err); - const spans = memoryExporter.getFinishedSpans(); + const spans = getTestSpans(); const insertSpan = spans[0]; assert.deepStrictEqual( @@ -379,12 +326,12 @@ describe('MongoDBInstrumentation', () => { }); it('should attach response hook data to the resulting span for find function', done => { - const span = provider.getTracer('default').startSpan('findRootSpan'); + const span = trace.getTracer('default').startSpan('findRootSpan'); context.with(trace.setSpan(context.active(), span), () => { collection.find({ a: 1 }).toArray((err, results) => { span.end(); assert.ifError(err); - const spans = memoryExporter.getFinishedSpans(); + const spans = getTestSpans(); const findSpan = spans[0]; const hookAttributeValue = JSON.parse( findSpan.attributes[dataAttributeName] as string @@ -411,11 +358,11 @@ describe('MongoDBInstrumentation', () => { }); it('should not do any harm when throwing an exception', done => { - const span = provider.getTracer('default').startSpan('findRootSpan'); + const span = trace.getTracer('default').startSpan('findRootSpan'); context.with(trace.setSpan(context.active(), span), () => { collection.find({ a: 1 }).toArray((err, results) => { span.end(); - const spans = memoryExporter.getFinishedSpans(); + const spans = getTestSpans(); assert.ifError(err); assertSpans(spans, 'mongodb.find', SpanKind.CLIENT); @@ -428,24 +375,20 @@ describe('MongoDBInstrumentation', () => { }); describe('Mixed operations with callback', () => { - beforeEach(() => { - memoryExporter.reset(); - }); - it('should create a span for find after callback insert', done => { const insertData = [{ a: 1 }, { a: 2 }, { a: 3 }]; - const span = provider.getTracer('default').startSpan('insertRootSpan'); + const span = trace.getTracer('default').startSpan('insertRootSpan'); context.with(trace.setSpan(context.active(), span), () => { collection.insertMany(insertData, (err, result) => { span.end(); assert.ifError(err); - const spans = memoryExporter.getFinishedSpans(); + const spans = getTestSpans(); const mainSpan = spans[spans.length - 1]; assertSpans(spans, 'mongodb.insert', SpanKind.CLIENT); - memoryExporter.reset(); + resetMemoryExporter(); collection.find({ a: 1 }).toArray((err, result) => { - const spans2 = memoryExporter.getFinishedSpans(); + const spans2 = getTestSpans(); spans2.push(mainSpan); assert.ifError(err); @@ -454,7 +397,6 @@ describe('MongoDBInstrumentation', () => { mainSpan.spanContext().spanId, spans2[0].parentSpanId ); - memoryExporter.reset(); done(); }); }); @@ -464,10 +406,6 @@ describe('MongoDBInstrumentation', () => { /** Should intercept command */ describe('Removing Instrumentation', () => { - beforeEach(() => { - memoryExporter.reset(); - }); - it('should unpatch plugin', () => { assert.doesNotThrow(() => { instrumentation.disable(); @@ -476,31 +414,31 @@ describe('MongoDBInstrumentation', () => { it('should not create a child span for query', done => { const insertData = [{ a: 1 }, { a: 2 }, { a: 3 }]; - const span = provider.getTracer('default').startSpan('insertRootSpan'); + const span = trace.getTracer('default').startSpan('insertRootSpan'); collection.insertMany(insertData, (err, result) => { span.end(); assert.ifError(err); - assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); + assert.strictEqual(getTestSpans().length, 1); done(); }); }); it('should not create a child span for cursor', done => { - const span = provider.getTracer('default').startSpan('findRootSpan'); + const span = trace.getTracer('default').startSpan('findRootSpan'); collection.find({}).toArray((err, result) => { span.end(); assert.ifError(err); - assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); + assert.strictEqual(getTestSpans().length, 1); done(); }); }); it('should not create a child span for command', done => { - const span = provider.getTracer('default').startSpan('indexRootSpan'); + const span = trace.getTracer('default').startSpan('indexRootSpan'); collection.createIndex({ a: 1 }, (err, result) => { span.end(); assert.ifError(err); - assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); + assert.strictEqual(getTestSpans().length, 1); done(); }); });