diff --git a/test/versioned/redis/package.json b/test/versioned/redis/package.json index c82030c434..a49fdca01a 100644 --- a/test/versioned/redis/package.json +++ b/test/versioned/redis/package.json @@ -12,7 +12,7 @@ "redis": ">=3.1.0 < 4.0.0" }, "files": [ - "redis.tap.js" + "redis.test.js" ] }, { @@ -23,9 +23,9 @@ "redis": ">=4.0.0" }, "files": [ - "redis-v4.tap.js", - "redis-v4-legacy-mode.tap.js", - "tls.tap.js" + "redis-v4.test.js", + "redis-v4-legacy-mode.test.js", + "tls.test.js" ] } ] diff --git a/test/versioned/redis/redis-v4-legacy-mode.tap.js b/test/versioned/redis/redis-v4-legacy-mode.tap.js deleted file mode 100644 index abacc5a95f..0000000000 --- a/test/versioned/redis/redis-v4-legacy-mode.tap.js +++ /dev/null @@ -1,258 +0,0 @@ -/* - * 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 helper = require('../../lib/agent_helper') -const { removeMatchedModules } = require('../../lib/cache-buster') -const params = require('../../lib/params') -const urltils = require('../../../lib/util/urltils') - -// Indicates unique database in Redis. 0-15 supported. -const DB_INDEX = 2 - -test('Redis instrumentation', function (t) { - t.autoend() - - let METRIC_HOST_NAME = null - let HOST_ID = null - - let agent - let client - - t.beforeEach(async function () { - agent = helper.instrumentMockedAgent() - - const redis = require('redis') - client = redis.createClient({ - legacyMode: true, - port: params.redis_port, - host: params.redis_host - }) - - await client.connect() - await client.v4.flushAll() - - await client.v4.select(DB_INDEX) - // eslint-disable-next-line new-cap - - METRIC_HOST_NAME = urltils.isLocalhost(params.redis_host) - ? agent.config.getHostnameSafe() - : params.redis_host - HOST_ID = METRIC_HOST_NAME + '/' + params.redis_port - - // need to capture attributes - agent.config.attributes.enabled = true - }) - - t.afterEach(async function () { - agent && helper.unloadAgent(agent) - if (client) { - await client.v4.flushAll() - await client.v4.quit() - } - // must purge require cache of redis related instrumentation - // otherwise it will not re-register on subsequent test runs - removeMatchedModules(/redis/) - }) - - t.test('should find Redis calls in the transaction trace', function (t) { - t.notOk(agent.getTransaction(), 'no transaction should be in play') - helper.runInTransaction(agent, async function transactionInScope() { - const transaction = agent.getTransaction() - t.ok(transaction, 'transaction should be visible') - - const ok = await client.v4.set('testkey', 'arglbargle') - t.ok(agent.getTransaction(), 'transaction should still be visible') - t.ok(ok, 'everything should be peachy after setting') - - const value = await client.v4.get('testkey') - t.ok(agent.getTransaction(), 'transaction should still still be visible') - t.equal(value, 'arglbargle', 'redis client should still work') - - const trace = transaction.trace - t.ok(trace, 'trace should exist') - t.ok(trace.root, 'root element should exist') - t.equal(trace.root.children.length, 2, 'there should be only two children of the root') - - const setSegment = trace.root.children[0] - const setAttributes = setSegment.getAttributes() - t.ok(setSegment, 'trace segment for set should exist') - t.equal(setSegment.name, 'Datastore/operation/Redis/set', 'should register the set') - t.equal(setAttributes.key, '"testkey"', 'should have the set key as a attribute') - t.equal(setSegment.children.length, 0, 'set should have no children') - - const getSegment = trace.root.children[1] - const getAttributes = getSegment.getAttributes() - t.ok(getSegment, 'trace segment for get should exist') - - t.equal(getSegment.name, 'Datastore/operation/Redis/get', 'should register the get') - - t.equal(getAttributes.key, '"testkey"', 'should have the get key as a attribute') - - t.ok(getSegment.timer.hrDuration, 'trace segment should have ended') - t.end() - }) - }) - - t.test('should create correct metrics', function (t) { - t.notOk(agent.getTransaction(), 'no transaction should be in play') - helper.runInTransaction(agent, async function transactionInScope() { - const transaction = agent.getTransaction() - await client.v4.set('testkey', 'arglbargle') - await client.v4.get('testkey') - transaction.end() - const unscoped = transaction.metrics.unscoped - const expected = { - 'Datastore/all': 2, - 'Datastore/allWeb': 2, - 'Datastore/Redis/all': 2, - 'Datastore/Redis/allWeb': 2, - 'Datastore/operation/Redis/set': 1, - 'Datastore/operation/Redis/get': 1 - } - checkMetrics(t, unscoped, expected) - t.end() - }) - }) - - t.test('should add `key` attribute to trace segment', function (t) { - t.notOk(agent.getTransaction(), 'no transaction should be in play') - agent.config.attributes.enabled = true - - helper.runInTransaction(agent, async function () { - await client.v4.set('saveme', 'foobar') - - const segment = agent.tracer.getSegment().children[0] - t.equal(segment.getAttributes().key, '"saveme"', 'should have `key` attribute') - t.end() - }) - }) - - t.test('should not add `key` attribute to trace segment', function (t) { - t.notOk(agent.getTransaction(), 'no transaction should be in play') - agent.config.attributes.enabled = false - - helper.runInTransaction(agent, async function () { - await client.v4.set('saveme', 'foobar') - - const segment = agent.tracer.getSegment().children[0] - t.notOk(segment.getAttributes().key, 'should not have `key` attribute') - t.end() - }) - }) - - t.test('should add datastore instance attributes to trace segments', function (t) { - t.notOk(agent.getTransaction(), 'no transaction should be in play') - // Enable. - agent.config.datastore_tracer.instance_reporting.enabled = true - agent.config.datastore_tracer.database_name_reporting.enabled = true - - helper.runInTransaction(agent, async function transactionInScope() { - const transaction = agent.getTransaction() - await client.v4.set('testkey', 'arglbargle') - - const trace = transaction.trace - const setSegment = trace.root.children[0] - const attributes = setSegment.getAttributes() - t.equal(attributes.host, METRIC_HOST_NAME, 'should have host as attribute') - t.equal( - attributes.port_path_or_id, - String(params.redis_port), - 'should have port as attribute' - ) - t.equal(attributes.database_name, String(DB_INDEX), 'should have database id as attribute') - t.equal(attributes.product, 'Redis', 'should have product attribute') - t.end() - }) - }) - - t.test('should not add instance attributes/metrics when disabled', function (t) { - t.notOk(agent.getTransaction(), 'no transaction should be in play') - // disable - agent.config.datastore_tracer.instance_reporting.enabled = false - agent.config.datastore_tracer.database_name_reporting.enabled = false - - helper.runInTransaction(agent, async function transactionInScope() { - const transaction = agent.getTransaction() - await client.v4.set('testkey', 'arglbargle') - - const setSegment = transaction.trace.root.children[0] - const attributes = setSegment.getAttributes() - t.equal(attributes.host, undefined, 'should not have host attribute') - t.equal(attributes.port_path_or_id, undefined, 'should not have port attribute') - t.equal(attributes.database_name, undefined, 'should not have db name attribute') - - transaction.end() - const unscoped = transaction.metrics.unscoped - t.equal( - unscoped['Datastore/instance/Redis/' + HOST_ID], - undefined, - 'should not have instance metric' - ) - t.end() - }) - }) - - t.test('should follow selected database', function (t) { - t.notOk(agent.getTransaction(), 'no transaction should be in play') - let transaction = null - const SELECTED_DB = 3 - helper.runInTransaction(agent, async function (tx) { - transaction = tx - await client.v4.set('select:test:key', 'foo') - t.ok(agent.getTransaction(), 'should not lose transaction state') - - await client.v4.select(SELECTED_DB) - t.ok(agent.getTransaction(), 'should not lose transaction state') - - await client.v4.set('select:test:key:2', 'bar') - t.ok(agent.getTransaction(), 'should not lose transaction state') - transaction.end() - verify() - t.end() - }) - - function verify() { - const setSegment1 = transaction.trace.root.children[0] - const selectSegment = transaction.trace.root.children[1] - const setSegment2 = transaction.trace.root.children[2] - - t.equal(setSegment1.name, 'Datastore/operation/Redis/set', 'should register the first set') - t.equal( - setSegment1.getAttributes().database_name, - String(DB_INDEX), - 'should have the starting database id as attribute for the first set' - ) - t.equal(selectSegment.name, 'Datastore/operation/Redis/select', 'should register the select') - t.equal( - selectSegment.getAttributes().database_name, - String(DB_INDEX), - 'should have the starting database id as attribute for the select' - ) - t.equal(setSegment2.name, 'Datastore/operation/Redis/set', 'should register the second set') - t.equal( - setSegment2.getAttributes().database_name, - String(SELECTED_DB), - 'should have the selected database id as attribute for the second set' - ) - } - }) -}) - -function checkMetrics(t, metrics, expected) { - Object.keys(expected).forEach(function (name) { - t.ok(metrics[name], 'should have metric ' + name) - if (metrics[name]) { - t.equal( - metrics[name].callCount, - expected[name], - 'should have ' + expected[name] + ' calls for ' + name - ) - } - }) -} diff --git a/test/versioned/redis/redis-v4-legacy-mode.test.js b/test/versioned/redis/redis-v4-legacy-mode.test.js new file mode 100644 index 0000000000..060e6c23b0 --- /dev/null +++ b/test/versioned/redis/redis-v4-legacy-mode.test.js @@ -0,0 +1,265 @@ +/* + * Copyright 2024 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict' + +const test = require('node:test') +const assert = require('node:assert') +const helper = require('../../lib/agent_helper') +const { removeMatchedModules } = require('../../lib/cache-buster') +const params = require('../../lib/params') +const urltils = require('../../../lib/util/urltils') +const { checkMetrics } = require('./utils') + +// Indicates unique database in Redis. 0-15 supported. +const DB_INDEX = 2 + +test('Redis instrumentation', async function (t) { + t.beforeEach(async function (ctx) { + const agent = helper.instrumentMockedAgent() + const redis = require('redis') + const client = redis.createClient({ + legacyMode: true, + port: params.redis_port, + host: params.redis_host + }) + + await client.connect() + await client.v4.flushAll() + + await client.v4.select(DB_INDEX) + // eslint-disable-next-line new-cap + + const METRIC_HOST_NAME = urltils.isLocalhost(params.redis_host) + ? agent.config.getHostnameSafe() + : params.redis_host + const HOST_ID = METRIC_HOST_NAME + '/' + params.redis_port + + // need to capture attributes + agent.config.attributes.enabled = true + ctx.nr = { + agent, + client, + HOST_ID, + METRIC_HOST_NAME + } + }) + + t.afterEach(async function (ctx) { + const { agent, client } = ctx.nr + helper.unloadAgent(agent) + await client.v4.flushAll() + await client.v4.quit() + // must purge require cache of redis related instrumentation + // otherwise it will not re-register on subsequent test runs + removeMatchedModules(/redis/) + }) + + await t.test('should find Redis calls in the transaction trace', function (t, end) { + const { agent, client } = t.nr + assert.ok(!agent.getTransaction(), 'no transaction should be in play') + helper.runInTransaction(agent, async function transactionInScope() { + const transaction = agent.getTransaction() + assert.ok(transaction, 'transaction should be visible') + + const ok = await client.v4.set('testkey', 'arglbargle') + assert.ok(agent.getTransaction(), 'transaction should still be visible') + assert.ok(ok, 'everything should be peachy after setting') + + const value = await client.v4.get('testkey') + assert.ok(agent.getTransaction(), 'transaction should still still be visible') + assert.equal(value, 'arglbargle', 'redis client should still work') + + const trace = transaction.trace + assert.ok(trace, 'trace should exist') + assert.ok(trace.root, 'root element should exist') + assert.equal(trace.root.children.length, 2, 'there should be only two children of the root') + + const setSegment = trace.root.children[0] + const setAttributes = setSegment.getAttributes() + assert.ok(setSegment, 'trace segment for set should exist') + assert.equal(setSegment.name, 'Datastore/operation/Redis/set', 'should register the set') + assert.equal(setAttributes.key, '"testkey"', 'should have the set key as a attribute') + assert.equal(setSegment.children.length, 0, 'set should have no children') + + const getSegment = trace.root.children[1] + const getAttributes = getSegment.getAttributes() + assert.ok(getSegment, 'trace segment for get should exist') + + assert.equal(getSegment.name, 'Datastore/operation/Redis/get', 'should register the get') + + assert.equal(getAttributes.key, '"testkey"', 'should have the get key as a attribute') + + assert.ok(getSegment.timer.hrDuration, 'trace segment should have ended') + end() + }) + }) + + await t.test('should create correct metrics', function (t, end) { + const { agent, client } = t.nr + assert.ok(!agent.getTransaction(), 'no transaction should be in play') + helper.runInTransaction(agent, async function transactionInScope() { + const transaction = agent.getTransaction() + await client.v4.set('testkey', 'arglbargle') + await client.v4.get('testkey') + transaction.end() + const metrics = transaction.metrics.unscoped + const expected = { + 'Datastore/all': 2, + 'Datastore/allWeb': 2, + 'Datastore/Redis/all': 2, + 'Datastore/Redis/allWeb': 2, + 'Datastore/operation/Redis/set': 1, + 'Datastore/operation/Redis/get': 1 + } + checkMetrics({ metrics, expected }) + end() + }) + }) + + await t.test('should add `key` attribute to trace segment', function (t, end) { + const { agent, client } = t.nr + assert.ok(!agent.getTransaction(), 'no transaction should be in play') + agent.config.attributes.enabled = true + + helper.runInTransaction(agent, async function () { + await client.v4.set('saveme', 'foobar') + + const segment = agent.tracer.getSegment().children[0] + assert.equal(segment.getAttributes().key, '"saveme"', 'should have `key` attribute') + end() + }) + }) + + await t.test('should not add `key` attribute to trace segment', function (t, end) { + const { agent, client } = t.nr + assert.ok(!agent.getTransaction(), 'no transaction should be in play') + agent.config.attributes.enabled = false + + helper.runInTransaction(agent, async function () { + await client.v4.set('saveme', 'foobar') + + const segment = agent.tracer.getSegment().children[0] + assert.ok(!segment.getAttributes().key, 'should not have `key` attribute') + end() + }) + }) + + await t.test('should add datastore instance attributes to trace segments', function (t, end) { + const { agent, client, METRIC_HOST_NAME } = t.nr + assert.ok(!agent.getTransaction(), 'no transaction should be in play') + // Enable. + agent.config.datastore_tracer.instance_reporting.enabled = true + agent.config.datastore_tracer.database_name_reporting.enabled = true + + helper.runInTransaction(agent, async function transactionInScope() { + const transaction = agent.getTransaction() + await client.v4.set('testkey', 'arglbargle') + + const trace = transaction.trace + const setSegment = trace.root.children[0] + const attributes = setSegment.getAttributes() + assert.equal(attributes.host, METRIC_HOST_NAME, 'should have host as attribute') + assert.equal( + attributes.port_path_or_id, + String(params.redis_port), + 'should have port as attribute' + ) + assert.equal( + attributes.database_name, + String(DB_INDEX), + 'should have database id as attribute' + ) + assert.equal(attributes.product, 'Redis', 'should have product attribute') + end() + }) + }) + + await t.test('should not add instance attributes/metrics when disabled', function (t, end) { + const { agent, client, HOST_ID } = t.nr + assert.ok(!agent.getTransaction(), 'no transaction should be in play') + // disable + agent.config.datastore_tracer.instance_reporting.enabled = false + agent.config.datastore_tracer.database_name_reporting.enabled = false + + helper.runInTransaction(agent, async function transactionInScope() { + const transaction = agent.getTransaction() + await client.v4.set('testkey', 'arglbargle') + + const setSegment = transaction.trace.root.children[0] + const attributes = setSegment.getAttributes() + assert.equal(attributes.host, undefined, 'should not have host attribute') + assert.equal(attributes.port_path_or_id, undefined, 'should not have port attribute') + assert.equal(attributes.database_name, undefined, 'should not have db name attribute') + + transaction.end() + const unscoped = transaction.metrics.unscoped + assert.equal( + unscoped['Datastore/instance/Redis/' + HOST_ID], + undefined, + 'should not have instance metric' + ) + end() + }) + }) + + await t.test('should follow selected database', function (t, end) { + const { agent, client } = t.nr + assert.ok(!agent.getTransaction(), 'no transaction should be in play') + let transaction = null + const SELECTED_DB = 3 + helper.runInTransaction(agent, async function (tx) { + transaction = tx + await client.v4.set('select:test:key', 'foo') + assert.ok(agent.getTransaction(), 'should not lose transaction state') + + await client.v4.select(SELECTED_DB) + assert.ok(agent.getTransaction(), 'should not lose transaction state') + + await client.v4.set('select:test:key:2', 'bar') + assert.ok(agent.getTransaction(), 'should not lose transaction state') + transaction.end() + verify() + end() + }) + + function verify() { + const setSegment1 = transaction.trace.root.children[0] + const selectSegment = transaction.trace.root.children[1] + const setSegment2 = transaction.trace.root.children[2] + + assert.equal( + setSegment1.name, + 'Datastore/operation/Redis/set', + 'should register the first set' + ) + assert.equal( + setSegment1.getAttributes().database_name, + String(DB_INDEX), + 'should have the starting database id as attribute for the first set' + ) + assert.equal( + selectSegment.name, + 'Datastore/operation/Redis/select', + 'should register the select' + ) + assert.equal( + selectSegment.getAttributes().database_name, + String(DB_INDEX), + 'should have the starting database id as attribute for the select' + ) + assert.equal( + setSegment2.name, + 'Datastore/operation/Redis/set', + 'should register the second set' + ) + assert.equal( + setSegment2.getAttributes().database_name, + String(SELECTED_DB), + 'should have the selected database id as attribute for the second set' + ) + } + }) +}) diff --git a/test/versioned/redis/redis-v4.tap.js b/test/versioned/redis/redis-v4.tap.js deleted file mode 100644 index 5b43c4225e..0000000000 --- a/test/versioned/redis/redis-v4.tap.js +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const tap = require('tap') -const test = tap.test -const helper = require('../../lib/agent_helper') -const { removeMatchedModules } = require('../../lib/cache-buster') -const params = require('../../lib/params') -const urltils = require('../../../lib/util/urltils') - -// Indicates unique database in Redis. 0-15 supported. -const DB_INDEX = 2 - -test('Redis instrumentation', function (t) { - t.autoend() - - let METRIC_HOST_NAME = null - let HOST_ID = null - - let agent - let client - - t.beforeEach(async function () { - agent = helper.instrumentMockedAgent() - - const redis = require('redis') - client = redis.createClient({ socket: { port: params.redis_port, host: params.redis_host } }) - - await client.connect() - await client.flushAll() - - await client.select(DB_INDEX) - // eslint-disable-next-line new-cap - - METRIC_HOST_NAME = urltils.isLocalhost(params.redis_host) - ? agent.config.getHostnameSafe() - : params.redis_host - HOST_ID = METRIC_HOST_NAME + '/' + params.redis_port - - // need to capture attributes - agent.config.attributes.enabled = true - }) - - t.afterEach(async function () { - agent && helper.unloadAgent(agent) - if (client) { - await client.flushAll() - await client.disconnect() - } - // must purge require cache of redis related instrumentation - // otherwise it will not re-register on subsequent test runs - removeMatchedModules(/redis/) - }) - - t.test('should find Redis calls in the transaction trace', function (t) { - t.notOk(agent.getTransaction(), 'no transaction should be in play') - helper.runInTransaction(agent, async function transactionInScope() { - const transaction = agent.getTransaction() - t.ok(transaction, 'transaction should be visible') - - const ok = await client.set('testkey', 'arglbargle') - t.ok(agent.getTransaction(), 'transaction should still be visible') - t.ok(ok, 'everything should be peachy after setting') - - const value = await client.get('testkey') - t.ok(agent.getTransaction(), 'transaction should still still be visible') - t.equal(value, 'arglbargle', 'redis client should still work') - - const trace = transaction.trace - t.ok(trace, 'trace should exist') - t.ok(trace.root, 'root element should exist') - t.equal(trace.root.children.length, 2, 'there should be only two children of the root') - - const setSegment = trace.root.children[0] - const setAttributes = setSegment.getAttributes() - t.ok(setSegment, 'trace segment for set should exist') - t.equal(setSegment.name, 'Datastore/operation/Redis/set', 'should register the set') - t.equal(setAttributes.key, '"testkey"', 'should have the set key as a attribute') - t.equal(setSegment.children.length, 0, 'set should have no children') - - const getSegment = trace.root.children[1] - const getAttributes = getSegment.getAttributes() - t.ok(getSegment, 'trace segment for get should exist') - - t.equal(getSegment.name, 'Datastore/operation/Redis/get', 'should register the get') - - t.equal(getAttributes.key, '"testkey"', 'should have the get key as a attribute') - - t.ok(getSegment.timer.hrDuration, 'trace segment should have ended') - t.end() - }) - }) - - t.test('should create correct metrics', function (t) { - t.notOk(agent.getTransaction(), 'no transaction should be in play') - helper.runInTransaction(agent, async function transactionInScope() { - const transaction = agent.getTransaction() - await client.set('testkey', 'arglbargle') - await client.get('testkey') - transaction.end() - const unscoped = transaction.metrics.unscoped - const expected = { - 'Datastore/all': 2, - 'Datastore/allWeb': 2, - 'Datastore/Redis/all': 2, - 'Datastore/Redis/allWeb': 2, - 'Datastore/operation/Redis/set': 1, - 'Datastore/operation/Redis/get': 1 - } - checkMetrics(t, unscoped, expected) - t.end() - }) - }) - - t.test('should handle multi commands', function (t) { - helper.runInTransaction(agent, async function transactionInScope() { - const transaction = agent.getTransaction() - const results = await client.multi().set('multi-key', 'multi-value').get('multi-key').exec() - - t.same(results, ['OK', 'multi-value'], 'should return expected results') - transaction.end() - const unscoped = transaction.metrics.unscoped - const expected = { - 'Datastore/all': 4, - 'Datastore/allWeb': 4, - 'Datastore/Redis/all': 4, - 'Datastore/Redis/allWeb': 4, - 'Datastore/operation/Redis/multi': 1, - 'Datastore/operation/Redis/set': 1, - 'Datastore/operation/Redis/get': 1, - 'Datastore/operation/Redis/exec': 1 - } - expected['Datastore/instance/Redis/' + HOST_ID] = 4 - checkMetrics(t, unscoped, expected) - t.end() - }) - }) - - t.test('should add `key` attribute to trace segment', function (t) { - t.notOk(agent.getTransaction(), 'no transaction should be in play') - agent.config.attributes.enabled = true - - helper.runInTransaction(agent, async function () { - await client.set('saveme', 'foobar') - - const segment = agent.tracer.getSegment().children[0] - t.equal(segment.getAttributes().key, '"saveme"', 'should have `key` attribute') - t.end() - }) - }) - - t.test('should not add `key` attribute to trace segment', function (t) { - t.notOk(agent.getTransaction(), 'no transaction should be in play') - agent.config.attributes.enabled = false - - helper.runInTransaction(agent, async function () { - await client.set('saveme', 'foobar') - - const segment = agent.tracer.getSegment().children[0] - t.notOk(segment.getAttributes().key, 'should not have `key` attribute') - t.end() - }) - }) - - t.test('should add datastore instance attributes to trace segments', function (t) { - t.notOk(agent.getTransaction(), 'no transaction should be in play') - // Enable. - agent.config.datastore_tracer.instance_reporting.enabled = true - agent.config.datastore_tracer.database_name_reporting.enabled = true - - helper.runInTransaction(agent, async function transactionInScope() { - const transaction = agent.getTransaction() - await client.set('testkey', 'arglbargle') - - const trace = transaction.trace - const setSegment = trace.root.children[0] - const attributes = setSegment.getAttributes() - t.equal(attributes.host, METRIC_HOST_NAME, 'should have host as attribute') - t.equal( - attributes.port_path_or_id, - String(params.redis_port), - 'should have port as attribute' - ) - t.equal(attributes.database_name, String(DB_INDEX), 'should have database id as attribute') - t.equal(attributes.product, 'Redis', 'should have product attribute') - t.end() - }) - }) - - t.test('should not add instance attributes/metrics when disabled', function (t) { - t.notOk(agent.getTransaction(), 'no transaction should be in play') - // disable - agent.config.datastore_tracer.instance_reporting.enabled = false - agent.config.datastore_tracer.database_name_reporting.enabled = false - - helper.runInTransaction(agent, async function transactionInScope() { - const transaction = agent.getTransaction() - await client.set('testkey', 'arglbargle') - - const setSegment = transaction.trace.root.children[0] - const attributes = setSegment.getAttributes() - t.equal(attributes.host, undefined, 'should not have host attribute') - t.equal(attributes.port_path_or_id, undefined, 'should not have port attribute') - t.equal(attributes.database_name, undefined, 'should not have db name attribute') - - transaction.end() - const unscoped = transaction.metrics.unscoped - t.equal( - unscoped['Datastore/instance/Redis/' + HOST_ID], - undefined, - 'should not have instance metric' - ) - t.end() - }) - }) - - t.test('should follow selected database', function (t) { - t.notOk(agent.getTransaction(), 'no transaction should be in play') - let transaction = null - const SELECTED_DB = 3 - helper.runInTransaction(agent, async function (tx) { - transaction = tx - await client.set('select:test:key', 'foo') - t.ok(agent.getTransaction(), 'should not lose transaction state') - - await client.select(SELECTED_DB) - t.ok(agent.getTransaction(), 'should not lose transaction state') - - await client.set('select:test:key:2', 'bar') - t.ok(agent.getTransaction(), 'should not lose transaction state') - transaction.end() - verify() - t.end() - }) - - function verify() { - const setSegment1 = transaction.trace.root.children[0] - const selectSegment = transaction.trace.root.children[1] - const setSegment2 = transaction.trace.root.children[2] - - t.equal(setSegment1.name, 'Datastore/operation/Redis/set', 'should register the first set') - t.equal( - setSegment1.getAttributes().database_name, - String(DB_INDEX), - 'should have the starting database id as attribute for the first set' - ) - t.equal(selectSegment.name, 'Datastore/operation/Redis/select', 'should register the select') - t.equal( - selectSegment.getAttributes().database_name, - String(DB_INDEX), - 'should have the starting database id as attribute for the select' - ) - t.equal(setSegment2.name, 'Datastore/operation/Redis/set', 'should register the second set') - t.equal( - setSegment2.getAttributes().database_name, - String(SELECTED_DB), - 'should have the selected database id as attribute for the second set' - ) - } - }) -}) - -function checkMetrics(t, metrics, expected) { - Object.keys(expected).forEach(function (name) { - t.ok(metrics[name], 'should have metric ' + name) - if (metrics[name]) { - t.equal( - metrics[name].callCount, - expected[name], - 'should have ' + expected[name] + ' calls for ' + name - ) - } - }) -} diff --git a/test/versioned/redis/redis-v4.test.js b/test/versioned/redis/redis-v4.test.js new file mode 100644 index 0000000000..830acef392 --- /dev/null +++ b/test/versioned/redis/redis-v4.test.js @@ -0,0 +1,289 @@ +/* + * Copyright 2020 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict' + +const test = require('node:test') +const assert = require('node:assert') +const helper = require('../../lib/agent_helper') +const { removeMatchedModules } = require('../../lib/cache-buster') +const params = require('../../lib/params') +const urltils = require('../../../lib/util/urltils') +const { checkMetrics } = require('./utils') + +// Indicates unique database in Redis. 0-15 supported. +const DB_INDEX = 2 + +test('Redis instrumentation', async function (t) { + t.beforeEach(async function (ctx) { + const agent = helper.instrumentMockedAgent() + + const redis = require('redis') + const client = redis.createClient({ + socket: { port: params.redis_port, host: params.redis_host } + }) + + await client.connect() + await client.flushAll() + + await client.select(DB_INDEX) + // eslint-disable-next-line new-cap + + const METRIC_HOST_NAME = urltils.isLocalhost(params.redis_host) + ? agent.config.getHostnameSafe() + : params.redis_host + const HOST_ID = METRIC_HOST_NAME + '/' + params.redis_port + + // need to capture attributes + agent.config.attributes.enabled = true + ctx.nr = { + agent, + client, + HOST_ID, + METRIC_HOST_NAME + } + }) + + t.afterEach(async function (ctx) { + const { agent, client } = ctx.nr + helper.unloadAgent(agent) + await client.flushAll() + await client.disconnect() + // must purge require cache of redis related instrumentation + // otherwise it will not re-register on subsequent test runs + removeMatchedModules(/redis/) + }) + + await t.test('should find Redis calls in the transaction trace', function (t, end) { + const { agent, client } = t.nr + assert.ok(!agent.getTransaction(), 'no transaction should be in play') + helper.runInTransaction(agent, async function transactionInScope() { + const transaction = agent.getTransaction() + assert.ok(transaction, 'transaction should be visible') + + const ok = await client.set('testkey', 'arglbargle') + assert.ok(agent.getTransaction(), 'transaction should still be visible') + assert.ok(ok, 'everything should be peachy after setting') + + const value = await client.get('testkey') + assert.ok(agent.getTransaction(), 'transaction should still still be visible') + assert.equal(value, 'arglbargle', 'redis client should still work') + + const trace = transaction.trace + assert.ok(trace, 'trace should exist') + assert.ok(trace.root, 'root element should exist') + assert.equal(trace.root.children.length, 2, 'there should be only two children of the root') + + const setSegment = trace.root.children[0] + const setAttributes = setSegment.getAttributes() + assert.ok(setSegment, 'trace segment for set should exist') + assert.equal(setSegment.name, 'Datastore/operation/Redis/set', 'should register the set') + assert.equal(setAttributes.key, '"testkey"', 'should have the set key as a attribute') + assert.equal(setSegment.children.length, 0, 'set should have no children') + + const getSegment = trace.root.children[1] + const getAttributes = getSegment.getAttributes() + assert.ok(getSegment, 'trace segment for get should exist') + + assert.equal(getSegment.name, 'Datastore/operation/Redis/get', 'should register the get') + + assert.equal(getAttributes.key, '"testkey"', 'should have the get key as a attribute') + + assert.ok(getSegment.timer.hrDuration, 'trace segment should have ended') + end() + }) + }) + + await t.test('should create correct metrics', function (t, end) { + const { agent, client } = t.nr + assert.ok(!agent.getTransaction(), 'no transaction should be in play') + helper.runInTransaction(agent, async function transactionInScope() { + const transaction = agent.getTransaction() + await client.set('testkey', 'arglbargle') + await client.get('testkey') + transaction.end() + const metrics = transaction.metrics.unscoped + const expected = { + 'Datastore/all': 2, + 'Datastore/allWeb': 2, + 'Datastore/Redis/all': 2, + 'Datastore/Redis/allWeb': 2, + 'Datastore/operation/Redis/set': 1, + 'Datastore/operation/Redis/get': 1 + } + checkMetrics({ metrics, expected }) + end() + }) + }) + + await t.test('should handle multi commands', function (t, end) { + const { agent, client, HOST_ID } = t.nr + helper.runInTransaction(agent, async function transactionInScope() { + const transaction = agent.getTransaction() + const results = await client.multi().set('multi-key', 'multi-value').get('multi-key').exec() + + assert.deepEqual(results, ['OK', 'multi-value'], 'should return expected results') + transaction.end() + const metrics = transaction.metrics.unscoped + const expected = { + 'Datastore/all': 4, + 'Datastore/allWeb': 4, + 'Datastore/Redis/all': 4, + 'Datastore/Redis/allWeb': 4, + 'Datastore/operation/Redis/multi': 1, + 'Datastore/operation/Redis/set': 1, + 'Datastore/operation/Redis/get': 1, + 'Datastore/operation/Redis/exec': 1 + } + expected['Datastore/instance/Redis/' + HOST_ID] = 4 + checkMetrics({ metrics, expected }) + end() + }) + }) + + await t.test('should add `key` attribute to trace segment', function (t, end) { + const { agent, client } = t.nr + assert.ok(!agent.getTransaction(), 'no transaction should be in play') + agent.config.attributes.enabled = true + + helper.runInTransaction(agent, async function () { + await client.set('saveme', 'foobar') + + const segment = agent.tracer.getSegment().children[0] + assert.equal(segment.getAttributes().key, '"saveme"', 'should have `key` attribute') + end() + }) + }) + + await t.test('should not add `key` attribute to trace segment', function (t, end) { + const { agent, client } = t.nr + assert.ok(!agent.getTransaction(), 'no transaction should be in play') + agent.config.attributes.enabled = false + + helper.runInTransaction(agent, async function () { + await client.set('saveme', 'foobar') + + const segment = agent.tracer.getSegment().children[0] + assert.ok(!segment.getAttributes().key, 'should not have `key` attribute') + end() + }) + }) + + await t.test('should add datastore instance attributes to trace segments', function (t, end) { + const { agent, client, METRIC_HOST_NAME } = t.nr + assert.ok(!agent.getTransaction(), 'no transaction should be in play') + // Enable. + agent.config.datastore_tracer.instance_reporting.enabled = true + agent.config.datastore_tracer.database_name_reporting.enabled = true + + helper.runInTransaction(agent, async function transactionInScope() { + const transaction = agent.getTransaction() + await client.set('testkey', 'arglbargle') + + const trace = transaction.trace + const setSegment = trace.root.children[0] + const attributes = setSegment.getAttributes() + assert.equal(attributes.host, METRIC_HOST_NAME, 'should have host as attribute') + assert.equal( + attributes.port_path_or_id, + String(params.redis_port), + 'should have port as attribute' + ) + assert.equal( + attributes.database_name, + String(DB_INDEX), + 'should have database id as attribute' + ) + assert.equal(attributes.product, 'Redis', 'should have product attribute') + end() + }) + }) + + await t.test('should not add instance attributes/metrics when disabled', function (t, end) { + const { agent, client, HOST_ID } = t.nr + assert.ok(!agent.getTransaction(), 'no transaction should be in play') + // disable + agent.config.datastore_tracer.instance_reporting.enabled = false + agent.config.datastore_tracer.database_name_reporting.enabled = false + + helper.runInTransaction(agent, async function transactionInScope() { + const transaction = agent.getTransaction() + await client.set('testkey', 'arglbargle') + + const setSegment = transaction.trace.root.children[0] + const attributes = setSegment.getAttributes() + assert.equal(attributes.host, undefined, 'should not have host attribute') + assert.equal(attributes.port_path_or_id, undefined, 'should not have port attribute') + assert.equal(attributes.database_name, undefined, 'should not have db name attribute') + + transaction.end() + const unscoped = transaction.metrics.unscoped + assert.equal( + unscoped['Datastore/instance/Redis/' + HOST_ID], + undefined, + 'should not have instance metric' + ) + end() + }) + }) + + await t.test('should follow selected database', function (t, end) { + const { agent, client } = t.nr + assert.ok(!agent.getTransaction(), 'no transaction should be in play') + let transaction = null + const SELECTED_DB = 3 + helper.runInTransaction(agent, async function (tx) { + transaction = tx + await client.set('select:test:key', 'foo') + assert.ok(agent.getTransaction(), 'should not lose transaction state') + + await client.select(SELECTED_DB) + assert.ok(agent.getTransaction(), 'should not lose transaction state') + + await client.set('select:test:key:2', 'bar') + assert.ok(agent.getTransaction(), 'should not lose transaction state') + transaction.end() + verify() + end() + }) + + function verify() { + const setSegment1 = transaction.trace.root.children[0] + const selectSegment = transaction.trace.root.children[1] + const setSegment2 = transaction.trace.root.children[2] + + assert.equal( + setSegment1.name, + 'Datastore/operation/Redis/set', + 'should register the first set' + ) + assert.equal( + setSegment1.getAttributes().database_name, + String(DB_INDEX), + 'should have the starting database id as attribute for the first set' + ) + assert.equal( + selectSegment.name, + 'Datastore/operation/Redis/select', + 'should register the select' + ) + assert.equal( + selectSegment.getAttributes().database_name, + String(DB_INDEX), + 'should have the starting database id as attribute for the select' + ) + assert.equal( + setSegment2.name, + 'Datastore/operation/Redis/set', + 'should register the second set' + ) + assert.equal( + setSegment2.getAttributes().database_name, + String(SELECTED_DB), + 'should have the selected database id as attribute for the second set' + ) + } + }) +}) diff --git a/test/versioned/redis/redis.tap.js b/test/versioned/redis/redis.test.js similarity index 50% rename from test/versioned/redis/redis.tap.js rename to test/versioned/redis/redis.test.js index 99d3641e2a..002a596623 100644 --- a/test/versioned/redis/redis.tap.js +++ b/test/versioned/redis/redis.test.js @@ -5,28 +5,22 @@ 'use strict' -const tap = require('tap') -const test = tap.test +const test = require('node:test') +const assert = require('node:assert') const helper = require('../../lib/agent_helper') const params = require('../../lib/params') const urltils = require('../../../lib/util/urltils') +const { tspl } = require('@matteo.collina/tspl') +const { checkMetrics } = require('./utils') // Indicates unique database in Redis. 0-15 supported. const DB_INDEX = 2 -test('Redis instrumentation', { timeout: 20000 }, function (t) { - t.autoend() - - let METRIC_HOST_NAME = null - let HOST_ID = null - - let agent - let client - - t.beforeEach(async function () { - agent = helper.instrumentMockedAgent() +test('Redis instrumentation', { timeout: 20000 }, async function (t) { + t.beforeEach(async function (ctx) { + const agent = helper.instrumentMockedAgent() const redis = require('redis') - client = redis.createClient(params.redis_port, params.redis_host) + const client = redis.createClient(params.redis_port, params.redis_host) await helper.flushRedisDb(client, DB_INDEX) await new Promise((resolve, reject) => { client.select(DB_INDEX, function (err) { @@ -37,75 +31,79 @@ test('Redis instrumentation', { timeout: 20000 }, function (t) { }) }) - METRIC_HOST_NAME = urltils.isLocalhost(params.redis_host) + const METRIC_HOST_NAME = urltils.isLocalhost(params.redis_host) ? agent.config.getHostnameSafe() : params.redis_host - HOST_ID = METRIC_HOST_NAME + '/' + params.redis_port + const HOST_ID = METRIC_HOST_NAME + '/' + params.redis_port // need to capture attributes agent.config.attributes.enabled = true // Start testing! - t.notOk(agent.getTransaction(), 'no transaction should be in play') + assert.ok(!agent.getTransaction(), 'no transaction should be in play') + ctx.nr = { + agent, + client, + HOST_ID, + METRIC_HOST_NAME + } }) - t.afterEach(function () { - client && client.end({ flush: false }) - agent && helper.unloadAgent(agent) + t.afterEach(function (ctx) { + const { agent, client } = ctx.nr + client.end({ flush: false }) + helper.unloadAgent(agent) }) - t.test('should find Redis calls in the transaction trace', function (t) { - t.plan(17) + await t.test('should find Redis calls in the transaction trace', async function (t) { + const { agent, client } = t.nr + const plan = tspl(t, { plan: 19 }) helper.runInTransaction(agent, function transactionInScope() { const transaction = agent.getTransaction() - t.ok(transaction, 'transaction should be visible') + plan.ok(transaction, 'transaction should be visible') client.set('testkey', 'arglbargle', function (error, ok) { - if (error) { - return t.fail(error) - } - - t.ok(agent.getTransaction(), 'transaction should still be visible') - t.ok(ok, 'everything should be peachy after setting') + plan.ok(!error) + plan.ok(agent.getTransaction(), 'transaction should still be visible') + plan.ok(ok, 'everything should be peachy after setting') client.get('testkey', function (error, value) { - if (error) { - return t.fail(error) - } - - t.ok(agent.getTransaction(), 'transaction should still still be visible') - t.equal(value, 'arglbargle', 'redis client should still work') + plan.ok(!error) + plan.ok(agent.getTransaction(), 'transaction should still still be visible') + plan.equal(value, 'arglbargle', 'redis client should still work') const trace = transaction.trace - t.ok(trace, 'trace should exist') - t.ok(trace.root, 'root element should exist') - t.equal(trace.root.children.length, 1, 'there should be only one child of the root') + plan.ok(trace, 'trace should exist') + plan.ok(trace.root, 'root element should exist') + plan.equal(trace.root.children.length, 1, 'there should be only one child of the root') const setSegment = trace.root.children[0] const setAttributes = setSegment.getAttributes() - t.ok(setSegment, 'trace segment for set should exist') - t.equal(setSegment.name, 'Datastore/operation/Redis/set', 'should register the set') - t.equal(setAttributes.key, '"testkey"', 'should have the set key as a attribute') - t.equal(setSegment.children.length, 1, 'set should have an only child') + plan.ok(setSegment, 'trace segment for set should exist') + plan.equal(setSegment.name, 'Datastore/operation/Redis/set', 'should register the set') + plan.equal(setAttributes.key, '"testkey"', 'should have the set key as a attribute') + plan.equal(setSegment.children.length, 1, 'set should have an only child') const getSegment = setSegment.children[0].children[0] const getAttributes = getSegment.getAttributes() - t.ok(getSegment, 'trace segment for get should exist') + plan.ok(getSegment, 'trace segment for get should exist') - t.equal(getSegment.name, 'Datastore/operation/Redis/get', 'should register the get') + plan.equal(getSegment.name, 'Datastore/operation/Redis/get', 'should register the get') - t.equal(getAttributes.key, '"testkey"', 'should have the get key as a attribute') + plan.equal(getAttributes.key, '"testkey"', 'should have the get key as a attribute') - t.ok(getSegment.children.length >= 1, 'get should have a callback segment') + plan.ok(getSegment.children.length >= 1, 'get should have a callback segment') - t.ok(getSegment.timer.hrDuration, 'trace segment should have ended') + plan.ok(getSegment.timer.hrDuration, 'trace segment should have ended') }) }) }) + await plan.completed }) - t.test('when called without a callback', function (t) { - t.plan(4) + await t.test('when called without a callback', async function (t) { + const { agent, client } = t.nr + const plan = tspl(t, { plan: 4 }) let transaction = null @@ -121,55 +119,51 @@ test('Redis instrumentation', { timeout: 20000 }, function (t) { // the command callback is executed, if exists. Since we don't have a callback, // we wait for the command to be removed from the queue. if (client.commandQueueLength > 0) { - t.comment('set command still in command queue. scheduling retry in 100ms') - setTimeout(triggerError, 100) return } - t.comment('executing hset which should error') // This will generate an error because `testKey` is not a hash. client.hset('testKey', 'hashKey', 'foobar') } }) client.on('error', function (err) { - if (t.ok(err, 'should emit errors on the client')) { - t.equal( - err.message, - 'WRONGTYPE Operation against a key holding the wrong kind of value', - 'errors should have the expected error message' - ) + plan.ok(err, 'should emit errors on the client') + plan.equal( + err.message, + 'WRONGTYPE Operation against a key holding the wrong kind of value', + 'errors should have the expected error message' + ) - // Ensure error triggering operation has completed before - // continuing test assertions. - transaction.end() - } + // Ensure error triggering operation has completed before + // continuing test assertions. + transaction.end() }) agent.on('transactionFinished', function (tx) { const redSeg = tx.trace.root.children[0] - t.equal(redSeg.name, 'Datastore/operation/Redis/set', 'should have untruncated redis segment') - t.equal(redSeg.children.length, 0, 'should have no children for redis segment') + plan.equal( + redSeg.name, + 'Datastore/operation/Redis/set', + 'should have untruncated redis segment' + ) + plan.equal(redSeg.children.length, 0, 'should have no children for redis segment') }) + await plan.completed }) - t.test('should create correct metrics', function (t) { - t.plan(14) + await t.test('should create correct metrics', async function (t) { + const { agent, client, HOST_ID } = t.nr + const plan = tspl(t, { plan: 16 }) helper.runInTransaction(agent, function transactionInScope() { const transaction = agent.getTransaction() client.set('testkey', 'arglbargle', function (error) { - if (error) { - return t.fail(error) - } - + plan.ok(!error) client.get('testkey', function (error) { - if (error) { - return t.fail(error) - } - + plan.ok(!error) transaction.end() - const unscoped = transaction.metrics.unscoped + const metrics = transaction.metrics.unscoped const expected = { 'Datastore/all': 2, 'Datastore/allWeb': 2, @@ -179,13 +173,15 @@ test('Redis instrumentation', { timeout: 20000 }, function (t) { 'Datastore/operation/Redis/get': 1 } expected['Datastore/instance/Redis/' + HOST_ID] = 2 - checkMetrics(t, unscoped, expected) + checkMetrics({ check: plan, metrics, expected }) }) }) }) + await plan.completed }) - t.test('should handle multi commands', function (t) { + await t.test('should handle multi commands', function (t, end) { + const { agent, client, HOST_ID } = t.nr helper.runInTransaction(agent, function transactionInScope() { const transaction = agent.getTransaction() client @@ -193,11 +189,10 @@ test('Redis instrumentation', { timeout: 20000 }, function (t) { .set('multi-key', 'multi-value') .get('multi-key') .exec(function (error, data) { - t.same(data, ['OK', 'multi-value'], 'should return expected results') - t.error(error) - + assert.deepEqual(data, ['OK', 'multi-value'], 'should return expected results') + assert.ok(!error) transaction.end() - const unscoped = transaction.metrics.unscoped + const metrics = transaction.metrics.unscoped const expected = { 'Datastore/all': 4, 'Datastore/allWeb': 4, @@ -209,45 +204,45 @@ test('Redis instrumentation', { timeout: 20000 }, function (t) { 'Datastore/operation/Redis/exec': 1 } expected['Datastore/instance/Redis/' + HOST_ID] = 4 - checkMetrics(t, unscoped, expected) - t.end() + checkMetrics({ metrics, expected }) + end() }) }) }) - t.test('should add `key` attribute to trace segment', function (t) { + await t.test('should add `key` attribute to trace segment', function (t, end) { + const { agent, client } = t.nr agent.config.attributes.enabled = true helper.runInTransaction(agent, function () { client.set('saveme', 'foobar', function (error) { // Regardless of error, key should still be captured. - t.error(error) - + assert.ok(!error) const segment = agent.tracer.getSegment().parent - t.equal(segment.getAttributes().key, '"saveme"', 'should have `key` attribute') - t.end() + assert.equal(segment.getAttributes().key, '"saveme"', 'should have `key` attribute') + end() }) }) }) - t.test('should not add `key` attribute to trace segment', function (t) { + await t.test('should not add `key` attribute to trace segment', function (t, end) { + const { agent, client } = t.nr agent.config.attributes.enabled = false helper.runInTransaction(agent, function () { client.set('saveme', 'foobar', function (error) { // Regardless of error, key should still be captured. - t.error(error) - + assert.ok(!error) const segment = agent.tracer.getSegment().parent - t.notOk(segment.getAttributes().key, 'should not have `key` attribute') - t.end() + assert.ok(!segment.getAttributes().key, 'should not have `key` attribute') + end() }) }) }) - t.test('should add datastore instance attributes to trace segments', function (t) { - t.plan(4) - + await t.test('should add datastore instance attributes to trace segments', async function (t) { + const { agent, client, METRIC_HOST_NAME } = t.nr + const plan = tspl(t, { plan: 5 }) // Enable. agent.config.datastore_tracer.instance_reporting.enabled = true agent.config.datastore_tracer.database_name_reporting.enabled = true @@ -255,28 +250,30 @@ test('Redis instrumentation', { timeout: 20000 }, function (t) { helper.runInTransaction(agent, function transactionInScope() { const transaction = agent.getTransaction() client.set('testkey', 'arglbargle', function (error) { - if (error) { - return t.fail(error) - } - + plan.ok(!error) const trace = transaction.trace const setSegment = trace.root.children[0] const attributes = setSegment.getAttributes() - t.equal(attributes.host, METRIC_HOST_NAME, 'should have host as attribute') - t.equal( + plan.equal(attributes.host, METRIC_HOST_NAME, 'should have host as attribute') + plan.equal( attributes.port_path_or_id, String(params.redis_port), 'should have port as attribute' ) - t.equal(attributes.database_name, String(DB_INDEX), 'should have database id as attribute') - t.equal(attributes.product, 'Redis', 'should have product attribute') + plan.equal( + attributes.database_name, + String(DB_INDEX), + 'should have database id as attribute' + ) + plan.equal(attributes.product, 'Redis', 'should have product attribute') }) }) + await plan.completed }) - t.test('should not add instance attributes/metrics when disabled', function (t) { - t.plan(5) - + await t.test('should not add instance attributes/metrics when disabled', async function (t) { + const { agent, client, HOST_ID } = t.nr + const plan = tspl(t, { plan: 5 }) // disable agent.config.datastore_tracer.instance_reporting.enabled = false agent.config.datastore_tracer.database_name_reporting.enabled = false @@ -284,70 +281,78 @@ test('Redis instrumentation', { timeout: 20000 }, function (t) { helper.runInTransaction(agent, function transactionInScope() { const transaction = agent.getTransaction() client.set('testkey', 'arglbargle', function (error) { - if (!t.error(error)) { - return t.end() - } - + plan.ok(!error) const setSegment = transaction.trace.root.children[0] const attributes = setSegment.getAttributes() - t.equal(attributes.host, undefined, 'should not have host attribute') - t.equal(attributes.port_path_or_id, undefined, 'should not have port attribute') - t.equal(attributes.database_name, undefined, 'should not have db name attribute') + plan.equal(attributes.host, undefined, 'should not have host attribute') + plan.equal(attributes.port_path_or_id, undefined, 'should not have port attribute') + plan.equal(attributes.database_name, undefined, 'should not have db name attribute') transaction.end() const unscoped = transaction.metrics.unscoped - t.equal( + plan.equal( unscoped['Datastore/instance/Redis/' + HOST_ID], undefined, 'should not have instance metric' ) }) }) + await plan.completed }) - t.test('should follow selected database', function (t) { - t.plan(12) + await t.test('should follow selected database', async function (t) { + const { agent, client } = t.nr + const plan = tspl(t, { plan: 12 }) let transaction = null const SELECTED_DB = 3 helper.runInTransaction(agent, function (tx) { transaction = tx client.set('select:test:key', 'foo', function (err) { - t.notOk(err, 'should not fail to set') - t.ok(agent.getTransaction(), 'should not lose transaction state') + plan.ok(!err, 'should not fail to set') + plan.ok(agent.getTransaction(), 'should not lose transaction state') client.select(SELECTED_DB, function (err) { - t.notOk(err, 'should not fail to select') - t.ok(agent.getTransaction(), 'should not lose transaction state') + plan.ok(!err, 'should not fail to select') + plan.ok(agent.getTransaction(), 'should not lose transaction state') client.set('select:test:key:2', 'bar', function (err) { - t.notOk(err, 'should not fail to set in db 2') - t.ok(agent.getTransaction(), 'should not lose transaction state') + plan.ok(!err, 'should not fail to set in db 2') + plan.ok(agent.getTransaction(), 'should not lose transaction state') transaction.end() verify() }) }) }) }) + await plan.completed function verify() { const setSegment1 = transaction.trace.root.children[0] const selectSegment = setSegment1.children[0].children[0] const setSegment2 = selectSegment.children[0].children[0] - t.equal(setSegment1.name, 'Datastore/operation/Redis/set', 'should register the first set') - t.equal( + plan.equal(setSegment1.name, 'Datastore/operation/Redis/set', 'should register the first set') + plan.equal( setSegment1.getAttributes().database_name, String(DB_INDEX), 'should have the starting database id as attribute for the first set' ) - t.equal(selectSegment.name, 'Datastore/operation/Redis/select', 'should register the select') - t.equal( + plan.equal( + selectSegment.name, + 'Datastore/operation/Redis/select', + 'should register the select' + ) + plan.equal( selectSegment.getAttributes().database_name, String(DB_INDEX), 'should have the starting database id as attribute for the select' ) - t.equal(setSegment2.name, 'Datastore/operation/Redis/set', 'should register the second set') - t.equal( + plan.equal( + setSegment2.name, + 'Datastore/operation/Redis/set', + 'should register the second set' + ) + plan.equal( setSegment2.getAttributes().database_name, String(SELECTED_DB), 'should have the selected database id as attribute for the second set' @@ -355,16 +360,3 @@ test('Redis instrumentation', { timeout: 20000 }, function (t) { } }) }) - -function checkMetrics(t, metrics, expected) { - Object.keys(expected).forEach(function (name) { - t.ok(metrics[name], 'should have metric ' + name) - if (metrics[name]) { - t.equal( - metrics[name].callCount, - expected[name], - 'should have ' + expected[name] + ' calls for ' + name - ) - } - }) -} diff --git a/test/versioned/redis/tls.tap.js b/test/versioned/redis/tls.test.js similarity index 82% rename from test/versioned/redis/tls.tap.js rename to test/versioned/redis/tls.test.js index 62f68c160c..b3798f0d56 100644 --- a/test/versioned/redis/tls.tap.js +++ b/test/versioned/redis/tls.test.js @@ -5,18 +5,19 @@ 'use strict' -const tap = require('tap') +const test = require('node:test') +const assert = require('node:assert') const helper = require('../../lib/agent_helper') const promiseResolvers = require('../../lib/promise-resolvers') const { redis_tls_host: HOST, redis_tls_port: PORT } = require('../../lib/params') const { removeModules } = require('../../lib/cache-buster') -tap.test('redis over tls connection', (t) => { +test('redis over tls connection', async (t) => { t.afterEach(() => { removeModules(['redis']) }) - t.test('should work with self-signed tls cert on server', async (t) => { + await t.test('should work with self-signed tls cert on server', async (t) => { const { promise, resolve } = promiseResolvers() const agent = helper.instrumentMockedAgent() const redis = require('redis') @@ -30,7 +31,7 @@ tap.test('redis over tls connection', (t) => { await client.connect() await client.flushAll() - t.teardown(async () => { + t.after(async () => { await client.flushAll() await client.disconnect() helper.unloadAgent(agent) @@ -40,7 +41,7 @@ tap.test('redis over tls connection', (t) => { const tx = agent.getTransaction() await client.set('tls-test', 'foo') const found = await client.get('tls-test') - t.equal(found, 'foo') + assert.equal(found, 'foo') tx.end() resolve() }) @@ -48,7 +49,7 @@ tap.test('redis over tls connection', (t) => { await promise }) - t.test('url parsing should add tls true', async (t) => { + await t.test('url parsing should add tls true', async (t) => { const { promise, resolve } = promiseResolvers() const agent = helper.instrumentMockedAgent() const redis = require('redis') @@ -61,7 +62,7 @@ tap.test('redis over tls connection', (t) => { await client.connect() await client.flushAll() - t.teardown(async () => { + t.after(async () => { await client.flushAll() await client.disconnect() helper.unloadAgent(agent) @@ -71,13 +72,11 @@ tap.test('redis over tls connection', (t) => { const tx = agent.getTransaction() await client.set('tls-test', 'foo') const found = await client.get('tls-test') - t.equal(found, 'foo') + assert.equal(found, 'foo') tx.end() resolve() }) await promise }) - - t.end() }) diff --git a/test/versioned/redis/utils.js b/test/versioned/redis/utils.js new file mode 100644 index 0000000000..e23d5390d3 --- /dev/null +++ b/test/versioned/redis/utils.js @@ -0,0 +1,24 @@ +/* + * Copyright 2024 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict' +const assert = require('node:assert') + +function checkMetrics({ check = assert, metrics, expected }) { + Object.keys(expected).forEach(function (name) { + check.ok(metrics[name], 'should have metric ' + name) + if (metrics[name]) { + check.equal( + metrics[name].callCount, + expected[name], + 'should have ' + expected[name] + ' calls for ' + name + ) + } + }) +} + +module.exports = { + checkMetrics +}