Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/collector/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ test/tracing/misc/typescript/ts_esm/dist
test/tracing/misc/typescript/ts_cjs/dist

test/tracing/misc/esbuild-external/dist/
test/tracing/misc/esbuild-external/.env
test/tracing/misc/esbuild-external/.env

test/tracing/opentelemetry/package-lock.json
test/tracing/opentelemetry/package.json
4 changes: 3 additions & 1 deletion packages/collector/test/test_util/ProcessControls.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ class ProcessControls {
}

if (process.env.RUN_ESM && !opts.execArgv) {
const esmLoader = [`--import=${path.join(__dirname, '..', '..', 'esm-register.mjs')}`];
const esmLoader = [
`--import=${opts.esmLoaderPath ? opts.esmLoaderPath : path.join(__dirname, '..', '..', 'esm-register.mjs')}`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otel integration has its complete own testing space (we dont use anything from outside). Offering a way to specify the esm handler inside the otel app space.

];

try {
// Custom appPath is provided, use that. here we check the exact file name for esm app
Expand Down
11 changes: 0 additions & 11 deletions packages/collector/test/tracing/opentelemetry/package.json

This file was deleted.

18 changes: 11 additions & 7 deletions packages/collector/test/tracing/opentelemetry/tedious-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,19 @@ process.on('SIGTERM', () => {
require('@instana/collector')();
const express = require('express');
const fs = require('fs');
const path = require('path');
const { isCI } = require('@instana/core/test/test_util');
const port = require('../../test_util/app-port')();

// collector -> typeorm -> mssql v10 -> tedious v16
// We cant install typeorm on root because we have mssql v11 installed on root.
// If we require tedious here, it will load the dependency from the
// node_modules folder of the collector package.
// TODO: https://jsw.ibm.com/browse/INSTA-7722
const tedious = require('../../../../../node_modules/tedious');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I auto-resolved this. We have to ensure we are using the right Tedious instance.
Its better to install Tedious in the local app folder to be 100% sure we are loading the right instance.

const tedious = require('tedious');

const tediousPath = require.resolve('tedious');
const expectedLocalPath = path.resolve(__dirname, 'node_modules', 'tedious');
if (!tediousPath.includes(expectedLocalPath)) {
throw new Error(
// eslint-disable-next-line max-len
`tedious must be loaded from local node_modules. Expected path containing: ${expectedLocalPath}, but got: ${tediousPath}`
);
}

const Connection = tedious.Connection;
const Request = tedious.Request;
Expand Down
148 changes: 148 additions & 0 deletions packages/collector/test/tracing/opentelemetry/tedious-app.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* (c) Copyright IBM Corp. 2025
*/

/* eslint-disable no-console */

// NOTE: c8 bug https://github.com/bcoe/c8/issues/166
process.on('SIGTERM', () => {
process.disconnect();
process.exit(0);
});

import express from 'express';
import fs from 'fs';
import bodyParser from 'body-parser';
import { createRequire } from 'module';
import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';
import testUtil from '../../../../core/test/test_util/index.js';
import getAppPort from '../../test_util/app-port.js';
const port = getAppPort();
const isCI = testUtil.isCI;
import tedious from 'tedious';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const require = createRequire(import.meta.url);
const tediousPath = require.resolve('tedious');
const expectedLocalPath = resolve(__dirname, 'node_modules', 'tedious');
if (!tediousPath.includes(expectedLocalPath)) {
throw new Error(
`tedious must be loaded from local node_modules. Expected path containing: ${expectedLocalPath}, but got: ${tediousPath}`
);
}

const Connection = tedious.Connection;
const Request = tedious.Request;
const app = express();

app.use(bodyParser.json());

// Locally:
// To obtain the credentials for the Azure SQL Database, you can find them in 1password. Search for
// "Team Node.js: Azure SQL credentials", download the file and copy this to your CMD line:
// export AZURE_SQL_CONFIG=~/Downloads/nodejs-tracer-azure-sql-server.json
if (!isCI() && !process.env.AZURE_SQL_CONFIG) {
throw new Error('Please set the env variable `AZURE_SQL_CONFIG`.');
}

const azureConfig = process.env.AZURE_SQL_CONFIG
? JSON.parse(fs.readFileSync(process.env.AZURE_SQL_CONFIG, 'utf-8'))
: null;

const config = {
server: azureConfig?.AZURE_SQL_SERVER || process.env.AZURE_SQL_SERVER,
authentication: {
type: 'default',
options: {
userName: azureConfig?.AZURE_SQL_USERNAME || process.env.AZURE_SQL_USERNAME,
password: azureConfig?.AZURE_SQL_PWD || process.env.AZURE_SQL_PWD
}
},
options: {
database: azureConfig?.AZURE_SQL_DATABASE || process.env.AZURE_SQL_DATABASE,
connectTimeout: 30000
}
};

let connected = false;
let connection;

const retryDelay = 30000;
const maxRetries = 2;
let currentRetry = 0;

(function connectWithRetry() {
if (connection) {
connection.close();
}
connection = new Connection(config);
connection.connect();

connection.on('connect', err => {
if (err) {
console.warn('Connection error', err);
if (currentRetry < maxRetries) {
currentRetry++;
console.warn(`Retrying connection after ${retryDelay} ms (Retry ${currentRetry}/${maxRetries})`);
setTimeout(connectWithRetry, retryDelay);
} else {
console.error('Maximum retries reached. Unable to establish a connection.');
connection.close();
}
} else {
connected = true;
console.warn('Connected to the database');
}
});
})();

const executeStatement = (query, isBatch, res) => {
const request = new Request(query, error => {
if (error) {
console.error('Error on executeStatement.', error);
res.status(500).send('Internal Server Error');
}
});

request.on('requestCompleted', () => {
res.send('OK');
});

if (isBatch) {
connection.execSqlBatch(request);
} else {
connection.execSql(request);
}
};

app.get('/', (req, res) => {
if (!connected) {
res.sendStatus(500);
} else {
res.sendStatus(200);
}
});

app.get('/packages', (req, res) => {
const query = 'SELECT * FROM packages';
executeStatement(query, false, res);
});

app.delete('/packages', (req, res) => {
const id = 11;
const query = `DELETE FROM packages WHERE id = ${id}`;
executeStatement(query, false, res);
});

app.post('/packages/batch', (req, res) => {
const batchQuery = `
INSERT INTO packages (id, name, version) VALUES (11, 'BatchPackage1', 1);
INSERT INTO packages (id, name, version) VALUES (11, 'BatchPackage2', 2);
`;
executeStatement(batchQuery, true, res);
});
app.listen(port, () => {
// eslint-disable-next-line no-console
console.warn(`Listening on port: ${port}`);
});
65 changes: 58 additions & 7 deletions packages/collector/test/tracing/opentelemetry/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ mochaSuiteFn('opentelemetry tests', function () {
return;
}

execSync('rm -rf package-lock.json', { cwd: __dirname, stdio: 'inherit' });
execSync('rm -rf package.json', { cwd: __dirname, stdio: 'inherit' });
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both files in gitignore.

execSync('rm -rf node_modules', { cwd: __dirname, stdio: 'inherit' });
execSync('./preinstall.sh', { cwd: __dirname, stdio: 'inherit' });
});

Expand All @@ -53,18 +56,35 @@ mochaSuiteFn('opentelemetry tests', function () {
if (process.env.INSTANA_TEST_SKIP_INSTALLING_DEPS === 'true') {
return;
}
execSync('npm install --no-save --no-package-lock --prefix ./ ./core.tgz', {

execSync('npm install --save --prefix ./ ./core.tgz', {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI if we dont use --save, multiple execSync commands will remove previous installations. Using package.json with save + gitignore.

cwd: __dirname,
stdio: 'inherit'
});

execSync('npm install --save --prefix ./ ./collector.tgz', {
cwd: __dirname,
stdio: 'inherit'
});

execSync('npm install --save --prefix ./ @opentelemetry/api@1.9.0', {
cwd: __dirname,
stdio: 'inherit'
});

execSync('npm install --no-save --no-package-lock --prefix ./ ./collector.tgz', {
execSync('npm install --save --prefix ./ "@opentelemetry/api-v1.3.0@npm:@opentelemetry/api@1.3.0"', {
cwd: __dirname,
stdio: 'inherit'
});
});

// TODO: Restify test is broken in v24. See Issue: https://github.com/restify/node-restify/issues/1984
const restifyTest = semver.gte(process.versions.node, '24.0.0') ? describe.skip : describe;
let restifyTest = semver.gte(process.versions.node, '24.0.0') ? describe.skip : describe;

if (process.env.RUN_ESM === 'true') {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to manually skip them because currently we don't have a strategy when multiple describes/apps are in one test file and some of them have esm support and some not. Just skipping them manually for now (not ideal).

restifyTest = describe.skip;
}

restifyTest('restify', function () {
describe('opentelemetry is enabled', function () {
globalAgent.setUpCleanUpHooks();
Expand Down Expand Up @@ -275,7 +295,12 @@ mochaSuiteFn('opentelemetry tests', function () {
});
});

describe('fs', function () {
let runFs = describe;
if (process.env.RUN_ESM === 'true') {
runFs = describe.skip;
}

runFs('fs', function () {
globalAgent.setUpCleanUpHooks();
const agentControls = globalAgent.instance;

Expand Down Expand Up @@ -396,7 +421,12 @@ mochaSuiteFn('opentelemetry tests', function () {
.then(() => retry(() => agentControls.getSpans().then(spans => expect(spans).to.be.empty))));
});

describe('socket.io', function () {
let runSocketIo = describe;
if (process.env.RUN_ESM === 'true') {
runSocketIo = describe.skip;
}

runSocketIo('socket.io', function () {
globalAgent.setUpCleanUpHooks();
const agentControls = globalAgent.instance;
let socketIOServerPort;
Expand Down Expand Up @@ -567,10 +597,20 @@ mochaSuiteFn('opentelemetry tests', function () {
this.timeout(1000 * 60 * 2);

before(async () => {
if (process.env.INSTANA_TEST_SKIP_INSTALLING_DEPS !== 'true') {
const rootPackageJson = require('../../../../../package.json');
const tediousVersion = rootPackageJson.devDependencies.tedious;
execSync(`npm i "tedious@${tediousVersion}" --prefix ./ --save`, {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Manually installing tedious into our otel test application space. Full production simulation. No more require tedious from root.

cwd: __dirname,
stdio: 'inherit'
});
}

controls = new ProcessControls({
appPath: path.join(__dirname, './tedious-app'),
useGlobalAgent: true,
cwd: __dirname,
esmLoaderPath: path.join(__dirname, 'node_modules', '@instana', 'collector', 'esm-register.mjs'),
enableOtelIntegration: true,
env: {
OTEL_API_VERSION: version
Expand Down Expand Up @@ -700,7 +740,12 @@ mochaSuiteFn('opentelemetry tests', function () {
});
});

describe('OracleDB', function () {
let runOracleDb = describe;
if (process.env.RUN_ESM === 'true') {
runOracleDb = describe.skip;
}

runOracleDb('OracleDB', function () {
this.timeout(1000 * 60);

describe('opentelemetry is enabled', function () {
Expand Down Expand Up @@ -836,12 +881,18 @@ mochaSuiteFn('opentelemetry tests', function () {
});
});

mochaSuiteFn('when otel sdk and instana is enabled', function () {
let runOtelSdkAndInstana = mochaSuiteFn;
if (process.env.RUN_ESM === 'true') {
runOtelSdkAndInstana = describe.skip;
}

runOtelSdkAndInstana('when otel sdk and instana is enabled', function () {
this.timeout(config.getTestTimeout() * 4);
before(async () => {
if (process.env.INSTANA_TEST_SKIP_INSTALLING_DEPS === 'true') {
return;
}

execSync('rm -rf ./otel-sdk-and-instana/node_modules', { cwd: __dirname, stdio: 'inherit' });
execSync('npm install --no-save --no-package-lock', {
cwd: path.join(__dirname, './otel-sdk-and-instana'),
Expand Down