Skip to content

Commit

Permalink
test(ioredis): test ioredis ESM usage
Browse files Browse the repository at this point in the history
This is an attempt to test ESM usage of ioredis based on Marc's comment at
#1731 (comment)

This *passes*, but the thing that scares me is enabling the ESM hook for
.test.ts tests as well. The IORedisInstrumentation patch method is being
called *both* for ESM and CommonJS for ioredis.test.ts -- I don't
understand that.

Refs: #1731
Refs: #1735
  • Loading branch information
trentm committed Oct 25, 2023
1 parent 1dc2e81 commit fb9140a
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"types": "build/src/index.d.ts",
"repository": "open-telemetry/opentelemetry-js-contrib",
"scripts": {
"test": "nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'",
"test": "cross-env NODE_NO_WARNINGS=1 NODE_OPTIONS='--loader @opentelemetry/instrumentation/hook.mjs' nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' 'test/**/*.test.mjs'",
"test:debug": "cross-env RUN_REDIS_TESTS_LOCAL=true ts-mocha --inspect-brk --no-timeouts -p tsconfig.json 'test/**/*.test.ts'",
"test:local": "cross-env RUN_REDIS_TESTS_LOCAL=true npm run test",
"test-all-versions": "tav",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ export class IORedisInstrumentation extends InstrumentationBase<any> {
module[Symbol.toStringTag] === 'Module'
? module.default // ESM
: module; // CommonJS
console.log(
'XXX IORedisInstrumentation: esm?',
module[Symbol.toStringTag] === 'Module'
);
diag.debug('Applying patch for ioredis');
if (isWrapped(moduleExports.prototype.sendCommand)) {
this._unwrap(moduleExports.prototype, 'sendCommand');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* 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 assert from 'assert';

import { context } from '@opentelemetry/api';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks';
import {
InMemorySpanExporter,
SimpleSpanProcessor,
} from '@opentelemetry/sdk-trace-base';
import testUtils from '@opentelemetry/contrib-test-utils';
import Redis from 'ioredis';

import { IORedisInstrumentation } from '../build/src/index.js';

const CONFIG = {
host: process.env.OPENTELEMETRY_REDIS_HOST || 'localhost',
port: parseInt(process.env.OPENTELEMETRY_REDIS_PORT || '63790', 10),
};
const REDIS_URL = `redis://${CONFIG.host}:${CONFIG.port}`;

describe('ioredis ESM', () => {
const shouldTestLocal = process.env.RUN_REDIS_TESTS_LOCAL;
const shouldTest = process.env.RUN_REDIS_TESTS || shouldTestLocal;
const memoryExporter = new InMemorySpanExporter();
const provider = new NodeTracerProvider();
let instrumentation;
let contextManager;
let client;

before(() => {
if (!shouldTest) {
this.skip();
}
if (shouldTestLocal) {
testUtils.startDocker('redis');
}

provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter));
instrumentation = new IORedisInstrumentation();
instrumentation.setTracerProvider(provider);

client = new Redis(REDIS_URL);
});

after(async () => {
await client.quit();
if (shouldTestLocal) {
testUtils.cleanUpDocker('redis');
}
});

beforeEach(() => {
contextManager = new AsyncHooksContextManager().enable();
context.setGlobalContextManager(contextManager);
});
afterEach(() => {
context.disable();
});

it('should create spans', async () => {
// Use a random part in key names because redis instance is used for parallel running tests.
const randomId = ((Math.random() * 2 ** 32) >>> 0).toString(16);
const testKeyName = `test-${randomId}`;

const tracer = provider.getTracer('ioredis-test');
await tracer.startActiveSpan('manual', async (span) => {
client.set(testKeyName, 'bar');
let val = await client.get(testKeyName);
assert(val === 'bar');
span.end();
});

const spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 3);
assert.strictEqual(spans[0].name, 'set');
assert.strictEqual(spans[0].attributes['db.system'], 'redis');
assert.strictEqual(spans[1].name, 'get');
assert.strictEqual(spans[1].attributes['db.system'], 'redis');
assert.strictEqual(spans[2].name, 'manual');
});
});

0 comments on commit fb9140a

Please sign in to comment.