diff --git a/lib/instrumentation/fastify.js b/lib/instrumentation/fastify.js index c2d28d2812..733d6b540a 100644 --- a/lib/instrumentation/fastify.js +++ b/lib/instrumentation/fastify.js @@ -11,6 +11,7 @@ const { buildMiddlewareSpecForMiddlewareFunction } = require('./fastify/spec-builders') const { MiddlewareMounterSpec } = require('../shim/specs') +const symbols = require('../symbols') /** * These are the events that occur during a fastify @@ -95,6 +96,9 @@ module.exports = function initialize(agent, fastify, moduleName, shim) { */ const wrappedExport = shim.wrapExport(fastify, function wrapFastifyModule(shim, fn) { return function wrappedFastifyModule() { + if (fn[symbols.nrEsmProxy] === true) { + fn = fn.fastify + } // call original function get get fastify object (which is singleton-ish) const fastifyForWrapping = fn.apply(this, arguments) diff --git a/test/versioned/fastify-esm/issue-2155.test.mjs b/test/versioned/fastify-esm/issue-2155.test.mjs new file mode 100644 index 0000000000..3d37bf9fc2 --- /dev/null +++ b/test/versioned/fastify-esm/issue-2155.test.mjs @@ -0,0 +1,63 @@ +/* + * Copyright 2021 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// We cannot replicate the issue using a dynamic `await import('fastify')`. +// Thus, we cannot write a typical `tap` based test. Instead, we need to rely +// on this script exiting cleanly to represent a successful test. + +import helper from '../../lib/agent_helper.js' +helper.instrumentMockedAgent() + +import assert from 'assert' +import http from 'http' + +import fastify from 'fastify' +const server = fastify({ + forceCloseConnections: true, + logger: { + level: 'silent' + } +}) + +server.route({ + method: 'GET', + path: '/', + handler(req, res) { + res.send('ok') + } +}) + +const timeout = setTimeout(() => { + // eslint-disable-next-line no-process-exit + process.exit(1) +}, 20_000) + +const address = await server.listen({ host: '127.0.0.1', port: 0 }) +const found = await new Promise((resolve, reject) => { + const req = http.request(address, (res) => { + let data = '' + res.on('data', (d) => { + data += d.toString() + }) + res.on('end', () => { + resolve(data) + }) + }) + + req.on('error', reject) + req.end() +}) + +assert.equal(found, 'ok') + +await server.close() +clearTimeout(timeout) + +// We really shouldn't need this `process.exit`, but on Node.js 18 with our +// versioned tests runner we'll see the runner consistently hang without it. +// To see it, use Node 18 and `npm run versioned:internal fastify-esm`. Note, +// that it must be "versioned:internal" and not "versioned:internal:major". +// eslint-disable-next-line no-process-exit +process.exit(0) diff --git a/test/versioned/fastify-esm/newrelic.cjs b/test/versioned/fastify-esm/newrelic.cjs new file mode 100644 index 0000000000..a9b929f3c9 --- /dev/null +++ b/test/versioned/fastify-esm/newrelic.cjs @@ -0,0 +1,25 @@ +/* + * Copyright 2022 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict' + +exports.config = { + app_name: ['My Application'], + license_key: 'license key here', + logging: { + level: 'trace', + filepath: '../../../newrelic_agent.log' + }, + utilization: { + detect_aws: false, + detect_pcf: false, + detect_azure: false, + detect_gcp: false, + detect_docker: false + }, + transaction_tracer: { + enabled: true + } +} diff --git a/test/versioned/fastify-esm/package.json b/test/versioned/fastify-esm/package.json new file mode 100644 index 0000000000..c69001cedf --- /dev/null +++ b/test/versioned/fastify-esm/package.json @@ -0,0 +1,24 @@ +{ + "name": "fastify-esm-tests", + "targets-comment": "omitted as it is covered by the standard fastify tests", + "version": "0.0.0", + "type": "module", + "private": true, + "tests": [ + { + "engines": { + "node": ">=16" + }, + "dependencies": { + "fastify": ">=3.0.0", + "middie": "5.3.0" + }, + "files": [ + "issue-2155.test.mjs" + ] + } + ], + "engines": { + "node": ">=16" + } +}