From 99048dcb2b3e63b81021a3eebd2d0575b90b88ed Mon Sep 17 00:00:00 2001 From: Amir Blum Date: Sun, 15 Aug 2021 12:23:35 +0300 Subject: [PATCH 01/13] chore: change test utils files structure to match repo --- packages/opentelemetry-test-utils/package.json | 3 ++- packages/opentelemetry-test-utils/{ => src}/index.ts | 0 .../opentelemetry-test-utils/{ => src}/resource-assertions.ts | 0 packages/opentelemetry-test-utils/{ => src}/test-utils.ts | 0 packages/opentelemetry-test-utils/tsconfig.json | 2 +- 5 files changed, 3 insertions(+), 2 deletions(-) rename packages/opentelemetry-test-utils/{ => src}/index.ts (100%) rename packages/opentelemetry-test-utils/{ => src}/resource-assertions.ts (100%) rename packages/opentelemetry-test-utils/{ => src}/test-utils.ts (100%) diff --git a/packages/opentelemetry-test-utils/package.json b/packages/opentelemetry-test-utils/package.json index b173a1da37..1ce9ed5636 100644 --- a/packages/opentelemetry-test-utils/package.json +++ b/packages/opentelemetry-test-utils/package.json @@ -3,7 +3,8 @@ "private": true, "version": "0.24.0", "description": "Test utilities.", - "main": "build/index.js", + "main": "build/src/index.js", + "types": "build/src/index.d.ts", "scripts": { "lint": "eslint . --ext .ts", "lint:fix": "eslint . --ext .ts --fix", diff --git a/packages/opentelemetry-test-utils/index.ts b/packages/opentelemetry-test-utils/src/index.ts similarity index 100% rename from packages/opentelemetry-test-utils/index.ts rename to packages/opentelemetry-test-utils/src/index.ts 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" ] } From 14722e517078a27725faacca8b6c382d76a094e1 Mon Sep 17 00:00:00 2001 From: Amir Blum Date: Sun, 15 Aug 2021 14:25:24 +0300 Subject: [PATCH 02/13] feat(test-utils): mocha plugin for instrumentation testing --- packages/opentelemetry-test-utils/README.md | 54 ++++++++++++++++++ .../opentelemetry-test-utils/package.json | 3 + .../opentelemetry-test-utils/src/index.ts | 1 + .../src/instrumentations/index.ts | 55 +++++++++++++++++++ .../instrumentation-singelton.ts | 42 ++++++++++++++ .../instrumentations/otel-default-provider.ts | 45 +++++++++++++++ .../src/instrumentations/otel-provider-api.ts | 41 ++++++++++++++ 7 files changed, 241 insertions(+) create mode 100644 packages/opentelemetry-test-utils/src/instrumentations/index.ts create mode 100644 packages/opentelemetry-test-utils/src/instrumentations/instrumentation-singelton.ts create mode 100644 packages/opentelemetry-test-utils/src/instrumentations/otel-default-provider.ts create mode 100644 packages/opentelemetry-test-utils/src/instrumentations/otel-provider-api.ts diff --git a/packages/opentelemetry-test-utils/README.md b/packages/opentelemetry-test-utils/README.md index 90970142e6..b284d2abda 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 mocah [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 `registerInstrumentation` and `getTestSpans` functions and use them to create instrumentation class instance and make assertions in the test: + +```js +import { getTestSpans, registerInstrumentation } from '@opentelemetry/test-utils'; + +const instrumentation = registerInstrumentation(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 1ce9ed5636..c11b4732b1 100644 --- a/packages/opentelemetry-test-utils/package.json +++ b/packages/opentelemetry-test-utils/package.json @@ -35,6 +35,9 @@ }, "dependencies": { "@opentelemetry/core": "0.24.0", + "@opentelemetry/exporter-jaeger": "^0.24.0", + "@opentelemetry/instrumentation": "^0.24.0", + "@opentelemetry/node": "^0.24.0", "@opentelemetry/resources": "0.24.0", "@opentelemetry/semantic-conventions": "0.24.0", "@opentelemetry/tracing": "0.24.0" diff --git a/packages/opentelemetry-test-utils/src/index.ts b/packages/opentelemetry-test-utils/src/index.ts index 13355c6afa..9ed12bb749 100644 --- a/packages/opentelemetry-test-utils/src/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..5adb5e5a26 --- /dev/null +++ b/packages/opentelemetry-test-utils/src/instrumentations/index.ts @@ -0,0 +1,55 @@ +/* + * 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 + } + } + registerInstrumentationTestingProvider({ + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: serviceName, + }), + }); + 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..3aa1804f1c --- /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 registerInstrumentation = ( + 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..ee9f8f0052 --- /dev/null +++ b/packages/opentelemetry-test-utils/src/instrumentations/otel-default-provider.ts @@ -0,0 +1,45 @@ +/* + * 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/node'; +import { + InMemorySpanExporter, + SimpleSpanProcessor, +} from '@opentelemetry/tracing'; +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..aff73de4e6 --- /dev/null +++ b/packages/opentelemetry-test-utils/src/instrumentations/otel-provider-api.ts @@ -0,0 +1,41 @@ +/* + * 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/tracing'; + +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(); +}; From 06ef13d1d72ef5d26c845e1ea35fece980257200 Mon Sep 17 00:00:00 2001 From: Amir Blum Date: Sun, 15 Aug 2021 17:24:29 +0300 Subject: [PATCH 03/13] chore(instrumentation-mongodb): use test-utils to run tests --- .../src/instrumentations/index.ts | 3 +- .../package.json | 6 +- .../test/mongodb.test.ts | 109 ++++++------------ 3 files changed, 37 insertions(+), 81 deletions(-) diff --git a/packages/opentelemetry-test-utils/src/instrumentations/index.ts b/packages/opentelemetry-test-utils/src/instrumentations/index.ts index 5adb5e5a26..bd709db0cb 100644 --- a/packages/opentelemetry-test-utils/src/instrumentations/index.ts +++ b/packages/opentelemetry-test-utils/src/instrumentations/index.ts @@ -38,11 +38,12 @@ export const mochaHooks = { // could not determine serviceName, continue regardless of this } } - registerInstrumentationTestingProvider({ + const provider = registerInstrumentationTestingProvider({ resource: new Resource({ [SemanticResourceAttributes.SERVICE_NAME]: serviceName, }), }); + getInstrumentation()?.setTracerProvider(provider); done(); }, diff --git a/plugins/node/opentelemetry-instrumentation-mongodb/package.json b/plugins/node/opentelemetry-instrumentation-mongodb/package.json index 0a074dd13c..c0f632e8df 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/test-utils' 'test/**/*.test.ts'", "codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../", "tdd": "npm run test -- --watch-extensions ts --watch", "clean": "rimraf build/*", @@ -48,9 +48,7 @@ }, "devDependencies": { "@opentelemetry/api": "1.0.2", - "@opentelemetry/context-async-hooks": "0.24.0", - "@opentelemetry/node": "0.24.0", - "@opentelemetry/tracing": "0.24.0", + "@opentelemetry/test-utils": "^0.24.0", "@types/mocha": "7.0.2", "@types/node": "14.17.9", "codecov": "3.8.3", diff --git a/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts b/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts index 7827442a8f..fe261a0d0a 100644 --- a/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts +++ b/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts @@ -17,19 +17,12 @@ // for testing locally "npm run docker:start" import { context, trace, SpanKind, Span } from '@opentelemetry/api'; -import { BasicTracerProvider } from '@opentelemetry/tracing'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; import * as assert from 'assert'; import { MongoDBInstrumentation, MongoDBInstrumentationConfig } from '../src'; import { MongoResponseHookInformation } from '../src/types'; +import { registerInstrumentation, getTestSpans, resetMemoryExporter } from '@opentelemetry/test-utils'; -const instrumentation = new MongoDBInstrumentation(); -instrumentation.enable(); -instrumentation.disable(); +const instrumentation = registerInstrumentation(new MongoDBInstrumentation()); import * as mongodb from 'mongodb'; import { assertSpans, accessCollection } from './utils'; @@ -37,7 +30,6 @@ import { assertSpans, accessCollection } from './utils'; 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. @@ -57,16 +49,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 => { @@ -82,10 +66,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 @@ -94,16 +74,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); } @@ -118,18 +97,15 @@ 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(), + getTestSpans(), 'mongodb.insert', SpanKind.CLIENT ); @@ -139,13 +115,14 @@ describe('MongoDBInstrumentation', () => { }); 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(), + getTestSpans(), 'mongodb.update', SpanKind.CLIENT ); @@ -155,13 +132,13 @@ describe('MongoDBInstrumentation', () => { }); 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(), + getTestSpans(), 'mongodb.remove', SpanKind.CLIENT ); @@ -173,18 +150,14 @@ 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(), + getTestSpans(), 'mongodb.find', SpanKind.CLIENT ); @@ -193,7 +166,7 @@ describe('MongoDBInstrumentation', () => { }); }); 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 => { @@ -203,8 +176,7 @@ describe('MongoDBInstrumentation', () => { assert(secondElement !== null); // assert that we correctly got the first as a find assertSpans( - memoryExporter - .getFinishedSpans() + getTestSpans() .filter( span => span.name.includes('mongodb.getMore') === false ), @@ -213,8 +185,7 @@ describe('MongoDBInstrumentation', () => { ); // assert that we correctly got the first as a find assertSpans( - memoryExporter - .getFinishedSpans() + getTestSpans() .filter(span => span.name.includes('mongodb.find') === false), 'mongodb.getMore', SpanKind.CLIENT @@ -228,18 +199,14 @@ 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(), + getTestSpans(), 'mongodb.createIndexes', SpanKind.CLIENT ); @@ -251,9 +218,6 @@ describe('MongoDBInstrumentation', () => { describe('when specifying a responseHook configuration', () => { const dataAttributeName = 'mongodb_data'; - beforeEach(() => { - memoryExporter.reset(); - }); describe('with a valid function', () => { beforeEach(() => { @@ -269,12 +233,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( @@ -288,12 +252,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 @@ -320,11 +284,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); @@ -337,24 +301,21 @@ 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); @@ -363,7 +324,6 @@ describe('MongoDBInstrumentation', () => { mainSpan.spanContext().spanId, spans2[0].parentSpanId ); - memoryExporter.reset(); done(); }); }); @@ -373,9 +333,6 @@ describe('MongoDBInstrumentation', () => { /** Should intercept command */ describe('Removing Instrumentation', () => { - beforeEach(() => { - memoryExporter.reset(); - }); it('should unpatch plugin', () => { assert.doesNotThrow(() => { @@ -385,31 +342,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(); }); }); From f9276ccf8539095e4fb76e1fab0d0fc0377855a6 Mon Sep 17 00:00:00 2001 From: Amir Blum Date: Sun, 15 Aug 2021 17:42:09 +0300 Subject: [PATCH 04/13] chore: lint and revert dep remove --- .../package.json | 1 + .../test/mongodb.test.ts | 50 ++++++------------- 2 files changed, 17 insertions(+), 34 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-mongodb/package.json b/plugins/node/opentelemetry-instrumentation-mongodb/package.json index c0f632e8df..70f6e7ad08 100644 --- a/plugins/node/opentelemetry-instrumentation-mongodb/package.json +++ b/plugins/node/opentelemetry-instrumentation-mongodb/package.json @@ -49,6 +49,7 @@ "devDependencies": { "@opentelemetry/api": "1.0.2", "@opentelemetry/test-utils": "^0.24.0", + "@opentelemetry/tracing": "0.24.0", "@types/mocha": "7.0.2", "@types/node": "14.17.9", "codecov": "3.8.3", diff --git a/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts b/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts index fe261a0d0a..f758d145fb 100644 --- a/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts +++ b/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts @@ -20,7 +20,11 @@ import { context, trace, SpanKind, Span } from '@opentelemetry/api'; import * as assert from 'assert'; import { MongoDBInstrumentation, MongoDBInstrumentationConfig } from '../src'; import { MongoResponseHookInformation } from '../src/types'; -import { registerInstrumentation, getTestSpans, resetMemoryExporter } from '@opentelemetry/test-utils'; +import { + registerInstrumentation, + getTestSpans, + resetMemoryExporter, +} from '@opentelemetry/test-utils'; const instrumentation = registerInstrumentation(new MongoDBInstrumentation()); @@ -104,11 +108,7 @@ describe('MongoDBInstrumentation', () => { collection.insertMany(insertData, (err, result) => { span.end(); assert.ifError(err); - assertSpans( - getTestSpans(), - 'mongodb.insert', - SpanKind.CLIENT - ); + assertSpans(getTestSpans(), 'mongodb.insert', SpanKind.CLIENT); done(); }); }); @@ -121,11 +121,7 @@ describe('MongoDBInstrumentation', () => { span.end(); console.log(getTestSpans()); assert.ifError(err); - assertSpans( - getTestSpans(), - 'mongodb.update', - SpanKind.CLIENT - ); + assertSpans(getTestSpans(), 'mongodb.update', SpanKind.CLIENT); done(); }); }); @@ -137,11 +133,7 @@ describe('MongoDBInstrumentation', () => { collection.deleteOne({ a: 3 }, (err, result) => { span.end(); assert.ifError(err); - assertSpans( - getTestSpans(), - 'mongodb.remove', - SpanKind.CLIENT - ); + assertSpans(getTestSpans(), 'mongodb.remove', SpanKind.CLIENT); done(); }); }); @@ -156,11 +148,7 @@ describe('MongoDBInstrumentation', () => { collection.find({ a: 1 }).toArray((err, result) => { span.end(); assert.ifError(err); - assertSpans( - getTestSpans(), - 'mongodb.find', - SpanKind.CLIENT - ); + assertSpans(getTestSpans(), 'mongodb.find', SpanKind.CLIENT); done(); }); }); @@ -176,17 +164,17 @@ describe('MongoDBInstrumentation', () => { assert(secondElement !== null); // assert that we correctly got the first as a find assertSpans( - getTestSpans() - .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( - getTestSpans() - .filter(span => span.name.includes('mongodb.find') === false), + getTestSpans().filter( + span => span.name.includes('mongodb.find') === false + ), 'mongodb.getMore', SpanKind.CLIENT ); @@ -205,11 +193,7 @@ describe('MongoDBInstrumentation', () => { collection.createIndex({ a: 1 }, (err, result) => { span.end(); assert.ifError(err); - assertSpans( - getTestSpans(), - 'mongodb.createIndexes', - SpanKind.CLIENT - ); + assertSpans(getTestSpans(), 'mongodb.createIndexes', SpanKind.CLIENT); done(); }); }); @@ -301,7 +285,6 @@ describe('MongoDBInstrumentation', () => { }); describe('Mixed operations with callback', () => { - it('should create a span for find after callback insert', done => { const insertData = [{ a: 1 }, { a: 2 }, { a: 3 }]; const span = trace.getTracer('default').startSpan('insertRootSpan'); @@ -333,7 +316,6 @@ describe('MongoDBInstrumentation', () => { /** Should intercept command */ describe('Removing Instrumentation', () => { - it('should unpatch plugin', () => { assert.doesNotThrow(() => { instrumentation.disable(); From da64c5398220357c70bc696fff2377b23fc8bf2f Mon Sep 17 00:00:00 2001 From: Amir Blum Date: Tue, 17 Aug 2021 10:31:41 +0300 Subject: [PATCH 05/13] Update packages/opentelemetry-test-utils/README.md Co-authored-by: Bartlomiej Obecny --- packages/opentelemetry-test-utils/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-test-utils/README.md b/packages/opentelemetry-test-utils/README.md index b284d2abda..1fea2dd735 100644 --- a/packages/opentelemetry-test-utils/README.md +++ b/packages/opentelemetry-test-utils/README.md @@ -3,7 +3,7 @@ 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 mocah [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 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. From a0f0ca731a0a997c9b195758195b3f57af38ba86 Mon Sep 17 00:00:00 2001 From: Amir Blum Date: Tue, 17 Aug 2021 10:41:59 +0300 Subject: [PATCH 06/13] style: rename registerInstrumentation function --- packages/opentelemetry-test-utils/README.md | 6 +++--- .../src/instrumentations/instrumentation-singelton.ts | 2 +- .../test/mongodb.test.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/opentelemetry-test-utils/README.md b/packages/opentelemetry-test-utils/README.md index 1fea2dd735..db3b3e2e0b 100644 --- a/packages/opentelemetry-test-utils/README.md +++ b/packages/opentelemetry-test-utils/README.md @@ -40,12 +40,12 @@ Or by using config file / package.json config: } ``` -3. In your `.spec` file, import `registerInstrumentation` and `getTestSpans` functions and use them to create instrumentation class instance and make assertions in the test: +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, registerInstrumentation } from '@opentelemetry/test-utils'; +import { getTestSpans, registerInstrumentationTesting } from '@opentelemetry/test-utils'; -const instrumentation = registerInstrumentation(new MyAwesomeInstrumentation()); +const instrumentation = registerInstrumentationTesting(new MyAwesomeInstrumentation()); it('some test', () => { // your code that generate spans for this test diff --git a/packages/opentelemetry-test-utils/src/instrumentations/instrumentation-singelton.ts b/packages/opentelemetry-test-utils/src/instrumentations/instrumentation-singelton.ts index 3aa1804f1c..732fed16d2 100644 --- a/packages/opentelemetry-test-utils/src/instrumentations/instrumentation-singelton.ts +++ b/packages/opentelemetry-test-utils/src/instrumentations/instrumentation-singelton.ts @@ -30,7 +30,7 @@ export const getInstrumentation = (): return _global[OTEL_TESTING_INSTRUMENTATION_SINGLETON] as T; }; -export const registerInstrumentation = ( +export const registerInstrumentationTesting = ( instrumentation: T ): T => { const existing = getInstrumentation(); diff --git a/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts b/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts index f758d145fb..d40784fd7a 100644 --- a/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts +++ b/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts @@ -21,12 +21,12 @@ import * as assert from 'assert'; import { MongoDBInstrumentation, MongoDBInstrumentationConfig } from '../src'; import { MongoResponseHookInformation } from '../src/types'; import { - registerInstrumentation, + registerInstrumentationTesting, getTestSpans, resetMemoryExporter, } from '@opentelemetry/test-utils'; -const instrumentation = registerInstrumentation(new MongoDBInstrumentation()); +const instrumentation = registerInstrumentationTesting(new MongoDBInstrumentation()); import * as mongodb from 'mongodb'; import { assertSpans, accessCollection } from './utils'; From c8f3f6012a6af0306995da47d2b1800938d53301 Mon Sep 17 00:00:00 2001 From: Amir Blum Date: Tue, 17 Aug 2021 10:52:09 +0300 Subject: [PATCH 07/13] chore: fix lint --- .../test/mongodb.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts b/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts index d40784fd7a..95f9e77666 100644 --- a/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts +++ b/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts @@ -26,7 +26,9 @@ import { resetMemoryExporter, } from '@opentelemetry/test-utils'; -const instrumentation = registerInstrumentationTesting(new MongoDBInstrumentation()); +const instrumentation = registerInstrumentationTesting( + new MongoDBInstrumentation() +); import * as mongodb from 'mongodb'; import { assertSpans, accessCollection } from './utils'; From 112249a0d7e43e9b8e336ed984b209211d1718a7 Mon Sep 17 00:00:00 2001 From: Amir Blum Date: Wed, 15 Sep 2021 20:26:47 +0300 Subject: [PATCH 08/13] fix: wrong import of test-utils package --- .../opentelemetry-instrumentation-mongodb/test/mongodb.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts b/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts index 9e7c05ccfb..d86c8490be 100644 --- a/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts +++ b/plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts @@ -24,7 +24,7 @@ import { registerInstrumentationTesting, getTestSpans, resetMemoryExporter, -} from '@opentelemetry/test-utils'; +} from '@opentelemetry/contrib-test-utils'; const instrumentation = registerInstrumentationTesting( new MongoDBInstrumentation() From ca66fb714d236ad7315e66389ad9743998e49b7d Mon Sep 17 00:00:00 2001 From: Amir Blum Date: Sun, 19 Sep 2021 14:15:48 +0300 Subject: [PATCH 09/13] fix: entry point for test utils package --- packages/opentelemetry-test-utils/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/opentelemetry-test-utils/package.json b/packages/opentelemetry-test-utils/package.json index 6ef24fe222..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" }, From e5e765045cf0e66e2d6f8b65fe672edf64a2f028 Mon Sep 17 00:00:00 2001 From: Amir Blum Date: Sun, 19 Sep 2021 14:17:51 +0300 Subject: [PATCH 10/13] style(test-utils): fix lint --- .../src/instrumentations/otel-default-provider.ts | 5 ++++- .../src/instrumentations/otel-provider-api.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/opentelemetry-test-utils/src/instrumentations/otel-default-provider.ts b/packages/opentelemetry-test-utils/src/instrumentations/otel-default-provider.ts index e00fced908..7882b72c9a 100644 --- a/packages/opentelemetry-test-utils/src/instrumentations/otel-default-provider.ts +++ b/packages/opentelemetry-test-utils/src/instrumentations/otel-default-provider.ts @@ -14,7 +14,10 @@ * limitations under the License. */ import { JaegerExporter } from '@opentelemetry/exporter-jaeger'; -import { NodeTracerProvider, NodeTracerConfig } from '@opentelemetry/sdk-trace-node'; +import { + NodeTracerProvider, + NodeTracerConfig, +} from '@opentelemetry/sdk-trace-node'; import { InMemorySpanExporter, SimpleSpanProcessor, diff --git a/packages/opentelemetry-test-utils/src/instrumentations/otel-provider-api.ts b/packages/opentelemetry-test-utils/src/instrumentations/otel-provider-api.ts index de2d37a681..05ad3d1a37 100644 --- a/packages/opentelemetry-test-utils/src/instrumentations/otel-provider-api.ts +++ b/packages/opentelemetry-test-utils/src/instrumentations/otel-provider-api.ts @@ -13,7 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { InMemorySpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import { + InMemorySpanExporter, + ReadableSpan, +} from '@opentelemetry/sdk-trace-base'; const OTEL_TESTING_MEMORY_EXPORTER = Symbol.for( 'opentelemetry.testing.memory_exporter' From 3b5c2de8d45063402294dc62466b8d43ee9bcb27 Mon Sep 17 00:00:00 2001 From: Amir Blum Date: Sun, 19 Sep 2021 16:20:22 +0300 Subject: [PATCH 11/13] fix: don't hoist mocha for the plugin to take linked test-utils --- .github/workflows/unit-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 5504e8528a..0cbd01cbe5 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' - name: Unit tests run: npm run test:ci:changed - name: Report Coverage From 128fb2626532570ea2e9026019ead5bdbd683b2c Mon Sep 17 00:00:00 2001 From: Amir Blum Date: Sun, 19 Sep 2021 16:34:02 +0300 Subject: [PATCH 12/13] ci: also don't hoist ts-mocha --- .github/workflows/unit-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 0cbd01cbe5..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' --nohoist='mocha' + 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 From 6babb6f8e4dfc905b0e41228accf8eec3bb2e819 Mon Sep 17 00:00:00 2001 From: Amir Blum Date: Sun, 19 Sep 2021 16:55:39 +0300 Subject: [PATCH 13/13] chore: remove test-utils from root package.json --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 1b1c1c2673..c2285aa142 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,6 @@ "author": "OpenTelemetry Authors", "license": "Apache-2.0", "devDependencies": { - "@opentelemetry/contrib-test-utils": "^0.25.0", "@commitlint/cli": "13.1.0", "@commitlint/config-conventional": "13.1.0", "@typescript-eslint/eslint-plugin": "4.29.0",