From 449ae03883d2a5e92d11590372993eedc976ab99 Mon Sep 17 00:00:00 2001 From: Bob Evans Date: Fri, 15 Nov 2024 14:33:51 -0500 Subject: [PATCH] test: Migrated `ioredis`, `prisma` and `undici` versioned tests to `node:test` (#2746) --- test/versioned/ioredis/ioredis.tap.js | 116 ---------- test/versioned/ioredis/ioredis.test.js | 113 +++++++++ test/versioned/ioredis/package.json | 2 +- test/versioned/prisma/package.json | 2 +- test/versioned/prisma/prisma.tap.js | 138 ----------- test/versioned/prisma/prisma.test.js | 141 ++++++++++++ test/versioned/prisma/utils.js | 52 +++-- test/versioned/undici/package.json | 2 +- .../{requests.tap.js => requests.test.js} | 215 ++++++++---------- 9 files changed, 380 insertions(+), 401 deletions(-) delete mode 100644 test/versioned/ioredis/ioredis.tap.js create mode 100644 test/versioned/ioredis/ioredis.test.js delete mode 100644 test/versioned/prisma/prisma.tap.js create mode 100644 test/versioned/prisma/prisma.test.js rename test/versioned/undici/{requests.tap.js => requests.test.js} (56%) diff --git a/test/versioned/ioredis/ioredis.tap.js b/test/versioned/ioredis/ioredis.tap.js deleted file mode 100644 index f854d219cd..0000000000 --- a/test/versioned/ioredis/ioredis.tap.js +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' - -const tap = require('tap') -const helper = require('../../lib/agent_helper') -require('../../lib/metrics_helper') -const params = require('../../lib/params') -const urltils = require('../../../lib/util/urltils') - -// Indicates unique database in Redis. 0-15 supported. -const DB_INDEX = 3 - -tap.test('ioredis instrumentation', (t) => { - let agent = null - let redisClient = null - let METRIC_HOST_NAME - let HOST_ID - - t.autoend() - t.beforeEach(async () => { - agent = helper.instrumentMockedAgent() - const Redis = require('ioredis') - redisClient = new Redis(params.redis_port, params.redis_host) - await helper.flushRedisDb(redisClient, DB_INDEX) - METRIC_HOST_NAME = urltils.isLocalhost(params.redis_host) - ? agent.config.getHostnameSafe() - : params.redis_host - HOST_ID = METRIC_HOST_NAME + '/' + params.redis_port - - await new Promise(async (resolve, reject) => { - redisClient.select(DB_INDEX, (err) => { - if (err) { - return reject(err) - } - - resolve() - }) - }) - }) - - t.afterEach(() => { - agent && helper.unloadAgent(agent) - redisClient && redisClient.disconnect() - }) - - t.test('creates expected metrics', { timeout: 5000 }, (t) => { - t.plan(6) - agent.on('transactionFinished', function (tx) { - const expected = [ - [{ name: 'Datastore/all' }], - [{ name: 'Datastore/Redis/all' }], - [{ name: 'Datastore/operation/Redis/set' }] - ] - expected['Datastore/instance/Redis/' + HOST_ID] = 2 - - t.assertMetrics(tx.metrics, expected, false, false) - t.end() - }) - - helper.runInTransaction(agent, (transaction) => { - redisClient - .set('testkey', 'testvalue') - .then(function () { - transaction.end() - }, t.error) - .catch(t.error) - }) - }) - - t.test('creates expected segments', { timeout: 5000 }, (t) => { - t.plan(5) - - agent.on('transactionFinished', function (tx) { - const root = tx.trace.root - t.equal(root.children.length, 2, 'root has two children') - - const setSegment = root.children[0] - t.equal(setSegment.name, 'Datastore/operation/Redis/set') - - // ioredis operations return promise, any 'then' callbacks will be sibling segments - // of the original redis call - const getSegment = root.children[1] - t.equal(getSegment.name, 'Datastore/operation/Redis/get') - t.equal(getSegment.children.length, 0, 'should not contain any segments') - - t.end() - }) - - helper.runInTransaction(agent, (transaction) => { - redisClient - .set('testkey', 'testvalue') - .then(() => redisClient.get('testkey')) - .then((value) => { - t.equal(value, 'testvalue', 'should have expected value') - transaction.end() - }) - .catch(t.error) - }) - }) - - // NODE-1524 regression - t.test('does not crash when ending out of transaction', (t) => { - helper.runInTransaction(agent, (transaction) => { - t.ok(agent.getTransaction(), 'transaction should be in progress') - redisClient.set('testkey', 'testvalue').then(function () { - t.notOk(agent.getTransaction(), 'transaction should have ended') - t.end() - }) - transaction.end() - }) - }) -}) diff --git a/test/versioned/ioredis/ioredis.test.js b/test/versioned/ioredis/ioredis.test.js new file mode 100644 index 0000000000..8f4506093b --- /dev/null +++ b/test/versioned/ioredis/ioredis.test.js @@ -0,0 +1,113 @@ +/* + * 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 params = require('../../lib/params') +const urltils = require('../../../lib/util/urltils') +const { tspl } = require('@matteo.collina/tspl') +const { assertMetrics } = require('../../lib/custom-assertions') + +// Indicates unique database in Redis. 0-15 supported. +const DB_INDEX = 3 + +test('ioredis instrumentation', async (t) => { + t.beforeEach(async (ctx) => { + const agent = helper.instrumentMockedAgent() + const Redis = require('ioredis') + const redisClient = new Redis(params.redis_port, params.redis_host) + await helper.flushRedisDb(redisClient, DB_INDEX) + const METRIC_HOST_NAME = urltils.isLocalhost(params.redis_host) + ? agent.config.getHostnameSafe() + : params.redis_host + const HOST_ID = METRIC_HOST_NAME + '/' + params.redis_port + + await new Promise(async (resolve, reject) => { + redisClient.select(DB_INDEX, (err) => { + if (err) { + return reject(err) + } + + resolve() + }) + }) + ctx.nr = { + agent, + redisClient, + HOST_ID + } + }) + + t.afterEach((ctx) => { + const { agent, redisClient } = ctx.nr + helper.unloadAgent(agent) + redisClient.disconnect() + }) + + await t.test('creates expected metrics', async (t) => { + const { agent, redisClient, HOST_ID } = t.nr + const plan = tspl(t, { plan: 6 }) + agent.on('transactionFinished', function (tx) { + const expected = [ + [{ name: 'Datastore/all' }], + [{ name: 'Datastore/Redis/all' }], + [{ name: 'Datastore/operation/Redis/set' }] + ] + expected['Datastore/instance/Redis/' + HOST_ID] = 2 + + assertMetrics(tx.metrics, expected, false, false, { assert: plan }) + }) + + helper.runInTransaction(agent, async (transaction) => { + await redisClient.set('testkey', 'testvalue') + transaction.end() + }) + + await plan.completed + }) + + await t.test('creates expected segments', async (t) => { + const { agent, redisClient } = t.nr + const plan = tspl(t, { plan: 5 }) + + agent.on('transactionFinished', function (tx) { + const root = tx.trace.root + plan.equal(root.children.length, 2, 'root has two children') + + const setSegment = root.children[0] + plan.equal(setSegment.name, 'Datastore/operation/Redis/set') + + // ioredis operations return promise, any 'then' callbacks will be sibling segments + // of the original redis call + const getSegment = root.children[1] + plan.equal(getSegment.name, 'Datastore/operation/Redis/get') + plan.equal(getSegment.children.length, 0, 'should not contain any segments') + }) + + helper.runInTransaction(agent, async (transaction) => { + await redisClient.set('testkey', 'testvalue') + const value = await redisClient.get('testkey') + plan.equal(value, 'testvalue', 'should have expected value') + transaction.end() + }) + await plan.completed + }) + + // NODE-1524 regression + await t.test('does not crash when ending out of transaction', (t, end) => { + const { agent, redisClient } = t.nr + helper.runInTransaction(agent, (transaction) => { + assert.ok(agent.getTransaction(), 'transaction should be in progress') + redisClient.set('testkey', 'testvalue').then(function () { + assert.ok(!agent.getTransaction(), 'transaction should have ended') + end() + }) + transaction.end() + }) + }) +}) diff --git a/test/versioned/ioredis/package.json b/test/versioned/ioredis/package.json index 7513fa3e48..7da94af355 100644 --- a/test/versioned/ioredis/package.json +++ b/test/versioned/ioredis/package.json @@ -12,7 +12,7 @@ "ioredis": ">=4.0.0" }, "files": [ - "ioredis.tap.js" + "ioredis.test.js" ] } ] diff --git a/test/versioned/prisma/package.json b/test/versioned/prisma/package.json index 78a1442e57..43f28fd1ed 100644 --- a/test/versioned/prisma/package.json +++ b/test/versioned/prisma/package.json @@ -15,7 +15,7 @@ "@prisma/client": ">=5.0.0 <5.9.0 || >=5.9.1" }, "files": [ - "prisma.tap.js" + "prisma.test.js" ] } ], diff --git a/test/versioned/prisma/prisma.tap.js b/test/versioned/prisma/prisma.tap.js deleted file mode 100644 index 8f51c086d6..0000000000 --- a/test/versioned/prisma/prisma.tap.js +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2023 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict' -const tap = require('tap') -const helper = require('../../lib/agent_helper') -const { findSegment } = require('../../lib/metrics_helper') -const { verify, verifySlowQueries, findMany, raw, rawUpdate } = require('./utils') -const { initPrismaApp, getPostgresUrl } = require('./setup') -const { upsertUsers } = require('./app') - -tap.test('Basic run through prisma functionality', { timeout: 30 * 1000 }, (t) => { - t.autoend() - let agent = null - let PrismaClient = null - let prisma = null - - t.before(async () => { - await initPrismaApp() - }) - - t.beforeEach(async () => { - process.env.DATABASE_URL = getPostgresUrl() - agent = helper.instrumentMockedAgent() - ;({ PrismaClient } = require('@prisma/client')) - prisma = new PrismaClient() - }) - - t.afterEach(async () => { - delete process.env.DATABASE_URL - helper.unloadAgent(agent) - agent = null - PrismaClient = null - prisma = null - }) - - t.test('Metrics and traces are recorded with a transaction', (t) => { - agent.config.datastore_tracer.instance_reporting.enabled = true - agent.config.datastore_tracer.database_name_reporting.enabled = true - - helper.runInTransaction(agent, async (tx) => { - const users = await upsertUsers(prisma) - t.equal(users.length, 2, 'should get two users') - tx.end() - verify(t, agent, tx) - t.end() - }) - }) - - t.test('should not add datastore instance attributes to trace segments if disabled', (t) => { - // Disable. - agent.config.datastore_tracer.instance_reporting.enabled = false - agent.config.datastore_tracer.database_name_reporting.enabled = false - - helper.runInTransaction(agent, async (tx) => { - const users = await upsertUsers(prisma) - t.equal(users.length, 2, 'should get two users') - const findManySegment = findSegment(tx.trace.root, findMany) - const attributes = findManySegment.getAttributes() - t.notOk(attributes.host, 'should not have a host set') - t.notOk(attributes.port_path_or_id, 'should not have a port set') - t.notOk(attributes.database_name, 'should not have a database name set') - t.equal(attributes.product, 'Prisma', 'product attribute should be "Prisma"') - t.end() - }) - }) - - t.test('Raw queries should be recorded', async (t) => { - const queries = [ - prisma.$queryRaw`SELECT * FROM "User"`, - prisma.$queryRawUnsafe('SELECT * FROM "User"') - ] - for (const query of queries) { - await helper.runInTransaction(agent, async (tx) => { - const users = await query - t.equal(users.length, 2, 'should get two users') - tx.end() - const rawSegment = findSegment(tx.trace.root, raw) - t.ok(rawSegment, `segment named ${raw} should exist`) - }) - } - t.end() - }) - - t.test('Raw statements should be recorded', async (t) => { - const queries = [ - prisma.$executeRaw`UPDATE "User" SET "name"='New Relic was here'`, - prisma.$executeRawUnsafe('UPDATE "User" SET "name"=\'New Relic was here\'') - ] - for (const query of queries) { - await helper.runInTransaction(agent, async (tx) => { - const count = await query - t.equal(count, 2, 'should modify two users') - tx.end() - const rawSegment = findSegment(tx.trace.root, rawUpdate) - t.ok(rawSegment, `segment named ${rawUpdate} should exist`) - }) - } - t.end() - }) - - t.test('should add datastore instance params to slow query traces', (t) => { - // enable slow queries - agent.config.transaction_tracer.explain_threshold = 0 - agent.config.transaction_tracer.record_sql = 'raw' - agent.config.slow_sql.enabled = true - helper.runInTransaction(agent, async (tx) => { - await prisma.$executeRaw`select * from pg_sleep(1);` - await upsertUsers(prisma) - tx.end() - verifySlowQueries(t, agent, ['select * from pg_sleep(1);', 'user.findMany', 'user.update']) - t.end() - }) - }) - - t.test('should not add datastore instance params to slow query traces when disabled', (t) => { - // enable slow queries - agent.config.transaction_tracer.explain_threshold = 0 - agent.config.transaction_tracer.record_sql = 'raw' - agent.config.slow_sql.enabled = true - // disable datastore instance - agent.config.datastore_tracer.instance_reporting.enabled = false - agent.config.datastore_tracer.database_name_reporting.enabled = false - - helper.runInTransaction(agent, async (tx) => { - await prisma.$executeRaw`select * from User;` - tx.end() - const queryParams = agent.queries.samples.values().next().value - t.notOk(queryParams.host, 'should not have a host set') - t.notOk(queryParams.port_path_or_id, 'should not have a port set') - t.notOk(queryParams.database_name, 'should not have a database name set') - - t.end() - }) - }) -}) diff --git a/test/versioned/prisma/prisma.test.js b/test/versioned/prisma/prisma.test.js new file mode 100644 index 0000000000..ce9f557799 --- /dev/null +++ b/test/versioned/prisma/prisma.test.js @@ -0,0 +1,141 @@ +/* + * Copyright 2023 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 { findSegment } = require('../../lib/metrics_helper') +const { verify, verifySlowQueries, findMany, raw, rawUpdate } = require('./utils') +const { initPrismaApp, getPostgresUrl } = require('./setup') +const { upsertUsers } = require('./app') + +test('Basic run through prisma functionality', { timeout: 30 * 1000 }, async (t) => { + t.before(async () => { + await initPrismaApp() + }) + + t.beforeEach(async (ctx) => { + process.env.DATABASE_URL = getPostgresUrl() + const agent = helper.instrumentMockedAgent() + const { PrismaClient } = require('@prisma/client') + const prisma = new PrismaClient() + ctx.nr = { + agent, + prisma + } + }) + + t.afterEach(async (ctx) => { + const { agent } = ctx.nr + delete process.env.DATABASE_URL + helper.unloadAgent(agent) + }) + + await t.test('Metrics and traces are recorded with a transaction', async (t) => { + const { agent, prisma } = t.nr + agent.config.datastore_tracer.instance_reporting.enabled = true + agent.config.datastore_tracer.database_name_reporting.enabled = true + + await helper.runInTransaction(agent, async (tx) => { + const users = await upsertUsers(prisma) + assert.equal(users.length, 2, 'should get two users') + tx.end() + verify(agent, tx) + }) + }) + + await t.test( + 'should not add datastore instance attributes to trace segments if disabled', + async (t) => { + const { agent, prisma } = t.nr + // Disable. + agent.config.datastore_tracer.instance_reporting.enabled = false + agent.config.datastore_tracer.database_name_reporting.enabled = false + + await helper.runInTransaction(agent, async (tx) => { + const users = await upsertUsers(prisma) + assert.equal(users.length, 2, 'should get two users') + const findManySegment = findSegment(tx.trace.root, findMany) + const attributes = findManySegment.getAttributes() + assert.ok(!attributes.host, 'should not have a host set') + assert.ok(!attributes.port_path_or_id, 'should not have a port set') + assert.ok(!attributes.database_name, 'should not have a database name set') + assert.equal(attributes.product, 'Prisma', 'product attribute should be "Prisma"') + }) + } + ) + + await t.test('Raw queries should be recorded', async (t) => { + const { agent, prisma } = t.nr + const queries = [ + prisma.$queryRaw`SELECT * FROM "User"`, + prisma.$queryRawUnsafe('SELECT * FROM "User"') + ] + for (const query of queries) { + await helper.runInTransaction(agent, async (tx) => { + const users = await query + assert.equal(users.length, 2, 'should get two users') + tx.end() + const rawSegment = findSegment(tx.trace.root, raw) + assert.ok(rawSegment, `segment named ${raw} should exist`) + }) + } + }) + + await t.test('Raw statements should be recorded', async (t) => { + const { agent, prisma } = t.nr + const queries = [ + prisma.$executeRaw`UPDATE "User" SET "name"='New Relic was here'`, + prisma.$executeRawUnsafe('UPDATE "User" SET "name"=\'New Relic was here\'') + ] + for (const query of queries) { + await helper.runInTransaction(agent, async (tx) => { + const count = await query + assert.equal(count, 2, 'should modify two users') + tx.end() + const rawSegment = findSegment(tx.trace.root, rawUpdate) + assert.ok(rawSegment, `segment named ${rawUpdate} should exist`) + }) + } + }) + + await t.test('should add datastore instance params to slow query traces', async (t) => { + const { agent, prisma } = t.nr + // enable slow queries + agent.config.transaction_tracer.explain_threshold = 0 + agent.config.transaction_tracer.record_sql = 'raw' + agent.config.slow_sql.enabled = true + await helper.runInTransaction(agent, async (tx) => { + await prisma.$executeRaw`select * from pg_sleep(1);` + await upsertUsers(prisma) + tx.end() + verifySlowQueries(agent, ['select * from pg_sleep(1);', 'user.findMany', 'user.update']) + }) + }) + + await t.test( + 'should not add datastore instance params to slow query traces when disabled', + async (t) => { + const { agent, prisma } = t.nr + // enable slow queries + agent.config.transaction_tracer.explain_threshold = 0 + agent.config.transaction_tracer.record_sql = 'raw' + agent.config.slow_sql.enabled = true + // disable datastore instance + agent.config.datastore_tracer.instance_reporting.enabled = false + agent.config.datastore_tracer.database_name_reporting.enabled = false + + await helper.runInTransaction(agent, async (tx) => { + await prisma.$executeRaw`select * from User;` + tx.end() + const queryParams = agent.queries.samples.values().next().value + assert.ok(!queryParams.host, 'should not have a host set') + assert.ok(!queryParams.port_path_or_id, 'should not have a port set') + assert.ok(!queryParams.database_name, 'should not have a database name set') + }) + } + ) +}) diff --git a/test/versioned/prisma/utils.js b/test/versioned/prisma/utils.js index 215f939f4b..25c6b66469 100644 --- a/test/versioned/prisma/utils.js +++ b/test/versioned/prisma/utils.js @@ -5,6 +5,7 @@ 'use strict' const utils = module.exports +const assert = require('node:assert') const { findSegment, getMetricHostName } = require('../../lib/metrics_helper') const { DB, PRISMA } = require('../../../lib/metrics/names') const params = require('../../lib/params') @@ -27,17 +28,17 @@ const raw = `${PRISMA.STATEMENT}User/select` utils.raw = raw const rawUpdate = `${PRISMA.STATEMENT}User/update` utils.rawUpdate = rawUpdate +const { assertSegments } = require('../../lib/custom-assertions') /** * Asserts all the expected datastore metrics for a given query * - * @param {Object} t tap test instance * @param {Object} agent mocked NR agent */ -function verifyMetrics(t, agent) { +function verifyMetrics(agent) { for (const [metricName, expectedCount] of Object.entries(expectedUpsertMetrics)) { const metric = agent.metrics.getMetric(metricName) - t.equal( + assert.equal( metric.callCount, expectedCount, `should have counted ${metricName} ${expectedCount} times` @@ -49,31 +50,30 @@ function verifyMetrics(t, agent) { * Asserts all relevant prisma segments and their associative datastore attributes. * It also asserts that every segment has a hrDuration which means it has ended * - * @param {Object} t tap test instance * @param {Object} agent mocked NR agent * @param {Object} transaction active NR transaction */ -function verifyTraces(t, agent, transaction) { +function verifyTraces(agent, transaction) { const host = getMetricHostName(agent, params.postgres_host) const trace = transaction.trace - t.ok(trace, 'trace should exist') - t.ok(trace.root, 'root element should exist') + assert.ok(trace, 'trace should exist') + assert.ok(trace.root, 'root element should exist') - t.assertSegments(trace.root, [findMany, update, update, findMany], { exact: true }) + assertSegments(trace.root, [findMany, update, update, findMany], { exact: true }) const findManySegment = findSegment(trace.root, findMany) - t.ok(findManySegment.timer.hrDuration, 'findMany segment should have ended') + assert.ok(findManySegment.timer.hrDuration, 'findMany segment should have ended') const updateSegment = findSegment(trace.root, update) - t.ok(updateSegment.timer.hrDuration, 'update segment should have ended') + assert.ok(updateSegment.timer.hrDuration, 'update segment should have ended') for (const segment of [findManySegment, updateSegment]) { const attributes = segment.getAttributes() const name = segment.name - t.equal(attributes.host, host, `host of segment ${name} should equal ${host}`) - t.equal( + assert.equal(attributes.host, host, `host of segment ${name} should equal ${host}`) + assert.equal( attributes.database_name, params.postgres_db, `database name of segment ${name} should be ${params.postgres_db}` ) - t.equal( + assert.equal( attributes.port_path_or_id, params.postgres_prisma_port.toString(), `port of segment ${name} should be ${params.postgres_prisma_port}` @@ -85,34 +85,37 @@ function verifyTraces(t, agent, transaction) { * Gets the sql traces from the agent query trace aggregator. It then asserts all their * associative datastore attributes + backtrace. * - * @param {Object} t tap test instance * @param {Object} agent mocked NR agent * @param {Number} [count=3] number of queries it expects in aggregator */ -utils.verifySlowQueries = function verifySlowQueries(t, agent, queries = []) { +utils.verifySlowQueries = function verifySlowQueries(agent, queries = []) { const metricHostName = getMetricHostName(agent, params.postgres_host) - t.equal(agent.queries.samples.size, queries.length, `should have ${queries.length} queries`) + assert.equal(agent.queries.samples.size, queries.length, `should have ${queries.length} queries`) let i = 0 for (const sample of agent.queries.samples.values()) { - t.equal(sample.trace.query, queries[i], 'Query name should be expected') + assert.equal(sample.trace.query, queries[i], 'Query name should be expected') const queryParams = sample.getParams() - t.equal(queryParams.host, metricHostName, 'instance data should show up in slow query params') + assert.equal( + queryParams.host, + metricHostName, + 'instance data should show up in slow query params' + ) - t.equal( + assert.equal( queryParams.port_path_or_id, String(params.postgres_prisma_port), 'instance data should show up in slow query params' ) - t.equal( + assert.equal( queryParams.database_name, params.postgres_db, 'database name should show up in slow query params' ) - t.ok(queryParams.backtrace, 'params should contain a backtrace') + assert.ok(queryParams.backtrace, 'params should contain a backtrace') i++ } } @@ -120,11 +123,10 @@ utils.verifySlowQueries = function verifySlowQueries(t, agent, queries = []) { /** * Helper that verifies both metrics and relevant segments on trace * - * @param {Object} t tap test instance * @param {Object} agent mocked NR agent * @param {Object} transaction active NR transaction */ -utils.verify = function verify(t, agent, transaction) { - verifyMetrics(t, agent) - verifyTraces(t, agent, transaction) +utils.verify = function verify(agent, transaction) { + verifyMetrics(agent) + verifyTraces(agent, transaction) } diff --git a/test/versioned/undici/package.json b/test/versioned/undici/package.json index d4bcc523ed..7c3ec58c6b 100644 --- a/test/versioned/undici/package.json +++ b/test/versioned/undici/package.json @@ -12,7 +12,7 @@ "undici": ">=5.0.0" }, "files": [ - "requests.tap.js" + "requests.test.js" ] } ], diff --git a/test/versioned/undici/requests.tap.js b/test/versioned/undici/requests.test.js similarity index 56% rename from test/versioned/undici/requests.tap.js rename to test/versioned/undici/requests.test.js index ba38bd905e..d4987fbc1b 100644 --- a/test/versioned/undici/requests.tap.js +++ b/test/versioned/undici/requests.test.js @@ -5,7 +5,9 @@ 'use strict' -const tap = require('tap') +const test = require('node:test') +const assert = require('node:assert') +const { assertSegments } = require('../../lib/custom-assertions') const { DESTINATIONS } = require('../../../lib/config/attribute-filter') const helper = require('../../lib/agent_helper') const metrics = require('../../lib/metrics_helper') @@ -17,57 +19,45 @@ const semver = require('semver') const fakeCert = require('../../lib/fake-cert') const cert = fakeCert({ commonName: 'localhost' }) -tap.test('Undici request tests', (t) => { - t.autoend() - - let agent - let undici - let server - let REQUEST_URL - let HOST - let PORT - - function createServer() { - server = http.createServer((req, res) => { - if (req.url.includes('/delay')) { - const parts = req.url.split('/') - const delayInMs = parts[parts.length - 1] - setTimeout(() => { - res.writeHead(200) - res.end('ok') - }, delayInMs) - } else if (req.url.includes('/status')) { - const parts = req.url.split('/') - const statusCode = parts[parts.length - 1] - res.writeHead(statusCode) - res.end() - } else { +function createServer() { + const server = http.createServer((req, res) => { + if (req.url.includes('/delay')) { + const parts = req.url.split('/') + const delayInMs = parts[parts.length - 1] + setTimeout(() => { res.writeHead(200) res.end('ok') - } - }) - - server.listen(0) - const { port } = server.address() - PORT = port - HOST = `localhost:${port}` - REQUEST_URL = `http://${HOST}` - return server - } + }, delayInMs) + } else if (req.url.includes('/status')) { + const parts = req.url.split('/') + const statusCode = parts[parts.length - 1] + res.writeHead(statusCode) + res.end() + } else { + res.writeHead(200) + res.end('ok') + } + }) - t.before(() => { - agent = helper.instrumentMockedAgent() + server.listen(0) + const { port } = server.address() + const PORT = port + const HOST = `localhost:${port}` + const REQUEST_URL = `http://${HOST}` + return { server, PORT, HOST, REQUEST_URL } +} - undici = require('undici') - server = createServer() - }) +test('Undici request tests', async (t) => { + const agent = helper.instrumentMockedAgent() + const undici = require('undici') + const { server, HOST, PORT, REQUEST_URL } = createServer() - t.teardown(() => { + t.after(() => { helper.unloadAgent(agent) server.close() }) - t.test('should not fail if request not in a transaction', async (t) => { + await t.test('should not fail if request not in a transaction', async () => { const { statusCode } = await undici.request(REQUEST_URL, { path: '/post', method: 'POST', @@ -77,12 +67,11 @@ tap.test('Undici request tests', (t) => { body: Buffer.from(`{"key":"value"}`) }) - t.equal(statusCode, 200) - t.end() + assert.equal(statusCode, 200) }) - t.test('should properly name segments', (t) => { - helper.runInTransaction(agent, async (tx) => { + await t.test('should properly name segments', async () => { + await helper.runInTransaction(agent, async (tx) => { const { statusCode } = await undici.request(REQUEST_URL, { path: '/post', method: 'POST', @@ -91,15 +80,14 @@ tap.test('Undici request tests', (t) => { }, body: Buffer.from(`{"key":"value"}`) }) - t.equal(statusCode, 200) + assert.equal(statusCode, 200) - t.assertSegments(tx.trace.root, [`External/${HOST}/post`], { exact: false }) + assertSegments(tx.trace.root, [`External/${HOST}/post`], { exact: false }) tx.end() - t.end() }) }) - t.test('should add HTTPS port to segment name when provided', async (t) => { + await t.test('should add HTTPS port to segment name when provided', async () => { const httpsServer = https.createServer( { key: cert.privateKey, cert: cert.certificate }, (req, res) => { @@ -108,7 +96,7 @@ tap.test('Undici request tests', (t) => { } ) - t.teardown(() => { + t.after(() => { httpsServer.close() }) @@ -121,13 +109,13 @@ tap.test('Undici request tests', (t) => { tls: { ca: cert.certificate } }) - t.teardown(() => { + t.after(() => { client.close() }) await client.request({ path: '/', method: 'GET' }) - t.assertSegments(transaction.trace.root, [`External/localhost:${port}/`], { + assertSegments(transaction.trace.root, [`External/localhost:${port}/`], { exact: false }) @@ -135,38 +123,37 @@ tap.test('Undici request tests', (t) => { }) }) - t.test('should add attributes to external segment', (t) => { - helper.runInTransaction(agent, async (tx) => { + await t.test('should add attributes to external segment', async () => { + await helper.runInTransaction(agent, async (tx) => { const { statusCode } = await undici.request(REQUEST_URL, { path: '/get?a=b&c=d', method: 'GET' }) - t.equal(statusCode, 200) + assert.equal(statusCode, 200) const segment = metrics.findSegment(tx.trace.root, `External/${HOST}/get`) const attrs = segment.getAttributes() - t.equal(attrs.url, `${REQUEST_URL}/get`) - t.equal(attrs.procedure, 'GET') + assert.equal(attrs.url, `${REQUEST_URL}/get`) + assert.equal(attrs.procedure, 'GET') const spanAttrs = segment.attributes.get(DESTINATIONS.SPAN_EVENT) - t.equal(spanAttrs['http.statusCode'], 200) - t.equal(spanAttrs['http.statusText'], 'OK') - t.equal(spanAttrs['request.parameters.a'], 'b') - t.equal(spanAttrs['request.parameters.c'], 'd') - t.equal(spanAttrs.hostname, 'localhost') - t.equal(spanAttrs.port, `${PORT}`) + assert.equal(spanAttrs['http.statusCode'], 200) + assert.equal(spanAttrs['http.statusText'], 'OK') + assert.equal(spanAttrs['request.parameters.a'], 'b') + assert.equal(spanAttrs['request.parameters.c'], 'd') + assert.equal(spanAttrs.hostname, 'localhost') + assert.equal(spanAttrs.port, `${PORT}`) tx.end() - t.end() }) }) - t.test('should add unscoped metrics for an external request', (t) => { + await t.test('should add unscoped metrics for an external request', async () => { // make sure metric aggregator is empty before asserting metrics agent.metrics.clear() - helper.runInTransaction(agent, async (tx) => { + await helper.runInTransaction(agent, async (tx) => { const { statusCode } = await undici.request(REQUEST_URL, { path: '/get?a=b&c=d', method: 'GET' }) - t.equal(statusCode, 200) + assert.equal(statusCode, 200) tx.end() const expectedNames = [ @@ -177,19 +164,17 @@ tap.test('Undici request tests', (t) => { ] expectedNames.forEach((metricName) => { const metric = agent.metrics.getOrCreateMetric(metricName) - t.equal( + assert.equal( metric.callCount, 1, `should record unscoped external metric of ${metricName} for an undici request` ) }) - - t.end() }) }) - t.test('concurrent requests', (t) => { - helper.runInTransaction(agent, async (tx) => { + await t.test('concurrent requests', async () => { + await helper.runInTransaction(agent, async (tx) => { const req1 = undici.request(REQUEST_URL, { path: '/post', method: 'POST', @@ -207,36 +192,34 @@ tap.test('Undici request tests', (t) => { body: Buffer.from(`{"key":"value"}`) }) const [{ statusCode }, { statusCode: statusCode2 }] = await Promise.all([req1, req2]) - t.equal(statusCode, 200) - t.equal(statusCode2, 200) - t.assertSegments(tx.trace.root, [`External/${HOST}/post`, `External/${HOST}/put`], { + assert.equal(statusCode, 200) + assert.equal(statusCode2, 200) + assertSegments(tx.trace.root, [`External/${HOST}/post`, `External/${HOST}/put`], { exact: false }) tx.end() - t.end() }) }) - t.test('invalid host', (t) => { - helper.runInTransaction(agent, async (tx) => { + await t.test('invalid host', async () => { + await helper.runInTransaction(agent, async (tx) => { try { await undici.request('https://invalidurl', { path: '/foo', method: 'GET' }) } catch (err) { - t.ok(err) - t.assertSegments(tx.trace.root, ['External/invalidurl/foo'], { exact: false }) - t.equal(tx.exceptions.length, 1) + assert.ok(err) + assertSegments(tx.trace.root, ['External/invalidurl/foo'], { exact: false }) + assert.equal(tx.exceptions.length, 1) tx.end() - t.end() } }) }) - t.test('should add errors to transaction when external segment exists', (t) => { + await t.test('should add errors to transaction when external segment exists', async () => { const abortController = new AbortController() - helper.runInTransaction(agent, async (tx) => { + await helper.runInTransaction(agent, async (tx) => { try { const req = undici.request(REQUEST_URL, { path: '/delay/1000', @@ -247,86 +230,81 @@ tap.test('Undici request tests', (t) => { }, 100) await req } catch (err) { - t.assertSegments(tx.trace.root, [`External/${HOST}/delay/1000`], { exact: false }) - t.equal(tx.exceptions.length, 1) + assertSegments(tx.trace.root, [`External/${HOST}/delay/1000`], { exact: false }) + assert.equal(tx.exceptions.length, 1) const expectedErrMsg = semver.gte(pkgVersion, '6.3.0') ? 'This operation was aborted' : 'Request aborted' - t.equal(tx.exceptions[0].error.message, expectedErrMsg) + assert.equal(tx.exceptions[0].error.message, expectedErrMsg) tx.end() - t.end() } }) }) - t.test('segments should end on error', (t) => { + await t.test('segments should end on error', async () => { const socketEndServer = http.createServer(function badHandler(req) { req.socket.end() }) - t.teardown(() => { + t.after(() => { socketEndServer.close() }) socketEndServer.listen(0) - helper.runInTransaction(agent, async (transaction) => { + await helper.runInTransaction(agent, async (transaction) => { const { port } = socketEndServer.address() const req = undici.request(`http://localhost:${port}`) try { await req } catch (error) { - t.assertSegments(transaction.trace.root, [`External/localhost:${port}/`], { + assertSegments(transaction.trace.root, [`External/localhost:${port}/`], { exact: false }) const segments = transaction.trace.root.children const segment = segments[segments.length - 1] - t.ok(segment.timer.start, 'should have started') - t.ok(segment.timer.hasEnd(), 'should have ended') + assert.ok(segment.timer.start, 'should have started') + assert.ok(segment.timer.hasEnd(), 'should have ended') transaction.end() - - t.end() } }) }) - t.test('400 status', (t) => { - helper.runInTransaction(agent, async (tx) => { + await t.test('400 status', async () => { + await helper.runInTransaction(agent, async (tx) => { const { statusCode } = await undici.request(REQUEST_URL, { path: '/status/400', method: 'GET' }) - t.equal(statusCode, 400) - t.assertSegments(tx.trace.root, [`External/${HOST}/status/400`], { exact: false }) + assert.equal(statusCode, 400) + assertSegments(tx.trace.root, [`External/${HOST}/status/400`], { exact: false }) tx.end() - t.end() }) }) - t.test('fetch', (t) => { - helper.runInTransaction(agent, async (tx) => { + await t.test('fetch', async () => { + await helper.runInTransaction(agent, async (tx) => { const res = await undici.fetch(REQUEST_URL) - t.equal(res.status, 200) - t.assertSegments(tx.trace.root, [`External/${HOST}/`], { exact: false }) + assert.equal(res.status, 200) + assertSegments(tx.trace.root, [`External/${HOST}/`], { exact: false }) tx.end() - t.end() }) }) - t.test('stream', (t) => { + await t.test('stream', async () => { const { Writable } = require('stream') - helper.runInTransaction(agent, async (tx) => { + await helper.runInTransaction(agent, async (tx) => { await undici.stream( REQUEST_URL, { path: '/get' }, ({ statusCode }) => { - t.equal(statusCode, 200) + assert.equal(statusCode, 200) return new Writable({ write(chunk, encoding, callback) { callback() @@ -334,15 +312,14 @@ tap.test('Undici request tests', (t) => { }) } ) - t.assertSegments(tx.trace.root, [`External/${HOST}/get`], { exact: false }) + assertSegments(tx.trace.root, [`External/${HOST}/get`], { exact: false }) tx.end() - t.end() }) }) - t.test('pipeline', (t) => { + await t.test('pipeline', (_t, end) => { const { pipeline, PassThrough, Readable, Writable } = require('stream') - helper.runInTransaction(agent, async (tx) => { + helper.runInTransaction(agent, (tx) => { pipeline( new Readable({ read() { @@ -356,7 +333,7 @@ tap.test('Undici request tests', (t) => { path: '/get' }, ({ statusCode, body }) => { - t.equal(statusCode, 200) + assert.equal(statusCode, 200) return pipeline(body, new PassThrough(), () => {}) } ), @@ -369,10 +346,10 @@ tap.test('Undici request tests', (t) => { } }), (err) => { - t.error(err) - t.assertSegments(tx.trace.root, [`External/${HOST}/get`], { exact: false }) + assert.ok(!err) + assertSegments(tx.trace.root, [`External/${HOST}/get`], { exact: false }) tx.end() - t.end() + end() } ) })