Skip to content

Commit 5e7ac3e

Browse files
committed
chore: Converted agent unit tests to node:test
1 parent b9f64b7 commit 5e7ac3e

9 files changed

+1121
-1005
lines changed

THIRD_PARTY_NOTICES.md

+18-1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ code, the source code can be found at [https://github.com/newrelic/node-newrelic
7171
* [proxyquire](#proxyquire)
7272
* [rfdc](#rfdc)
7373
* [rimraf](#rimraf)
74+
* [self-cert](#self-cert)
7475
* [should](#should)
7576
* [sinon](#sinon)
7677
* [superagent](#superagent)
@@ -4023,6 +4024,22 @@ IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
40234024
40244025
```
40254026

4027+
### self-cert
4028+
4029+
This product includes source derived from [self-cert](https://github.com/jsumners/self-cert) ([v2.0.0](https://github.com/jsumners/self-cert/tree/v2.0.0)), distributed under the [MIT License](https://github.com/jsumners/self-cert/blob/v2.0.0/Readme.md):
4030+
4031+
```
4032+
MIT License
4033+
4034+
Copyright (c) <year> <copyright holders>
4035+
4036+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4037+
4038+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
4039+
4040+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
4041+
```
4042+
40264043
### should
40274044

40284045
This product includes source derived from [should](https://github.com/shouldjs/should.js) ([v13.2.3](https://github.com/shouldjs/should.js/tree/v13.2.3)), distributed under the [MIT License](https://github.com/shouldjs/should.js/blob/v13.2.3/LICENSE):
@@ -4053,7 +4070,7 @@ THE SOFTWARE.
40534070

40544071
### sinon
40554072

4056-
This product includes source derived from [sinon](https://github.com/sinonjs/sinon) ([v4.5.0](https://github.com/sinonjs/sinon/tree/v4.5.0)), distributed under the [BSD-3-Clause License](https://github.com/sinonjs/sinon/blob/v4.5.0/LICENSE):
4073+
This product includes source derived from [sinon](https://github.com/sinonjs/sinon) ([v5.1.1](https://github.com/sinonjs/sinon/tree/v5.1.1)), distributed under the [BSD-3-Clause License](https://github.com/sinonjs/sinon/blob/v5.1.1/LICENSE):
40574074

40584075
```
40594076
(The BSD License)

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,9 @@
256256
"proxyquire": "^1.8.0",
257257
"rfdc": "^1.3.1",
258258
"rimraf": "^2.6.3",
259+
"self-cert": "^2.0.0",
259260
"should": "*",
260-
"sinon": "^4.5.0",
261+
"sinon": "^5.1.1",
261262
"superagent": "^9.0.1",
262263
"tap": "^16.3.4",
263264
"temp": "^0.8.1"

test/lib/fake-cert.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright 2024 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
'use strict'
7+
8+
const selfCert = require('self-cert')
9+
module.exports = selfCert({
10+
attrs: {
11+
stateName: 'Georgia',
12+
locality: 'Atlanta',
13+
orgName: 'New Relic',
14+
shortName: 'new_relic'
15+
},
16+
expires: new Date('2099-12-31')
17+
})

test/lib/promise-resolvers.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2024 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
'use strict'
7+
8+
/**
9+
* Implements https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers
10+
*
11+
* This can be removed once Node.js v22 is the minimum.
12+
*
13+
* @returns {{resolve, reject, promise: Promise<unknown>}}
14+
*/
15+
module.exports = function promiseResolvers() {
16+
if (typeof Promise.withResolvers === 'function') {
17+
// Node.js >=22 natively supports this.
18+
return Promise.withResolvers()
19+
}
20+
21+
let resolve
22+
let reject
23+
const promise = new Promise((a, b) => {
24+
resolve = a
25+
reject = b
26+
})
27+
return { promise, resolve, reject }
28+
}

test/lib/test-collector.js

+194
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/*
2+
* Copyright 2024 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
'use strict'
7+
8+
// This provides an in-process http server to use in place of
9+
// collector.newrelic.com. It allows for custom handlers so that test specific
10+
// assertions can be made.
11+
12+
const https = require('node:https')
13+
const querystring = require('node:querystring')
14+
const helper = require('./agent_helper')
15+
const fakeCert = require('./fake-cert')
16+
17+
class Collector {
18+
#handlers = new Map()
19+
#server
20+
#address
21+
22+
constructor() {
23+
this.#server = https.createServer({
24+
key: fakeCert.privateKey,
25+
cert: fakeCert.certificate
26+
})
27+
this.#server.on('request', (req, res) => {
28+
const qs = querystring.decode(req.url.slice(req.url.indexOf('?') + 1))
29+
const handler = this.#handlers.get(qs.method)
30+
if (typeof handler !== 'function') {
31+
res.writeHead(500)
32+
return res.end('handler not found: ' + req.url)
33+
}
34+
35+
res.json = function ({ payload, code = 200 }) {
36+
this.writeHead(code, { 'content-type': 'application/json' })
37+
this.end(JSON.stringify(payload))
38+
}
39+
40+
handler.isDone = true
41+
handler(req, res)
42+
})
43+
44+
// We don't need this server keeping the process alive.
45+
this.#server.unref()
46+
}
47+
48+
/**
49+
* A configuration object that can be passed to an "agent" instance so that
50+
* the agent will communicate with this test server instead of the real
51+
* server.
52+
*
53+
* Important: the `.listen` method must be invoked first in order to have
54+
* the `host` and `port` defined.
55+
*
56+
* @returns {object}
57+
*/
58+
get agentConfig() {
59+
return {
60+
host: this.host,
61+
port: this.port,
62+
license_key: 'testing',
63+
certificates: [this.cert]
64+
}
65+
}
66+
67+
/**
68+
* The host the server is listening on.
69+
*
70+
* @returns {string}
71+
*/
72+
get host() {
73+
return this.#address?.address
74+
}
75+
76+
/**
77+
* The port number the server is listening on.
78+
*
79+
* @returns {number}
80+
*/
81+
get port() {
82+
return this.#address?.port
83+
}
84+
85+
/**
86+
* A copy of the public certificate used to secure the server. Use this
87+
* like `new Agent({ certificates: [collector.cert] })`.
88+
*
89+
* @returns {string}
90+
*/
91+
get cert() {
92+
return fakeCert.certificate
93+
}
94+
95+
/**
96+
* The most basic `agent_settings` handler. Useful when you do not need to
97+
* customize the handler.
98+
*
99+
* @returns {function}
100+
*/
101+
get agentSettingsHandler() {
102+
return function (req, res) {
103+
res.json({ payload: { return_value: [] } })
104+
}
105+
}
106+
107+
/**
108+
* The most basic `preconnect` handler. Useful when you do not need to
109+
* customize the handler.
110+
*
111+
* @returns {function}
112+
*/
113+
get preconnectHandler() {
114+
const host = this.host
115+
const port = this.port
116+
return function (req, res) {
117+
res.json({
118+
payload: {
119+
return_value: {
120+
redirect_host: `${host}:${port}`,
121+
security_policies: {}
122+
}
123+
}
124+
})
125+
}
126+
}
127+
128+
/**
129+
* Adds a new handler for the provided endpoint.
130+
*
131+
* @param {string} endpoint A string like
132+
* `/agent_listener/invoke_raw_method?method=preconnect`. Notice that a query
133+
* string with the `method` parameter is present. This is required, as the
134+
* value of `method` will be used to look up the handler when receiving
135+
* requests.
136+
* @param {function} handler A typical `(req, res) => {}` handler. For
137+
* convenience, `res` is extended with a `json({ payload, code = 200 })`
138+
* method for easily sending JSON responses.
139+
*/
140+
addHandler(endpoint, handler) {
141+
const qs = querystring.decode(endpoint.slice(endpoint.indexOf('?') + 1))
142+
this.#handlers.set(qs.method, handler)
143+
}
144+
145+
/**
146+
* Shutdown the server and forcefully close all current connections.
147+
*/
148+
close() {
149+
this.#server.closeAllConnections()
150+
}
151+
152+
/**
153+
* Determine if a handler has been invoked.
154+
*
155+
* @param {string} method Name of the method to check, e.g. "preconnect".
156+
* @returns {boolean}
157+
*/
158+
isDone(method) {
159+
return this.#handlers.get(method)?.isDone === true
160+
}
161+
162+
/**
163+
* Start the server listening for requests.
164+
*
165+
* @returns {Promise<object>} Returns a standard server address object.
166+
*/
167+
async listen() {
168+
let address
169+
await new Promise((resolve, reject) => {
170+
this.#server.listen(0, '127.0.0.1', (err) => {
171+
if (err) {
172+
return reject(err)
173+
}
174+
address = this.#server.address()
175+
resolve()
176+
})
177+
})
178+
179+
this.#address = address
180+
181+
// Add handlers for the required agent startup connections. These should
182+
// be overwritten by tests that exercise the startup phase, but adding these
183+
// stubs makes it easier to test other connection events.
184+
this.addHandler(helper.generateCollectorPath('preconnect', 42), this.preconnectHandler)
185+
this.addHandler(helper.generateCollectorPath('connect', 42), (req, res) => {
186+
res.json({ payload: { return_value: { agent_run_id: 42 } } })
187+
})
188+
this.addHandler(helper.generateCollectorPath('agent_settings', 42), this.agentSettingsHandler)
189+
190+
return address
191+
}
192+
}
193+
194+
module.exports = Collector

0 commit comments

Comments
 (0)