diff --git a/lib/config/index.js b/lib/config/index.js index 62c77d1706..24d9f3d97a 100644 --- a/lib/config/index.js +++ b/lib/config/index.js @@ -839,7 +839,7 @@ Config.prototype.logUnknown = function logUnknown(json, key) { /** * Gets the user set host display name. If not provided, it returns the default value. * - * This function is written is this strange way because of the use of caching variables. + * This function is written in this strange way because of the use of caching variables. * I wanted to cache the DisplayHost, but if I attached the variable to the config object, * it sends the extra variable to New Relic, which is not desired. * diff --git a/test/unit/errors/error-collector.test.js b/test/unit/errors/error-collector.test.js index b35b92e1db..d6d87e984a 100644 --- a/test/unit/errors/error-collector.test.js +++ b/test/unit/errors/error-collector.test.js @@ -1,13 +1,14 @@ /* - * Copyright 2020 New Relic Corporation. All rights reserved. + * Copyright 2024 New Relic Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ 'use strict' -const tap = require('tap') -const test = tap.test +const test = require('node:test') +const assert = require('node:assert') +const { match } = require('../../lib/custom-assertions') const helper = require('../../lib/agent_helper') const Exception = require('../../../lib/errors').Exception const ErrorCollector = require('../../../lib/errors/error-collector') @@ -20,6 +21,7 @@ const Metrics = require('../../../lib/metrics') const API = require('../../../api') const DESTS = require('../../../lib/config/attribute-filter').DESTINATIONS const NAMES = require('../../../lib/metrics/names') +const http = require('http') function createTransaction(agent, code, isWeb) { if (typeof isWeb === 'undefined') { @@ -47,709 +49,664 @@ function createBackgroundTransaction(agent) { return createTransaction(agent, null, false) } -tap.test('Errors', (t) => { - t.autoend() - let agent = null +function getErrorTraces(errorCollector) { + return errorCollector.traceAggregator.errors +} - t.beforeEach(() => { - if (agent) { - helper.unloadAgent(agent) - } - agent = helper.loadMockedAgent({ - attributes: { - enabled: true - } - }) - }) +function getErrorEvents(errorCollector) { + return errorCollector.eventAggregator.getEvents() +} - t.afterEach(() => { - helper.unloadAgent(agent) - }) +function getFirstErrorIntrinsicAttributes(aggregator) { + return getFirstError(aggregator)[4].intrinsics +} + +function getFirstErrorCustomAttributes(aggregator) { + return getFirstError(aggregator)[4].userAttributes +} + +function getFirstError(aggregator) { + const errors = getErrorTraces(aggregator) + assert.equal(errors.length, 1) + return errors[0] +} + +function getFirstEventIntrinsicAttributes(aggregator) { + return getFirstEvent(aggregator)[0] +} + +function getFirstEventCustomAttributes(aggregator) { + return getFirstEvent(aggregator)[1] +} + +function getFirstEventAgentAttributes(aggregator) { + return getFirstEvent(aggregator)[2] +} + +function getFirstEvent(aggregator) { + const events = getErrorEvents(aggregator) + assert.equal(events.length, 1) + return events[0] +} + +test('Errors', async (t) => { + function beforeEach(ctx) { + ctx.nr = {} + ctx.nr.agent = helper.loadMockedAgent({ attributes: { enabled: true } }) + + ctx.nr.tx = new Transaction(ctx.nr.agent) + ctx.nr.tx.url = '/' + + ctx.nr.errors = ctx.nr.agent.errors + } + + function afterEach(ctx) { + helper.unloadAgent(ctx.nr.agent) + } - t.test('agent attribute format', (t) => { - t.autoend() + await t.test('agent attribute format', async (t) => { const PARAMS = 4 - let trans = null - let error = null - t.beforeEach(() => { - trans = new Transaction(agent) - trans.url = '/' - error = agent.errors - }) + t.beforeEach(beforeEach) + t.afterEach(afterEach) - t.test('record captured params', (t) => { - trans.trace.attributes.addAttribute(DESTS.TRANS_SCOPE, 'request.parameters.a', 'A') - error.add(trans, new Error()) - agent.errors.onTransactionFinished(trans) + await t.test('record captured params', (t) => { + const { agent, errors, tx } = t.nr + tx.trace.attributes.addAttribute(DESTS.TRANS_SCOPE, 'request.parameters.a', 'A') + errors.add(tx, Error()) + agent.errors.onTransactionFinished(tx) - const errorTraces = getErrorTraces(error) + const errorTraces = getErrorTraces(errors) let params = errorTraces[0][PARAMS] - t.same(params.agentAttributes, { 'request.parameters.a': 'A' }) + assert.equal(match(params.agentAttributes, { 'request.parameters.a': 'A' }), true) // Error events - const errorEvents = getErrorEvents(error) + const errorEvents = getErrorEvents(errors) params = errorEvents[0][2] - t.same(params, { 'request.parameters.a': 'A' }) - t.end() + assert.equal(match(params, { 'request.parameters.a': 'A' }), true) }) - t.test('records custom parameters', (t) => { - trans.trace.addCustomAttribute('a', 'A') - error.add(trans, new Error()) - agent.errors.onTransactionFinished(trans) + await t.test('record custom parameters', (t) => { + const { agent, errors, tx } = t.nr + tx.trace.addCustomAttribute('a', 'A') + errors.add(tx, Error()) + agent.errors.onTransactionFinished(tx) - const errorTraces = getErrorTraces(error) + const errorTraces = getErrorTraces(errors) let params = errorTraces[0][PARAMS] + assert.equal(match(params.userAttributes, { a: 'A' }), true) - t.same(params.userAttributes, { a: 'A' }) - - // error events - const errorEvents = getErrorEvents(error) + const errorEvents = getErrorEvents(errors) params = errorEvents[0][1] - - t.same(params, { a: 'A' }) - t.end() + assert.equal(match(params, { a: 'A' }), true) }) - t.test('merge custom parameters', (t) => { - trans.trace.addCustomAttribute('a', 'A') - error.add(trans, new Error(), { b: 'B' }) - agent.errors.onTransactionFinished(trans) + await t.test('merge custom parameters', (t) => { + const { agent, errors, tx } = t.nr + tx.trace.addCustomAttribute('a', 'A') + errors.add(tx, Error(), { b: 'B' }) + agent.errors.onTransactionFinished(tx) - const errorTraces = getErrorTraces(error) + const errorTraces = getErrorTraces(errors) let params = errorTraces[0][PARAMS] + assert.equal(match(params.userAttributes, { a: 'A', b: 'B' }), true) - t.same(params.userAttributes, { - a: 'A', - b: 'B' - }) - - // error events - const errorEvents = getErrorEvents(error) + const errorEvents = getErrorEvents(errors) params = errorEvents[0][1] - - t.same(params, { - a: 'A', - b: 'B' - }) - t.end() + assert.equal(match(params, { a: 'A', b: 'B' }), true) }) - t.test('overrides existing custom attributes with new custom attributes', (t) => { - trans.trace.custom.a = 'A' - error.add(trans, new Error(), { a: 'AA' }) - agent.errors.onTransactionFinished(trans) + await t.test('overrides existing custom attributes with new custom attributes', (t) => { + const { agent, errors, tx } = t.nr + tx.trace.custom.a = 'A' + errors.add(tx, Error(), { a: 'AA' }) + agent.errors.onTransactionFinished(tx) - const errorTraces = getErrorTraces(error) + const errorTraces = getErrorTraces(errors) let params = errorTraces[0][PARAMS] + assert.equal(match(params.userAttributes, { a: 'AA' }), true) - t.same(params.userAttributes, { - a: 'AA' - }) - - // error events - const errorEvents = getErrorEvents(error) + const errorEvents = getErrorEvents(errors) params = errorEvents[0][1] - - t.same(params, { - a: 'AA' - }) - t.end() + assert.equal(match(params, { a: 'AA' }), true) }) - t.test('does not add custom attributes in high security mode', (t) => { + await t.test('does not add custom attributes in high security mode', (t) => { + const { agent, errors, tx } = t.nr agent.config.high_security = true - error.add(trans, new Error(), { a: 'AA' }) - agent.errors.onTransactionFinished(trans) + errors.add(tx, Error(), { a: 'AA' }) + agent.errors.onTransactionFinished(tx) - const errorTraces = getErrorTraces(error) + const errorTraces = getErrorTraces(errors) let params = errorTraces[0][PARAMS] + assert.equal(match(params.userAttributes, {}), true) - t.same(params.userAttributes, {}) - - // error events - const errorEvents = getErrorEvents(error) + const errorEvents = getErrorEvents(errors) params = errorEvents[0][1] - - t.same(params, {}) - t.end() + assert.equal(match(params, {}), true) }) - t.test('redacts the error message in high security mode', (t) => { + await t.test('redacts the error message in high security mode', (t) => { + const { agent, errors, tx } = t.nr agent.config.high_security = true - error.add(trans, new Error('this should not be here'), { a: 'AA' }) - agent.errors.onTransactionFinished(trans) + errors.add(tx, Error('should be omitted'), { a: 'AA' }) + agent.errors.onTransactionFinished(tx) - const errorTraces = getErrorTraces(error) - t.equal(errorTraces[0][2], '') - t.equal(errorTraces[0][4].stack_trace[0], 'Error: ') - t.end() + const errorTraces = getErrorTraces(errors) + assert.equal(errorTraces[0][2], '') + assert.equal(errorTraces[0][4].stack_trace[0], 'Error: ') }) - t.test('redacts the error message when strip_exception_messages.enabled', (t) => { + await t.test('redacts the error message when strip_exception_messages.enabled', (t) => { + const { agent, errors, tx } = t.nr agent.config.strip_exception_messages.enabled = true - error.add(trans, new Error('this should not be here'), { a: 'AA' }) - agent.errors.onTransactionFinished(trans) + errors.add(tx, Error('should be omitted'), { a: 'AA' }) + agent.errors.onTransactionFinished(tx) - const errorTraces = getErrorTraces(error) - t.equal(errorTraces[0][2], '') - t.equal(errorTraces[0][4].stack_trace[0], 'Error: ') - t.end() + const errorTraces = getErrorTraces(errors) + assert.equal(errorTraces[0][2], '') + assert.equal(errorTraces[0][4].stack_trace[0], 'Error: ') }) }) - t.test('transaction id with distributed tracing enabled', (t) => { - t.autoend() - let errorJSON - let transaction - let error - - t.beforeEach(() => { - agent.config.distributed_tracing.enabled = true - error = new Error('this is an error') + await t.test('transaction id with distributed tracing enabled', async (t) => { + t.beforeEach((ctx) => { + beforeEach(ctx) + ctx.nr.agent.config.distributed_tracing.enabled = true }) + t.afterEach(afterEach) - t.test('should have a transaction id when there is a transaction', (t) => { - transaction = new Transaction(agent) + await t.test('should have a transaction id when there is a transaction', (t) => { + const { agent } = t.nr + const tx = new Transaction(agent) - agent.errors.add(transaction, error) - agent.errors.onTransactionFinished(transaction) + agent.errors.add(tx, Error('boom')) + agent.errors.onTransactionFinished(tx) const errorTraces = getErrorTraces(agent.errors) - errorJSON = errorTraces[0] - + const errorJSON = errorTraces[0] const transactionId = errorJSON[5] - t.equal(transactionId, transaction.id) - transaction.end() - t.end() + + assert.equal(transactionId, tx.id) + tx.end() }) - t.test('should not have a transaction id when there is no transaction', (t) => { - agent.errors.add(null, error) + await t.test('should not have a transaction id when there is no transaction', (t) => { + const { agent } = t.nr - const errorTraces = getErrorTraces(agent.errors) - errorJSON = errorTraces[0] + agent.errors.add(null, Error('boom')) + const errorTraces = getErrorTraces(agent.errors) + const errorJSON = errorTraces[0] const transactionId = errorJSON[5] - t.notOk(transactionId) - t.end() + assert.equal(transactionId, undefined) }) }) - t.test('guid attribute with distributed tracing enabled', (t) => { - t.autoend() - let errorJSON - let transaction - let error - - t.beforeEach(() => { - agent.config.distributed_tracing.enabled = true - error = new Error('this is an error') + await t.test('guid attribute with distributed tracing enabled', async (t) => { + t.beforeEach((ctx) => { + beforeEach(ctx) + ctx.nr.agent.config.distributed_tracing.enabled = true }) + t.afterEach(afterEach) - t.test('should have a guid attribute when there is a transaction', (t) => { - transaction = new Transaction(agent) - const aggregator = agent.errors + await t.test('should have a guid attribute when there is a transaction', (t) => { + const { agent, errors } = t.nr + const tx = new Transaction(agent) - agent.errors.add(transaction, error) - agent.errors.onTransactionFinished(transaction) + agent.errors.add(tx, Error('boom')) + agent.errors.onTransactionFinished(tx) const errorTraces = getErrorTraces(agent.errors) - errorJSON = errorTraces[0] - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - + const errorJSON = errorTraces[0] const transactionId = errorJSON[5] - t.equal(transactionId, transaction.id) - t.equal(attributes.guid, transaction.id) - transaction.end() - t.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + + assert.equal(transactionId, tx.id) + assert.equal(attributes.guid, tx.id) + tx.end() }) - t.test('should not have a guid attribute when there is no transaction', (t) => { - agent.errors.add(null, error) - const aggregator = agent.errors + await t.test('should not have a guid attribute when there is no transaction', (t) => { + const { agent, errors } = t.nr + agent.errors.add(null, Error('boom')) const errorTraces = getErrorTraces(agent.errors) - errorJSON = errorTraces[0] - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - + const errorJSON = errorTraces[0] const transactionId = errorJSON[5] - t.notOk(transactionId) - t.notOk(attributes.guid) - t.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + + assert.equal(transactionId, undefined) + assert.equal(attributes.guid, undefined) }) }) - t.test('transaction id with distributed tracing disabled', (t) => { - t.autoend() - let errorJSON - let transaction - let error - - t.beforeEach(() => { - agent.config.distributed_tracing.enabled = false - error = new Error('this is an error') + await t.test('transaction id with distributed tracing disabled', async (t) => { + t.beforeEach((ctx) => { + beforeEach(ctx) + ctx.nr.agent.config.distributed_tracing.enabled = false }) + t.afterEach(afterEach) - t.test('should have a transaction id when there is a transaction', (t) => { - transaction = new Transaction(agent) + await t.test('should have a transaction id when there is a transaction', (t) => { + const { agent } = t.nr + const tx = new Transaction(agent) - agent.errors.add(transaction, error) - agent.errors.onTransactionFinished(transaction) + agent.errors.add(tx, Error('boom')) + agent.errors.onTransactionFinished(tx) const errorTraces = getErrorTraces(agent.errors) - errorJSON = errorTraces[0] - + const errorJSON = errorTraces[0] const transactionId = errorJSON[5] - t.equal(transactionId, transaction.id) - transaction.end() - t.end() + + assert.equal(transactionId, tx.id) + tx.end() }) - t.test('should not have a transaction id when there is no transaction', (t) => { - agent.errors.add(null, error) + await t.test('should not have a transaction id when there is no transaction', (t) => { + const { agent } = t.nr - const errorTraces = getErrorTraces(agent.errors) - errorJSON = errorTraces[0] + agent.errors.add(null, Error('boom')) + const errorTraces = getErrorTraces(agent.errors) + const errorJSON = errorTraces[0] const transactionId = errorJSON[5] - t.notOk(transactionId) - t.end() + assert.equal(transactionId, undefined) }) }) - t.test('guid attribute with distributed tracing disabled', (t) => { - t.autoend() - let errorJSON - let transaction - let error - - t.beforeEach(() => { - agent.config.distributed_tracing.enabled = false - error = new Error('this is an error') + await t.test('guid attribute with distributed tracing disabled', async (t) => { + t.beforeEach((ctx) => { + beforeEach(ctx) + ctx.nr.agent.config.distributed_tracing.enabled = false }) + t.afterEach(afterEach) - t.test('should have a guid attribute when there is a transaction', (t) => { - transaction = new Transaction(agent) - const aggregator = agent.errors + await t.test('should have a guid attribute when there is a transaction', (t) => { + const { agent, errors } = t.nr + const tx = new Transaction(agent) - agent.errors.add(transaction, error) - agent.errors.onTransactionFinished(transaction) + agent.errors.add(tx, Error('boom')) + agent.errors.onTransactionFinished(tx) const errorTraces = getErrorTraces(agent.errors) - errorJSON = errorTraces[0] - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - + const errorJSON = errorTraces[0] const transactionId = errorJSON[5] - t.equal(transactionId, transaction.id) - t.equal(attributes.guid, transaction.id) - transaction.end() - t.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + + assert.equal(transactionId, tx.id) + assert.equal(attributes.guid, tx.id) + tx.end() }) - t.test('should not have a guid attribute when there is no transaction', (t) => { - agent.errors.add(null, error) - const aggregator = agent.errors + await t.test('should not have a guid attribute when there is no transaction', (t) => { + const { agent, errors } = t.nr + agent.errors.add(null, Error('boom')) const errorTraces = getErrorTraces(agent.errors) - errorJSON = errorTraces[0] - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - + const errorJSON = errorTraces[0] const transactionId = errorJSON[5] - t.notOk(transactionId) - t.notOk(attributes.guid) - t.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + + assert.equal(transactionId, undefined) + assert.equal(attributes.guid, undefined) }) }) - t.test('display name', (t) => { - t.autoend() + await t.test('display name', async (t) => { const PARAMS = 4 + t.beforeEach(beforeEach) + t.afterEach(afterEach) + + await t.test('should be in agent attributes if set by user', (t) => { + // This test skips the beforeEach because: + // 1. beforeEach creates a new agent + // 2. beforeEach creates a new transaction + // 3. transaction creates a new trace + // 4. trace invokes getDisplayHost(), thus caching the default value + // 5. test function is invoked + // 6. agent config is updated + // 7. new transaction is created + // 8. new transaction creates a new trace + // 9. new trace invokes getDisplayHost() + // 10. getDisplayHost() returns the original cached value because the agent has been reused + helper.unloadAgent(t.nr.agent) + const agent = helper.loadMockedAgent({ + attributes: { enabled: true }, + process_host: { + display_name: 'test-value' + } + }) + t.after(() => helper.unloadAgent(agent)) - let trans - let error - - t.test('should be in agent attributes if set by user', (t) => { - agent.config.process_host.display_name = 'test-value' - - trans = new Transaction(agent) - trans.url = '/' + const tx = new Transaction(agent) + tx.url = '/' - error = agent.errors - error.add(trans, new Error()) - error.onTransactionFinished(trans) + const errors = agent.errors + errors.add(tx, Error()) + errors.onTransactionFinished(tx) - const errorTraces = getErrorTraces(error) + const errorTraces = getErrorTraces(errors) const params = errorTraces[0][PARAMS] - t.same(params.agentAttributes, { - 'host.displayName': 'test-value' - }) - t.end() + assert.equal(match(params.agentAttributes, { 'host.displayName': 'test-value' }), true) }) - t.test('should not be in agent attributes if not set by user', (t) => { - trans = new Transaction(agent) - trans.url = '/' + await t.test('should not be in agent attributes if not set by user', (t) => { + const { errors, tx } = t.nr - error = agent.errors - error.add(trans, new Error()) - error.onTransactionFinished(trans) + errors.add(tx, Error()) + errors.onTransactionFinished(tx) - const errorTraces = getErrorTraces(error) + const errorTraces = getErrorTraces(errors) const params = errorTraces[0][PARAMS] - t.same(params.agentAttributes, {}) - t.end() + assert.equal(match(params.agentAttributes, {}), true) }) }) - t.test('ErrorCollector', (t) => { - t.autoend() - let metrics = null - let collector = null - let harvester = null - let errorCollector = null + await t.test('ErrorCollector', async (t) => { + t.beforeEach((ctx) => { + beforeEach(ctx) - t.beforeEach(() => { - metrics = new Metrics(5, {}, {}) - collector = {} - harvester = { add() {} } + ctx.nr.metrics = new Metrics(5, {}, {}) + ctx.nr.collector = {} + ctx.nr.harvester = { + add() {} + } - errorCollector = new ErrorCollector( - agent.config, + ctx.nr.errorCollector = new ErrorCollector( + ctx.nr.agent.config, new ErrorTraceAggregator( - { - periodMs: 60, - transport: null, - limit: 20 - }, - collector, - harvester + { periodMs: 60, transport: null, limit: 20 }, + ctx.nr.collector, + ctx.nr.harvester ), new ErrorEventAggregator( + { periodMs: 60, transport: null, limit: 20 }, { - periodMs: 60, - transport: null, - limit: 20 - }, - { - collector, - metrics, - harvester + collector: ctx.nr.collector, + metrics: ctx.nr.metrics, + harvester: ctx.nr.harvester } ), - metrics + ctx.nr.metrics ) }) - t.afterEach(() => { - errorCollector = null - harvester = null - collector = null - metrics = null - }) + t.afterEach(afterEach) - t.test('should preserve the name field on errors', (t) => { + await t.test('should preserve the name field on errors', (t) => { + const { agent, errors } = t.nr const api = new API(agent) - - const testError = new Error('EVERYTHING IS BROKEN') + const testError = Error('EVERYTHING IS BROKEN') testError.name = 'GAMEBREAKER' api.noticeError(testError) - const errorTraces = getErrorTraces(agent.errors) + const errorTraces = getErrorTraces(errors) const error = errorTraces[0] - t.equal(error[error.length - 3], testError.name) - t.end() + assert.equal(error[error.length - 3], testError.name) }) - t.test('should not gather application errors if it is switched off by user config', (t) => { - const error = new Error('this error will never be seen') - agent.config.error_collector.enabled = false - t.teardown(() => { - agent.config.error_collector.enabled = true - }) - - const errorTraces = getErrorTraces(errorCollector) - t.equal(errorTraces.length, 0) - - errorCollector.add(null, error) + await t.test( + 'should not gather application errors if it is switched off by user config', + (t) => { + const { agent, errorCollector } = t.nr + agent.config.error_collector.enabled = false - t.equal(errorTraces.length, 0) + const errorTraces = getErrorTraces(errorCollector) + assert.equal(errorTraces.length, 0) - t.end() - }) + errorCollector.add(null, Error('boom')) + assert.equal(errorTraces.length, 0) + } + ) - t.test('should not gather user errors if it is switched off by user config', (t) => { - const error = new Error('this error will never be seen') + await t.test('should not gather user errors if it is switched off by user config', (t) => { + const { agent, errorCollector } = t.nr agent.config.error_collector.enabled = false - t.teardown(() => { - agent.config.error_collector.enabled = true - }) const errorTraces = getErrorTraces(errorCollector) - t.equal(errorTraces.length, 0) + assert.equal(errorTraces.length, 0) - errorCollector.addUserError(null, error) - - t.equal(errorTraces.length, 0) - - t.end() + errorCollector.addUserError(null, Error('boom')) + assert.equal(errorTraces.length, 0) }) - t.test('should not gather errors if it is switched off by server config', (t) => { - const error = new Error('this error will never be seen') + await t.test('should not gather errors if it is switched off by server config', (t) => { + const { agent, errorCollector } = t.nr agent.config.collect_errors = false - t.teardown(() => { - agent.config.collect_errors = true - }) const errorTraces = getErrorTraces(errorCollector) - t.equal(errorTraces.length, 0) + assert.equal(errorTraces.length, 0) - errorCollector.add(null, error) - - t.equal(errorTraces.length, 0) - - t.end() + errorCollector.add(null, Error('boom')) + assert.equal(errorTraces.length, 0) }) - t.test('should gather the same error in two transactions', (t) => { - const error = new Error('this happened once') + await t.test('should gather the same error in two transactions', (t) => { + const { agent, errors } = t.nr + const error = Error('this happened once') const first = new Transaction(agent) const second = new Transaction(agent) - const errorTraces = getErrorTraces(agent.errors) - t.equal(errorTraces.length, 0) + const errorTraces = getErrorTraces(errors) + assert.equal(errorTraces.length, 0) - agent.errors.add(first, error) - t.equal(first.exceptions.length, 1) + errors.add(first, error) + assert.equal(first.exceptions.length, 1) - agent.errors.add(second, error) - t.equal(second.exceptions.length, 1) + errors.add(second, error) + assert.equal(second.exceptions.length, 1) first.end() - t.equal(errorTraces.length, 1) + assert.equal(errorTraces.length, 1) second.end() - t.equal(errorTraces.length, 2) - t.end() + assert.equal(errorTraces.length, 2) }) - t.test('should not gather the same error twice in the same transaction', (t) => { - const error = new Error('this happened once') + await t.test('should not gather the same error twice in the same transaction', (t) => { + const { errorCollector } = t.nr + const error = Error('this happened once') const errorTraces = getErrorTraces(errorCollector) - t.equal(errorTraces.length, 0) + assert.equal(errorTraces.length, 0) errorCollector.add(null, error) errorCollector.add(null, error) - t.equal(errorTraces.length, 1) - t.end() + assert.equal(errorTraces.length, 1) }) - t.test('should not break on read only objects', (t) => { - const error = new Error('this happened once') + await t.test('should not break on read only objects', (t) => { + const { errorCollector } = t.nr + const error = Error('this happened once') Object.freeze(error) const errorTraces = getErrorTraces(errorCollector) - t.equal(errorTraces.length, 0) + assert.equal(errorTraces.length, 0) errorCollector.add(null, error) errorCollector.add(null, error) - - t.equal(errorTraces.length, 1) - t.end() + assert.equal(errorTraces.length, 1) }) - t.test('add()', (t) => { - t.doesNotThrow(() => { - const aggregator = agent.errors - const error = new Error() + await t.test('add()', (t) => { + const { errors } = t.nr + assert.doesNotThrow(() => { + const error = Error() Object.freeze(error) - aggregator.add(error) + errors.add(error) }, 'when handling immutable errors') - - t.end() }) - t.test('when finalizing transactions', (t) => { - t.autoend() - let finalizeCollector = null - - t.beforeEach(() => { - finalizeCollector = agent.errors + await t.test('when finalizing transactions', async (t) => { + // We must unload the singleton agent in this nested test prior to any + // of the subtests running. Otherwise, we will get an error about the agent + // already being created when `loadMockedAgent` is invoked. + helper.unloadAgent(t.nr.agent) + t.beforeEach((ctx) => { + helper.unloadAgent(ctx.nr.agent) + beforeEach(ctx) }) - t.test('should capture errors for transactions ending in error', (t) => { - finalizeCollector.onTransactionFinished(createTransaction(agent, 400)) - finalizeCollector.onTransactionFinished(createTransaction(agent, 500)) + await t.test('should capture errors for transactions ending in error', (t) => { + const { agent, errors } = t.nr + errors.onTransactionFinished(createTransaction(agent, 400)) + errors.onTransactionFinished(createTransaction(agent, 500)) - const errorTraces = getErrorTraces(finalizeCollector) - t.equal(errorTraces.length, 2) - t.end() + const errorTraces = getErrorTraces(errors) + assert.equal(errorTraces.length, 2) }) - t.test('should generate transaction error metric', (t) => { - const transaction = createTransaction(agent, 200) - - finalizeCollector.add(transaction, new Error('error1')) - finalizeCollector.add(transaction, new Error('error2')) + await t.test('should generate transaction error metric', (t) => { + const { agent, errors } = t.nr + const tx = createTransaction(agent, 200) - finalizeCollector.onTransactionFinished(transaction) + errors.add(tx, Error('error1')) + errors.add(tx, Error('erorr2')) + errors.onTransactionFinished(tx) const metric = agent.metrics.getMetric('Errors/WebTransaction/TestJS/path') - t.equal(metric.callCount, 2) - t.end() + assert.equal(metric.callCount, 2) }) - t.test('should generate transaction error metric when added from API', (t) => { + await t.test('should generate transaction error metric when added from API', (t) => { + const { agent, errors } = t.nr const api = new API(agent) - const transaction = createTransaction(agent, 200) + const tx = createTransaction(agent, 200) agent.tracer.getTransaction = () => { - return transaction + return tx } - - api.noticeError(new Error('error1')) - api.noticeError(new Error('error2')) - - finalizeCollector.onTransactionFinished(transaction) + api.noticeError(Error('error1')) + api.noticeError(Error('error2')) + errors.onTransactionFinished(tx) const metric = agent.metrics.getMetric('Errors/WebTransaction/TestJS/path') - t.equal(metric.callCount, 2) - t.end() + assert.equal(metric.callCount, 2) }) - t.test('should not generate transaction error metric for ignored error', (t) => { + await t.test('should not generate transaction error metric for ignored error', (t) => { + const { agent, errors } = t.nr agent.config.error_collector.ignore_classes = ['Error'] - const transaction = createTransaction(agent, 200) + const tx = createTransaction(agent, 200) - finalizeCollector.add(transaction, new Error('error1')) - finalizeCollector.add(transaction, new Error('error2')) - - finalizeCollector.onTransactionFinished(transaction) + errors.add(tx, Error('error1')) + errors.add(tx, Error('error2')) + errors.onTransactionFinished(tx) const metric = agent.metrics.getMetric('Errors/WebTransaction/TestJS/path') - t.notOk(metric) - t.end() + assert.equal(metric, undefined) }) - t.test('should not generate transaction error metric for expected error', (t) => { + await t.test('should not generate transaction error metric for expected error', (t) => { + const { agent, errors } = t.nr agent.config.error_collector.expected_classes = ['Error'] - const transaction = createTransaction(agent, 200) + const tx = createTransaction(agent, 200) - finalizeCollector.add(transaction, new Error('error1')) - finalizeCollector.add(transaction, new Error('error2')) - - finalizeCollector.onTransactionFinished(transaction) + errors.add(tx, Error('error1')) + errors.add(tx, Error('error2')) + errors.onTransactionFinished(tx) const metric = agent.metrics.getMetric('Errors/WebTransaction/TestJS/path') - t.notOk(metric) - t.end() + assert.equal(metric, undefined) }) - t.test( + await t.test( 'should generate transaction error metric for unexpected error via noticeError', (t) => { + const { agent, errors } = t.nr const api = new API(agent) - const transaction = createTransaction(agent, 200) - - agent.tracer.getTransaction = () => { - return transaction - } + const tx = createTransaction(agent, 200) - api.noticeError(new Error('unexpected error')) - api.noticeError(new Error('another unexpected error')) + agent.tracer.getTransaction = () => tx - finalizeCollector.onTransactionFinished(transaction) + api.noticeError(Error('unexpected error')) + api.noticeError(Error('another unexpected error')) + errors.onTransactionFinished(tx) const metric = agent.metrics.getMetric('Errors/WebTransaction/TestJS/path') - t.equal(metric.callCount, 2) - t.end() + assert.equal(metric.callCount, 2) } ) - t.test( + await t.test( 'should not generate transaction error metric for expected error via noticeError', (t) => { + const { agent, errors } = t.nr const api = new API(agent) - const transaction = createTransaction(agent, 200) - - agent.tracer.getTransaction = () => { - return transaction - } + const tx = createTransaction(agent, 200) - api.noticeError(new Error('expected error'), {}, true) - api.noticeError(new Error('another expected error'), {}, true) + agent.tracer.getTransaction = () => tx - finalizeCollector.onTransactionFinished(transaction) + api.noticeError(Error('expected error'), {}, true) + api.noticeError(Error('another expected error'), {}, true) + errors.onTransactionFinished(tx) const metric = agent.metrics.getMetric('Errors/WebTransaction/TestJS/path') - - t.notOk(metric) - t.end() + assert.equal(metric, undefined) } ) - t.test('should ignore errors if related transaction is ignored', (t) => { - const transaction = createTransaction(agent, 500) - transaction.ignore = true + await t.test('should ignore errors if related transaction is ignored', (t) => { + const { agent, errors } = t.nr + const tx = createTransaction(agent, 500) + tx.ignore = true - // add errors by various means - finalizeCollector.add(transaction, new Error('no')) - const error = new Error('ignored') + // Add errors by various means + errors.add(tx, Error('no')) + const error = Error('ignored') const exception = new Exception({ error }) - transaction.addException(exception) - finalizeCollector.onTransactionFinished(transaction) + tx.addException(exception) + errors.onTransactionFinished(tx) const metric = agent.metrics.getMetric('Errors/WebTransaction/TestJS/path') - t.notOk(metric) - t.end() + assert.equal(metric, undefined) }) - t.test('should ignore 404 errors for transactions', (t) => { - finalizeCollector.onTransactionFinished(createTransaction(agent, 400)) + await t.test('should ignore 404 errors for transactions', (t) => { + const { agent, errors } = t.nr + errors.onTransactionFinished(createTransaction(agent, 400)) // 404 errors are ignored by default - finalizeCollector.onTransactionFinished(createTransaction(agent, 404)) - finalizeCollector.onTransactionFinished(createTransaction(agent, 404)) - finalizeCollector.onTransactionFinished(createTransaction(agent, 404)) - finalizeCollector.onTransactionFinished(createTransaction(agent, 404)) + errors.onTransactionFinished(createTransaction(agent, 404)) + errors.onTransactionFinished(createTransaction(agent, 404)) + errors.onTransactionFinished(createTransaction(agent, 404)) + errors.onTransactionFinished(createTransaction(agent, 404)) - const errorTraces = getErrorTraces(finalizeCollector) - t.equal(errorTraces.length, 1) + const errorTraces = getErrorTraces(errors) + assert.equal(errorTraces.length, 1) const metric = agent.metrics.getMetric('Errors/WebTransaction/TestJS/path') - t.equal(metric.callCount, 1) - t.end() + assert.equal(metric.callCount, 1) }) - t.test('should ignore 404 errors for transactions with exceptions attached', (t) => { + await t.test('should ignore 404 errors for transactions with exceptions attached', (t) => { + const { agent, errors } = t.nr const notIgnored = createTransaction(agent, 400) - const error = new Error('bad request') + const error = Error('bad request') const exception = new Exception({ error }) notIgnored.addException(exception) - finalizeCollector.onTransactionFinished(notIgnored) + errors.onTransactionFinished(notIgnored) // 404 errors are ignored by default, but making sure the config is set - finalizeCollector.config.error_collector.ignore_status_codes = [404] + errors.config.error_collector.ignore_status_codes = [404] const ignored = createTransaction(agent, 404) - agent.errors.add(ignored, new Error('ignored')) - finalizeCollector.onTransactionFinished(ignored) + agent.errors.add(ignored, Error('ignored')) + errors.onTransactionFinished(ignored) - const errorTraces = getErrorTraces(finalizeCollector) - t.equal(errorTraces.length, 1) + const errorTraces = getErrorTraces(errors) + assert.equal(errorTraces.length, 1) const metric = agent.metrics.getMetric('Errors/WebTransaction/TestJS/path') - t.equal(metric.callCount, 1) - t.end() + assert.equal(metric.callCount, 1) }) - t.test( + await t.test( 'should collect exceptions added with noticeError() API even if the status ' + 'code is in ignore_status_codes config', (t) => { + const { agent, errors } = t.nr const api = new API(agent) const tx = createTransaction(agent, 404) @@ -758,637 +715,559 @@ tap.test('Errors', (t) => { } // 404 errors are ignored by default, but making sure the config is set - finalizeCollector.config.error_collector.ignore_status_codes = [404] + errors.config.error_collector.ignore_status_codes = [404] // this should be ignored agent.errors.add(tx, new Error('should be ignored')) // this should go through api.noticeError(new Error('should go through')) - finalizeCollector.onTransactionFinished(tx) + errors.onTransactionFinished(tx) - const errorTraces = getErrorTraces(finalizeCollector) - t.equal(errorTraces.length, 1) - t.equal(errorTraces[0][2], 'should go through') - t.end() + const errorTraces = getErrorTraces(errors) + assert.equal(errorTraces.length, 1) + assert.equal(errorTraces[0][2], 'should go through') } ) }) - t.test('with no exception and no transaction', (t) => { - t.test('should have no errors', (t) => { - agent.errors.add(null, null) + await t.test('with no exception and no transaction', async (t) => { + helper.unloadAgent(t.nr.agent) + await t.test('should have no errors', (t) => { + const { errors } = t.nr + errors.add(null, null) - const errorTraces = getErrorTraces(agent.errors) - t.equal(errorTraces.length, 0) - t.end() + const errorTraces = getErrorTraces(errors) + assert.equal(errorTraces.length, 0) }) - t.end() }) - t.test('with no error and a transaction with status code', (t) => { - t.beforeEach(() => { - agent.errors.add(new Transaction(agent), null) + await t.test('with no error and a transaction without status code', async (t) => { + helper.unloadAgent(t.nr.agent) + t.beforeEach((ctx) => { + helper.unloadAgent(ctx.nr.agent) + beforeEach(ctx) + t.nr.errors.add(new Transaction(ctx.nr.agent), null) }) - t.test('should have no errors', (t) => { - const errorTraces = getErrorTraces(agent.errors) - t.equal(errorTraces.length, 0) - t.end() + await t.test('should have no errors', (t) => { + const { errors } = t.nr + const errorTraces = getErrorTraces(errors) + assert.equal(errorTraces.length, 0) }) - t.end() }) - t.test('with no error and a transaction with a status code', (t) => { - t.autoend() - let noErrorStatusTracer - let errorJSON - let transaction + await t.test('with no error and a transaction with a status code', async (t) => { + helper.unloadAgent(t.nr.agent) + t.beforeEach((ctx) => { + helper.unloadAgent(ctx.nr.agent) + beforeEach(ctx) - t.beforeEach(() => { - noErrorStatusTracer = agent.errors + ctx.nr.errors.add(new Transaction(ctx.nr.agent), null) - transaction = new Transaction(agent) - transaction.statusCode = 503 // PDX wut wut + ctx.nr.tx = new Transaction(ctx.nr.agent) + ctx.nr.tx.statusCode = 503 - noErrorStatusTracer.add(transaction, null) - noErrorStatusTracer.onTransactionFinished(transaction) + ctx.nr.errors.add(ctx.nr.tx, null) + ctx.nr.errors.onTransactionFinished(ctx.nr.tx) - const errorTraces = getErrorTraces(noErrorStatusTracer) - errorJSON = errorTraces[0] + ctx.nr.errorTraces = getErrorTraces(ctx.nr.errors) + ctx.nr.errorJSON = ctx.nr.errorTraces[0] }) - t.test('should have one error', (t) => { - const errorTraces = getErrorTraces(noErrorStatusTracer) - t.equal(errorTraces.length, 1) - t.end() + await t.test('should have no errors', (t) => { + const { errorTraces } = t.nr + assert.equal(errorTraces.length, 1) }) - t.test('should not care what time it was traced', (t) => { - t.equal(errorJSON[0], 0) - t.end() + await t.test('should not care what time it was traced', (t) => { + const { errorJSON } = t.nr + assert.equal(errorJSON[0], 0) }) - t.test('should have the default scope', (t) => { - t.equal(errorJSON[1], 'Unknown') - t.end() + await t.test('should have the default scope', (t) => { + const { errorJSON } = t.nr + assert.equal(errorJSON[1], 'Unknown') }) - t.test('should have an HTTP status code error message', (t) => { - t.equal(errorJSON[2], 'HttpError 503') - t.end() + await t.test('should have an HTTP status code error message', (t) => { + const { errorJSON } = t.nr + assert.equal(errorJSON[2], 'HttpError 503') }) - t.test('should default to a type of Error', (t) => { - t.equal(errorJSON[3], 'Error') - t.end() + await t.test('should default to a type of Error', (t) => { + const { errorJSON } = t.nr + assert.equal(errorJSON[3], 'Error') }) - t.test('should not have a stack trace in the params', (t) => { - const params = errorJSON[4] - t.notHas(params, 'stack_trace') - t.end() + await t.test('should not have a stack trace in the params', (t) => { + const { errorJSON } = t.nr + assert.equal(errorJSON[4].stack_trace, undefined) }) - t.test('should have a transaction id', (t) => { - const transactionId = errorJSON[5] - t.equal(transactionId, transaction.id) - t.end() + await t.test('should have a transaction id', (t) => { + const { errorJSON, tx } = t.nr + assert.equal(errorJSON[5], tx.id) }) - t.test('should have 6 elements in errorJson', (t) => { - t.equal(errorJSON.length, 6) - t.end() + await t.test('should have 6 elements in errorJson', (t) => { + const { errorJSON } = t.nr + assert.equal(errorJSON.length, 6) }) }) - t.test('with transaction agent attrs, status code, and no error', (t) => { - let errorJSON = null - let params = null - let transaction + await t.test('with transaction agent attrs, status code, and no error', async (t) => { + helper.unloadAgent(t.nr.agent) + t.beforeEach((ctx) => { + helper.unloadAgent(ctx.nr.agent) + beforeEach(ctx) - t.beforeEach(() => { - transaction = new Transaction(agent) - transaction.statusCode = 501 - transaction.url = '/' - transaction.trace.attributes.addAttributes(DESTS.TRANS_SCOPE, { + ctx.nr.tx.statusCode = 501 + ctx.nr.tx.trace.attributes.addAttributes(DESTS.TRANS_SCOPE, { test_param: 'a value', thing: true }) - agent.errors.add(transaction, null) - agent.errors.onTransactionFinished(transaction) + ctx.nr.errors.add(ctx.nr.tx, null) + ctx.nr.errors.onTransactionFinished(ctx.nr.tx) - const errorTraces = getErrorTraces(agent.errors) - errorJSON = errorTraces[0] - params = errorJSON[4] + ctx.nr.errorTraces = getErrorTraces(ctx.nr.errors) + ctx.nr.errorJSON = ctx.nr.errorTraces[0] + ctx.nr.params = ctx.nr.errorJSON[4] }) - t.test('should have one error', (t) => { - const errorTraces = getErrorTraces(agent.errors) - t.equal(errorTraces.length, 1) - t.end() + await t.test('should have one error', (t) => { + const { errors } = t.nr + const errorTraces = getErrorTraces(errors) + assert.equal(errorTraces.length, 1) }) - t.test('should not care what time it was traced', (t) => { - t.equal(errorJSON[0], 0) - t.end() + await t.test('should not care what time it was traced', (t) => { + const { errorJSON } = t.nr + assert.equal(errorJSON[0], 0) }) - t.test('should be scoped to the transaction', (t) => { - t.equal(errorJSON[1], 'WebTransaction/WebFrameworkUri/(not implemented)') - t.end() + await t.test('should be scoped to the transaction', (t) => { + const { errorJSON } = t.nr + assert.equal(errorJSON[1], 'WebTransaction/WebFrameworkUri/(not implemented)') }) - t.test('should have an HTTP status code message', (t) => { - t.equal(errorJSON[2], 'HttpError 501') - t.end() + await t.test('should have an HTTP status code message', (t) => { + const { errorJSON } = t.nr + assert.equal(errorJSON[2], 'HttpError 501') }) - t.test('should default to a type of Error', (t) => { - t.equal(errorJSON[3], 'Error') - t.end() + await t.test('should default to a type of Error', (t) => { + const { errorJSON } = t.nr + assert.equal(errorJSON[3], 'Error') }) - t.test('should not have a stack trace in the params', (t) => { - t.notHas(params, 'stack_trace') - t.end() + await t.test('should not have a stack trace in the params', (t) => { + const { params } = t.nr + assert.equal(params.stack_trace, undefined) }) - t.test('should have a transaction id', (t) => { + await t.test('should have a transaction id', (t) => { + const { errorJSON, tx } = t.nr const transactionId = errorJSON[5] - t.equal(transactionId, transaction.id) - t.end() + assert.equal(transactionId, tx.id) }) - t.test('should not have a request URL', (t) => { - t.notOk(params['request.uri']) - t.end() + await t.test('should not have a request URL', (t) => { + const { params } = t.nr + assert.equal(params['request.uri'], undefined) }) - t.test('should parse out the first agent parameter', (t) => { - t.equal(params.agentAttributes.test_param, 'a value') - t.end() + await t.test('should parse out the first agent parameter', (t) => { + const { params } = t.nr + assert.equal(params.agentAttributes.test_param, 'a value') }) - t.test('should parse out the other agent parameter', (t) => { - t.equal(params.agentAttributes.thing, true) - t.end() + await t.test('should parse out the other agent parameter', (t) => { + const { params } = t.nr + assert.equal(params.agentAttributes.thing, true) }) - t.end() }) - t.test('with attributes.enabled disabled', (t) => { - const transaction = new Transaction(agent) - transaction.statusCode = 501 + await t.test('with attributes.enabled disabled', (t) => { + const { agent, errors } = t.nr + const tx = new Transaction(agent) - transaction.url = '/test_action.json?test_param=a%20value&thing' + tx.statusCode = 501 + tx.url = '/test_action.json?test_param=a%20value&thing' - agent.errors.add(transaction, null) - agent.errors.onTransactionFinished(transaction) + errors.add(tx, null) + errors.onTransactionFinished(tx) - const errorTraces = getErrorTraces(agent.errors) + const errorTraces = getErrorTraces(errors) const errorJSON = errorTraces[0] const params = errorJSON[4] - - t.notHas(params, 'request_params') - t.end() + assert.equal(params.request_params, undefined) }) - t.test('with attributes.enabled and attributes.exclude set', (t) => { + await t.test('with attributes.enabled and attributes.exclude set', (t) => { + const { agent, errors } = t.nr + agent.config.attributes.exclude = ['thing'] agent.config.emit('attributes.exclude') - const transaction = new Transaction(agent) - transaction.statusCode = 501 - - transaction.trace.attributes.addAttributes(DESTS.TRANS_SCOPE, { + const tx = new Transaction(agent) + tx.statusCode = 501 + tx.trace.attributes.addAttributes(DESTS.TRANS_SCOPE, { test_param: 'a value', thing: 5 }) - agent.errors.add(transaction, null) - agent._transactionFinished(transaction) + errors.add(tx, null) + agent._transactionFinished(tx) - const errorTraces = getErrorTraces(agent.errors) + const errorTraces = getErrorTraces(errors) const errorJSON = errorTraces[0] const params = errorJSON[4] - - t.same(params.agentAttributes, { test_param: 'a value' }) - t.end() + assert.equal(match(params.agentAttributes, { test_param: 'a value' }), true) }) - t.test('with a thrown TypeError object and no transaction', (t) => { - t.autoend() - let typeErrorTracer - let errorJSON - - t.beforeEach(() => { - typeErrorTracer = agent.errors - - const exception = new Error('Dare to be the same!') + await t.test('with a thrown TypeError object and no transaction', async (t) => { + helper.unloadAgent(t.nr.agent) + t.beforeEach((ctx) => { + helper.unloadAgent(ctx.nr.agent) - typeErrorTracer.add(null, exception) + const exception = Error('Dare to be the same!') + ctx.nr.errors.add(null, exception) - const errorTraces = getErrorTraces(agent.errors) - errorJSON = errorTraces[0] + ctx.nr.errorTraces = getErrorTraces(ctx.nr.errors) + ctx.nr.errorJSON = ctx.nr.errorTraces[0] }) - t.test('should have one error', (t) => { - const errorTraces = getErrorTraces(agent.errors) - t.equal(errorTraces.length, 1) - t.end() + await t.test('should have one error', (t) => { + assert.equal(t.nr.errorTraces.length, 1) }) - t.test('should not care what time it was traced', (t) => { - t.equal(errorJSON[0], 0) - t.end() + await t.test('should not care what time it was traced', (t) => { + assert.equal(t.nr.errorJSON[0], 0) }) - t.test('should have the default scope', (t) => { - t.equal(errorJSON[1], 'Unknown') - t.end() + await t.test('should have the default scope', (t) => { + assert.equal(t.nr.errorJSON[1], 'Unknown') }) - t.test('should fish the message out of the exception', (t) => { - t.equal(errorJSON[2], 'Dare to be the same!') - t.end() + await t.test('should fish the message out of the exception', (t) => { + assert.equal(t.nr.errorJSON[2], 'Dare to be the same!') }) - t.test('should have a type of TypeError', (t) => { - t.equal(errorJSON[3], 'Error') - t.end() + await t.test('should have a type of TypeError', (t) => { + assert.equal(t.nr.errorJSON[3], 'Error') }) - t.test('should have a stack trace in the params', (t) => { - const params = errorJSON[4] - t.hasProp(params, 'stack_trace') - t.equal(params.stack_trace[0], 'Error: Dare to be the same!') - t.end() + await t.test('should have a stack trace in the params', (t) => { + const params = t.nr.errorJSON[4] + assert.equal(Object.hasOwn(params, 'stack_trace'), true) + assert.equal(params.stack_trace[0], 'Error: Dare to be the same!') }) - t.test('should not have a transaction id', (t) => { - const transactionId = errorJSON[5] - t.notOk(transactionId) - t.end() + await t.test('should not have a transaction id', (t) => { + const transactionId = t.nr.errorJSON[5] + assert.equal(transactionId, undefined) }) }) - t.test('with a thrown TypeError and a transaction with no params', (t) => { - t.autoend() - let typeErrorTracer - let errorJSON - let transaction - - t.beforeEach(() => { - typeErrorTracer = agent.errors + await t.test('with a thrown TypeError and a transaction with no params', async (t) => { + helper.unloadAgent(t.nr.agent) + t.beforeEach((ctx) => { + helper.unloadAgent(ctx.nr.agent) - transaction = new Transaction(agent) + ctx.nr.tx = new Transaction(ctx.nr.agent) const exception = new TypeError('Dare to be different!') + ctx.nr.errors.add(ctx.nr.tx, exception) + ctx.nr.errors.onTransactionFinished(ctx.nr.tx) - typeErrorTracer.add(transaction, exception) - typeErrorTracer.onTransactionFinished(transaction) - - const errorTraces = getErrorTraces(typeErrorTracer) - errorJSON = errorTraces[0] + ctx.nr.errorTraces = getErrorTraces(ctx.nr.errors) + ctx.nr.errorJSON = ctx.nr.errorTraces[0] }) - t.test('should have one error', (t) => { - const errorTraces = getErrorTraces(typeErrorTracer) - t.equal(errorTraces.length, 1) - t.end() + await t.test('should have one error', (t) => { + assert.equal(t.nr.errorTraces.length, 1) }) - t.test('should not care what time it was traced', (t) => { - t.equal(errorJSON[0], 0) - t.end() + await t.test('should not care what time it was traced', (t) => { + assert.equal(t.nr.errorJSON[0], 0) }) - t.test('should have the default scope', (t) => { - t.equal(errorJSON[1], 'Unknown') - t.end() + await t.test('should have the default scope', (t) => { + assert.equal(t.nr.errorJSON[1], 'Unknown') }) - t.test('should fish the message out of the exception', (t) => { - t.equal(errorJSON[2], 'Dare to be different!') - t.end() + await t.test('should fish the message out of the exception', (t) => { + assert.equal(t.nr.errorJSON[2], 'Dare to be different!') }) - t.test('should have a type of TypeError', (t) => { - t.equal(errorJSON[3], 'TypeError') - t.end() + await t.test('should have a type of TypeError', (t) => { + assert.equal(t.nr.errorJSON[3], 'TypeError') }) - t.test('should have a stack trace in the params', (t) => { - const params = errorJSON[4] - t.hasProp(params, 'stack_trace') - t.equal(params.stack_trace[0], 'TypeError: Dare to be different!') - t.end() + await t.test('should have a stack trace in the params', (t) => { + const params = t.nr.errorJSON[4] + assert.equal(Object.hasOwn(params, 'stack_trace'), true) + assert.equal(params.stack_trace[0], 'TypeError: Dare to be different!') }) - t.test('should have a transaction id', (t) => { - const transactionId = errorJSON[5] - t.equal(transactionId, transaction.id) - t.end() + await t.test('should have a transaction id', (t) => { + const transactionId = t.nr.errorJSON[5] + assert.equal(transactionId, t.nr.tx.id) }) }) - t.test('with a thrown `TypeError` and a transaction with agent attrs', (t) => { - t.autoend() - let errorJSON = null - let params = null - let transaction + await t.test('with a thrown TypeError and a transaction with agent attrs', async (t) => { + helper.unloadAgent(t.nr.agent) + t.beforeEach((ctx) => { + helper.unloadAgent(ctx.nr.agent) - t.beforeEach(() => { - transaction = new Transaction(agent) + const tx = new Transaction(ctx.nr.agent) const exception = new TypeError('wanted JSON, got XML') + ctx.nr.tx = tx - transaction.trace.attributes.addAttributes(DESTS.TRANS_SCOPE, { + tx.trace.attributes.addAttributes(DESTS.TRANS_SCOPE, { test_param: 'a value', thing: true }) - transaction.url = '/test_action.json' + tx.url = '/test_action.json' - agent.errors.add(transaction, exception) - agent.errors.onTransactionFinished(transaction) + ctx.nr.errors.add(tx, exception) + ctx.nr.errors.onTransactionFinished(tx) - const errorTraces = getErrorTraces(agent.errors) - errorJSON = errorTraces[0] - params = errorJSON[4] + ctx.nr.errorTraces = getErrorTraces(ctx.nr.errors) + ctx.nr.errorJSON = ctx.nr.errorTraces[0] + ctx.nr.params = ctx.nr.errorJSON[4] }) - t.test('should have one error', (t) => { - const errorTraces = getErrorTraces(agent.errors) - t.equal(errorTraces.length, 1) - t.end() + await t.test('should have one error', (t) => { + assert.equal(t.nr.errorTraces.length, 1) }) - t.test('should not care what time it was traced', (t) => { - t.equal(errorJSON[0], 0) - t.end() + await t.test('should not care what time it was traced', (t) => { + assert.equal(t.nr.errorJSON[0], 0) }) - t.test("should have the URL's scope", (t) => { - t.equal(errorJSON[1], 'WebTransaction/NormalizedUri/*') - t.end() + await t.test("should have the URL's scope", (t) => { + assert.equal(t.nr.errorJSON[1], 'WebTransaction/NormalizedUri/*') }) - t.test('should fish the message out of the exception', (t) => { - t.equal(errorJSON[2], 'wanted JSON, got XML') - t.end() + await t.test('should fish the message out of the exception', (t) => { + assert.equal(t.nr.errorJSON[2], 'wanted JSON, got XML') }) - t.test('should have a type of TypeError', (t) => { - t.equal(errorJSON[3], 'TypeError') - t.end() + await t.test('should have a type of TypeError', (t) => { + assert.equal(t.nr.errorJSON[3], 'TypeError') }) - t.test('should have a stack trace in the params', (t) => { - t.hasProp(params, 'stack_trace') - t.equal(params.stack_trace[0], 'TypeError: wanted JSON, got XML') - t.end() + await t.test('should have a stack trace in the params', (t) => { + const { params } = t.nr + assert.equal(Object.hasOwn(params, 'stack_trace'), true) + assert.equal(params.stack_trace[0], 'TypeError: wanted JSON, got XML') }) - t.test('should have a transaction id', (t) => { - const transactionId = errorJSON[5] - t.equal(transactionId, transaction.id) - t.end() + await t.test('should have a transaction id', (t) => { + const transactionId = t.nr.errorJSON[5] + assert.equal(transactionId, t.nr.tx.id) }) - t.test('should not have a request URL', (t) => { - t.notOk(params['request.uri']) - t.end() + await t.test('should not have a request URL', (t) => { + assert.equal(t.nr.params['request.uri'], undefined) }) - t.test('should parse out the first agent parameter', (t) => { - t.equal(params.agentAttributes.test_param, 'a value') - t.end() + await t.test('should parse out the first agent parameter', (t) => { + assert.equal(t.nr.params.agentAttributes.test_param, 'a value') }) - t.test('should parse out the other agent parameter', (t) => { - t.equal(params.agentAttributes.thing, true) - t.end() + await t.test('should parse out the other agent parameter', (t) => { + assert.equal(t.nr.params.agentAttributes.thing, true) }) }) - t.test('with a thrown string and a transaction', (t) => { - t.autoend() - let thrownTracer - let errorJSON - let transaction + await t.test('with a thrown string and a transaction', async (t) => { + helper.unloadAgent(t.nr.agent) + t.beforeEach((ctx) => { + helper.unloadAgent(ctx.nr.agent) - t.beforeEach(() => { - thrownTracer = agent.errors - - transaction = new Transaction(agent) + const tx = new Transaction(ctx.nr.agent) const exception = 'Dare to be different!' + ctx.nr.tx = tx - thrownTracer.add(transaction, exception) - thrownTracer.onTransactionFinished(transaction) + ctx.nr.errors.add(tx, exception) + ctx.nr.errors.onTransactionFinished(tx) - const errorTraces = getErrorTraces(thrownTracer) - errorJSON = errorTraces[0] + ctx.nr.errorTraces = getErrorTraces(ctx.nr.errors) + ctx.nr.errorJSON = ctx.nr.errorTraces[0] }) - t.test('should have one error', (t) => { - const errorTraces = getErrorTraces(thrownTracer) - t.equal(errorTraces.length, 1) - t.end() + await t.test('should have one error', (t) => { + assert.equal(t.nr.errorTraces.length, 1) }) - t.test('should not care what time it was traced', (t) => { - t.equal(errorJSON[0], 0) - t.end() + await t.test('should not care what time it was traced', (t) => { + assert.equal(t.nr.errorJSON[0], 0) }) - t.test('should have the default scope', (t) => { - t.equal(errorJSON[1], 'Unknown') - t.end() + await t.test('should have the default scope', (t) => { + assert.equal(t.nr.errorJSON[1], 'Unknown') }) - t.test('should turn the string into the message', (t) => { - t.equal(errorJSON[2], 'Dare to be different!') - t.end() + await t.test('should turn the string into the message', (t) => { + assert.equal(t.nr.errorJSON[2], 'Dare to be different!') }) - t.test('should default to a type of Error', (t) => { - t.equal(errorJSON[3], 'Error') - t.end() + await t.test('should default to a type of Error', (t) => { + assert.equal(t.nr.errorJSON[3], 'Error') }) - t.test('should have no stack trace', (t) => { - t.notHas(errorJSON[4], 'stack_trace') - t.end() + await t.test('should have no stack trace', (t) => { + assert.equal(t.nr.errorJSON[4].stack_trace, undefined) }) - t.test('should have a transaction id', (t) => { - const transactionId = errorJSON[5] - t.equal(transactionId, transaction.id) - t.end() + await t.test('should have a transaction id', (t) => { + const transactionId = t.nr.errorJSON[5] + assert.equal(transactionId, t.nr.tx.id) }) }) - t.test('with a thrown string and a transaction with agent parameters', (t) => { - t.autoend() - let errorJSON = null - let params = null - let transaction + await t.test('with a thrown string and a transaction with agent parameters', async (t) => { + helper.unloadAgent(t.nr.agent) + t.beforeEach((ctx) => { + helper.unloadAgent(ctx.nr.agent) - t.beforeEach(() => { - transaction = new Transaction(agent) + const tx = new Transaction(ctx.nr.agent) const exception = 'wanted JSON, got XML' + ctx.nr.tx = tx - transaction.trace.attributes.addAttributes(DESTS.TRANS_SCOPE, { + tx.trace.attributes.addAttributes(DESTS.TRANS_SCOPE, { test_param: 'a value', thing: true }) + tx.url = '/test_action.json' - transaction.url = '/test_action.json' + ctx.nr.errors.add(tx, exception) + ctx.nr.errors.onTransactionFinished(tx) - agent.errors.add(transaction, exception) - agent.errors.onTransactionFinished(transaction) - - const errorTraces = getErrorTraces(agent.errors) - errorJSON = errorTraces[0] - params = errorJSON[4] + ctx.nr.errorTraces = getErrorTraces(ctx.nr.errors) + ctx.nr.errorJSON = ctx.nr.errorTraces[0] + ctx.nr.params = ctx.nr.errorJSON[4] }) - t.test('should have one error', (t) => { - const errorTraces = getErrorTraces(agent.errors) - t.equal(errorTraces.length, 1) - t.end() + await t.test('should have one error', (t) => { + assert.equal(t.nr.errorTraces.length, 1) }) - t.test('should not care what time it was traced', (t) => { - t.equal(errorJSON[0], 0) - t.end() + await t.test('should not care what time it was traced', (t) => { + assert.equal(t.nr.errorJSON[0], 0) }) - t.test("should have the transaction's name", (t) => { - t.equal(errorJSON[1], 'WebTransaction/NormalizedUri/*') - t.end() + await t.test("should have the transaction's name", (t) => { + assert.equal(t.nr.errorJSON[1], 'WebTransaction/NormalizedUri/*') }) - t.test('should turn the string into the message', (t) => { - t.equal(errorJSON[2], 'wanted JSON, got XML') - t.end() + await t.test('should turn the string into the message', (t) => { + assert.equal(t.nr.errorJSON[2], 'wanted JSON, got XML') }) - t.test('should default to a type of Error', (t) => { - t.equal(errorJSON[3], 'Error') - t.end() + await t.test('should default to a type of Error', (t) => { + assert.equal(t.nr.errorJSON[3], 'Error') }) - t.test('should not have a stack trace in the params', (t) => { - t.notHas(params, 'stack_trace') - t.end() + await t.test('should not have a stack trace in the params', (t) => { + assert.equal(t.nr.params.stack_trace, undefined) }) - t.test('should have a transaction id', (t) => { - const transactionId = errorJSON[5] - t.equal(transactionId, transaction.id) - t.end() + await t.test('should have a transaction id', (t) => { + const transactionId = t.nr.errorJSON[5] + assert.equal(transactionId, t.nr.tx.id) }) - t.test('should not have a request URL', (t) => { - t.notOk(params['request.uri']) - t.end() + await t.test('should not have a request URL', (t) => { + assert.equal(t.nr.params['request.uri'], undefined) }) - t.test('should parse out the first agent parameter', (t) => { - t.equal(params.agentAttributes.test_param, 'a value') - t.end() + await t.test('should parse out the first agent parameter', (t) => { + assert.equal(t.nr.params.agentAttributes.test_param, 'a value') }) - t.test('should parse out the other agent parameter', (t) => { - t.equal(params.agentAttributes.thing, true) - t.end() + await t.test('should parse out the other agent parameter', (t) => { + assert.equal(t.nr.params.agentAttributes.thing, true) }) }) - t.test('with an internal server error (500) and an exception', (t) => { - t.autoend() - const name = 'WebTransaction/Uri/test-request/zxrkbl' - let error + await t.test('with an internal server error (500) and an exception', async (t) => { + helper.unloadAgent(t.nr.agent) + t.beforeEach((ctx) => { + helper.unloadAgent(ctx.nr.agent) - t.beforeEach(() => { - errorCollector = agent.errors + const tx = new Transaction(ctx.nr.agent) + const exception = new Exception({ error: Error('500 test error') }) + ctx.nr.tx = tx - const transaction = new Transaction(agent) - const exception = new Exception({ error: new Error('500 test error') }) + tx.addException(exception) + tx.url = '/test-request/zxrkbl' + tx.name = 'WebTransaction/Uri/test-request/zxrkbl' + tx.statusCode = 500 + tx.end() - transaction.addException(exception) - transaction.url = '/test-request/zxrkbl' - transaction.name = 'WebTransaction/Uri/test-request/zxrkbl' - transaction.statusCode = 500 - transaction.end() - error = getErrorTraces(errorCollector)[0] + ctx.nr.error = getErrorTraces(ctx.nr.errors)[0] }) - t.test("should associate errors with the transaction's name", (t) => { - const errorName = error[1] - - t.equal(errorName, name) - t.end() + await t.test("should associate errors with the transaction's name", (t) => { + const errorName = t.nr.error[1] + assert.equal(errorName, 'WebTransaction/Uri/test-request/zxrkbl') }) - t.test('should associate errors with a message', (t) => { - const message = error[2] - - t.match(message, /500 test error/) - t.end() + await t.test('should associate errors with a message', (t) => { + const message = t.nr.error[2] + assert.match(message, /500 test error/) }) - t.test('should associate errors with a message class', (t) => { - const messageClass = error[3] - - t.equal(messageClass, 'Error') - t.end() + await t.test('should associate errors with a message class', (t) => { + const messageClass = t.nr.error[3] + assert.equal(messageClass, 'Error') }) - t.test('should associate errors with parameters', (t) => { - const params = error[4] - - t.ok(params && params.stack_trace) - t.equal(params.stack_trace[0], 'Error: 500 test error') - t.end() + await t.test('should associate errors with parameters', (t) => { + const params = t.nr.error[4] + assert.ok(params && params.stack_trace) + assert.equal(params.stack_trace[0], 'Error: 500 test error') }) }) - t.test('with a tracer unavailable (503) error', (t) => { - t.autoend() - const name = 'WebTransaction/Uri/test-request/zxrkbl' - let error + await t.test('with tracer unavailable (503) error', async (t) => { + helper.unloadAgent(t.nr.agent) + t.beforeEach((ctx) => { + helper.unloadAgent(ctx.nr.agent) - t.beforeEach(() => { - errorCollector = agent.errors + const tx = new Transaction(ctx.nr.agent) + ctx.nr.tx = tx - const transaction = new Transaction(agent) - transaction.url = '/test-request/zxrkbl' - transaction.name = 'WebTransaction/Uri/test-request/zxrkbl' - transaction.statusCode = 503 - transaction.end() - error = getErrorTraces(errorCollector)[0] + tx.url = '/test-request/zxrkbl' + tx.name = 'WebTransaction/Uri/test-request/zxrkbl' + tx.statusCode = 503 + tx.end() + + ctx.nr.error = getErrorTraces(ctx.nr.errors)[0] }) - t.test("should associate errors with the transaction's name", (t) => { - const errorName = error[1] - t.equal(errorName, name) - t.end() + await t.test("should associate errors with the transaction's name", (t) => { + const errorName = t.nr.error[1] + assert.equal(errorName, 'WebTransaction/Uri/test-request/zxrkbl') }) - t.test('should associate errors with a message', (t) => { - const message = error[2] - t.equal(message, 'HttpError 503') - t.end() + await t.test('should associate errors with a message', (t) => { + const message = t.nr.error[2] + assert.equal(message, 'HttpError 503') }) - t.test('should associate errors with an error type', (t) => { - const messageClass = error[3] - t.equal(messageClass, 'Error') - t.end() + + await t.test('should associate errors with an error type', (t) => { + const messageClass = t.nr.error[3] + assert.equal(messageClass, 'Error') }) }) - t.test('should allow throwing null', (t) => { + await t.test('should allow throwing null', (t) => { + const { agent } = t.nr const api = new API(agent) try { @@ -1396,289 +1275,280 @@ tap.test('Errors', (t) => { throw null }) } catch (err) { - t.equal(err, null) + assert.equal(err, null) } - t.end() }) - t.test('should copy parameters from background transactions', async (t) => { + await t.test('should copy parameters from background transactions', (t, end) => { + const { agent, errors } = t.nr const api = new API(agent) - await api.startBackgroundTransaction('job', () => { + api.startBackgroundTransaction('job', () => { api.addCustomAttribute('jobType', 'timer') api.noticeError(new Error('record an error')) agent.getTransaction().end() - const errorTraces = getErrorTraces(agent.errors) + const errorTraces = getErrorTraces(errors) - t.equal(errorTraces.length, 1) - t.equal(errorTraces[0][2], 'record an error') + assert.equal(errorTraces.length, 1) + assert.equal(errorTraces[0][2], 'record an error') + end() }) }) - t.test('should generate expected error metric for expected errors', (t) => { + await t.test('should generate expected error metric for expected errors', (t) => { + const { agent, errors } = t.nr agent.config.error_collector.expected_classes = ['Error'] const transaction = createTransaction(agent, 200) - errorCollector.add(transaction, new Error('error1')) - errorCollector.add(transaction, new Error('error2')) + errors.add(transaction, Error('error1')) + errors.add(transaction, Error('error2')) - errorCollector.onTransactionFinished(transaction) + errors.onTransactionFinished(transaction) - const metric = metrics.getMetric(NAMES.ERRORS.EXPECTED) - t.equal(metric.callCount, 2) - t.end() + const metric = agent.metrics.getMetric(NAMES.ERRORS.EXPECTED) + assert.equal(metric.callCount, 2) }) - t.test('should not generate expected error metric for unexpected errors', (t) => { + await t.test('should not generate expected error metric for unexpected errors', (t) => { + const { agent, errors } = t.nr const transaction = createTransaction(agent, 200) - errorCollector.add(transaction, new Error('error1')) - errorCollector.add(transaction, new Error('error2')) + errors.add(transaction, Error('error1')) + errors.add(transaction, Error('error2')) - errorCollector.onTransactionFinished(transaction) + errors.onTransactionFinished(transaction) const metric = agent.metrics.getMetric(NAMES.ERRORS.EXPECTED) - t.notOk(metric) - t.end() + assert.equal(metric, undefined) }) - t.test('should not generate expected error metric for ignored errors', (t) => { + await t.test('should not generate expected error metric for ignored errors', (t) => { + const { agent, errors } = t.nr agent.config.error_collector.expected_classes = ['Error'] agent.config.error_collector.ignore_classes = ['Error'] // takes precedence const transaction = createTransaction(agent, 200) - errorCollector.add(transaction, new Error('error1')) - errorCollector.add(transaction, new Error('error2')) + errors.add(transaction, Error('error1')) + errors.add(transaction, Error('error2')) - errorCollector.onTransactionFinished(transaction) + errors.onTransactionFinished(transaction) const metric = agent.metrics.getMetric(NAMES.ERRORS.EXPECTED) - t.notOk(metric) - t.end() + assert.equal(metric, undefined) }) - t.test('should generate all error metric for unexpected errors', (t) => { + await t.test('should generate all error metric for unexpected errors', (t) => { + const { agent, errors } = t.nr const transaction = createTransaction(agent, 200) - errorCollector.add(transaction, new Error('error1')) - errorCollector.add(transaction, new Error('error2')) + errors.add(transaction, Error('error1')) + errors.add(transaction, Error('error2')) - errorCollector.onTransactionFinished(transaction) + errors.onTransactionFinished(transaction) - const metric = metrics.getMetric(NAMES.ERRORS.ALL) - t.equal(metric.callCount, 2) - t.end() + const metric = agent.metrics.getMetric(NAMES.ERRORS.ALL) + assert.equal(metric.callCount, 2) }) - t.test('should not generate all error metric for expected errors', (t) => { + await t.test('should not generate all error metric for expected errors', (t) => { + const { agent, errors } = t.nr agent.config.error_collector.expected_classes = ['Error'] const transaction = createTransaction(agent, 200) - errorCollector.add(transaction, new Error('error1')) - errorCollector.add(transaction, new Error('error2')) + errors.add(transaction, new Error('error1')) + errors.add(transaction, new Error('error2')) - errorCollector.onTransactionFinished(transaction) + errors.onTransactionFinished(transaction) - const metric = metrics.getMetric(NAMES.ERRORS.ALL) - t.notOk(metric) - t.end() + const metric = agent.metrics.getMetric(NAMES.ERRORS.ALL) + assert.equal(metric, undefined) }) - t.test('should not generate all error metric for ignored errors', (t) => { + await t.test('should not generate all error metric for ignored errors', (t) => { + const { agent, errors } = t.nr agent.config.error_collector.ignore_classes = ['Error'] const transaction = createTransaction(agent, 200) - errorCollector.add(transaction, new Error('error1')) - errorCollector.add(transaction, new Error('error2')) + errors.add(transaction, Error('error1')) + errors.add(transaction, Error('error2')) - errorCollector.onTransactionFinished(transaction) + errors.onTransactionFinished(transaction) - const metric = metrics.getMetric(NAMES.ERRORS.ALL) - t.notOk(metric) - t.end() + const metric = agent.metrics.getMetric(NAMES.ERRORS.ALL) + assert.equal(metric, undefined) }) - t.test('should generate web error metric for unexpected web errors', (t) => { + await t.test('should generate web error metric for unexpected web errors', (t) => { + const { agent, errors } = t.nr const transaction = createWebTransaction(agent) - errorCollector.add(transaction, new Error('error1')) - errorCollector.add(transaction, new Error('error2')) + errors.add(transaction, Error('error1')) + errors.add(transaction, Error('error2')) - errorCollector.onTransactionFinished(transaction) + errors.onTransactionFinished(transaction) - const metric = metrics.getMetric(NAMES.ERRORS.WEB) - t.equal(metric.callCount, 2) - t.end() + const metric = agent.metrics.getMetric(NAMES.ERRORS.WEB) + assert.equal(metric.callCount, 2) }) - t.test('should not generate web error metric for expected web errors', (t) => { + await t.test('should not generate web error metric for expected web errors', (t) => { + const { agent, errors } = t.nr agent.config.error_collector.expected_classes = ['Error'] const transaction = createTransaction(agent, 200) - errorCollector.add(transaction, new Error('error1')) - errorCollector.add(transaction, new Error('error2')) + errors.add(transaction, Error('error1')) + errors.add(transaction, Error('error2')) - errorCollector.onTransactionFinished(transaction) + errors.onTransactionFinished(transaction) - const metric = metrics.getMetric(NAMES.ERRORS.WEB) - t.notOk(metric) - t.end() + const metric = agent.metrics.getMetric(NAMES.ERRORS.WEB) + assert.equal(metric, undefined) }) - t.test('should not generate web error metric for ignored web errors', (t) => { + await t.test('should not generate web error metric for ignored web errors', (t) => { + const { agent, errors } = t.nr agent.config.error_collector.ignore_classes = ['Error'] const transaction = createTransaction(agent, 200) - errorCollector.add(transaction, new Error('error1')) - errorCollector.add(transaction, new Error('error2')) + errors.add(transaction, Error('error1')) + errors.add(transaction, Error('error2')) - errorCollector.onTransactionFinished(transaction) + errors.onTransactionFinished(transaction) - const metric = metrics.getMetric(NAMES.ERRORS.WEB) - t.notOk(metric) - t.end() + const metric = agent.metrics.getMetric(NAMES.ERRORS.WEB) + assert.equal(metric, undefined) }) - t.test('should not generate web error metric for unexpected non-web errors', (t) => { + await t.test('should not generate web error metric for unexpected non-web errors', (t) => { + const { agent, errors } = t.nr const transaction = createBackgroundTransaction(agent) - errorCollector.add(transaction, new Error('error1')) - errorCollector.add(transaction, new Error('error2')) + errors.add(transaction, Error('error1')) + errors.add(transaction, Error('error2')) - errorCollector.onTransactionFinished(transaction) + errors.onTransactionFinished(transaction) - const metric = metrics.getMetric(NAMES.ERRORS.WEB) - t.notOk(metric) - t.end() + const metric = agent.metrics.getMetric(NAMES.ERRORS.WEB) + assert.equal(metric, undefined) }) - t.test('should generate other error metric for unexpected non-web errors', (t) => { + await t.test('should generate other error metric for unexpected non-web errors', (t) => { + const { agent, errors } = t.nr const transaction = createBackgroundTransaction(agent) - errorCollector.add(transaction, new Error('error1')) - errorCollector.add(transaction, new Error('error2')) + errors.add(transaction, Error('error1')) + errors.add(transaction, Error('error2')) - errorCollector.onTransactionFinished(transaction) + errors.onTransactionFinished(transaction) - const metric = metrics.getMetric(NAMES.ERRORS.OTHER) - t.equal(metric.callCount, 2) - t.end() + const metric = agent.metrics.getMetric(NAMES.ERRORS.OTHER) + assert.equal(metric.callCount, 2) }) - t.test('should not generate other error metric for expected non-web errors', (t) => { + await t.test('should not generate other error metric for expected non-web errors', (t) => { + const { agent, errors } = t.nr agent.config.error_collector.expected_classes = ['Error'] const transaction = createBackgroundTransaction(agent) - errorCollector.add(transaction, new Error('error1')) - errorCollector.add(transaction, new Error('error2')) + errors.add(transaction, Error('error1')) + errors.add(transaction, Error('error2')) - errorCollector.onTransactionFinished(transaction) + errors.onTransactionFinished(transaction) - const metric = metrics.getMetric(NAMES.ERRORS.OTHER) - t.notOk(metric) - t.end() + const metric = agent.metrics.getMetric(NAMES.ERRORS.OTHER) + assert.equal(metric, undefined) }) - t.test('should not generate other error metric for ignored non-web errors', (t) => { + await t.test('should not generate other error metric for ignored non-web errors', (t) => { + const { agent, errors } = t.nr agent.config.error_collector.ignore_classes = ['Error'] const transaction = createBackgroundTransaction(agent) - errorCollector.add(transaction, new Error('error1')) - errorCollector.add(transaction, new Error('error2')) + errors.add(transaction, Error('error1')) + errors.add(transaction, Error('error2')) - errorCollector.onTransactionFinished(transaction) + errors.onTransactionFinished(transaction) - const metric = metrics.getMetric(NAMES.ERRORS.OTHER) - t.notOk(metric) - t.end() + const metric = agent.metrics.getMetric(NAMES.ERRORS.OTHER) + assert.equal(metric, undefined) }) - t.test('should not generate other error metric for unexpected web errors', (t) => { + await t.test('should not generate other error metric for unexpected web errors', (t) => { + const { agent, errors } = t.nr const transaction = createWebTransaction(agent) - errorCollector.add(transaction, new Error('error1')) - errorCollector.add(transaction, new Error('error2')) + errors.add(transaction, Error('error1')) + errors.add(transaction, Error('error2')) - errorCollector.onTransactionFinished(transaction) + errors.onTransactionFinished(transaction) - const metric = metrics.getMetric(NAMES.ERRORS.OTHER) - t.notOk(metric) - t.end() + const metric = agent.metrics.getMetric(NAMES.ERRORS.OTHER) + assert.equal(metric, undefined) }) - t.test('clearAll()', (t) => { - let aggregator - - t.beforeEach(() => { - aggregator = agent.errors - }) - - t.test('clears collected errors', (t) => { - aggregator.add(null, new Error('error1')) + await t.test('clearAll() clears collected errors', (t) => { + const { errors } = t.nr + errors.add(null, new Error('error1')) - t.equal(getErrorTraces(aggregator).length, 1) - t.equal(getErrorEvents(aggregator).length, 1) + assert.equal(getErrorTraces(errors).length, 1) + assert.equal(getErrorEvents(errors).length, 1) - aggregator.clearAll() + errors.clearAll() - t.equal(getErrorTraces(aggregator).length, 0) - t.equal(getErrorEvents(aggregator).length, 0) - t.end() - }) - t.end() + assert.equal(getErrorTraces(errors).length, 0) + assert.equal(getErrorEvents(errors).length, 0) }) }) - t.test('traced errors', (t) => { - t.autoend() - let aggregator + await t.test('traced errors', async (t) => { + t.beforeEach(beforeEach) + t.afterEach(afterEach) - t.beforeEach(() => { - aggregator = agent.errors - }) + await t.test('without transaction', async (t) => { + helper.unloadAgent(t.nr.agent) - t.test('without transaction', (t) => { - t.autoend() - t.test('should contain no intrinsic attributes', (t) => { - const error = new Error('some error') - aggregator.add(null, error) + await t.test('should contain no intrinsic attributes', (t) => { + const { errors } = t.nr + const error = Error('some error') + errors.add(null, error) - const errorTraces = getErrorTraces(aggregator) - t.equal(errorTraces.length, 1) + const errorTraces = getErrorTraces(errors) + assert.equal(errorTraces.length, 1) - const attributes = getFirstErrorIntrinsicAttributes(aggregator, t) - t.ok(typeof attributes === 'object') - t.end() + const attributes = getFirstErrorIntrinsicAttributes(errors) + assert.equal(typeof attributes === 'object', true) }) - t.test('should contain supplied custom attributes, with filter rules', (t) => { + await t.test('should contain supplied custom attributes, with filter rules', (t) => { + const { agent, errors } = t.nr agent.config.error_collector.attributes.exclude.push('c') agent.config.emit('error_collector.attributes.exclude') - const error = new Error('some error') + const error = Error('some error') const customAttributes = { a: 'b', c: 'ignored' } - aggregator.add(null, error, customAttributes) + errors.add(null, error, customAttributes) - const attributes = getFirstErrorCustomAttributes(aggregator, t) - t.equal(attributes.a, 'b') - t.notOk(attributes.c) - t.end() + const attributes = getFirstErrorCustomAttributes(errors) + assert.equal(attributes.a, 'b') + assert.equal(attributes.c, undefined) }) }) - t.test('on transaction finished', (t) => { - t.autoend() - t.test('should generate an event if the transaction is an HTTP error', (t) => { + await t.test('on transaction finished', async (t) => { + helper.unloadAgent(t.nr.agent) + + await t.test('should generate an event if the transaction is an HTTP error', (t) => { + const { agent, errors } = t.nr const transaction = createTransaction(agent, 500) - aggregator.add(transaction) + errors.add(transaction) transaction.end() - const collectedError = getErrorTraces(aggregator)[0] - t.ok(collectedError) - t.end() + const collectedError = getErrorTraces(errors)[0] + assert.ok(collectedError) }) - t.test('should contain CAT intrinsic parameters', (t) => { + await t.test('should contain CAT intrinsic parameters', (t) => { + const { agent, errors } = t.nr agent.config.cross_application_tracer.enabled = true agent.config.distributed_tracing.enabled = false @@ -1687,44 +1557,44 @@ tap.test('Errors', (t) => { transaction.referringTransactionGuid = '1234' transaction.incomingCatId = '2345' - const error = new Error('some error') - aggregator.add(transaction, error) + const error = Error('some error') + errors.add(transaction, error) transaction.end() - const attributes = getFirstErrorIntrinsicAttributes(aggregator, t) + const attributes = getFirstErrorIntrinsicAttributes(errors) - t.ok(typeof attributes === 'object') - t.ok(typeof attributes.path_hash === 'string') - t.equal(attributes.referring_transaction_guid, '1234') - t.equal(attributes.client_cross_process_id, '2345') - t.end() + assert.ok(typeof attributes === 'object') + assert.ok(typeof attributes.path_hash === 'string') + assert.equal(attributes.referring_transaction_guid, '1234') + assert.equal(attributes.client_cross_process_id, '2345') }) - t.test('should contain DT intrinsic parameters', (t) => { + await t.test('should contain DT intrinsic parameters', (t) => { + const { agent, errors } = t.nr agent.config.distributed_tracing.enabled = true agent.config.primary_application_id = 'test' agent.config.account_id = 1 const transaction = createTransaction(agent, 200) const error = new Error('some error') - aggregator.add(transaction, error) + errors.add(transaction, error) transaction.end() - const attributes = getFirstErrorIntrinsicAttributes(aggregator, t) + const attributes = getFirstErrorIntrinsicAttributes(errors) - t.ok(typeof attributes === 'object') - t.equal(attributes.traceId, transaction.traceId) - t.equal(attributes.guid, transaction.id) - t.equal(attributes.priority, transaction.priority) - t.equal(attributes.sampled, transaction.sampled) - t.notOk(attributes.parentId) - t.notOk(attributes.parentSpanId) - t.equal(transaction.sampled, true) - t.ok(transaction.priority > 1) - t.end() + assert.ok(typeof attributes === 'object') + assert.equal(attributes.traceId, transaction.traceId) + assert.equal(attributes.guid, transaction.id) + assert.equal(attributes.priority, transaction.priority) + assert.equal(attributes.sampled, transaction.sampled) + assert.equal(attributes.parentId, undefined) + assert.equal(attributes.parentSpanId, undefined) + assert.equal(transaction.sampled, true) + assert.ok(transaction.priority > 1) }) - t.test('should contain DT intrinsic parameters', (t) => { + await t.test('should contain DT intrinsic parameters', (t) => { + const { agent, errors } = t.nr agent.config.distributed_tracing.enabled = true agent.config.primary_application_id = 'test' agent.config.account_id = 1 @@ -1733,26 +1603,26 @@ tap.test('Errors', (t) => { transaction.isDistributedTrace = null transaction._acceptDistributedTracePayload(payload) - const error = new Error('some error') - aggregator.add(transaction, error) + const error = Error('some error') + errors.add(transaction, error) transaction.end() - const attributes = getFirstErrorIntrinsicAttributes(aggregator, t) - - t.ok(typeof attributes === 'object') - t.equal(attributes.traceId, transaction.traceId) - t.equal(attributes.guid, transaction.id) - t.equal(attributes.priority, transaction.priority) - t.equal(attributes.sampled, transaction.sampled) - t.equal(attributes['parent.type'], 'App') - t.equal(attributes['parent.app'], agent.config.primary_application_id) - t.equal(attributes['parent.account'], agent.config.account_id) - t.notOk(attributes.parentId) - t.notOk(attributes.parentSpanId) - t.end() - }) - - t.test('should contain Synthetics intrinsic parameters', (t) => { + const attributes = getFirstErrorIntrinsicAttributes(errors) + + assert.ok(typeof attributes === 'object') + assert.equal(attributes.traceId, transaction.traceId) + assert.equal(attributes.guid, transaction.id) + assert.equal(attributes.priority, transaction.priority) + assert.equal(attributes.sampled, transaction.sampled) + assert.equal(attributes['parent.type'], 'App') + assert.equal(attributes['parent.app'], agent.config.primary_application_id) + assert.equal(attributes['parent.account'], agent.config.account_id) + assert.equal(attributes.parentId, undefined) + assert.equal(attributes.parentSpanId, undefined) + }) + + await t.test('should contain Synthetics intrinsic parameters', (t) => { + const { agent, errors } = t.nr const transaction = createTransaction(agent, 200) transaction.syntheticsData = { @@ -1763,388 +1633,395 @@ tap.test('Errors', (t) => { monitorId: 'monId' } - const error = new Error('some error') - aggregator.add(transaction, error) + const error = Error('some error') + errors.add(transaction, error) transaction.end() - const attributes = getFirstErrorIntrinsicAttributes(aggregator, t) + const attributes = getFirstErrorIntrinsicAttributes(errors) - t.ok(typeof attributes === 'object') - t.equal(attributes.synthetics_resource_id, 'resId') - t.equal(attributes.synthetics_job_id, 'jobId') - t.equal(attributes.synthetics_monitor_id, 'monId') - t.end() + assert.ok(typeof attributes === 'object') + assert.equal(attributes.synthetics_resource_id, 'resId') + assert.equal(attributes.synthetics_job_id, 'jobId') + assert.equal(attributes.synthetics_monitor_id, 'monId') }) - t.test('should contain custom parameters', (t) => { + await t.test('should contain custom parameters', (t) => { + const { agent, errors } = t.nr const transaction = createTransaction(agent, 500) - const error = new Error('some error') + const error = Error('some error') const customParameters = { a: 'b' } - aggregator.add(transaction, error, customParameters) + errors.add(transaction, error, customParameters) transaction.end() - const attributes = getFirstErrorCustomAttributes(aggregator, t) - t.equal(attributes.a, 'b') - t.end() + const attributes = getFirstErrorCustomAttributes(errors) + assert.equal(attributes.a, 'b') }) - t.test('should merge supplied custom params with those on the trace', (t) => { + await t.test('should merge supplied custom params with those on the trace', (t) => { + const { agent, errors } = t.nr agent.config.attributes.enabled = true const transaction = createTransaction(agent, 500) transaction.trace.addCustomAttribute('a', 'b') - const error = new Error('some error') + const error = Error('some error') const customParameters = { c: 'd' } - aggregator.add(transaction, error, customParameters) + errors.add(transaction, error, customParameters) transaction.end() - const attributes = getFirstErrorCustomAttributes(aggregator, t) - t.equal(attributes.a, 'b') - t.equal(attributes.c, 'd') - t.end() + const attributes = getFirstErrorCustomAttributes(errors) + assert.equal(attributes.a, 'b') + assert.equal(attributes.c, 'd') }) - t.end() }) }) - t.test('error events', (t) => { - t.autoend() - let aggregator - - t.beforeEach(() => { - aggregator = agent.errors - }) + await t.test('error events', async (t) => { + t.beforeEach(beforeEach) + t.afterEach(afterEach) - t.test('should omit the error message when in high security mode', (t) => { + await t.test('should omit the error message when in high security mode', (t) => { + const { agent } = t.nr agent.config.high_security = true agent.errors.add(null, new Error('some error')) const events = getErrorEvents(agent.errors) - t.equal(events[0][0]['error.message'], '') + assert.equal(events[0][0]['error.message'], '') agent.config.high_security = false - t.end() }) - t.test('not spill over reservoir size', (t) => { - if (agent) { - helper.unloadAgent(agent) - } - agent = helper.loadMockedAgent({ error_collector: { max_event_samples_stored: 10 } }) + await t.test('not spill over reservoir size', (t) => { + helper.unloadAgent(t.nr.agent) + const agent = helper.loadMockedAgent({ error_collector: { max_event_samples_stored: 10 } }) + t.after(() => helper.unloadAgent(agent)) for (let i = 0; i < 20; i++) { - agent.errors.add(null, new Error('some error')) + agent.errors.add(null, Error('some error')) } const events = getErrorEvents(agent.errors) - t.equal(events.length, 10) - t.end() + assert.equal(events.length, 10) }) - t.test('without transaction', (t) => { - t.test('using add()', (t) => { - t.test('should contain intrinsic attributes', (t) => { - const error = new Error('some error') + await t.test('without transaction', async (t) => { + helper.unloadAgent(t.nr.agent) + + await t.test('using add()', async (t) => { + helper.unloadAgent(t.nr.agent) + + await t.test('should contain intrinsic attributes', (t) => { + const { errors } = t.nr + const error = Error('some error') const nowSeconds = Date.now() / 1000 - aggregator.add(null, error) - - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.ok(typeof attributes === 'object') - t.equal(attributes.type, 'TransactionError') - t.ok(typeof attributes['error.class'] === 'string') - t.ok(typeof attributes['error.message'] === 'string') - t.ok(Math.abs(attributes.timestamp - nowSeconds) <= 1) - t.equal(attributes.transactionName, 'Unknown') - t.end() + errors.add(null, error) + + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.ok(typeof attributes === 'object') + assert.equal(attributes.type, 'TransactionError') + assert.ok(typeof attributes['error.class'] === 'string') + assert.ok(typeof attributes['error.message'] === 'string') + assert.ok(Math.abs(attributes.timestamp - nowSeconds) <= 1) + assert.equal(attributes.transactionName, 'Unknown') }) - t.test('should not contain guid intrinsic attributes', (t) => { - const error = new Error('some error') - aggregator.add(null, error) + await t.test('should not contain guid intrinsic attributes', (t) => { + const { errors } = t.nr + const error = Error('some error') + errors.add(null, error) - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.notOk(attributes.guid) - t.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.equal(attributes.guid, undefined) }) - t.test('should set transactionName to Unknown', (t) => { - const error = new Error('some error') - aggregator.add(null, error) + await t.test('should set transactionName to Unknown', (t) => { + const { errors } = t.nr + const error = Error('some error') + errors.add(null, error) - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.equal(attributes.transactionName, 'Unknown') - t.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.equal(attributes.transactionName, 'Unknown') }) - t.test('should contain supplied custom attributes, with filter rules', (t) => { + await t.test('should contain supplied custom attributes, with filter rules', (t) => { + const { agent, errors } = t.nr agent.config.attributes.enabled = true agent.config.attributes.exclude.push('c') agent.config.emit('attributes.exclude') - const error = new Error('some error') + const error = Error('some error') const customAttributes = { a: 'b', c: 'ignored' } - aggregator.add(null, error, customAttributes) + errors.add(null, error, customAttributes) - const attributes = getFirstEventCustomAttributes(aggregator, t) - t.equal(Object.keys(attributes).length, 1) - t.equal(attributes.a, 'b') - t.notOk(attributes.c) - t.end() + const attributes = getFirstEventCustomAttributes(errors) + assert.equal(Object.keys(attributes).length, 1) + assert.equal(attributes.a, 'b') + assert.equal(attributes.c, undefined) }) - t.test('should contain agent attributes', (t) => { + await t.test('should contain agent attributes', (t) => { + const { agent, errors } = t.nr agent.config.attributes.enabled = true - const error = new Error('some error') - aggregator.add(null, error, { a: 'a' }) + const error = Error('some error') + errors.add(null, error, { a: 'a' }) - const agentAttributes = getFirstEventAgentAttributes(aggregator, t) - const customAttributes = getFirstEventCustomAttributes(aggregator, t) + const agentAttributes = getFirstEventAgentAttributes(errors) + const customAttributes = getFirstEventCustomAttributes(errors) - t.equal(Object.keys(customAttributes).length, 1) - t.equal(Object.keys(agentAttributes).length, 0) - t.end() + assert.equal(Object.keys(customAttributes).length, 1) + assert.equal(Object.keys(agentAttributes).length, 0) }) - t.end() }) - t.test('using noticeError() API', (t) => { - let api - t.beforeEach(() => { - api = new API(agent) + await t.test('using noticeError() API', async (t) => { + helper.unloadAgent(t.nr.agent) + t.beforeEach((ctx) => { + helper.unloadAgent(ctx.nr.agent) + beforeEach(ctx) + ctx.nr.api = new API(ctx.nr.agent) }) - t.test('should contain intrinsic parameters', (t) => { - const error = new Error('some error') + await t.test('should contain intrinsic parameters', (t) => { + const { api, errors } = t.nr + const error = Error('some error') const nowSeconds = Date.now() / 1000 api.noticeError(error) - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.ok(typeof attributes === 'object') - t.equal(attributes.type, 'TransactionError') - t.ok(typeof attributes['error.class'] === 'string') - t.ok(typeof attributes['error.message'] === 'string') - t.ok(Math.abs(attributes.timestamp - nowSeconds) <= 1) - t.equal(attributes.transactionName, 'Unknown') - t.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.ok(typeof attributes === 'object') + assert.equal(attributes.type, 'TransactionError') + assert.ok(typeof attributes['error.class'] === 'string') + assert.ok(typeof attributes['error.message'] === 'string') + assert.ok(Math.abs(attributes.timestamp - nowSeconds) <= 1) + assert.equal(attributes.transactionName, 'Unknown') }) - t.test('should set transactionName to Unknown', (t) => { - const error = new Error('some error') + await t.test('should set transactionName to Unknown', (t) => { + const { api, errors } = t.nr + const error = Error('some error') api.noticeError(error) - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.equal(attributes.transactionName, 'Unknown') - t.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.equal(attributes.transactionName, 'Unknown') }) - t.test('should contain expected attributes, with filter rules', (t) => { + await t.test('should contain expected attributes, with filter rules', (t) => { + const { agent, api, errors } = t.nr agent.config.attributes.enabled = true agent.config.attributes.exclude = ['c'] agent.config.emit('attributes.exclude') - const error = new Error('some error') + const error = Error('some error') let customAttributes = { a: 'b', c: 'ignored' } api.noticeError(error, customAttributes) - const agentAttributes = getFirstEventAgentAttributes(aggregator, t) - customAttributes = getFirstEventCustomAttributes(aggregator, t) + const agentAttributes = getFirstEventAgentAttributes(errors) + customAttributes = getFirstEventCustomAttributes(errors) - t.equal(Object.keys(customAttributes).length, 1) - t.notOk(customAttributes.c) - t.equal(Object.keys(agentAttributes).length, 0) - t.end() + assert.equal(Object.keys(customAttributes).length, 1) + assert.equal(customAttributes.c, undefined) + assert.equal(Object.keys(agentAttributes).length, 0) }) - t.test('should preserve expected flag for noticeError', (t) => { - const error = new Error('some noticed error') + await t.test('should preserve expected flag for noticeError', (t) => { + const { api, errors } = t.nr + const error = Error('some noticed error') api.noticeError(error, null, true) - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.equal(attributes['error.expected'], true) - t.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.equal(attributes['error.expected'], true) }) - t.test('unexpected noticeError should default to expected: false', (t) => { - const error = new Error('another noticed error') + + await t.test('unexpected noticeError should default to expected: false', (t) => { + const { api, errors } = t.nr + const error = Error('another noticed error') api.noticeError(error) - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.equal(attributes['error.expected'], false) - t.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.equal(attributes['error.expected'], false) }) - t.test('noticeError expected:true should be definable without customAttributes', (t) => { - const error = new Error('yet another noticed expected error') - api.noticeError(error, true) - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.equal(attributes['error.expected'], true) - t.end() - }) - t.test('noticeError expected:false should be definable without customAttributes', (t) => { - const error = new Error('yet another noticed unexpected error') - api.noticeError(error, false) + await t.test( + 'noticeError expected:true should be definable without customAttributes', + (t) => { + const { api, errors } = t.nr + const error = Error('yet another noticed expected error') + api.noticeError(error, true) - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.equal(attributes['error.expected'], false) - t.end() - }) - t.test( + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.equal(attributes['error.expected'], true) + } + ) + + await t.test( + 'noticeError expected:false should be definable without customAttributes', + (t) => { + const { api, errors } = t.nr + const error = Error('yet another noticed unexpected error') + api.noticeError(error, false) + + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.equal(attributes['error.expected'], false) + } + ) + + await t.test( 'noticeError should not interfere with agentAttributes and customAttributes', (t) => { - const error = new Error('and even yet another noticed error') + const { api, errors } = t.nr + const error = Error('and even yet another noticed error') let customAttributes = { a: 'b', c: 'd' } api.noticeError(error, customAttributes, true) - const agentAttributes = getFirstEventAgentAttributes(aggregator, t) - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - customAttributes = getFirstEventCustomAttributes(aggregator, t) + const agentAttributes = getFirstEventAgentAttributes(errors) + const attributes = getFirstEventIntrinsicAttributes(errors) + customAttributes = getFirstEventCustomAttributes(errors) - t.equal(Object.keys(customAttributes).length, 2) - t.ok(customAttributes.c) - t.equal(attributes['error.expected'], true) - t.equal(Object.keys(agentAttributes).length, 0) - t.end() + assert.equal(Object.keys(customAttributes).length, 2) + assert.ok(customAttributes.c) + assert.equal(attributes['error.expected'], true) + assert.equal(Object.keys(agentAttributes).length, 0) } ) - t.end() }) - t.end() }) - t.test('on transaction finished', (t) => { - t.test('should generate an event if the transaction is an HTTP error', (t) => { + await t.test('on transaction finished', async (t) => { + helper.unloadAgent(t.nr.agent) + + await t.test('should generate an event if the transaction is an HTTP error', (t) => { + const { agent, errors } = t.nr const transaction = createTransaction(agent, 500) - aggregator.add(transaction) + errors.add(transaction) transaction.end() - const errorEvents = getErrorEvents(aggregator) + const errorEvents = getErrorEvents(errors) const collectedError = errorEvents[0] - t.ok(collectedError) - t.end() + assert.ok(collectedError) }) - t.test('should contain required intrinsic attributes', (t) => { + await t.test('should contain required intrinsic attributes', (t) => { + const { agent, errors } = t.nr const transaction = createTransaction(agent, 200) - const error = new Error('some error') + const error = Error('some error') const nowSeconds = Date.now() / 1000 - aggregator.add(transaction, error) + errors.add(transaction, error) transaction.end() - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - - t.ok(typeof attributes === 'object') - t.equal(attributes.type, 'TransactionError') - t.ok(typeof attributes['error.class'] === 'string') - t.ok(typeof attributes['error.message'] === 'string') - t.equal(attributes.guid, transaction.id) - t.ok(Math.abs(attributes.timestamp - nowSeconds) <= 1) - t.equal(attributes.transactionName, transaction.name) - t.end() - }) - - t.test('transaction-specific intrinsic attributes on a transaction', (t) => { - let transaction - let error - - t.beforeEach(() => { - transaction = createTransaction(agent, 500) - error = new Error('some error') - aggregator.add(transaction, error) + const attributes = getFirstEventIntrinsicAttributes(errors) + + assert.ok(typeof attributes === 'object') + assert.equal(attributes.type, 'TransactionError') + assert.ok(typeof attributes['error.class'] === 'string') + assert.ok(typeof attributes['error.message'] === 'string') + assert.equal(attributes.guid, transaction.id) + assert.ok(Math.abs(attributes.timestamp - nowSeconds) <= 1) + assert.equal(attributes.transactionName, transaction.name) + }) + + await t.test('transaction-specific intrinsic attributes on a transaction', async (t) => { + helper.unloadAgent(t.nr.agent) + t.beforeEach((ctx) => { + helper.unloadAgent(ctx.nr.agent) + beforeEach(ctx) + + ctx.nr.tx = createTransaction(ctx.nr.agent, 500) + ctx.nr.error = Error('some error') + ctx.nr.errors.add(ctx.nr.tx, ctx.nr.error) }) - t.test('includes transaction duration', (t) => { - transaction.end() - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.equal(attributes.duration, transaction.timer.getDurationInMillis() / 1000) - t.end() + await t.test('includes transaction duration', (t) => { + const { errors, tx } = t.nr + tx.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.equal(attributes.duration, tx.timer.getDurationInMillis() / 1000) }) - t.test('includes queueDuration if available', (t) => { - transaction.measure(NAMES.QUEUETIME, null, 100) - transaction.end() - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.equal(attributes.queueDuration, 0.1) - t.end() + await t.test('includes queueDuration if available', (t) => { + const { errors, tx } = t.nr + tx.measure(NAMES.QUEUETIME, null, 100) + tx.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.equal(attributes.queueDuration, 0.1) }) - t.test('includes externalDuration if available', (t) => { - transaction.measure(NAMES.EXTERNAL.ALL, null, 100) - transaction.end() - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.equal(attributes.externalDuration, 0.1) - t.end() + await t.test('includes externalDuration if available', (t) => { + const { errors, tx } = t.nr + tx.measure(NAMES.EXTERNAL.ALL, null, 100) + tx.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.equal(attributes.externalDuration, 0.1) }) - t.test('includes databaseDuration if available', (t) => { - transaction.measure(NAMES.DB.ALL, null, 100) - transaction.end() - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.equal(attributes.databaseDuration, 0.1) - t.end() + await t.test('includes databaseDuration if available', (t) => { + const { errors, tx } = t.nr + tx.measure(NAMES.DB.ALL, null, 100) + tx.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.equal(attributes.databaseDuration, 0.1) }) - t.test('includes externalCallCount if available', (t) => { - transaction.measure(NAMES.EXTERNAL.ALL, null, 100) - transaction.measure(NAMES.EXTERNAL.ALL, null, 100) - transaction.end() - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.equal(attributes.externalCallCount, 2) - t.end() + await t.test('includes externalCallCount if available', (t) => { + const { errors, tx } = t.nr + tx.measure(NAMES.EXTERNAL.ALL, null, 100) + tx.measure(NAMES.EXTERNAL.ALL, null, 100) + tx.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.equal(attributes.externalCallCount, 2) }) - t.test('includes databaseCallCount if available', (t) => { - transaction.measure(NAMES.DB.ALL, null, 100) - transaction.measure(NAMES.DB.ALL, null, 100) - transaction.end() - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.equal(attributes.databaseCallCount, 2) - t.end() + await t.test('includes databaseCallCount if available', (t) => { + const { errors, tx } = t.nr + tx.measure(NAMES.DB.ALL, null, 100) + tx.measure(NAMES.DB.ALL, null, 100) + tx.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.equal(attributes.databaseCallCount, 2) }) - t.test('includes internal synthetics attributes', (t) => { - transaction.syntheticsData = { + await t.test('includes internal synthetics attributes', (t) => { + const { errors, tx } = t.nr + tx.syntheticsData = { version: 1, accountId: 123, resourceId: 'resId', jobId: 'jobId', monitorId: 'monId' } - transaction.end() - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.equal(attributes['nr.syntheticsResourceId'], 'resId') - t.equal(attributes['nr.syntheticsJobId'], 'jobId') - t.equal(attributes['nr.syntheticsMonitorId'], 'monId') - t.end() + tx.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.equal(attributes['nr.syntheticsResourceId'], 'resId') + assert.equal(attributes['nr.syntheticsJobId'], 'jobId') + assert.equal(attributes['nr.syntheticsMonitorId'], 'monId') }) - t.test('includes internal transactionGuid attribute', (t) => { - transaction.end() - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.equal(attributes['nr.transactionGuid'], transaction.id) - t.end() + await t.test('includes internal transactionGuid attribute', (t) => { + const { errors, tx } = t.nr + tx.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.equal(attributes['nr.transactionGuid'], tx.id) }) - t.test('includes guid attribute', (t) => { - transaction.end() - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.equal(attributes.guid, transaction.id) - t.end() + await t.test('includes guid attribute', (t) => { + const { errors, tx } = t.nr + tx.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.equal(attributes.guid, tx.id) }) - t.test('includes traceId attribute', (t) => { - transaction.referringTransactionGuid = '1234' - transaction.end() - const attributes = getFirstEventIntrinsicAttributes(aggregator, t) - t.equal(attributes.traceId, transaction.traceId) - t.end() + await t.test('includes traceId attribute', (t) => { + const { errors, tx } = t.nr + tx.referringTransactionGuid = '1234' + tx.end() + const attributes = getFirstEventIntrinsicAttributes(errors) + assert.equal(attributes.traceId, tx.traceId) }) - t.test('includes http port if the transaction is a web transaction', (t) => { - const http = require('http') - - helper.unloadAgent(agent) - agent = helper.instrumentMockedAgent() + await t.test('includes http port if the transaction is a web transaction', (t, end) => { + helper.unloadAgent(t.nr.agent) + const agent = helper.instrumentMockedAgent() + t.after(() => helper.unloadAgent(agent)) const server = http.createServer(function createServerCb(req, res) { - t.ok(agent.getTransaction()) + assert.ok(agent.getTransaction()) // Return HTTP error, so that when the transaction ends, an error // event is generated. res.statusCode = 500 @@ -2158,291 +2035,273 @@ tap.test('Errors', (t) => { agent.on('transactionFinished', function (tx) { process.nextTick(() => { - const attributes = getFirstEventIntrinsicAttributes(agent.errors, t) - t.equal(attributes.port, tx.port) + const attributes = getFirstEventIntrinsicAttributes(agent.errors) + assert.equal(attributes.port, tx.port) server.close() - t.end() + end() }) }) }) - t.end() }) - t.test('should contain custom attributes, with filter rules', (t) => { + await t.test('should contain custom attributes, with filter rules', (t) => { + const { agent, errors } = t.nr agent.config.attributes.exclude.push('c') agent.config.emit('attributes.exclude') const transaction = createTransaction(agent, 500) - const error = new Error('some error') + const error = Error('some error') const customAttributes = { a: 'b', c: 'ignored' } - aggregator.add(transaction, error, customAttributes) + errors.add(transaction, error, customAttributes) transaction.end() - const attributes = getFirstEventCustomAttributes(aggregator, t) - t.equal(attributes.a, 'b') - t.notOk(attributes.c) - t.end() + const attributes = getFirstEventCustomAttributes(errors) + assert.equal(attributes.a, 'b') + assert.equal(attributes.c, undefined) }) - t.test('should merge new custom attrs with trace custom attrs', (t) => { + await t.test('should merge new custom attrs with trace custom attrs', (t) => { + const { agent, errors } = t.nr const transaction = createTransaction(agent, 500) transaction.trace.addCustomAttribute('a', 'b') - const error = new Error('some error') + const error = Error('some error') const customAttributes = { c: 'd' } - aggregator.add(transaction, error, customAttributes) + errors.add(transaction, error, customAttributes) transaction.end() - const attributes = getFirstEventCustomAttributes(aggregator, t) - t.equal(Object.keys(attributes).length, 2) - t.equal(attributes.a, 'b') - t.equal(attributes.c, 'd') - t.end() + const attributes = getFirstEventCustomAttributes(errors) + assert.equal(Object.keys(attributes).length, 2) + assert.equal(attributes.a, 'b') + assert.equal(attributes.c, 'd') }) - t.test('should contain agent attributes', (t) => { + await t.test('should contain agent attributes', (t) => { + const { agent, errors } = t.nr agent.config.attributes.enabled = true const transaction = createTransaction(agent, 500) transaction.trace.attributes.addAttribute(DESTS.TRANS_SCOPE, 'host.displayName', 'myHost') const error = new Error('some error') - aggregator.add(transaction, error, { a: 'a' }) + errors.add(transaction, error, { a: 'a' }) transaction.end() - const agentAttributes = getFirstEventAgentAttributes(aggregator, t) - const customAttributes = getFirstEventCustomAttributes(aggregator, t) + const agentAttributes = getFirstEventAgentAttributes(errors) + const customAttributes = getFirstEventCustomAttributes(errors) - t.equal(Object.keys(customAttributes).length, 1) - t.equal(customAttributes.a, 'a') - t.equal(Object.keys(agentAttributes).length, 1) - t.equal(agentAttributes['host.displayName'], 'myHost') - t.end() + assert.equal(Object.keys(customAttributes).length, 1) + assert.equal(customAttributes.a, 'a') + assert.equal(Object.keys(agentAttributes).length, 1) + assert.equal(agentAttributes['host.displayName'], 'myHost') }) - t.end() }) }) }) -function getErrorTraces(errorCollector) { - return errorCollector.traceAggregator.errors -} - -function getErrorEvents(errorCollector) { - return errorCollector.eventAggregator.getEvents() -} - -function getFirstErrorIntrinsicAttributes(aggregator, t) { - return getFirstError(aggregator, t)[4].intrinsics -} - -function getFirstErrorCustomAttributes(aggregator, t) { - return getFirstError(aggregator, t)[4].userAttributes -} - -function getFirstError(aggregator, t) { - const errors = getErrorTraces(aggregator) - t.equal(errors.length, 1) - return errors[0] -} - -function getFirstEventIntrinsicAttributes(aggregator, t) { - return getFirstEvent(aggregator, t)[0] -} - -function getFirstEventCustomAttributes(aggregator, t) { - return getFirstEvent(aggregator, t)[1] -} - -function getFirstEventAgentAttributes(aggregator, t) { - return getFirstEvent(aggregator, t)[2] -} +test('When using the async listener', async (t) => { + t.beforeEach((ctx) => { + ctx.nr = {} + ctx.nr.agent = helper.instrumentMockedAgent() -function getFirstEvent(aggregator, t) { - const events = getErrorEvents(aggregator) - t.equal(events.length, 1) - return events[0] -} - -test('When using the async listener', (t) => { - t.autoend() - - let agent = null - let transaction = null - let active = null - let json = null - - t.beforeEach((t) => { - agent = helper.instrumentMockedAgent() - - helper.temporarilyOverrideTapUncaughtBehavior(tap, t) - }) - - t.afterEach(() => { - transaction.end() - - helper.unloadAgent(agent) - agent = null - transaction = null - active = null - json = null - }) - - t.test('should not have a domain active', (t) => { - executeThrowingTransaction(() => { - t.notOk(active) - t.end() + ctx.nr.uncaughtHandler = () => ctx.diagnostic('uncaught handler not defined') + ctx.nr.listeners = process.listeners('uncaughtException') + process.removeAllListeners('uncaughtException') + process.once('uncaughtException', () => { + ctx.nr.uncaughtHandler() }) }) - t.test('should find a single error', (t) => { - executeThrowingTransaction(() => { - const errorTraces = getErrorTraces(agent.errors) - t.equal(errorTraces.length, 1) - t.end() - }) + t.afterEach((ctx) => { + helper.unloadAgent(ctx.nr.agent) + for (const l of ctx.nr.listeners) { + process.on('uncaughtException', l) + } }) - t.test('should find traced error', (t) => { - executeThrowingTransaction(() => { - t.ok(json) - t.end() + await t.test('should not have a domain active', (t, end) => { + const { agent } = t.nr + let active + t.nr.uncaughtHandler = () => { + assert.equal(active, undefined) + end() + } + process.nextTick(() => { + const disruptor = agent.tracer.transactionProxy(() => { + active = process.domain + throw Error('sample error') + }) + disruptor() }) }) - t.test('should have 6 elements in the trace', (t) => { - executeThrowingTransaction(() => { - t.equal(json.length, 6) - t.end() + await t.test('should find a single error', (t, end) => { + const { agent } = t.nr + t.nr.uncaughtHandler = () => { + const traces = getErrorTraces(agent.errors) + assert.equal(traces.length, 1) + end() + } + process.nextTick(() => { + const disruptor = agent.tracer.transactionProxy(() => { + throw Error('sample error') + }) + disruptor() }) }) - t.test('should have the default name', (t) => { - executeThrowingTransaction(() => { - const { 1: name } = json - t.equal(name, 'Unknown') - t.end() + await t.test('should find traced error', (t, end) => { + const { agent } = t.nr + t.nr.uncaughtHandler = () => { + const traces = getErrorTraces(agent.errors) + assert.notEqual(traces[0], undefined) + end() + } + process.nextTick(() => { + const disruptor = agent.tracer.transactionProxy(() => { + throw Error('sample error') + }) + disruptor() }) }) - t.test("should have the error's message", (t) => { - executeThrowingTransaction(() => { - const { 2: message } = json - t.equal(message, 'sample error') - t.end() + await t.test('should have 6 elements in the trace', (t, end) => { + const { agent } = t.nr + t.nr.uncaughtHandler = () => { + const traces = getErrorTraces(agent.errors) + assert.equal(traces[0].length, 6) + end() + } + process.nextTick(() => { + const disruptor = agent.tracer.transactionProxy(() => { + throw Error('sample error') + }) + disruptor() }) }) - t.test("should have the error's constructor name (type)", (t) => { - executeThrowingTransaction(() => { - const { 3: name } = json - t.equal(name, 'Error') - t.end() + await t.test('should have the default name', (t, end) => { + const { agent } = t.nr + t.nr.uncaughtHandler = () => { + const traces = getErrorTraces(agent.errors) + assert.equal(traces[0][1], 'Unknown') + end() + } + process.nextTick(() => { + const disruptor = agent.tracer.transactionProxy(() => { + throw Error('sample error') + }) + disruptor() }) }) - t.test('should default to passing the stack trace as a parameter', (t) => { - executeThrowingTransaction(() => { - const { 4: params } = json - t.ok(params) - t.ok(params.stack_trace) - t.equal(params.stack_trace[0], 'Error: sample error') - t.end() + await t.test('should have the error message', (t, end) => { + const { agent } = t.nr + t.nr.uncaughtHandler = () => { + const traces = getErrorTraces(agent.errors) + assert.equal(traces[0][2], 'sample error') + end() + } + process.nextTick(() => { + const disruptor = agent.tracer.transactionProxy(() => { + throw Error('sample error') + }) + disruptor() }) }) - function executeThrowingTransaction(handledErrorCallback) { + await t.test('should have the error constructor name (type)', (t, end) => { + const { agent } = t.nr + t.nr.uncaughtHandler = () => { + const traces = getErrorTraces(agent.errors) + assert.equal(traces[0][3], 'Error') + end() + } process.nextTick(() => { - process.once('uncaughtException', () => { - const errorTraces = getErrorTraces(agent.errors) - json = errorTraces[0] - - return handledErrorCallback() + const disruptor = agent.tracer.transactionProxy(() => { + throw Error('sample error') }) + disruptor() + }) + }) - const disruptor = agent.tracer.transactionProxy(function transactionProxyCb() { - transaction = agent.getTransaction() - active = process.domain - - // trigger the error handler - throw new Error('sample error') + await t.test('should default to passing the stack trace as a parameter', (t, end) => { + const { agent } = t.nr + t.nr.uncaughtHandler = () => { + const traces = getErrorTraces(agent.errors) + const params = traces[0][4] + assert.notEqual(params, undefined) + assert.notEqual(params.stack_trace, undefined) + assert.equal(params.stack_trace[0], 'Error: sample error') + end() + } + process.nextTick(() => { + const disruptor = agent.tracer.transactionProxy(() => { + throw Error('sample error') }) - disruptor() }) - } + }) }) -tap.test('_processErrors', (t) => { - t.beforeEach((t) => { - t.context.agent = helper.loadMockedAgent({ - attributes: { - enabled: true - } +test('_processErrors', async (t) => { + t.beforeEach((ctx) => { + ctx.nr = {} + ctx.nr.agent = helper.loadMockedAgent({ + attributes: { enabled: true } }) - const transaction = new Transaction(t.context.agent) - transaction.url = '/' - t.context.transaction = transaction - t.context.errorCollector = t.context.agent.errors + const tx = new Transaction(ctx.nr.agent) + tx.url = '/' + ctx.nr.tx = tx + + ctx.nr.errorCollector = ctx.nr.agent.errors }) - t.afterEach((t) => { - helper.unloadAgent(t.context.agent) + t.afterEach((ctx) => { + helper.unloadAgent(ctx.nr.agent) }) - t.test('invalid errorType should return no iterableProperty', (t) => { - const { errorCollector, transaction } = t.context + await t.test('invalid errorType should return no iterableProperty', (t) => { + const { errorCollector, tx } = t.nr const errorType = 'invalid' - const result = errorCollector._getIterableProperty(transaction, errorType) + const result = errorCollector._getIterableProperty(tx, errorType) - t.equal(result, null) - t.end() + assert.equal(result, null) }) - t.test('if errorType is transaction, should return no iterableProperty', (t) => { - const { errorCollector, transaction } = t.context + await t.test('if errorType is transaction, should return no iterableProperty', (t) => { + const { errorCollector, tx } = t.nr const errorType = 'transaction' - const result = errorCollector._getIterableProperty(transaction, errorType) + const result = errorCollector._getIterableProperty(tx, errorType) - t.equal(result, null) - t.end() + assert.equal(result, null) }) - t.test('if type is user, return an array of objects', (t) => { - const { errorCollector, transaction } = t.context + await t.test('if type is user, return an array of objects', (t) => { + const { errorCollector, tx } = t.nr const errorType = 'user' - const result = errorCollector._getIterableProperty(transaction, errorType) + const result = errorCollector._getIterableProperty(tx, errorType) - t.same(result, []) - t.end() + assert.equal(match(result, []), true) }) - t.test('if type is transactionException, return an array of objects', (t) => { - const { errorCollector, transaction } = t.context + await t.test('if type is transactionException, return an array of objects', (t) => { + const { errorCollector, tx } = t.nr const errorType = 'transactionException' - const result = errorCollector._getIterableProperty(transaction, errorType) + const result = errorCollector._getIterableProperty(tx, errorType) - t.same(result, []) - t.end() + assert.equal(match(result, []), true) }) - t.test( + await t.test( 'if iterableProperty is null and errorType is not transaction, do not modify collectedErrors or expectedErrors', (t) => { - const { errorCollector, transaction } = t.context + const { errorCollector, tx } = t.nr const errorType = 'error' const collectedErrors = 0 const expectedErrors = 0 - const result = errorCollector._processErrors( - transaction, - collectedErrors, - expectedErrors, - errorType - ) + const result = errorCollector._processErrors(tx, collectedErrors, expectedErrors, errorType) - t.same(result, [collectedErrors, expectedErrors]) - t.end() + assert.equal(match(result, [collectedErrors, expectedErrors]), true) } ) - - t.end() })