diff --git a/package.json b/package.json index 1deaf29591..66ba014d5d 100644 --- a/package.json +++ b/package.json @@ -162,6 +162,7 @@ "memcached": "^2.2.2", "mimic-response": "^2.1.0", "mkdirp": "^0.5.1", + "module-details-from-path": "^1.0.3", "mongodb": "^4.1.0", "mongodb-core": "^3.2.7", "mysql": "^2.18.1", diff --git a/test/_utils.js b/test/_utils.js index 76292697a3..f4ace20717 100644 --- a/test/_utils.js +++ b/test/_utils.js @@ -1,5 +1,9 @@ 'use strict' +const fs = require('fs') + +const moduleDetailsFromPath = require('module-details-from-path') + // Lookup the property "str" (given in dot-notation) in the object "obj". // If the property isn't found, then `undefined` is returned. function dottedLookup (obj, str) { @@ -30,7 +34,36 @@ function findObjInArray (arr, key, val) { return result } +// "Safely" get the version of the given package, if possible. Otherwise return +// null. +// +// Here "safely" means avoiding `require("$packageName/package.json")` because +// that can fail if the package uses an old form of "exports" +// (e.g. https://github.com/elastic/apm-agent-nodejs/issues/2350). +function safeGetPackageVersion (packageName) { + let file + try { + file = require.resolve(packageName) + } catch (_err) { + return null + } + + // Use the same logic as require-in-the-middle for finding the 'basedir' of + // the package from `file`. + const details = moduleDetailsFromPath(file) + if (!details) { + return null + } + + try { + return JSON.parse(fs.readFileSync(details.basedir + '/package.json')).version + } catch (_err) { + return null + } +} + module.exports = { dottedLookup, - findObjInArray + findObjInArray, + safeGetPackageVersion } diff --git a/test/config.test.js b/test/config.test.js index 1bdd0b6d69..5b82c044db 100644 --- a/test/config.test.js +++ b/test/config.test.js @@ -17,6 +17,7 @@ var test = require('tape') const Agent = require('../lib/agent') const { MockAPMServer } = require('./_mock_apm_server') const { NoopTransport } = require('../lib/noop-transport') +const { safeGetPackageVersion } = require('./_utils') var config = require('../lib/config') var Instrumentation = require('../lib/instrumentation') var apmVersion = require('../package').version @@ -860,7 +861,7 @@ usePathAsTransactionNameTests.forEach(function (usePathAsTransactionNameTest) { test('disableInstrumentations', function (t) { var expressGraphqlVersion = require('express-graphql/package.json').version - var esVersion = require('@elastic/elasticsearch/package.json').version + var esVersion = safeGetPackageVersion('@elastic/elasticsearch') // require('apollo-server-core') is a hard crash on nodes < 12.0.0 const apolloServerCoreVersion = require('apollo-server-core/package.json').version diff --git a/test/instrumentation/modules/@elastic/elasticsearch.test.js b/test/instrumentation/modules/@elastic/elasticsearch.test.js index 9b339e17f4..11403b7bec 100644 --- a/test/instrumentation/modules/@elastic/elasticsearch.test.js +++ b/test/instrumentation/modules/@elastic/elasticsearch.test.js @@ -10,9 +10,11 @@ const agent = require('../../../..').start({ spanFramesMinDuration: -1 // always capture stack traces with spans }) +const { safeGetPackageVersion } = require('../../../_utils') + // Skip (exit the process) if this package version doesn't support this version // of node. -const esVersion = require('@elastic/elasticsearch/package.json').version +const esVersion = safeGetPackageVersion('@elastic/elasticsearch') const semver = require('semver') if (semver.lt(process.version, '10.0.0') && semver.gte(esVersion, '7.12.0')) { console.log(`# SKIP @elastic/elasticsearch@${esVersion} does not support node ${process.version}`) @@ -34,7 +36,6 @@ const { MockES } = require('./_mock_es') const host = (process.env.ES_HOST || 'localhost') + ':9200' const node = 'http://' + host -const pkgVersion = require('@elastic/elasticsearch/package.json').version test('client.ping with promise', function (t) { resetAgent(checkDataAndEnd(t, 'HEAD', '/', null)) @@ -334,7 +335,7 @@ test('DeserializationError', function (t) { }) }) -if (semver.gte(pkgVersion, '7.14.0')) { +if (semver.gte(esVersion, '7.14.0')) { test('ProductNotSupportedError', function (t) { // Create a mock Elasticsearch server that responds to "GET /" with a body // that triggers ProductNotSupportedError. @@ -377,7 +378,7 @@ if (semver.gte(pkgVersion, '7.14.0')) { }) } -if (semver.gte(pkgVersion, '7.7.0')) { +if (semver.gte(esVersion, '7.7.0')) { // Abort handling was added to @elastic/elasticsearch@7.7.0. test('request.abort() works', function (t) {