Skip to content
57 changes: 57 additions & 0 deletions test/lib/assert-metrics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2024 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

'use strict'

module.exports = {
assertMetricValues
}

const assert = require('node:assert')

/**
* @param {Transaction} transaction Nodejs agent transaction
* @param {Array} expected Array of metric data where metric data is in this form:
* [
* {
* “name”:”name of metric”,
* “scope”:”scope of metric”,
* },
* [count,
* total time,
* exclusive time,
* min time,
* max time,
* sum of squares]
* ]
* @param {boolean} exact When true, found and expected metric lengths should match
*/
function assertMetricValues(transaction, expected, exact) {
const metrics = transaction.metrics

for (let i = 0; i < expected.length; ++i) {
let expectedMetric = Object.assign({}, expected[i])
let name = null
let scope = null

if (typeof expectedMetric === 'string') {
name = expectedMetric
expectedMetric = {}
} else {
name = expectedMetric[0].name
scope = expectedMetric[0].scope
}

const metric = metrics.getMetric(name, scope)
assert.ok(metric, 'should have expected metric name')

assert.deepStrictEqual(metric.toJSON(), expectedMetric[1], 'metric values should match')
}

if (exact) {
const metricsJSON = metrics.toJSON()
assert.equal(metricsJSON.length, expected.length, 'metrics length should match')
}
}
53 changes: 46 additions & 7 deletions test/lib/test-collector.js
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple of quality-of-life improvements to increase the usefulness of this tool.

Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ class Collector {
#handlers = new Map()
#server
#address
#runId

constructor() {
constructor({ runId = 42 } = {}) {
this.#runId = runId
this.#server = https.createServer({
key: fakeCert.privateKey,
cert: fakeCert.certificate
Expand All @@ -37,6 +39,27 @@ class Collector {
this.end(JSON.stringify(payload))
}

req.body = function () {
let resolve
let reject
const promise = new Promise((res, rej) => {
resolve = res
reject = rej
})

let data = ''
this.on('data', (d) => {
data += d
})
this.on('end', () => {
resolve(data)
})
this.on('error', (error) => {
reject(error)
})
return promise
}

handler.isDone = true
handler(req, res)
})
Expand Down Expand Up @@ -104,6 +127,19 @@ class Collector {
}
}

/**
* the most basic `connect` handler. Useful when you do not need to
* customize the handler.
*
* @returns {function}
*/
get connectHandler() {
const runId = this.#runId
return function (req, res) {
res.json({ payload: { return_value: { agent_run_id: runId } } })
}
}

/**
* The most basic `preconnect` handler. Useful when you do not need to
* customize the handler.
Expand Down Expand Up @@ -135,7 +171,9 @@ class Collector {
* requests.
* @param {function} handler A typical `(req, res) => {}` handler. For
* convenience, `res` is extended with a `json({ payload, code = 200 })`
* method for easily sending JSON responses.
* method for easily sending JSON responses. Also, `req` is extended with
* a `body()` method that returns a promise which resolves to the string
* data supplied via POST-like requests.
*/
addHandler(endpoint, handler) {
const qs = querystring.decode(endpoint.slice(endpoint.indexOf('?') + 1))
Expand Down Expand Up @@ -181,11 +219,12 @@ class Collector {
// Add handlers for the required agent startup connections. These should
// be overwritten by tests that exercise the startup phase, but adding these
// stubs makes it easier to test other connection events.
this.addHandler(helper.generateCollectorPath('preconnect', 42), this.preconnectHandler)
this.addHandler(helper.generateCollectorPath('connect', 42), (req, res) => {
res.json({ payload: { return_value: { agent_run_id: 42 } } })
})
this.addHandler(helper.generateCollectorPath('agent_settings', 42), this.agentSettingsHandler)
this.addHandler(helper.generateCollectorPath('preconnect', this.#runId), this.preconnectHandler)
this.addHandler(helper.generateCollectorPath('connect', this.#runId), this.connectHandler)
this.addHandler(
helper.generateCollectorPath('agent_settings', this.#runId),
this.agentSettingsHandler
)

return address
}
Expand Down
Loading