From 7667ce6b80d50e28f8a4d8ee026eddd4fb3e8e3e Mon Sep 17 00:00:00 2001 From: Bob Evans Date: Wed, 17 Apr 2024 15:30:04 -0400 Subject: [PATCH] chore: migrated koa versioned tests to use agent_helper, moved symbols to lib/symbols, consolidated koa instrumentation into one folder --- lib/instrumentation/grpc-js/grpc.js | 4 +- .../{koa/lib => }/http-methods.js | 0 .../koa/{lib => }/instrumentation.js | 58 +- lib/instrumentation/koa/lib/symbols.js | 13 - lib/instrumentation/koa/nr-hooks.js | 8 +- .../koa/{lib => }/route-instrumentation.js | 5 +- .../koa/{lib => }/router-instrumentation.js | 13 +- lib/instrumentation/superagent.js | 3 +- lib/symbols.js | 4 + test/lib/agent_helper.js | 9 + .../koa/instrumentation.test.js | 2 +- test/unit/instrumentation/koa/koa.test.js | 2 +- test/unit/instrumentation/koa/route.test.js | 4 +- test/unit/instrumentation/koa/router.test.js | 4 +- test/versioned/koa/code-level-metrics.tap.js | 46 +- test/versioned/koa/koa-route.tap.js | 184 ++-- test/versioned/koa/koa.tap.js | 182 ++-- test/versioned/koa/router-common.js | 818 ++++++------------ test/versioned/koa/utils.js | 19 + 19 files changed, 514 insertions(+), 864 deletions(-) rename lib/instrumentation/{koa/lib => }/http-methods.js (100%) rename lib/instrumentation/koa/{lib => }/instrumentation.js (76%) delete mode 100644 lib/instrumentation/koa/lib/symbols.js rename lib/instrumentation/koa/{lib => }/route-instrumentation.js (85%) rename lib/instrumentation/koa/{lib => }/router-instrumentation.js (91%) create mode 100644 test/versioned/koa/utils.js diff --git a/lib/instrumentation/grpc-js/grpc.js b/lib/instrumentation/grpc-js/grpc.js index e57879a4e2..e27a07d6be 100644 --- a/lib/instrumentation/grpc-js/grpc.js +++ b/lib/instrumentation/grpc-js/grpc.js @@ -7,7 +7,7 @@ const recordExternal = require('../../metrics/recorders/http_external') const recordHttp = require('../../metrics/recorders/http') -const specs = require('../../shim/specs') +const { TransactionSpec } = require('../../shim/specs') const { DESTINATIONS } = require('../../config/attribute-filter') const DESTINATION = DESTINATIONS.TRANS_EVENT | DESTINATIONS.ERROR_EVENT const semver = require('semver') @@ -149,7 +149,7 @@ function wrapRegister(shim, original) { args[1] = shim.bindCreateTransaction( instrumentedHandler, - new specs.TransactionSpec({ type: shim.WEB }) + new TransactionSpec({ type: shim.WEB }) ) return original.apply(this, args) diff --git a/lib/instrumentation/koa/lib/http-methods.js b/lib/instrumentation/http-methods.js similarity index 100% rename from lib/instrumentation/koa/lib/http-methods.js rename to lib/instrumentation/http-methods.js diff --git a/lib/instrumentation/koa/lib/instrumentation.js b/lib/instrumentation/koa/instrumentation.js similarity index 76% rename from lib/instrumentation/koa/lib/instrumentation.js rename to lib/instrumentation/koa/instrumentation.js index f05c15ece2..1eadd3819b 100644 --- a/lib/instrumentation/koa/lib/instrumentation.js +++ b/lib/instrumentation/koa/instrumentation.js @@ -4,7 +4,8 @@ */ 'use strict' -const symbols = require('./symbols') +const symbols = require('../../symbols') +const { MiddlewareSpec, MiddlewareMounterSpec } = require('../../shim/specs') module.exports = function initialize(shim, Koa) { // Koa's exports are different depending on using CJS or MJS - https://github.com/koajs/koa/issues/1513 @@ -22,7 +23,7 @@ module.exports = function initialize(shim, Koa) { shim.wrapMiddlewareMounter( proto, 'use', - new shim.specs.MiddlewareMounterSpec({ + new MiddlewareMounterSpec({ wrapper: wrapMiddleware }) ) @@ -58,7 +59,7 @@ function wrapMiddleware(shim, middleware) { return shim.recordMiddleware( middleware, - new shim.specs.MiddlewareSpec({ + new MiddlewareSpec({ type: shim.MIDDLEWARE, promise: true, appendPath: true, @@ -70,34 +71,49 @@ function wrapMiddleware(shim, middleware) { ) } -function wrapCreateContext(shim, fn, fnName, context) { - // Many of the properties on the `context` object are just aliases for the same - // property on the `request` or `response` objects. We take advantage of this - // by just intercepting the `request` or `response` property and don't touch - // the `context` property. - // - // See: https://github.com/koajs/koa/blob/master/lib/context.js#L186-L241 +/** + * Many of the properties on the `context` object are just aliases for the same + * property on the `request` or `response` objects. We take advantage of this + * by just intercepting the `request` or `response` property and don't touch + * the `context` property. + * See: https://github.com/koajs/koa/blob/master/lib/context.js#L186-L241 + * + * @param {Shim} shim instance of shim + * @param {function} _fn createContext function + * @param {string} _fnName name of function + * @param {object} context koa ctx object + */ +function wrapCreateContext(shim, _fn, _fnName, context) { + wrapResponseBody(shim, context) + wrapMatchedRoute(shim, context) + wrapResponseStatus(shim, context) +} +function wrapResponseBody(shim, context) { // The `context.body` and `context.response.body` properties are how users set // the response contents. It is roughly equivalent to `res.send()` in Express. // Under the hood, these set the `_body` property on the `context.response`. - context[symbols.body] = context.response.body - context[symbols.bodySet] = false + context[symbols.koaBody] = context.response.body + context[symbols.koaBodySet] = false + Object.defineProperty(context.response, '_body', { - get: () => context[symbols.body], + get: () => context[symbols.koaBody], set: function setBody(val) { if (!context[symbols.koaRouter]) { shim.savePossibleTransactionName(context.req) } - context[symbols.body] = val - context[symbols.bodySet] = true + context[symbols.koaBody] = val + context[symbols.koaBodySet] = true } }) +} - context[symbols.matchedRoute] = null +function wrapMatchedRoute(shim, context) { + context[symbols.koaMatchedRoute] = null context[symbols.koaRouter] = false + Object.defineProperty(context, '_matchedRoute', { - get: () => context[symbols.matchedRoute], + get: () => context[symbols.koaMatchedRoute], set: (val) => { const match = getLayerForTransactionName(context) @@ -111,7 +127,7 @@ function wrapCreateContext(shim, fn, fnName, context) { if (currentSegment) { const transaction = currentSegment.transaction - if (context[symbols.matchedRoute]) { + if (context[symbols.koaMatchedRoute]) { transaction.nameState.popPath() } @@ -120,13 +136,15 @@ function wrapCreateContext(shim, fn, fnName, context) { } } - context[symbols.matchedRoute] = val + context[symbols.koaMatchedRoute] = val // still true if somehow match is undefined because we are // using koa-router naming and don't want to allow default naming context[symbols.koaRouter] = true } }) +} +function wrapResponseStatus(shim, context) { // Sometimes people just set `context.status` or `context.response.status` // without setting a body. When this happens we'll want to use that as the // response point to name the transaction. `context.status` is just an alias @@ -143,7 +161,7 @@ function wrapCreateContext(shim, fn, fnName, context) { Object.defineProperty(context.response, 'status', { get: () => statusDescriptor.get.call(context.response), set: function setStatus(val) { - if (!context[symbols.bodySet] && !context[symbols.koaRouter]) { + if (!context[symbols.koaBodySet] && !context[symbols.koaRouter]) { shim.savePossibleTransactionName(context.req) } return statusDescriptor.set.call(this, val) diff --git a/lib/instrumentation/koa/lib/symbols.js b/lib/instrumentation/koa/lib/symbols.js deleted file mode 100644 index cf6168cdf2..0000000000 --- a/lib/instrumentation/koa/lib/symbols.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2022 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -module.exports = { - body: Symbol('body'), - bodySet: Symbol('bodySet'), - koaRouter: Symbol('koaRouter'), - matchedRoute: Symbol('matchedRoute') -} diff --git a/lib/instrumentation/koa/nr-hooks.js b/lib/instrumentation/koa/nr-hooks.js index c9623164f2..2ab22b5a93 100644 --- a/lib/instrumentation/koa/nr-hooks.js +++ b/lib/instrumentation/koa/nr-hooks.js @@ -12,24 +12,24 @@ module.exports = [ type: InstrumentationDescriptor.TYPE_WEB_FRAMEWORK, moduleName: 'koa', shimName: 'koa', - onRequire: require('./lib/instrumentation') + onRequire: require('./instrumentation') }, { type: InstrumentationDescriptor.TYPE_WEB_FRAMEWORK, moduleName: 'koa-router', shimName: 'koa', - onRequire: require('./lib/router-instrumentation') + onRequire: require('./router-instrumentation') }, { type: InstrumentationDescriptor.TYPE_WEB_FRAMEWORK, moduleName: '@koa/router', shimName: 'koa', - onRequire: require('./lib/router-instrumentation') + onRequire: require('./router-instrumentation') }, { type: InstrumentationDescriptor.TYPE_WEB_FRAMEWORK, moduleName: 'koa-route', shimName: 'koa', - onRequire: require('./lib/route-instrumentation') + onRequire: require('./route-instrumentation') } ] diff --git a/lib/instrumentation/koa/lib/route-instrumentation.js b/lib/instrumentation/koa/route-instrumentation.js similarity index 85% rename from lib/instrumentation/koa/lib/route-instrumentation.js rename to lib/instrumentation/koa/route-instrumentation.js index 03b2b04156..e0e6ccaeb4 100644 --- a/lib/instrumentation/koa/lib/route-instrumentation.js +++ b/lib/instrumentation/koa/route-instrumentation.js @@ -5,7 +5,8 @@ 'use strict' -const { METHODS } = require('./http-methods') +const { METHODS } = require('../http-methods') +const { MiddlewareSpec } = require('../../shim/specs') module.exports = function instrumentRoute(shim, route) { shim.setFramework(shim.KOA) @@ -16,7 +17,7 @@ module.exports = function instrumentRoute(shim, route) { const middleware = methodFn.apply(route, arguments) return shim.recordMiddleware( middleware, - new shim.specs.MiddlewareSpec({ + new MiddlewareSpec({ route: arguments[0], next: shim.LAST, name: shim.getName(arguments[1]), diff --git a/lib/instrumentation/koa/lib/router-instrumentation.js b/lib/instrumentation/koa/router-instrumentation.js similarity index 91% rename from lib/instrumentation/koa/lib/router-instrumentation.js rename to lib/instrumentation/koa/router-instrumentation.js index f147ea04c7..829313970d 100644 --- a/lib/instrumentation/koa/lib/router-instrumentation.js +++ b/lib/instrumentation/koa/router-instrumentation.js @@ -4,7 +4,8 @@ */ 'use strict' -const symbols = require('./symbols') +const symbols = require('../../symbols') +const { MiddlewareSpec, MiddlewareMounterSpec } = require('../../shim/specs') module.exports = function instrumentRouter(shim, Router) { shim.setFramework(shim.KOA) @@ -19,7 +20,7 @@ module.exports = function instrumentRouter(shim, Router) { shim.wrapMiddlewareMounter( proto, 'param', - new shim.specs.MiddlewareMounterSpec({ + new MiddlewareMounterSpec({ route: shim.FIRST, wrapper: wrapParamware }) @@ -29,7 +30,7 @@ module.exports = function instrumentRouter(shim, Router) { function wrapParamware(shim, paramware, fnName, route) { return shim.recordParamware( paramware, - new shim.specs.MiddlewareSpec({ + new MiddlewareSpec({ name: route, next: shim.LAST, promise: true, @@ -46,7 +47,7 @@ function wrapMiddleware(shim, fn, name, layer) { return } - const spec = new shim.specs.MiddlewareSpec({ + const spec = new MiddlewareSpec({ route: () => layer.path, // defer retrieval next: shim.LAST, promise: true, @@ -72,7 +73,7 @@ function wrapAllowedMethods(shim, fn, name, allowedMethodsMiddleware) { return shim.recordMiddleware( wrapped, - new shim.specs.MiddlewareSpec({ + new MiddlewareSpec({ name: allowedMethodsMiddleware.name, promise: true, appendPath: false, @@ -99,7 +100,7 @@ function wrapRoutes(shim, fn, name, dispatchMiddleware) { } const wrappedDispatch = shim.recordMiddleware( dispatchMiddleware, - new shim.specs.MiddlewareSpec({ + new MiddlewareSpec({ type: shim.ROUTER, promise: true, appendPath: false, diff --git a/lib/instrumentation/superagent.js b/lib/instrumentation/superagent.js index 0d4560668f..e4f4d47227 100644 --- a/lib/instrumentation/superagent.js +++ b/lib/instrumentation/superagent.js @@ -5,8 +5,7 @@ 'use strict' -const http = require('http') -const METHODS = http.METHODS.map((method) => method.toLowerCase()) +const { METHODS } = require('./http-methods') module.exports = function instrument(agent, superagent, moduleName, shim) { shim.wrapExport(superagent, function wrapRequest(shim, request) { diff --git a/lib/symbols.js b/lib/symbols.js index 8d48933cf6..fdac740724 100644 --- a/lib/symbols.js +++ b/lib/symbols.js @@ -11,6 +11,10 @@ module.exports = { databaseName: Symbol('databaseName'), disableDT: Symbol('Disable distributed tracing'), // description for backwards compatibility executorContext: Symbol('executorContext'), + koaBody: Symbol('body'), + koaBodySet: Symbol('bodySet'), + koaRouter: Symbol('koaRouter'), + koaMatchedRoute: Symbol('matchedRoute'), name: Symbol('name'), onceExecuted: Symbol('onceExecuted'), offTheRecord: Symbol('offTheRecord'), diff --git a/test/lib/agent_helper.js b/test/lib/agent_helper.js index fb4ddbb3d9..7cbac195fd 100644 --- a/test/lib/agent_helper.js +++ b/test/lib/agent_helper.js @@ -653,3 +653,12 @@ helper.isSupportedVersion = function isSupportedVersion(version) { helper.destroyProxyAgent = function destroyProxyAgent() { require('../../lib/collector/http-agents').proxyAgent().destroy() } + +/** + * Gets a shim instance for a package. + * @param {object} pkg exported obj that is instrumented + * @returns The existing or newly created shim. + */ +helper.getShim = function getShim(pkg) { + return pkg?.[symbols.shim] +} diff --git a/test/unit/instrumentation/koa/instrumentation.test.js b/test/unit/instrumentation/koa/instrumentation.test.js index 0780afd3fb..51516c3a84 100644 --- a/test/unit/instrumentation/koa/instrumentation.test.js +++ b/test/unit/instrumentation/koa/instrumentation.test.js @@ -7,7 +7,7 @@ const tap = require('tap') const sinon = require('sinon') -const initialize = require('../../../../lib/instrumentation/koa/lib/instrumentation') +const initialize = require('../../../../lib/instrumentation/koa/instrumentation') tap.test('Koa instrumentation', (t) => { t.beforeEach((t) => { diff --git a/test/unit/instrumentation/koa/koa.test.js b/test/unit/instrumentation/koa/koa.test.js index 9dcb301a92..25178e79c5 100644 --- a/test/unit/instrumentation/koa/koa.test.js +++ b/test/unit/instrumentation/koa/koa.test.js @@ -14,7 +14,7 @@ tap.beforeEach((t) => { t.context.agent = helper.instrumentMockedAgent({ moduleName: 'koa', type: InstrumentationDescriptor.TYPE_WEB_FRAMEWORK, - onRequire: require('../../../../lib/instrumentation/koa/lib/instrumentation'), + onRequire: require('../../../../lib/instrumentation/koa/instrumentation'), shimName: 'koa' }) diff --git a/test/unit/instrumentation/koa/route.test.js b/test/unit/instrumentation/koa/route.test.js index f7e75b3dc7..8ea33c61ab 100644 --- a/test/unit/instrumentation/koa/route.test.js +++ b/test/unit/instrumentation/koa/route.test.js @@ -6,7 +6,7 @@ 'use strict' const tap = require('tap') -const { METHODS } = require('../../../../lib/instrumentation/koa/lib/http-methods') +const { METHODS } = require('../../../../lib/instrumentation/http-methods') const helper = require('../../../lib/agent_helper') const InstrumentationDescriptor = require('../../../../lib/instrumentation-descriptor') const symbols = require('../../../../lib/symbols') @@ -15,7 +15,7 @@ tap.beforeEach((t) => { t.context.agent = helper.instrumentMockedAgent({ moduleName: 'koa-route', type: InstrumentationDescriptor.TYPE_WEB_FRAMEWORK, - onRequire: require('../../../../lib/instrumentation/koa/lib/route-instrumentation'), + onRequire: require('../../../../lib/instrumentation/koa/route-instrumentation'), shimName: 'koa' }) diff --git a/test/unit/instrumentation/koa/router.test.js b/test/unit/instrumentation/koa/router.test.js index e9da897d60..f29b36023e 100644 --- a/test/unit/instrumentation/koa/router.test.js +++ b/test/unit/instrumentation/koa/router.test.js @@ -7,8 +7,8 @@ const tap = require('tap') -const instrumentation = require('../../../../lib/instrumentation/koa/lib/router-instrumentation') -const { METHODS } = require('../../../../lib/instrumentation/koa/lib/http-methods') +const instrumentation = require('../../../../lib/instrumentation/koa/router-instrumentation') +const { METHODS } = require('../../../../lib/instrumentation/http-methods') const helper = require('../../../lib/agent_helper') const InstrumentationDescriptor = require('../../../../lib/instrumentation-descriptor') const symbols = require('../../../../lib/symbols') diff --git a/test/versioned/koa/code-level-metrics.tap.js b/test/versioned/koa/code-level-metrics.tap.js index 0a79320128..889c3fdbb4 100644 --- a/test/versioned/koa/code-level-metrics.tap.js +++ b/test/versioned/koa/code-level-metrics.tap.js @@ -6,13 +6,8 @@ 'use strict' const tap = require('tap') -const utils = require('@newrelic/test-utilities') +const helper = require('../../lib/agent_helper') const http = require('http') -const hooks = require('../../../lib/instrumentation/koa/nr-hooks') - -// This adds all the assertions to tap's `Test` class. -utils.assert.extendTap(tap) - let koaRouterAvailable let atKoaRouterAvailable @@ -31,20 +26,15 @@ try { } async function setupApp({ useKoaRouter, useAtKoaRouter, isCLMEnabled }) { - const helper = utils.TestAgent.makeInstrumented({ code_level_metrics: { enabled: isCLMEnabled } }) + const agent = helper.instrumentMockedAgent({ code_level_metrics: { enabled: isCLMEnabled } }) let router - helper.registerInstrumentation(hooks[0]) - if (useKoaRouter) { - helper.registerInstrumentation(hooks[1]) - const Router = require('koa-router') router = new Router() } if (useAtKoaRouter) { - helper.registerInstrumentation(hooks[2]) const Router = require('@koa/router') router = new Router() } @@ -53,7 +43,7 @@ async function setupApp({ useKoaRouter, useAtKoaRouter, isCLMEnabled }) { const app = new Koa() const server = await startServer(app) - return { helper, app, router, server } + return { agent, app, router, server } } async function makeRequest(params) { @@ -87,10 +77,10 @@ async function startServer(app) { }) } -async function teardownApp(server, helper) { +async function teardownApp(server, agent) { return new Promise((resolve) => { - if (helper) { - helper.unload() + if (agent) { + helper.unloadAgent(agent) } if (server) { @@ -104,12 +94,12 @@ async function teardownApp(server, helper) { tap.test('Vanilla koa, no router', (t) => { t.autoend() - let helper + let agent let app let server ;[true, false].forEach((isCLMEnabled) => { t.test(`should ${isCLMEnabled ? 'add' : 'not add'} CLM attributes`, async (t) => { - ;({ helper, app, server } = await setupApp({ isCLMEnabled })) + ;({ agent, app, server } = await setupApp({ isCLMEnabled })) app.use(function one(_, next) { next() @@ -119,7 +109,7 @@ tap.test('Vanilla koa, no router', (t) => { ctx.body = 'done' }) - helper.agent.on('transactionFinished', (transaction) => { + agent.on('transactionFinished', (transaction) => { const baseSegment = transaction.trace.root.children[0] t.clmAttrs({ segments: [ @@ -144,7 +134,7 @@ tap.test('Vanilla koa, no router', (t) => { t.equal(response, 'done', 'should return the correct data') t.teardown(async () => { - await teardownApp(server, helper) + await teardownApp(server, agent) }) }) }) @@ -153,13 +143,13 @@ tap.test('Vanilla koa, no router', (t) => { tap.test('Using koa-router', { skip: !koaRouterAvailable }, (t) => { t.autoend() - let helper + let agent let app let server let router ;[true, false].forEach((isCLMEnabled) => { t.test(`should ${isCLMEnabled ? 'add' : 'not add'} CLM attributes`, async (t) => { - ;({ helper, app, server, router } = await setupApp({ isCLMEnabled, useKoaRouter: true })) + ;({ agent, app, server, router } = await setupApp({ isCLMEnabled, useKoaRouter: true })) const Router = require('koa-router') const nestedRouter = new Router() @@ -175,7 +165,7 @@ tap.test('Using koa-router', { skip: !koaRouterAvailable }, (t) => { router.use('/:first', nestedRouter.routes()) app.use(router.routes()) - helper.agent.on('transactionFinished', (transaction) => { + agent.on('transactionFinished', (transaction) => { const baseSegment = transaction.trace.root.children[0] t.clmAttrs({ @@ -204,7 +194,7 @@ tap.test('Using koa-router', { skip: !koaRouterAvailable }, (t) => { const response = await makeRequest({ port: server.address().port, path: '/123/second' }) t.equal(response, 'winner winner, chicken dinner', 'should return the correct data') t.teardown(async () => { - await teardownApp(server, helper) + await teardownApp(server, agent) }) }) }) @@ -213,13 +203,13 @@ tap.test('Using koa-router', { skip: !koaRouterAvailable }, (t) => { tap.test('Using @koa/router', { skip: !atKoaRouterAvailable }, (t) => { t.autoend() - let helper + let agent let app let server let router ;[true, false].forEach((isCLMEnabled) => { t.test(`should ${isCLMEnabled ? 'add' : 'not add'} CLM attributes`, async (t) => { - ;({ helper, app, server, router } = await setupApp({ isCLMEnabled, useAtKoaRouter: true })) + ;({ agent, app, server, router } = await setupApp({ isCLMEnabled, useAtKoaRouter: true })) const Router = require('@koa/router') const nestedRouter = new Router() @@ -235,7 +225,7 @@ tap.test('Using @koa/router', { skip: !atKoaRouterAvailable }, (t) => { router.use('/:first', nestedRouter.routes()) app.use(router.routes()) - helper.agent.on('transactionFinished', (transaction) => { + agent.on('transactionFinished', (transaction) => { const baseSegment = transaction.trace.root.children[0] t.clmAttrs({ @@ -265,7 +255,7 @@ tap.test('Using @koa/router', { skip: !atKoaRouterAvailable }, (t) => { t.equal(response, 'winner winner, chicken dinner', 'should return the correct data') t.teardown(async () => { - await teardownApp(server, helper) + await teardownApp(server, agent) }) }) }) diff --git a/test/versioned/koa/koa-route.tap.js b/test/versioned/koa/koa-route.tap.js index ee67bdd54f..9048776692 100644 --- a/test/versioned/koa/koa-route.tap.js +++ b/test/versioned/koa/koa-route.tap.js @@ -6,49 +6,33 @@ 'use strict' const tap = require('tap') -const utils = require('@newrelic/test-utilities') -const http = require('http') -const hooks = require('../../../lib/instrumentation/koa/nr-hooks') - -utils(tap) +const helper = require('../../lib/agent_helper') +require('../../lib/metrics_helper') +const { run } = require('./utils') tap.test('koa-route instrumentation', function (t) { - let helper = null - let app = null - let server = null - let route = null - - t.beforeEach(function () { - helper = utils.TestAgent.makeInstrumented() - helper.registerInstrumentation(hooks[0]) - helper.registerInstrumentation(hooks[3]) + t.beforeEach(function (t) { + t.context.agent = helper.instrumentMockedAgent() const Koa = require('koa') - app = new Koa() - route = require('koa-route') + t.context.app = new Koa() + t.context.route = require('koa-route') }) - t.afterEach(function () { - server.close() - app = null - route = null - helper && helper.unload() + t.afterEach(function (t) { + t.context.server.close() + helper.unloadAgent(t.context.agent) }) t.test('should name and produce segments for koa-route middleware', function (t) { + const { agent, app, route } = t.context const first = route.get('/resource', function firstMiddleware(ctx) { ctx.body = 'hello' }) app.use(first) - helper.agent.on('transactionFinished', function (tx) { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//resource', - children: [ - { - name: 'Nodejs/Middleware/Koa/firstMiddleware//resource' - } - ] - } + agent.on('transactionFinished', function (tx) { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//resource', + ['Nodejs/Middleware/Koa/firstMiddleware//resource'] ]) t.equal( tx.name, @@ -57,10 +41,11 @@ tap.test('koa-route instrumentation', function (t) { ) t.end() }) - run('/resource') + run({ path: '/resource', context: t.context }) }) t.test('should name the transaction after the last responder', function (t) { + const { agent, app, route } = t.context const first = route.get('/:first', function firstMiddleware(ctx, param, next) { ctx.body = 'first' return next() @@ -70,21 +55,13 @@ tap.test('koa-route instrumentation', function (t) { }) app.use(first) app.use(second) - helper.agent.on('transactionFinished', function (tx) { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:second', - children: [ - { - name: 'Nodejs/Middleware/Koa/firstMiddleware//:first', - children: [ - { - name: 'Nodejs/Middleware/Koa/secondMiddleware//:second' - } - ] - } - ] - } + agent.on('transactionFinished', function (tx) { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:second', + [ + 'Nodejs/Middleware/Koa/firstMiddleware//:first', + ['Nodejs/Middleware/Koa/secondMiddleware//:second'] + ] ]) t.equal( tx.name, @@ -93,10 +70,11 @@ tap.test('koa-route instrumentation', function (t) { ) t.end() }) - run() + run({ context: t.context }) }) t.test('should name the transaction properly when responding after next', function (t) { + const { agent, app, route } = t.context const first = route.get('/:first', function firstMiddleware(ctx, param, next) { return next().then(function respond() { ctx.body = 'first' @@ -107,21 +85,13 @@ tap.test('koa-route instrumentation', function (t) { }) app.use(first) app.use(second) - helper.agent.on('transactionFinished', function (tx) { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:first', - children: [ - { - name: 'Nodejs/Middleware/Koa/firstMiddleware//:first', - children: [ - { - name: 'Nodejs/Middleware/Koa/secondMiddleware//:second' - } - ] - } - ] - } + agent.on('transactionFinished', function (tx) { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:first', + [ + 'Nodejs/Middleware/Koa/firstMiddleware//:first', + ['Nodejs/Middleware/Koa/secondMiddleware//:second'] + ] ]) t.equal( tx.name, @@ -130,10 +100,11 @@ tap.test('koa-route instrumentation', function (t) { ) t.end() }) - run() + run({ context: t.context }) }) t.test('should work with early responding', function (t) { + const { agent, app, route } = t.context const first = route.get('/:first', function firstMiddleware(ctx) { ctx.body = 'first' return Promise.resolve() @@ -143,16 +114,10 @@ tap.test('koa-route instrumentation', function (t) { }) app.use(first) app.use(second) - helper.agent.on('transactionFinished', function (tx) { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:first', - children: [ - { - name: 'Nodejs/Middleware/Koa/firstMiddleware//:first' - } - ] - } + agent.on('transactionFinished', function (tx) { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:first', + ['Nodejs/Middleware/Koa/firstMiddleware//:first'] ]) t.equal( tx.name, @@ -161,10 +126,11 @@ tap.test('koa-route instrumentation', function (t) { ) t.end() }) - run() + run({ context: t.context }) }) t.test('should name the transaction after the source of the error that occurred', function (t) { + const { agent, app, route } = t.context const first = route.get('/:first', function firstMiddleware(ctx, param, next) { return next() }) @@ -173,21 +139,13 @@ tap.test('koa-route instrumentation', function (t) { }) app.use(first) app.use(second) - helper.agent.on('transactionFinished', function (tx) { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:second', - children: [ - { - name: 'Nodejs/Middleware/Koa/firstMiddleware//:first', - children: [ - { - name: 'Nodejs/Middleware/Koa/secondMiddleware//:second' - } - ] - } - ] - } + agent.on('transactionFinished', function (tx) { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:second', + [ + 'Nodejs/Middleware/Koa/firstMiddleware//:first', + ['Nodejs/Middleware/Koa/secondMiddleware//:second'] + ] ]) t.equal( tx.name, @@ -196,10 +154,11 @@ tap.test('koa-route instrumentation', function (t) { ) t.end() }) - run() + run({ context: t.context }) }) t.test('should work properly when used along with non-route middleware', function (t) { + const { agent, app, route } = t.context const first = function firstMiddleware(ctx, next) { return next() } @@ -213,26 +172,16 @@ tap.test('koa-route instrumentation', function (t) { app.use(first) app.use(second) app.use(third) - helper.agent.on('transactionFinished', function (tx) { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//resource', - children: [ - { - name: 'Nodejs/Middleware/Koa/firstMiddleware', - children: [ - { - name: 'Nodejs/Middleware/Koa/secondMiddleware//resource', - children: [ - { - name: 'Nodejs/Middleware/Koa/thirdMiddleware' - } - ] - } - ] - } + agent.on('transactionFinished', function (tx) { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//resource', + [ + 'Nodejs/Middleware/Koa/firstMiddleware', + [ + 'Nodejs/Middleware/Koa/secondMiddleware//resource', + ['Nodejs/Middleware/Koa/thirdMiddleware'] ] - } + ] ]) t.equal( tx.name, @@ -241,19 +190,8 @@ tap.test('koa-route instrumentation', function (t) { ) t.end() }) - run('/resource') + run({ path: '/resource', context: t.context }) }) - t.autoend() - - function run(path) { - server = app.listen(0, function () { - http - .get({ - port: server.address().port, - path: path || '/123' - }) - .end() - }) - } + t.end() }) diff --git a/test/versioned/koa/koa.tap.js b/test/versioned/koa/koa.tap.js index 39e06776bc..9f2844fcce 100644 --- a/test/versioned/koa/koa.tap.js +++ b/test/versioned/koa/koa.tap.js @@ -6,36 +6,27 @@ 'use strict' const tap = require('tap') -const utils = require('@newrelic/test-utilities') const http = require('http') -const hooks = require('../../../lib/instrumentation/koa/nr-hooks') - -utils(tap) +const helper = require('../../lib/agent_helper') +require('../../lib/metrics_helper') tap.test('Koa instrumentation', (t) => { t.autoend() - let helper = null - let app = null - let server = null - let testShim - t.beforeEach(() => { - helper = utils.TestAgent.makeInstrumented() - helper.registerInstrumentation(hooks[0]) + t.context.agent = helper.instrumentMockedAgent() const Koa = require('koa') - app = new Koa() - testShim = helper.getShim(Koa) + t.context.app = new Koa() + t.context.testShim = helper.getShim(Koa) }) - t.afterEach(() => { - server && server.close() - app = null - helper && helper.unload() + t.afterEach((t) => { + t.context.server.close() + helper.unloadAgent(t.context.agent) }) t.test('Should name after koa framework and verb when body set', (t) => { - t.plan(2) + const { agent, app } = t.context app.use(function one(ctx, next) { return next().then(() => { @@ -47,7 +38,7 @@ tap.test('Koa instrumentation', (t) => { ctx.body = 'done' }) - helper.agent.on('transactionFinished', (tx) => { + agent.on('transactionFinished', (tx) => { t.equal( tx.name, 'WebTransaction/WebFrameworkUri/Koa/GET//', @@ -59,7 +50,7 @@ tap.test('Koa instrumentation', (t) => { }) t.test('Should name (not found) when no work is performed', (t) => { - t.plan(2) + const { agent, app } = t.context app.use(function one(ctx, next) { return next().then(() => { @@ -71,7 +62,7 @@ tap.test('Koa instrumentation', (t) => { // do nothing }) - helper.agent.on('transactionFinished', (tx) => { + agent.on('transactionFinished', (tx) => { t.equal( tx.name, 'WebTransaction/WebFrameworkUri/Koa/GET/(not found)', @@ -83,20 +74,20 @@ tap.test('Koa instrumentation', (t) => { }) t.test('names the transaction after the middleware that sets the body', (t) => { - t.plan(2) + const { agent, app } = t.context app.use(function one(ctx, next) { - const tx = helper.getTransaction() + const tx = agent.getTransaction() return next().then(() => tx.nameState.appendPath('one-end')) }) app.use(function two(ctx) { - const tx = helper.getTransaction() + const tx = agent.getTransaction() tx.nameState.appendPath('two') ctx.body = 'done' }) - helper.agent.on('transactionFinished', (tx) => { + agent.on('transactionFinished', (tx) => { t.equal( tx.name, 'WebTransaction/WebFrameworkUri/Koa/GET//two', @@ -108,27 +99,27 @@ tap.test('Koa instrumentation', (t) => { }) t.test('names the transaction after the last middleware that sets the body', (t) => { - t.plan(2) + const { agent, app } = t.context app.use(function one(ctx, next) { - const tx = helper.getTransaction() + const tx = agent.getTransaction() return next().then(() => tx.nameState.appendPath('one-end')) }) app.use(function two(ctx, next) { - const tx = helper.getTransaction() + const tx = agent.getTransaction() tx.nameState.appendPath('two') ctx.body = 'not actually done' return next() }) app.use(function three(ctx) { - const tx = helper.getTransaction() + const tx = agent.getTransaction() tx.nameState.appendPath('three') ctx.body = 'done' }) - helper.agent.on('transactionFinished', (tx) => { + agent.on('transactionFinished', (tx) => { t.equal( tx.name, 'WebTransaction/WebFrameworkUri/Koa/GET//three', @@ -140,20 +131,20 @@ tap.test('Koa instrumentation', (t) => { }) t.test('names the transaction off the status setting middleware', (t) => { - t.plan(4) + const { agent, app } = t.context app.use(function one(ctx, next) { - const tx = helper.getTransaction() + const tx = agent.getTransaction() return next().then(() => tx.nameState.appendPath('one-end')) }) app.use(function two(ctx) { - const tx = helper.getTransaction() + const tx = agent.getTransaction() tx.nameState.appendPath('two') ctx.status = 202 }) - helper.agent.on('transactionFinished', (tx) => { + agent.on('transactionFinished', (tx) => { t.equal( tx.name, 'WebTransaction/WebFrameworkUri/Koa/GET//two', @@ -164,19 +155,20 @@ tap.test('Koa instrumentation', (t) => { run(t, 'Accepted', (err, res) => { t.error(err) t.equal(res.statusCode, 202, 'should not interfere with status code setting') + t.end() }) }) t.test('names the transaction when body set even if status set after', (t) => { - t.plan(4) + const { agent, app } = t.context app.use(function one(ctx, next) { - const tx = helper.getTransaction() + const tx = agent.getTransaction() return next().then(() => tx.nameState.appendPath('one-end')) }) app.use(function two(ctx) { - const tx = helper.getTransaction() + const tx = agent.getTransaction() tx.nameState.appendPath('two') ctx.body = 'done' @@ -184,7 +176,7 @@ tap.test('Koa instrumentation', (t) => { ctx.status = 202 }) - helper.agent.on('transactionFinished', (tx) => { + agent.on('transactionFinished', (tx) => { t.equal( tx.name, 'WebTransaction/WebFrameworkUri/Koa/GET//two', @@ -195,11 +187,12 @@ tap.test('Koa instrumentation', (t) => { run(t, (err, res) => { t.error(err) t.equal(res.statusCode, 202, 'should not interfere with status code setting') + t.end() }) }) t.test('produces transaction trace with multiple middleware', (t) => { - t.plan(2) + const { agent, app } = t.context app.use(function one(ctx, next) { return next() @@ -208,7 +201,7 @@ tap.test('Koa instrumentation', (t) => { ctx.response.body = 'done' }) - helper.agent.on('transactionFinished', (tx) => { + agent.on('transactionFinished', (tx) => { checkSegments(t, tx) }) @@ -216,7 +209,7 @@ tap.test('Koa instrumentation', (t) => { }) t.test('correctly records actions interspersed among middleware', (t) => { - t.plan(2) + const { agent, app, testShim } = t.context app.use(function one(ctx, next) { testShim.createSegment('testSegment') @@ -233,24 +226,18 @@ tap.test('Koa instrumentation', (t) => { ctx.body = 'done' }) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//', - children: [ - { - name: 'Nodejs/Middleware/Koa/one', - children: [ - { name: 'Truncated/testSegment' }, - { - name: 'Nodejs/Middleware/Koa/two', - children: [{ name: 'timers.setTimeout' }, { name: 'Nodejs/Middleware/Koa/three' }] - }, - { name: 'Truncated/nestedSegment' } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//', + [ + 'Nodejs/Middleware/Koa/one', + [ + 'Truncated/testSegment', + 'Nodejs/Middleware/Koa/two', + ['timers.setTimeout', ['Callback: '], 'Nodejs/Middleware/Koa/three'], + 'Truncated/nestedSegment' ] - } + ] ]) }) @@ -258,29 +245,32 @@ tap.test('Koa instrumentation', (t) => { }) t.test('maintains transaction state between middleware', (t) => { - t.plan(7) - + const { agent, app } = t.context let tx app.use(async function one(ctx, next) { - tx = helper.agent.getTransaction() + tx = agent.getTransaction() await next() - t.transaction(tx) + t.ok(tx) }) app.use(async function two(ctx, next) { - t.transaction(tx, 'two has transaction context') + t.equal(tx.id, agent.getTransaction().id, 'two has transaction context') await next() }) app.use(function three(ctx, next) { - t.transaction(tx, 'three has transaction context') + t.equal(tx.id, agent.getTransaction().id, 'three has transaction context') return new Promise((resolve) => { setImmediate(() => { next().then(() => { - t.transaction(tx, 'still have context after in-context timer hop') + t.equal( + tx.id, + agent.getTransaction().id, + 'still have context after in-context timer hop' + ) resolve() }) }) @@ -288,19 +278,28 @@ tap.test('Koa instrumentation', (t) => { }) app.use(function four(ctx) { - t.transaction(tx, 'four has transaction context') + t.equal(tx.id, agent.getTransaction().id, 'four has transaction context') ctx.body = 'done' }) - helper.agent.on('transactionFinished', function (txn) { - checkSegments(t, txn) + agent.on('transactionFinished', function (txn) { + t.assertSegments(tx.trace.root, [ + txn.name, + [ + 'Nodejs/Middleware/Koa/one', + [ + 'Nodejs/Middleware/Koa/two', + ['Nodejs/Middleware/Koa/three', ['Nodejs/Middleware/Koa/four']] + ] + ] + ]) }) run(t) }) t.test('errors handled within middleware are not recorded', (t) => { - t.plan(4) + const { agent, app } = t.context app.use(function one(ctx, next) { return next().catch(function (err) { @@ -314,8 +313,8 @@ tap.test('Koa instrumentation', (t) => { ctx.body = 'done' }) - helper.agent.on('transactionFinished', (tx) => { - const errors = helper.agent.errors.traceAggregator.errors + agent.on('transactionFinished', (tx) => { + const errors = agent.errors.traceAggregator.errors t.equal(errors.length, 0, 'no errors are recorded') checkSegments(t, tx) }) @@ -324,7 +323,7 @@ tap.test('Koa instrumentation', (t) => { }) t.test('errors not handled by middleware are recorded', (t) => { - t.plan(5) + const { agent, app } = t.context app.use(function one(ctx, next) { return next().catch(function (err) { @@ -337,8 +336,8 @@ tap.test('Koa instrumentation', (t) => { throw new Error('middleware error') }) - helper.agent.on('transactionFinished', (tx) => { - const errors = helper.agent.errors.traceAggregator.errors + agent.on('transactionFinished', (tx) => { + const errors = agent.errors.traceAggregator.errors t.equal(errors.length, 1, 'recorded expected number of errors') const error = errors[0][2] t.equal(error, 'middleware error', 'recorded expected error') @@ -348,7 +347,7 @@ tap.test('Koa instrumentation', (t) => { }) t.test('errors caught by default error listener are recorded', (t) => { - t.plan(5) + const { agent, app } = t.context app.use(function one(ctx, next) { return next() @@ -360,8 +359,8 @@ tap.test('Koa instrumentation', (t) => { t.equal(err.message, 'middleware error', 'caught expected error') }) - helper.agent.on('transactionFinished', (tx) => { - const errors = helper.agent.errors.traceAggregator.errors + agent.on('transactionFinished', (tx) => { + const errors = agent.errors.traceAggregator.errors t.equal(errors.length, 1, 'recorded expected number of errors') const error = errors[0][2] t.equal(error, 'middleware error', 'recorded expected error') @@ -377,8 +376,8 @@ tap.test('Koa instrumentation', (t) => { expected = 'done' } - server = app.listen(0, () => { - http.get({ port: server.address().port }, (res) => { + t.context.server = t.context.app.listen(0, () => { + http.get({ port: t.context.server.address().port }, (res) => { let body = '' res.on('data', (data) => (body += data.toString('utf8'))) res.on('error', (err) => cb && cb(err)) @@ -386,9 +385,13 @@ tap.test('Koa instrumentation', (t) => { if (expected) { t.equal(body, expected, 'should send expected response') } - if (cb) { - cb(null, res) + + if (!cb) { + t.end() + return } + + cb(null, res) }) }) }) @@ -396,17 +399,10 @@ tap.test('Koa instrumentation', (t) => { }) function checkSegments(t, tx) { - t.exactSegments(tx.trace.root, [ - { - // Until koa-router is instrumented and transaction naming is addressed, - // names will be inconsistent depending on whether there is an error. - name: tx.name, - children: [ - { - name: 'Nodejs/Middleware/Koa/one', - children: [{ name: 'Nodejs/Middleware/Koa/two' }] - } - ] - } + t.assertSegments(tx.trace.root, [ + // Until koa-router is instrumented and transaction naming is addressed, + // names will be inconsistent depending on whether there is an error. + tx.name, + ['Nodejs/Middleware/Koa/one', ['Nodejs/Middleware/Koa/two']] ]) } diff --git a/test/versioned/koa/router-common.js b/test/versioned/koa/router-common.js index 726b1478f3..50492b76ea 100644 --- a/test/versioned/koa/router-common.js +++ b/test/versioned/koa/router-common.js @@ -5,24 +5,15 @@ 'use strict' -const hooks = require('../../../lib/instrumentation/koa/nr-hooks') - module.exports = (pkg) => { const tap = require('tap') - const utils = require('@newrelic/test-utilities') - const http = require('http') + require('../../lib/metrics_helper') + const helper = require('../../lib/agent_helper') const semver = require('semver') - - utils(tap) + const { run } = require('./utils') tap.test(`${pkg} instrumentation`, (t) => { - let helper = null - let app = null - let server = null - let router = null - let Router = null - let pkgVersion = null - + const { version: pkgVersion } = require(`${pkg}/package.json`) const paramMiddlewareName = 'Nodejs/Middleware/Koa/middleware//:first' /** @@ -43,33 +34,19 @@ module.exports = (pkg) => { return spanName } - function testSetup() { - ;({ version: pkgVersion } = require(`${pkg}/package.json`)) - helper = utils.TestAgent.makeInstrumented() - - hooks.forEach((hook) => { - helper.registerInstrumentation(hook) - }) + function testSetup(t) { + t.context.agent = helper.instrumentMockedAgent() const Koa = require('koa') - app = new Koa() - Router = require(pkg) - router = new Router() + t.context.app = new Koa() + const Router = require(pkg) + t.context.router = new Router() + t.context.Router = Router } - function tearDown() { - return new Promise((resolve) => { - server.close(() => { - app = null - router = null - Router = null - helper && helper.unload() - - server = null - - resolve() - }) - }) + function tearDown(t) { + t.context.server.close() + helper.unloadAgent(t.context.agent) } t.test('with single router', (t) => { @@ -78,6 +55,7 @@ module.exports = (pkg) => { t.autoend() t.test('should name and produce segments for matched path', (t) => { + const { agent, router, app } = t.context router.get( '/:first', function firstMiddleware(ctx, next) { @@ -91,26 +69,16 @@ module.exports = (pkg) => { ) app.use(router.routes()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:first', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/firstMiddleware//:first', - children: [ - { - name: 'Nodejs/Middleware/Koa/secondMiddleware//:first' - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:first', + [ + 'Koa/Router: /', + [ + 'Nodejs/Middleware/Koa/firstMiddleware//:first', + ['Nodejs/Middleware/Koa/secondMiddleware//:first'] ] - } + ] ]) t.equal( tx.name, @@ -119,29 +87,19 @@ module.exports = (pkg) => { ) t.end() }) - run() + run({ context: t.context }) }) t.test('should name after matched path using middleware() alias', (t) => { + const { agent, router, app } = t.context router.get('/:first', function firstMiddleware(ctx) { ctx.body = 'first' }) app.use(router.middleware()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:first', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/firstMiddleware//:first' - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:first', + ['Koa/Router: /', ['Nodejs/Middleware/Koa/firstMiddleware//:first']] ]) t.equal( tx.name, @@ -150,42 +108,33 @@ module.exports = (pkg) => { ) t.end() }) - run() + run({ context: t.context }) }) t.test('should handle transaction state loss', (t) => { + const { agent, router, app } = t.context let savedCtx = null router.get('/:any', (ctx) => { savedCtx = ctx }) app.use(router.middleware()) - helper.agent.on('transactionFinished', () => { + agent.on('transactionFinished', () => { t.doesNotThrow(() => (savedCtx._matchedRoute = 'test')) t.end() }) - run() + run({ context: t.context }) }) t.test('should name and produce segments for matched regex path', (t) => { + const { agent, router, app } = t.context router.get(/.*rst$/, function firstMiddleware(ctx) { ctx.body = 'first' }) app.use(router.routes()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//.*rst$', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/firstMiddleware//.*rst$/' - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//.*rst$', + ['Koa/Router: /', ['Nodejs/Middleware/Koa/firstMiddleware//.*rst$/']] ]) t.equal( tx.name, @@ -194,29 +143,19 @@ module.exports = (pkg) => { ) t.end() }) - run('/first') + run({ path: '/first', context: t.context }) }) t.test('should name and produce segments for matched wildcard path', (t) => { + const { agent, router, app } = t.context router.get('/:first/(.*)', function firstMiddleware(ctx) { ctx.body = 'first' }) app.use(router.routes()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:first/(.*)', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/firstMiddleware//:first/(.*)' - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:first/(.*)', + ['Koa/Router: /', ['Nodejs/Middleware/Koa/firstMiddleware//:first/(.*)']] ]) t.equal( tx.name, @@ -225,10 +164,11 @@ module.exports = (pkg) => { ) t.end() }) - run('/123/456') + run({ path: '/123/456', context: t.context }) }) t.test('should name and produce segments with router paramware', (t) => { + const { agent, router, app } = t.context router.param('first', function firstParamware(id, ctx, next) { ctx.body = 'first' return next() @@ -237,31 +177,19 @@ module.exports = (pkg) => { return next() }) app.use(router.routes()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:first', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: paramMiddlewareName, - children: [ - { - name: 'Nodejs/Middleware/Koa/firstParamware//[param handler :first]', - children: [ - { - name: 'Nodejs/Middleware/Koa/firstMiddleware//:first' - } - ] - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:first', + [ + 'Koa/Router: /', + [ + paramMiddlewareName, + [ + 'Nodejs/Middleware/Koa/firstParamware//[param handler :first]', + ['Nodejs/Middleware/Koa/firstMiddleware//:first'] + ] ] - } + ] ]) t.equal( tx.name, @@ -270,38 +198,29 @@ module.exports = (pkg) => { ) t.end() }) - run() + run({ context: t.context }) }) t.test('should name transaction after matched path with erroring parameware', (t) => { + const { agent, router, app } = t.context router.param('first', function firstParamware() { throw new Error('wrong param') }) router.get('/:first', function firstMiddleware() {}) app.use(router.routes()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:first', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: paramMiddlewareName, - children: [ - { - name: 'Nodejs/Middleware/Koa/firstParamware//[param handler :first]' - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:first', + [ + 'Koa/Router: /', + [ + paramMiddlewareName, + ['Nodejs/Middleware/Koa/firstParamware//[param handler :first]'] ] - } + ] ]) - const errors = helper.agent.errors.eventAggregator + const errors = agent.errors.eventAggregator t.equal(errors.length, 1, 'the error has been recorded') t.equal( tx.name, @@ -310,10 +229,11 @@ module.exports = (pkg) => { ) t.end() }) - run() + run({ context: t.context }) }) t.test('should name the transaction after the last matched path (layer)', (t) => { + const { agent, router, app } = t.context router.get('/:first', function firstMiddleware(ctx, next) { ctx.body = 'first' return next().then(function someMoreContent() { @@ -325,26 +245,16 @@ module.exports = (pkg) => { }) app.use(router.routes()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:second', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/firstMiddleware//:first', - children: [ - { - name: 'Nodejs/Middleware/Koa/secondMiddleware//:second' - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:second', + [ + 'Koa/Router: /', + [ + 'Nodejs/Middleware/Koa/firstMiddleware//:first', + ['Nodejs/Middleware/Koa/secondMiddleware//:second'] ] - } + ] ]) t.equal( tx.name, @@ -353,10 +263,11 @@ module.exports = (pkg) => { ) t.end() }) - run() + run({ context: t.context }) }) t.test('tx name should not be named after error handling middleware', (t) => { + const { agent, router, app } = t.context app.use(function errorHandler(ctx, next) { return next().catch((err) => { ctx.body = { err: err.message } @@ -368,28 +279,15 @@ module.exports = (pkg) => { }) app.use(router.routes()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:first', - children: [ - { - name: 'Nodejs/Middleware/Koa/errorHandler', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/firstMiddleware//:first' - } - ] - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:first', + [ + 'Nodejs/Middleware/Koa/errorHandler', + ['Koa/Router: /', ['Nodejs/Middleware/Koa/firstMiddleware//:first']] + ] ]) - const errors = helper.agent.errors.eventAggregator + const errors = agent.errors.eventAggregator t.equal(errors.length, 0, 'should not record error') t.equal( tx.name, @@ -398,10 +296,11 @@ module.exports = (pkg) => { ) t.end() }) - run() + run({ context: t.context }) }) t.test('transaction name should not be affected by unhandled error', (t) => { + const { agent, router, app } = t.context app.use(function errorHandler(ctx, next) { return next() }) @@ -411,28 +310,15 @@ module.exports = (pkg) => { }) app.use(router.routes()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:first', - children: [ - { - name: 'Nodejs/Middleware/Koa/errorHandler', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/firstMiddleware//:first' - } - ] - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:first', + [ + 'Nodejs/Middleware/Koa/errorHandler', + ['Koa/Router: /', ['Nodejs/Middleware/Koa/firstMiddleware//:first']] + ] ]) - const errors = helper.agent.errors.eventAggregator + const errors = agent.errors.eventAggregator t.equal(errors.length, 1, 'error should be recorded') t.equal( tx.name, @@ -441,10 +327,11 @@ module.exports = (pkg) => { ) t.end() }) - run() + run({ context: t.context }) }) t.test('should name tx after route declarations with supported http methods', (t) => { + const { agent, router, app } = t.context // This will register the same middleware (i.e. secondMiddleware) // under both the /:first and /:second routes. Use does not register middleware // w/ supported methods they cannot handle routes. @@ -456,31 +343,19 @@ module.exports = (pkg) => { ctx.body = ' second' }) app.use(router.routes()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:second', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/secondMiddleware//:first', - children: [ - { - name: 'Nodejs/Middleware/Koa/secondMiddleware//:second', - children: [ - { - name: 'Nodejs/Middleware/Koa/terminalMiddleware//:second' - } - ] - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:second', + [ + 'Koa/Router: /', + [ + 'Nodejs/Middleware/Koa/secondMiddleware//:first', + [ + 'Nodejs/Middleware/Koa/secondMiddleware//:second', + ['Nodejs/Middleware/Koa/terminalMiddleware//:second'] + ] ] - } + ] ]) t.equal( tx.name, @@ -489,10 +364,11 @@ module.exports = (pkg) => { ) t.end() }) - run() + run({ context: t.context }) }) t.test('names transaction (not found) with array of paths and no handler', (t) => { + const { agent, router, app } = t.context // This will register the same middleware (i.e. secondMiddleware) // under both the /:first and /:second routes. router.use(['/:first', '/:second'], function secondMiddleware(ctx, next) { @@ -500,16 +376,10 @@ module.exports = (pkg) => { return next() }) app.use(router.routes()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET/(not found)', - children: [ - { - name: 'Koa/Router: /' - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET/(not found)', + ['Koa/Router: /'] ]) t.equal( tx.name, @@ -518,12 +388,13 @@ module.exports = (pkg) => { ) t.end() }) - run() + run({ context: t.context }) }) t.test( 'names tx (not found) when no matching route and base middleware does not set body', (t) => { + const { agent, router, app } = t.context app.use(function baseMiddleware(ctx, next) { next() }) @@ -534,21 +405,10 @@ module.exports = (pkg) => { ctx.body = 'first' }) app.use(router.routes()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET/(not found)', - children: [ - { - name: 'Nodejs/Middleware/Koa/baseMiddleware', - children: [ - { - name: 'Koa/Router: /' - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET/(not found)', + ['Nodejs/Middleware/Koa/baseMiddleware', ['Koa/Router: /']] ]) t.equal( tx.name, @@ -557,7 +417,7 @@ module.exports = (pkg) => { ) t.end() }) - run('/') + run({ path: '/', context: t.context }) } ) }) @@ -568,7 +428,8 @@ module.exports = (pkg) => { t.autoend() t.test('should name transaction after last route for identical matches', (t) => { - Router = require(pkg) + const { agent, router, app } = t.context + const Router = require(pkg) const router2 = new Router() router.get('/:first', function firstMiddleware(ctx, next) { ctx.body = 'first' @@ -580,36 +441,21 @@ module.exports = (pkg) => { }) app.use(router.routes()) app.use(router2.routes()) - helper.agent.on('transactionFinished', (tx) => { + agent.on('transactionFinished', (tx) => { // NOTE: due to an implementation detail in koa-compose, // sequential middleware will show up as nested. This is due to // the dispatch function blocking its returned promise on the // resolution of a recursively returned promise. // https://github.com/koajs/compose/blob/e754ca3c13e9248b3f453d98ea0b618e09578e2d/index.js#L42-L44 - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:second', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/firstMiddleware//:first', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/secondMiddleware//:second' - } - ] - } - ] - } - ] - } + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:second', + [ + 'Koa/Router: /', + [ + 'Nodejs/Middleware/Koa/firstMiddleware//:first', + ['Koa/Router: /', ['Nodejs/Middleware/Koa/secondMiddleware//:second']] ] - } + ] ]) t.equal( tx.name, @@ -618,11 +464,12 @@ module.exports = (pkg) => { ) t.end() }) - run() + run({ context: t.context }) }) t.test('should name tx after last matched route even if body not set', (t) => { - Router = require(pkg) + const { agent, router, app } = t.context + const Router = require(pkg) const router2 = new Router() router.get('/first', function firstMiddleware(ctx, next) { ctx.body = 'first' @@ -632,36 +479,21 @@ module.exports = (pkg) => { router2.get('/:second', function secondMiddleware() {}) app.use(router.routes()) app.use(router2.routes()) - helper.agent.on('transactionFinished', (tx) => { + agent.on('transactionFinished', (tx) => { // NOTE: due to an implementation detail in koa-compose, // sequential middleware will show up as nested. This is due to // the dispatch function blocking its returned promise on the // resolution of a recursively returned promise. // https://github.com/koajs/compose/blob/e754ca3c13e9248b3f453d98ea0b618e09578e2d/index.js#L42-L44 - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:second', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/firstMiddleware//first', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/secondMiddleware//:second' - } - ] - } - ] - } - ] - } + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:second', + [ + 'Koa/Router: /', + [ + 'Nodejs/Middleware/Koa/firstMiddleware//first', + ['Koa/Router: /', ['Nodejs/Middleware/Koa/secondMiddleware//:second']] ] - } + ] ]) t.equal( tx.name, @@ -670,7 +502,7 @@ module.exports = (pkg) => { ) t.end() }) - run('/first') + run({ path: '/first', context: t.context }) }) }) @@ -680,27 +512,17 @@ module.exports = (pkg) => { t.autoend() t.test('should name after most last matched path', (t) => { + const { agent, router, Router, app } = t.context const router2 = new Router() router2.get('/:second', function secondMiddleware(ctx) { ctx.body = ' second' }) router.use('/:first', router2.routes()) app.use(router.routes()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:first/:second', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: getNestedSpanName('secondMiddleware') - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:first/:second', + ['Koa/Router: /', [getNestedSpanName('secondMiddleware')]] ]) t.equal( tx.name, @@ -709,10 +531,11 @@ module.exports = (pkg) => { ) t.end() }) - run('/123/456/') + run({ path: '/123/456/', context: t.context }) }) t.test('app-level middleware should not rename tx from matched path', (t) => { + const { agent, router, Router, app } = t.context app.use(function appLevelMiddleware(ctx, next) { return next().then(() => { ctx.body = 'do not want this to set the name' @@ -729,26 +552,13 @@ module.exports = (pkg) => { router.use('/:first', nestedRouter.routes()) app.use(router.routes()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:first/second', - children: [ - { - name: 'Nodejs/Middleware/Koa/appLevelMiddleware', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: getNestedSpanName('terminalMiddleware') - } - ] - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:first/second', + [ + 'Nodejs/Middleware/Koa/appLevelMiddleware', + ['Koa/Router: /', [getNestedSpanName('terminalMiddleware')]] + ] ]) t.equal( tx.name, @@ -757,10 +567,11 @@ module.exports = (pkg) => { ) t.end() }) - run('/123/second') + run({ path: '/123/second', context: t.context }) }) t.test('app-level middleware should not rename tx from matched prefix path', (t) => { + const { agent, router, app } = t.context app.use(function appLevelMiddleware(ctx, next) { return next().then(() => { ctx.body = 'do not want this to set the name' @@ -776,26 +587,13 @@ module.exports = (pkg) => { router.prefix('/:first') app.use(router.routes()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:first/second', - children: [ - { - name: 'Nodejs/Middleware/Koa/appLevelMiddleware', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/terminalMiddleware//:first/:second' - } - ] - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:first/second', + [ + 'Nodejs/Middleware/Koa/appLevelMiddleware', + ['Koa/Router: /', ['Nodejs/Middleware/Koa/terminalMiddleware//:first/:second']] + ] ]) t.equal( tx.name, @@ -804,7 +602,7 @@ module.exports = (pkg) => { ) t.end() }) - run('/123/second') + run({ path: '/123/second', context: t.context }) }) }) @@ -817,57 +615,37 @@ module.exports = (pkg) => { t.autoend() t.test('should name transaction after status `method now allowed` message', (t) => { + const { agent, router, app } = t.context router.post('/:first', function firstMiddleware() {}) app.use(router.routes()) app.use(router.allowedMethods({ throw: true })) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET/(method not allowed)', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/allowedMethods' - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET/(method not allowed)', + ['Koa/Router: /', ['Nodejs/Middleware/Koa/allowedMethods']] ]) t.equal( tx.name, 'WebTransaction/WebFrameworkUri/Koa/GET/(method not allowed)', 'transaction should be named after corresponding status code message' ) - const errors = helper.agent.errors.eventAggregator + const errors = agent.errors.eventAggregator t.equal(errors.length, 1, 'the error has been recorded') t.end() }) - run() + run({ context: t.context }) }) t.test('should name transaction after status `not implemented` message', (t) => { - router = new Router({ methods: ['POST'] }) + const { agent, Router, app } = t.context + const router = new Router({ methods: ['POST'] }) router.post('/:first', function firstMiddleware() {}) app.use(router.routes()) app.use(router.allowedMethods({ throw: true })) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET/(not implemented)', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/allowedMethods' - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET/(not implemented)', + ['Koa/Router: /', ['Nodejs/Middleware/Koa/allowedMethods']] ]) t.equal( tx.name, @@ -875,14 +653,15 @@ module.exports = (pkg) => { 'transaction should be named after corresponding status code message' ) - const errors = helper.agent.errors.eventAggregator + const errors = agent.errors.eventAggregator t.equal(errors.length, 1, 'the error has been recorded') t.end() }) - run() + run({ context: t.context }) }) t.test('error handler normalizes tx name if body is reset without status', (t) => { + const { agent, router, Router, app } = t.context app.use(function errorHandler(ctx, next) { return next().catch(() => { // resetting the body without manually persisting ctx.status @@ -899,42 +678,30 @@ module.exports = (pkg) => { app.use(router.routes()) app.use(router.allowedMethods({ throw: true })) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/NormalizedUri/*', - children: [ - { - name: 'Nodejs/Middleware/Koa/errorHandler', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/allowedMethods' - } - ] - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/NormalizedUri/*', + [ + 'Nodejs/Middleware/Koa/errorHandler', + ['Koa/Router: /', ['Nodejs/Middleware/Koa/allowedMethods']] + ] ]) t.equal( tx.name, 'WebTransaction/NormalizedUri/*', 'should have normalized transaction name' ) - const errors = helper.agent.errors.eventAggregator + const errors = agent.errors.eventAggregator t.equal(errors.length, 0, 'error should not be recorded') t.end() }) - run('/123/456') + run({ path: '/123/456', context: t.context }) }) t.test( 'should name tx after status message when base middleware does not set body', (t) => { + const { agent, router, Router, app } = t.context // Because allowedMethods throws & no user catching, it is considered // unhandled and will push the base route back on app.use(function baseMiddleware(ctx, next) { @@ -950,38 +717,25 @@ module.exports = (pkg) => { app.use(router.routes()) app.use(router.allowedMethods({ throw: true })) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET/(method not allowed)', - children: [ - { - name: 'Nodejs/Middleware/Koa/baseMiddleware', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/allowedMethods' - } - ] - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET/(method not allowed)', + [ + 'Nodejs/Middleware/Koa/baseMiddleware', + ['Koa/Router: /', ['Nodejs/Middleware/Koa/allowedMethods']] + ] ]) t.equal( tx.name, 'WebTransaction/WebFrameworkUri/Koa/GET/(method not allowed)', 'should name after returned status code' ) - const errors = helper.agent.errors.eventAggregator + const errors = agent.errors.eventAggregator t.equal(errors.length, 1, 'should notice thrown error') t.end() }) - run('/123/456') + run({ path: '/123/456', context: t.context }) } ) }) @@ -992,24 +746,14 @@ module.exports = (pkg) => { t.autoend() t.test('should name transaction after status `method now allowed` message', (t) => { + const { agent, router, app } = t.context router.post('/:first', function firstMiddleware() {}) app.use(router.routes()) app.use(router.allowedMethods()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET/(method not allowed)', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/allowedMethods' - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET/(method not allowed)', + ['Koa/Router: /', ['Nodejs/Middleware/Koa/allowedMethods']] ]) t.equal( tx.name, @@ -1017,33 +761,23 @@ module.exports = (pkg) => { 'transaction should be named after corresponding status code message' ) // Agent will automatically create error for 405 status code. - const errors = helper.agent.errors.eventAggregator + const errors = agent.errors.eventAggregator t.equal(errors.length, 1, 'the error has been recorded') t.end() }) - run() + run({ context: t.context }) }) t.test('should name transaction after status `not implemented` message', (t) => { - router = new Router({ methods: ['POST'] }) + const { agent, app, Router } = t.context + const router = new Router({ methods: ['POST'] }) router.post('/:first', function firstMiddleware() {}) app.use(router.routes()) app.use(router.allowedMethods()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET/(not implemented)', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/allowedMethods' - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET/(not implemented)', + ['Koa/Router: /', ['Nodejs/Middleware/Koa/allowedMethods']] ]) t.equal( tx.name, @@ -1051,14 +785,15 @@ module.exports = (pkg) => { 'transaction should be named after corresponding status code message' ) // Agent will automatically create error for 501 status code. - const errors = helper.agent.errors.eventAggregator + const errors = agent.errors.eventAggregator t.equal(errors.length, 1, 'the error has been recorded') t.end() }) - run() + run({ context: t.context }) }) t.test('should name tx after `method not allowed` with prefixed router', (t) => { + const { agent, router, app } = t.context app.use(function appLevelMiddleware(ctx, next) { return next().then(() => { ctx.body = 'should not set the name' @@ -1071,26 +806,13 @@ module.exports = (pkg) => { app.use(router.routes()) app.use(router.allowedMethods()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET/(method not allowed)', - children: [ - { - name: 'Nodejs/Middleware/Koa/appLevelMiddleware', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/allowedMethods' - } - ] - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET/(method not allowed)', + [ + 'Nodejs/Middleware/Koa/appLevelMiddleware', + ['Koa/Router: /', ['Nodejs/Middleware/Koa/allowedMethods']] + ] ]) t.equal( tx.name, @@ -1099,11 +821,12 @@ module.exports = (pkg) => { ) t.end() }) - run('/123/second') + run({ path: '/123/second', context: t.context }) }) t.test('should name tx after `not implemented` with prefixed router', (t) => { - router = new Router({ methods: ['POST'] }) + const { agent, app, Router } = t.context + const router = new Router({ methods: ['POST'] }) app.use(function appLevelMiddleware(ctx, next) { return next().then(() => { @@ -1117,26 +840,13 @@ module.exports = (pkg) => { app.use(router.routes()) app.use(router.allowedMethods()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET/(not implemented)', - children: [ - { - name: 'Nodejs/Middleware/Koa/appLevelMiddleware', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/allowedMethods' - } - ] - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET/(not implemented)', + [ + 'Nodejs/Middleware/Koa/appLevelMiddleware', + ['Koa/Router: /', ['Nodejs/Middleware/Koa/allowedMethods']] + ] ]) t.equal( tx.name, @@ -1145,31 +855,21 @@ module.exports = (pkg) => { ) t.end() }) - run('/123/first') + run({ path: '/123/first', context: t.context }) }) t.test('should name and produce segments for existing matched path', (t) => { - router = new Router({ methods: ['GET'] }) + const { agent, app, Router } = t.context + const router = new Router({ methods: ['GET'] }) router.get('/:first', function firstMiddleware(ctx) { ctx.body = 'first' }) app.use(router.routes()) app.use(router.allowedMethods()) - helper.agent.on('transactionFinished', (tx) => { - t.exactSegments(tx.trace.root, [ - { - name: 'WebTransaction/WebFrameworkUri/Koa/GET//:first', - children: [ - { - name: 'Koa/Router: /', - children: [ - { - name: 'Nodejs/Middleware/Koa/firstMiddleware//:first' - } - ] - } - ] - } + agent.on('transactionFinished', (tx) => { + t.assertSegments(tx.trace.root, [ + 'WebTransaction/WebFrameworkUri/Koa/GET//:first', + ['Koa/Router: /', ['Nodejs/Middleware/Koa/firstMiddleware//:first']] ]) t.equal( tx.name, @@ -1178,22 +878,10 @@ module.exports = (pkg) => { ) t.end() }) - run() + run({ context: t.context }) }) }) }) - - t.autoend() - - function run(path) { - server = app.listen(0, function () { - http - .get({ - port: server.address().port, - path: path || '/123' - }) - .end() - }) - } + t.end() }) } diff --git a/test/versioned/koa/utils.js b/test/versioned/koa/utils.js new file mode 100644 index 0000000000..c367aca4af --- /dev/null +++ b/test/versioned/koa/utils.js @@ -0,0 +1,19 @@ +/* + * Copyright 2024 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict' +const utils = module.exports +const http = require('http') + +utils.run = function run({ path = '/123', context }) { + context.server = context.app.listen(0, function () { + http + .get({ + port: context.server.address().port, + path + }) + .end() + }) +}