diff --git a/test/unit/adaptive-sampler.test.js b/test/unit/adaptive-sampler.test.js index 8d50b27304..8cfd713f10 100644 --- a/test/unit/adaptive-sampler.test.js +++ b/test/unit/adaptive-sampler.test.js @@ -1,163 +1,151 @@ /* - * 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 = require('node:test') +const assert = require('node:assert') +const sinon = require('sinon') const helper = require('../lib/agent_helper') const AdaptiveSampler = require('../../lib/adaptive-sampler') -const sinon = require('sinon') -tap.test('AdaptiveSampler', (t) => { - let sampler = null - const shared = { - 'should count the number of traces sampled': (t) => { - t.equal(sampler.sampled, 0) - t.ok(sampler.shouldSample(0.1234)) - t.equal(sampler.sampled, 1) - t.end() - }, - - 'should not sample transactions with priorities lower than the min': (t) => { - t.equal(sampler.sampled, 0) - sampler._samplingThreshold = 0.5 - t.notOk(sampler.shouldSample(0)) - t.equal(sampler.sampled, 0) - t.ok(sampler.shouldSample(1)) - t.equal(sampler.sampled, 1) - t.end() - }, - - 'should adjust the min priority when throughput increases': (t) => { - sampler._reset(sampler.samplingTarget) - sampler._seen = 2 * sampler.samplingTarget - sampler._adjustStats(sampler.samplingTarget) - t.equal(sampler.samplingThreshold, 0.5) - t.end() - }, - - 'should only take the first 10 on the first harvest': (t) => { - t.equal(sampler.samplingThreshold, 0) - - // Change this to maxSampled if we change the way the back off works. - for (let i = 0; i <= 2 * sampler.samplingTarget; ++i) { - sampler.shouldSample(0.99999999) - } - - t.equal(sampler.sampled, 10) - t.equal(sampler.samplingThreshold, 1) - t.end() - }, - - 'should backoff on sampling after reaching the sampled target': (t) => { - sampler._seen = 10 * sampler.samplingTarget - - // Flag the sampler as not in the first period - sampler._reset() - - // The minimum sampled priority is not adjusted until the `target` number of - // transactions have been sampled, this is why the first 10 checks are all - // 0.9. At that point the current count of seen transactions should be close - // to the previous period's transaction count. - // - // In this test, however, the seen for this period is small compared the - // previous period (10 vs 100). This causes the MSP to drop to 0.3 but - // quickly normalizes again. This is an artifact of the test's use of infinite - // priority transactions in order to make the test predictable. - const epsilon = 0.000001 - const expectedMSP = [ - 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.316227766016838, 0.5500881229337736, - 0.6957797474657306, 0.7910970452225743, 0.8559144986383691, 0.9013792551037068, - 0.9340820391176599, 0.9580942670418969, 0.976025777575764, 0.9896031249412947, 1.0 - ] - - // Change this to maxSampled if we change the way the back off works. - for (let i = 0; i <= 2 * sampler.samplingTarget; ++i) { - const expected = expectedMSP[i] - t.ok( - sampler.samplingThreshold >= expected - epsilon && - sampler.samplingThreshold <= expected + epsilon - ) - - sampler.shouldSample(Infinity) - } - t.end() +const shared = { + 'should count the number of traces sampled': (t) => { + const { sampler } = t.nr + assert.equal(sampler.sampled, 0) + assert.ok(sampler.shouldSample(0.1234)) + assert.equal(sampler.sampled, 1) + }, + + 'should not sample transactions with priorities lower than the min': (t) => { + const { sampler } = t.nr + assert.equal(sampler.sampled, 0) + sampler._samplingThreshold = 0.5 + assert.equal(sampler.shouldSample(0), false) + assert.equal(sampler.sampled, 0) + assert.ok(sampler.shouldSample(1)) + assert.equal(sampler.sampled, 1) + }, + + 'should adjust the min priority when throughput increases': (t) => { + const { sampler } = t.nr + sampler._reset(sampler.samplingTarget) + sampler._seen = 2 * sampler.samplingTarget + sampler._adjustStats(sampler.samplingTarget) + assert.equal(sampler.samplingThreshold, 0.5) + }, + + 'should only take the first 10 on the first harvest': (t) => { + const { sampler } = t.nr + assert.equal(sampler.samplingThreshold, 0) + + // Change this to maxSampled if we change the way the back off works. + for (let i = 0; i <= 2 * sampler.samplingTarget; ++i) { + sampler.shouldSample(0.99999999) } - } - t.test('in serverless mode', (t) => { - let agent = null - t.beforeEach(() => { - agent = helper.loadMockedAgent({ - serverless_mode: { - enabled: true - } - }) - sampler = agent.transactionSampler - }) - - t.afterEach(() => { - helper.unloadAgent(agent) - sampler = null - }) + assert.equal(sampler.sampled, 10) + assert.equal(sampler.samplingThreshold, 1) + }, + + 'should backoff on sampling after reaching the sampled target': (t) => { + const { sampler } = t.nr + sampler._seen = 10 * sampler.samplingTarget + + // Flag the sampler as not in the first period + sampler._reset() + + // The minimum sampled priority is not adjusted until the `target` number of + // transactions have been sampled, this is why the first 10 checks are all + // 0.9. At that point the current count of seen transactions should be close + // to the previous period's transaction count. + // + // In this test, however, the seen for this period is small compared the + // previous period (10 vs 100). This causes the MSP to drop to 0.3 but + // quickly normalizes again. This is an artifact of the test's use of infinite + // priority transactions in order to make the test predictable. + const epsilon = 0.000001 + const expectedMSP = [ + 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.316227766016838, 0.5500881229337736, + 0.6957797474657306, 0.7910970452225743, 0.8559144986383691, 0.9013792551037068, + 0.9340820391176599, 0.9580942670418969, 0.976025777575764, 0.9896031249412947, 1.0 + ] + + // Change this to maxSampled if we change the way the back off works. + for (let i = 0; i <= 2 * sampler.samplingTarget; ++i) { + const expected = expectedMSP[i] + assert.ok( + sampler.samplingThreshold >= expected - epsilon && + sampler.samplingThreshold <= expected + epsilon + ) + + sampler.shouldSample(Infinity) + } + } +} - Object.getOwnPropertyNames(shared).forEach((testName) => { - t.test(testName, shared[testName]) +test('in serverless mode', async (t) => { + t.beforeEach((ctx) => { + ctx.nr = {} + ctx.nr.agent = helper.loadMockedAgent({ + serverless_mode: { enabled: true } }) - - t.test( - 'should reset itself after a transaction outside the window has been created', - async (t) => { - const spy = sinon.spy(sampler, '_reset') - sampler.samplingPeriod = 50 - t.equal(spy.callCount, 0) - agent.emit('transactionStarted', { timer: { start: Date.now() } }) - t.equal(spy.callCount, 1) - - return new Promise((resolve) => { - setTimeout(() => { - t.equal(spy.callCount, 1) - agent.emit('transactionStarted', { timer: { start: Date.now() } }) - t.equal(spy.callCount, 2) - resolve() - }, 100) - }) - } - ) - t.end() + ctx.nr.sampler = ctx.nr.agent.transactionSampler }) - t.test('in standard mode', (t) => { - t.beforeEach(() => { - sampler = new AdaptiveSampler({ - period: 100, - target: 10 - }) - }) - - t.afterEach(() => { - sampler.samplePeriod = 0 // Clear sample interval. - }) + t.afterEach((ctx) => { + helper.unloadAgent(ctx.nr.agent) + }) - Object.getOwnPropertyNames(shared).forEach((testName) => { - t.test(testName, shared[testName]) - }) + for (const [name, fn] of Object.entries(shared)) { + await t.test(name, fn) + } - t.test('should reset itself according to the period', async (t) => { + await t.test( + 'should reset itself after a transaction outside the window has been created', + async (t) => { + const { agent, sampler } = t.nr const spy = sinon.spy(sampler, '_reset') sampler.samplingPeriod = 50 + assert.equal(spy.callCount, 0) + agent.emit('transactionStarted', { timer: { start: Date.now() } }) + assert.equal(spy.callCount, 1) - return new Promise((resolve) => { + await new Promise((resolve) => { setTimeout(() => { - t.equal(spy.callCount, 4) + assert.equal(spy.callCount, 1) + agent.emit('transactionStarted', { timer: { start: Date.now() } }) + assert.equal(spy.callCount, 2) resolve() - }, 235) + }, 100) }) + } + ) +}) + +test('in standard mode', async (t) => { + t.beforeEach((ctx) => { + ctx.nr = {} + ctx.nr.sampler = new AdaptiveSampler({ period: 100, target: 10 }) + }) + + for (const [name, fn] of Object.entries(shared)) { + await t.test(name, fn) + } + + await t.test('should reset itself according to the period', async (t) => { + const { sampler } = t.nr + const spy = sinon.spy(sampler, '_reset') + sampler.samplingPeriod = 50 + + await new Promise((resolve) => { + setTimeout(() => { + assert.equal(spy.callCount, 4) + resolve() + }, 235) }) - t.end() }) - t.end() }) diff --git a/test/unit/analytics_events.test.js b/test/unit/analytics_events.test.js index 08df83d972..66a0b792cc 100644 --- a/test/unit/analytics_events.test.js +++ b/test/unit/analytics_events.test.js @@ -1,293 +1,279 @@ /* - * 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 = require('node:test') +const assert = require('node:assert') +const { match } = require('../lib/custom-assertions') const Transaction = require('../../lib/transaction') const helper = require('../lib/agent_helper') const DESTS = require('../../lib/config/attribute-filter').DESTINATIONS - const LIMIT = 10 -tap.test('Analytics events', function (t) { - t.autoend() - - let agent = null - let trans = null - - t.beforeEach(function () { - if (agent) { - return - } // already instantiated - agent = helper.loadMockedAgent({ - transaction_events: { - max_samples_stored: LIMIT - } - }) - agent.config.attributes.enabled = true - }) - - t.afterEach(function () { - if (!agent) { - return - } // already destroyed - helper.unloadAgent(agent) - agent = null +test.beforeEach((ctx) => { + ctx.nr = {} + ctx.nr.agent = helper.loadMockedAgent({ + transaction_events: { max_samples_stored: LIMIT } }) + ctx.nr.agent.config.attributes.enabled = true +}) - t.test('when there are attributes on transaction', function (t) { - t.autoend() +test.afterEach((ctx) => { + helper.unloadAgent(ctx.nr.agent) +}) - t.beforeEach(function () { - trans = new Transaction(agent) - }) +test('when there are attributes on transaction', async (t) => { + helper.unloadAgent(t.nr.agent) + t.beforeEach((ctx) => { + ctx.nr.trans = new Transaction(ctx.nr.agent) + }) - t.test('event should contain those attributes', function (t) { - trans.trace.attributes.addAttribute(DESTS.TRANS_EVENT, 'test', 'TEST') - agent._addEventFromTransaction(trans) + await t.test('event should contain those attributes', (t) => { + const { agent, trans } = t.nr + trans.trace.attributes.addAttribute(DESTS.TRANS_EVENT, 'test', 'TEST') + agent._addEventFromTransaction(trans) - const first = 0 - const agentAttrs = 2 + const first = 0 + const agentAttrs = 2 - const events = getTransactionEvents(agent) - const firstEvent = events[first] - t.equal(firstEvent[agentAttrs].test, 'TEST') - t.end() - }) + const events = getTransactionEvents(agent) + const firstEvent = events[first] + assert.equal(firstEvent[agentAttrs].test, 'TEST') }) +}) - t.test('when host name is specified by user', function (t) { - t.autoend() - - t.beforeEach(function () { - agent.config.process_host.display_name = 'test-value' - trans = new Transaction(agent) - }) +test('when host name is specified by user', async (t) => { + helper.unloadAgent(t.nr.agent) + t.beforeEach((ctx) => { + ctx.nr.agent.config.process_host.display_name = 'test-value' + ctx.nr.trans = new Transaction(ctx.nr.agent) + }) - t.test('name should be sent with event', function (t) { - agent._addEventFromTransaction(trans) + await t.test('name should be sent with event', (t) => { + const { agent, trans } = t.nr + agent._addEventFromTransaction(trans) - const first = 0 - const agentAttrs = 2 + const first = 0 + const agentAttrs = 2 - const events = getTransactionEvents(agent) - const firstEvent = events[first] - t.same(firstEvent[agentAttrs], { + const events = getTransactionEvents(agent) + const firstEvent = events[first] + assert.equal( + match(firstEvent[agentAttrs], { 'host.displayName': 'test-value' - }) - t.end() - }) + }), + true + ) }) +}) - t.test('when analytics events are disabled', function (t) { - t.autoend() +test('when analytics events are disabled', async (t) => { + helper.unloadAgent(t.nr.agent) - t.test('collector cannot enable remotely', function (t) { - agent.config.transaction_events.enabled = false - t.doesNotThrow(function () { - agent.config.onConnect({ collect_analytics_events: true }) - }) - t.equal(agent.config.transaction_events.enabled, false) - t.end() + await t.test('collector cannot enable remotely', (t) => { + const { agent } = t.nr + agent.config.transaction_events.enabled = false + assert.doesNotThrow(function () { + agent.config.onConnect({ collect_analytics_events: true }) }) + assert.equal(agent.config.transaction_events.enabled, false) }) +}) - t.test('when analytics events are enabled', function (t) { - t.autoend() +test('when analytics events are enabled', async (t) => { + helper.unloadAgent(t.nr.agent) - t.test('collector can disable remotely', function (t) { - agent.config.transaction_events.enabled = true - t.doesNotThrow(function () { - agent.config.onConnect({ collect_analytics_events: false }) - }) - t.equal(agent.config.transaction_events.enabled, false) - t.end() + await t.test('collector can disable remotely', (t) => { + const { agent } = t.nr + agent.config.transaction_events.enabled = true + assert.doesNotThrow(function () { + agent.config.onConnect({ collect_analytics_events: false }) }) + assert.equal(agent.config.transaction_events.enabled, false) }) +}) - t.test('on transaction finished', function (t) { - t.autoend() - - t.beforeEach(function () { - trans = new Transaction(agent) - }) - - t.test('should queue an event', async function (t) { - agent._addEventFromTransaction = (transaction) => { - t.equal(transaction, trans) - trans.end() - t.end() - } - }) - - t.test('should generate an event from transaction', function (t) { - trans.end() - - const events = getTransactionEvents(agent) - - t.equal(events.length, 1) - - const event = events[0] - t.ok(Array.isArray(event)) - const eventValues = event[0] - t.equal(typeof eventValues, 'object') - t.equal(typeof eventValues.webDuration, 'number') - t.not(Number.isNaN(eventValues.webDuration)) - t.equal(eventValues.webDuration, trans.timer.getDurationInMillis() / 1000) - t.equal(typeof eventValues.timestamp, 'number') - t.not(Number.isNaN(eventValues.timestamp)) - t.equal(eventValues.timestamp, trans.timer.start) - t.equal(eventValues.name, trans.name) - t.equal(eventValues.duration, trans.timer.getDurationInMillis() / 1000) - t.equal(eventValues.type, 'Transaction') - t.equal(eventValues.error, false) - t.end() - }) +test('on transaction finished', async (t) => { + helper.unloadAgent(t.nr.agent) + t.beforeEach((ctx) => { + ctx.nr.trans = new Transaction(ctx.nr.agent) + }) - t.test('should flag errored transactions', function (t) { - trans.addException(new Error('wuh oh')) + await t.test('should queue an event', async (t) => { + const { agent, trans } = t.nr + agent._addEventFromTransaction = (transaction) => { + assert.equal(transaction, trans) trans.end() + } + }) - const events = getTransactionEvents(agent) - t.equal(events.length, 1) - - const event = events[0] - t.ok(Array.isArray(event)) - const eventValues = event[0] - t.equal(typeof eventValues, 'object') - t.equal(typeof eventValues.webDuration, 'number') - t.not(Number.isNaN(eventValues.webDuration)) - t.equal(eventValues.webDuration, trans.timer.getDurationInMillis() / 1000) - t.equal(typeof eventValues.timestamp, 'number') - t.not(Number.isNaN(eventValues.timestamp)) - t.equal(eventValues.timestamp, trans.timer.start) - t.equal(eventValues.name, trans.name) - t.equal(eventValues.duration, trans.timer.getDurationInMillis() / 1000) - t.equal(eventValues.type, 'Transaction') - t.equal(eventValues.error, true) - t.end() - }) + await t.test('should generate an event from transaction', (t) => { + const { agent, trans } = t.nr + trans.end() + + const events = getTransactionEvents(agent) + + assert.equal(events.length, 1) + + const event = events[0] + assert.ok(Array.isArray(event)) + const eventValues = event[0] + assert.equal(typeof eventValues, 'object') + assert.equal(typeof eventValues.webDuration, 'number') + assert.equal(Number.isNaN(eventValues.webDuration), false) + assert.equal(eventValues.webDuration, trans.timer.getDurationInMillis() / 1000) + assert.equal(typeof eventValues.timestamp, 'number') + assert.equal(Number.isNaN(eventValues.timestamp), false) + assert.equal(eventValues.timestamp, trans.timer.start) + assert.equal(eventValues.name, trans.name) + assert.equal(eventValues.duration, trans.timer.getDurationInMillis() / 1000) + assert.equal(eventValues.type, 'Transaction') + assert.equal(eventValues.error, false) + }) - t.test('should add DT parent attributes with an accepted payload', function (t) { - agent.config.distributed_tracing.enabled = true - agent.config.primary_application_id = 'test' - agent.config.account_id = 1 - trans = new Transaction(agent) - const payload = trans._createDistributedTracePayload().text() - trans.isDistributedTrace = null - trans._acceptDistributedTracePayload(payload) - trans.end() + await t.test('should flag errored transactions', (t) => { + const { agent, trans } = t.nr + trans.addException(new Error('wuh oh')) + trans.end() + + const events = getTransactionEvents(agent) + assert.equal(events.length, 1) + + const event = events[0] + assert.ok(Array.isArray(event)) + const eventValues = event[0] + assert.equal(typeof eventValues, 'object') + assert.equal(typeof eventValues.webDuration, 'number') + assert.equal(Number.isNaN(eventValues.webDuration), false) + assert.equal(eventValues.webDuration, trans.timer.getDurationInMillis() / 1000) + assert.equal(typeof eventValues.timestamp, 'number') + assert.equal(Number.isNaN(eventValues.timestamp), false) + assert.equal(eventValues.timestamp, trans.timer.start) + assert.equal(eventValues.name, trans.name) + assert.equal(eventValues.duration, trans.timer.getDurationInMillis() / 1000) + assert.equal(eventValues.type, 'Transaction') + assert.equal(eventValues.error, true) + }) - const events = getTransactionEvents(agent) - - t.equal(events.length, 1) - - const attributes = events[0][0] - t.equal(attributes.traceId, trans.traceId) - t.equal(attributes.guid, trans.id) - t.equal(attributes.priority, trans.priority) - t.equal(attributes.sampled, trans.sampled) - t.equal(attributes.parentId, trans.id) - 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.equal(attributes.error, false) - t.equal(trans.sampled, true) - t.ok(trans.priority > 1) - t.end() - }) + await t.test('should add DT parent attributes with an accepted payload', (t) => { + const { agent } = t.nr + agent.config.distributed_tracing.enabled = true + agent.config.primary_application_id = 'test' + agent.config.account_id = 1 + const trans = new Transaction(agent) + const payload = trans._createDistributedTracePayload().text() + trans.isDistributedTrace = null + trans._acceptDistributedTracePayload(payload) + trans.end() + + const events = getTransactionEvents(agent) + + assert.equal(events.length, 1) + + const attributes = events[0][0] + assert.equal(attributes.traceId, trans.traceId) + assert.equal(attributes.guid, trans.id) + assert.equal(attributes.priority, trans.priority) + assert.equal(attributes.sampled, trans.sampled) + assert.equal(attributes.parentId, trans.id) + 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.error, false) + assert.equal(trans.sampled, true) + assert.ok(trans.priority > 1) + }) - t.test('should add DT attributes', function (t) { - agent.config.distributed_tracing.enabled = true - trans = new Transaction(agent) - trans.end() + await t.test('should add DT attributes', (t) => { + const { agent } = t.nr + agent.config.distributed_tracing.enabled = true + const trans = new Transaction(agent) + trans.end() - const events = getTransactionEvents(agent) + const events = getTransactionEvents(agent) - t.equal(events.length, 1) + assert.equal(events.length, 1) - const attributes = events[0][0] - t.equal(attributes.traceId, trans.traceId) - t.equal(attributes.guid, trans.id) - t.equal(attributes.priority, trans.priority) - t.equal(attributes.sampled, trans.sampled) - t.equal(trans.sampled, true) - t.ok(trans.priority > 1) - t.end() - }) + const attributes = events[0][0] + assert.equal(attributes.traceId, trans.traceId) + assert.equal(attributes.guid, trans.id) + assert.equal(attributes.priority, trans.priority) + assert.equal(attributes.sampled, trans.sampled) + assert.equal(trans.sampled, true) + assert.ok(trans.priority > 1) + }) - t.test('should contain user and agent attributes', function (t) { - trans.end() + await t.test('should contain user and agent attributes', (t) => { + const { agent, trans } = t.nr + trans.end() - const events = getTransactionEvents(agent) + const events = getTransactionEvents(agent) - t.equal(events.length, 1) + assert.equal(events.length, 1) - const event = events[0] - t.equal(typeof event[0], 'object') - t.equal(typeof event[1], 'object') - t.equal(typeof event[2], 'object') - t.end() - }) - - t.test('should contain custom attributes', function (t) { - trans.trace.addCustomAttribute('a', 'b') - trans.end() + const event = events[0] + assert.equal(typeof event[0], 'object') + assert.equal(typeof event[1], 'object') + assert.equal(typeof event[2], 'object') + }) - const events = getTransactionEvents(agent) - const event = events[0] - t.equal(event[1].a, 'b') - t.end() - }) + await t.test('should contain custom attributes', (t) => { + const { agent, trans } = t.nr + trans.trace.addCustomAttribute('a', 'b') + trans.end() - t.test('includes internal synthetics attributes', function (t) { - trans.syntheticsData = { - version: 1, - accountId: 123, - resourceId: 'resId', - jobId: 'jobId', - monitorId: 'monId' - } + const events = getTransactionEvents(agent) + const event = events[0] + assert.equal(event[1].a, 'b') + }) - trans.syntheticsInfoData = { - version: 1, - type: 'unitTest', - initiator: 'cli', - attributes: { - 'Attr-Test': 'value', - 'attr2Test': 'value1', - 'xTest-Header': 'value2' - } + await t.test('includes internal synthetics attributes', (t) => { + const { agent, trans } = t.nr + trans.syntheticsData = { + version: 1, + accountId: 123, + resourceId: 'resId', + jobId: 'jobId', + monitorId: 'monId' + } + + trans.syntheticsInfoData = { + version: 1, + type: 'unitTest', + initiator: 'cli', + attributes: { + 'Attr-Test': 'value', + 'attr2Test': 'value1', + 'xTest-Header': 'value2' } + } + + trans.end() + + const events = getTransactionEvents(agent) + const event = events[0] + const attributes = event[0] + assert.equal(attributes['nr.syntheticsResourceId'], 'resId') + assert.equal(attributes['nr.syntheticsJobId'], 'jobId') + assert.equal(attributes['nr.syntheticsMonitorId'], 'monId') + assert.equal(attributes['nr.syntheticsType'], 'unitTest') + assert.equal(attributes['nr.syntheticsInitiator'], 'cli') + assert.equal(attributes['nr.syntheticsAttrTest'], 'value') + assert.equal(attributes['nr.syntheticsAttr2Test'], 'value1') + assert.equal(attributes['nr.syntheticsXTestHeader'], 'value2') + }) - trans.end() - - const events = getTransactionEvents(agent) - const event = events[0] - const attributes = event[0] - t.equal(attributes['nr.syntheticsResourceId'], 'resId') - t.equal(attributes['nr.syntheticsJobId'], 'jobId') - t.equal(attributes['nr.syntheticsMonitorId'], 'monId') - t.equal(attributes['nr.syntheticsType'], 'unitTest') - t.equal(attributes['nr.syntheticsInitiator'], 'cli') - t.equal(attributes['nr.syntheticsAttrTest'], 'value') - t.equal(attributes['nr.syntheticsAttr2Test'], 'value1') - t.equal(attributes['nr.syntheticsXTestHeader'], 'value2') - t.end() - }) - - t.test('not spill over reservoir size', function (t) { - for (let i = 0; i < 20; i++) { - agent._addEventFromTransaction(trans) - } - t.equal(getTransactionEvents(agent).length, LIMIT) - t.end() - }) + await t.test('not spill over reservoir size', (t) => { + const { agent, trans } = t.nr + for (let i = 0; i < 20; i++) { + agent._addEventFromTransaction(trans) + } + assert.equal(getTransactionEvents(agent).length, LIMIT) }) }) diff --git a/test/unit/apdex.test.js b/test/unit/apdex.test.js index 2fab81448f..26b4f48d03 100644 --- a/test/unit/apdex.test.js +++ b/test/unit/apdex.test.js @@ -1,94 +1,87 @@ /* - * 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 = require('node:test') +const assert = require('node:assert') + const ApdexStats = require('../../lib/stats/apdex') -tap.Test.prototype.addAssert( - 'verifyApdexStats', - 2, - function verifyApdexStats(actualStats, expectedStats) { - this.equal(actualStats.satisfying, expectedStats.satisfying) - this.equal(actualStats.tolerating, expectedStats.tolerating) - this.equal(actualStats.frustrating, expectedStats.frustrating) + +function verifyApdexStats(actualStats, expectedStats) { + assert.equal(actualStats.satisfying, expectedStats.satisfying) + assert.equal(actualStats.tolerating, expectedStats.tolerating) + assert.equal(actualStats.frustrating, expectedStats.frustrating) +} + +test.beforeEach((ctx) => { + ctx.nr = { + statistics: new ApdexStats(0.3) } -) - -tap.test('ApdexStats', function (t) { - t.autoend() - t.beforeEach(function (t) { - t.context.statistics = new ApdexStats(0.3) - }) - - t.test('should throw when created with no tolerating value', function (t) { - t.throws(function () { - // eslint-disable-next-line no-new - new ApdexStats() - }, 'Apdex summary must be created with apdexT') - t.end() - }) - - t.test('should export apdexT in the 4th field of the timeslice', function (t) { - const { statistics } = t.context - t.equal(statistics.toJSON()[3], 0.3) - t.end() - }) - - t.test('should export apdexT in the 5th field (why?) of the timeslice', function (t) { - const { statistics } = t.context - t.equal(statistics.toJSON()[4], 0.3) - t.end() - }) - - t.test('should correctly summarize a sample set of statistics', function (t) { - const { statistics } = t.context - statistics.recordValueInMillis(1251) - statistics.recordValueInMillis(250) - statistics.recordValueInMillis(487) - - const expectedStats = { satisfying: 1, tolerating: 1, frustrating: 1 } - - t.verifyApdexStats(statistics, expectedStats) - t.end() - }) - - t.test('should correctly summarize another simple set of statistics', function (t) { - const { statistics } = t.context - statistics.recordValueInMillis(120) - statistics.recordValueInMillis(120) - statistics.recordValueInMillis(120) - statistics.recordValueInMillis(120) - - const expectedStats = { satisfying: 4, tolerating: 0, frustrating: 0 } - - t.verifyApdexStats(statistics, expectedStats) - t.end() - }) - - t.test('should correctly merge summaries', function (t) { - const { statistics } = t.context - statistics.recordValueInMillis(1251) - statistics.recordValueInMillis(250) - statistics.recordValueInMillis(487) - - const expectedStats = { satisfying: 1, tolerating: 1, frustrating: 1 } - t.verifyApdexStats(statistics, expectedStats) - - const other = new ApdexStats(0.3) - other.recordValueInMillis(120) - other.recordValueInMillis(120) - other.recordValueInMillis(120) - other.recordValueInMillis(120) - - const expectedOtherStats = { satisfying: 4, tolerating: 0, frustrating: 0 } - t.verifyApdexStats(other, expectedOtherStats) - - statistics.merge(other) - - const expectedMergedStats = { satisfying: 5, tolerating: 1, frustrating: 1 } - t.verifyApdexStats(statistics, expectedMergedStats) - t.end() - }) +}) + +test('should throw when created with no tolerating value', () => { + assert.throws(function () { + // eslint-disable-next-line no-new + new ApdexStats() + }, 'Apdex summary must be created with apdexT') +}) + +test('should export apdexT in the 4th field of the timeslice', (t) => { + const { statistics } = t.nr + assert.equal(statistics.toJSON()[3], 0.3) +}) + +test('should export apdexT in the 5th field (why?) of the timeslice', (t) => { + const { statistics } = t.nr + assert.equal(statistics.toJSON()[4], 0.3) +}) + +test('should correctly summarize a sample set of statistics', (t) => { + const { statistics } = t.nr + statistics.recordValueInMillis(1251) + statistics.recordValueInMillis(250) + statistics.recordValueInMillis(487) + + const expectedStats = { satisfying: 1, tolerating: 1, frustrating: 1 } + + verifyApdexStats(statistics, expectedStats) +}) + +test('should correctly summarize another simple set of statistics', (t) => { + const { statistics } = t.nr + statistics.recordValueInMillis(120) + statistics.recordValueInMillis(120) + statistics.recordValueInMillis(120) + statistics.recordValueInMillis(120) + + const expectedStats = { satisfying: 4, tolerating: 0, frustrating: 0 } + + verifyApdexStats(statistics, expectedStats) +}) + +test('should correctly merge summaries', (t) => { + const { statistics } = t.nr + statistics.recordValueInMillis(1251) + statistics.recordValueInMillis(250) + statistics.recordValueInMillis(487) + + const expectedStats = { satisfying: 1, tolerating: 1, frustrating: 1 } + verifyApdexStats(statistics, expectedStats) + + const other = new ApdexStats(0.3) + other.recordValueInMillis(120) + other.recordValueInMillis(120) + other.recordValueInMillis(120) + other.recordValueInMillis(120) + + const expectedOtherStats = { satisfying: 4, tolerating: 0, frustrating: 0 } + verifyApdexStats(other, expectedOtherStats) + + statistics.merge(other) + + const expectedMergedStats = { satisfying: 5, tolerating: 1, frustrating: 1 } + verifyApdexStats(statistics, expectedMergedStats) }) diff --git a/test/unit/attributes.test.js b/test/unit/attributes.test.js index bfb5f07857..506c0e5d57 100644 --- a/test/unit/attributes.test.js +++ b/test/unit/attributes.test.js @@ -1,12 +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 = require('node:test') +const assert = require('node:assert') +const { match } = require('../lib/custom-assertions') const helper = require('../lib/agent_helper') const { Attributes } = require('../../lib/attributes') const AttributeFilter = require('../../lib/config/attribute-filter') @@ -14,30 +16,16 @@ const AttributeFilter = require('../../lib/config/attribute-filter') const DESTINATIONS = AttributeFilter.DESTINATIONS const TRANSACTION_SCOPE = 'transaction' -tap.test('#addAttribute', (t) => { - t.autoend() - - let agent = null - - t.beforeEach(() => { - agent = helper.loadMockedAgent() - }) - - t.afterEach(() => { - helper.unloadAgent(agent) - }) - - t.test('adds an attribute to instance', (t) => { +test('#addAttribute', async (t) => { + await t.test('adds an attribute to instance', () => { const inst = new Attributes(TRANSACTION_SCOPE) inst.addAttribute(DESTINATIONS.TRANS_SCOPE, 'test', 'success') const attributes = inst.get(DESTINATIONS.TRANS_SCOPE) - t.equal(attributes.test, 'success') - - t.end() + assert.equal(attributes.test, 'success') }) - t.test('does not add attribute if key length limit is exceeded', (t) => { + await t.test('does not add attribute if key length limit is exceeded', () => { const tooLong = [ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', 'Cras id lacinia erat. Suspendisse mi nisl, sodales vel est eu,', @@ -49,37 +37,21 @@ tap.test('#addAttribute', (t) => { inst.addAttribute(DESTINATIONS.TRANS_SCOPE, tooLong, 'will fail') const attributes = Object.keys(inst.attributes) - t.equal(attributes.length, 0) - - t.end() + assert.equal(attributes.length, 0) }) }) -tap.test('#addAttributes', (t) => { - t.autoend() - - let agent = null - - t.beforeEach(() => { - agent = helper.loadMockedAgent() - }) - - t.afterEach(() => { - helper.unloadAgent(agent) - }) - - t.test('adds multiple attributes to instance', (t) => { +test('#addAttributes', async (t) => { + await t.test('adds multiple attributes to instance', () => { const inst = new Attributes(TRANSACTION_SCOPE) inst.addAttributes(DESTINATIONS.TRANS_SCOPE, { one: '1', two: '2' }) const attributes = inst.get(DESTINATIONS.TRANS_SCOPE) - t.equal(attributes.one, '1') - t.equal(attributes.two, '2') - - t.end() + assert.equal(attributes.one, '1') + assert.equal(attributes.two, '2') }) - t.test('only allows non-null-type primitive attribute values', (t) => { + await t.test('only allows non-null-type primitive attribute values', () => { const inst = new Attributes(TRANSACTION_SCOPE, 10) const attributes = { first: 'first', @@ -96,20 +68,18 @@ tap.test('#addAttributes', (t) => { inst.addAttributes(DESTINATIONS.TRANS_SCOPE, attributes) const res = inst.get(DESTINATIONS.TRANS_SCOPE) - t.equal(Object.keys(res).length, 3) + assert.equal(Object.keys(res).length, 3) const hasAttribute = Object.hasOwnProperty.bind(res) - t.notOk(hasAttribute('second')) - t.notOk(hasAttribute('third')) - t.notOk(hasAttribute('sixth')) - t.notOk(hasAttribute('seventh')) - t.notOk(hasAttribute('eighth')) - t.notOk(hasAttribute('ninth')) - - t.end() + assert.equal(hasAttribute('second'), false) + assert.equal(hasAttribute('third'), false) + assert.equal(hasAttribute('sixth'), false) + assert.equal(hasAttribute('seventh'), false) + assert.equal(hasAttribute('eighth'), false) + assert.equal(hasAttribute('ninth'), false) }) - t.test('disallows adding more than maximum allowed attributes', (t) => { + await t.test('disallows adding more than maximum allowed attributes', () => { const inst = new Attributes(TRANSACTION_SCOPE, 3) const attributes = { first: 1, @@ -121,39 +91,23 @@ tap.test('#addAttributes', (t) => { inst.addAttributes(DESTINATIONS.TRANS_SCOPE, attributes) const res = inst.get(DESTINATIONS.TRANS_SCOPE) - t.equal(Object.keys(res).length, 3) - - t.end() + assert.equal(Object.keys(res).length, 3) }) - t.test('Overwrites value of added attribute with same key', (t) => { + await t.test('Overwrites value of added attribute with same key', () => { const inst = new Attributes(TRANSACTION_SCOPE, 2) inst.addAttribute(0x01, 'Roboto', 1) inst.addAttribute(0x01, 'Roboto', 99) const res = inst.get(0x01) - t.equal(Object.keys(res).length, 1) - t.equal(res.Roboto, 99) - - t.end() + assert.equal(Object.keys(res).length, 1) + assert.equal(res.Roboto, 99) }) }) -tap.test('#get', (t) => { - t.autoend() - - let agent = null - - t.beforeEach(() => { - agent = helper.loadMockedAgent() - }) - - t.afterEach(() => { - helper.unloadAgent(agent) - }) - - t.test('gets attributes by destination, truncating values if necessary', (t) => { +test('#get', async (t) => { + await t.test('gets attributes by destination, truncating values if necessary', () => { const longVal = [ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', 'Cras id lacinia erat. Suspendisse mi nisl, sodales vel est eu,', @@ -167,17 +121,15 @@ tap.test('#get', (t) => { inst.addAttribute(0x01, 'tooLong', longVal) inst.addAttribute(0x08, 'wrongDest', 'hello') - t.ok(Buffer.byteLength(longVal) > 255) + assert.ok(Buffer.byteLength(longVal) > 255) const res = inst.get(0x01) - t.equal(res.valid, 50) - - t.equal(Buffer.byteLength(res.tooLong), 255) + assert.equal(res.valid, 50) - t.end() + assert.equal(Buffer.byteLength(res.tooLong), 255) }) - t.test('only returns attributes up to specified limit', (t) => { + await t.test('only returns attributes up to specified limit', () => { const inst = new Attributes(TRANSACTION_SCOPE, 2) inst.addAttribute(0x01, 'first', 'first') inst.addAttribute(0x01, 'second', 'second') @@ -186,44 +138,38 @@ tap.test('#get', (t) => { const res = inst.get(0x01) const hasAttribute = Object.hasOwnProperty.bind(res) - t.equal(Object.keys(res).length, 2) - t.notOk(hasAttribute('third')) - - t.end() + assert.equal(Object.keys(res).length, 2) + assert.equal(hasAttribute('third'), false) }) }) -tap.test('#hasValidDestination', (t) => { - t.autoend() - - let agent = null - - t.beforeEach(() => { - agent = helper.loadMockedAgent() +test('#hasValidDestination', async (t) => { + t.beforeEach((ctx) => { + ctx.nr = {} + ctx.nr.agent = helper.loadMockedAgent() }) - t.afterEach(() => { - helper.unloadAgent(agent) + t.afterEach((ctx) => { + helper.unloadAgent(ctx.nr.agent) }) - t.test('should return true if single destination valid', (t) => { + await t.test('should return true if single destination valid', () => { const attributes = new Attributes(TRANSACTION_SCOPE) const hasDestination = attributes.hasValidDestination(DESTINATIONS.TRANS_EVENT, 'testAttr') - t.equal(hasDestination, true) - t.end() + assert.equal(hasDestination, true) }) - t.test('should return true if all destinations valid', (t) => { + await t.test('should return true if all destinations valid', () => { const attributes = new Attributes(TRANSACTION_SCOPE) const destinations = DESTINATIONS.TRANS_EVENT | DESTINATIONS.TRANS_TRACE const hasDestination = attributes.hasValidDestination(destinations, 'testAttr') - t.equal(hasDestination, true) - t.end() + assert.equal(hasDestination, true) }) - t.test('should return true if only one destination valid', (t) => { + await t.test('should return true if only one destination valid', (t) => { + const { agent } = t.nr const attributeName = 'testAttr' agent.config.transaction_events.attributes.exclude = [attributeName] agent.config.emit('transaction_events.attributes.exclude') @@ -232,11 +178,11 @@ tap.test('#hasValidDestination', (t) => { const destinations = DESTINATIONS.TRANS_EVENT | DESTINATIONS.TRANS_TRACE const hasDestination = attributes.hasValidDestination(destinations, attributeName) - t.equal(hasDestination, true) - t.end() + assert.equal(hasDestination, true) }) - t.test('should return false if no valid destinations', (t) => { + await t.test('should return false if no valid destinations', (t) => { + const { agent } = t.nr const attributeName = 'testAttr' agent.config.attributes.exclude = [attributeName] agent.config.emit('attributes.exclude') @@ -245,25 +191,12 @@ tap.test('#hasValidDestination', (t) => { const destinations = DESTINATIONS.TRANS_EVENT | DESTINATIONS.TRANS_TRACE const hasDestination = attributes.hasValidDestination(destinations, attributeName) - t.equal(hasDestination, false) - t.end() + assert.equal(hasDestination, false) }) }) -tap.test('#reset', (t) => { - t.autoend() - - let agent = null - - t.beforeEach(() => { - agent = helper.loadMockedAgent() - }) - - t.afterEach(() => { - helper.unloadAgent(agent) - }) - - t.test('resets instance attributes', (t) => { +test('#reset', async (t) => { + await t.test('resets instance attributes', () => { const inst = new Attributes(TRANSACTION_SCOPE) inst.addAttribute(0x01, 'first', 'first') inst.addAttribute(0x01, 'second', 'second') @@ -271,8 +204,6 @@ tap.test('#reset', (t) => { inst.reset() - t.same(inst.attributes, {}) - - t.end() + assert.equal(match(inst.attributes, {}), true) }) })