Skip to content

Commit be89053

Browse files
committed
feat: Fixed code after flatenning segment tree
1 parent c6c2799 commit be89053

13 files changed

+659
-829
lines changed

tests/agent-testing.js

+99-7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
*/
55

66
'use strict'
7+
const tap = require('tap')
8+
tap.Test.prototype.addAssert('assertSegments', 4, assertSegments)
9+
710

811
// TODO: ideally, we wouldn't be reachign into internals
912
// and would have an API (in agent package?) to get what we need.
@@ -27,20 +30,19 @@ function findSpanById(agent, spanId) {
2730
})
2831
}
2932

30-
function findSegmentByName(root, name) {
33+
function findSegmentByName(trace, root, name) {
34+
const children = trace.getChildren(root.id)
3135
if (root.name === name) {
3236
return root
33-
} else if (root.children && root.children.length) {
34-
for (let i = 0; i < root.children.length; i++) {
35-
const child = root.children[i]
36-
const found = findSegmentByName(child, name)
37+
} else if (children.length) {
38+
for (let i = 0; i < children.length; i++) {
39+
const child = children[i]
40+
const found = findSegmentByName(trace, child, name)
3741
if (found) {
3842
return found
3943
}
4044
}
4145
}
42-
43-
return null
4446
}
4547

4648
function temporarySetEnv(t, key, value) {
@@ -63,6 +65,96 @@ function setupEnvConfig(t, enabled = true, appName = 'test app') {
6365
temporarySetEnv(t, 'NEW_RELIC_APP_NAME', appName)
6466
}
6567

68+
/**
69+
* @param {TraceSegment} parent Parent segment
70+
* @param {Array} expected Array of strings that represent segment names.
71+
* If an item in the array is another array, it
72+
* represents children of the previous item.
73+
* @param {boolean} options.exact If true, then the expected segments must match
74+
* exactly, including their position and children on all
75+
* levels. When false, then only check that each child
76+
* exists.
77+
* @param {array} options.exclude Array of segment names that should be excluded from
78+
* validation. This is useful, for example, when a
79+
* segment may or may not be created by code that is not
80+
* directly under test. Only used when `exact` is true.
81+
*/
82+
function assertSegments(trace, parent, expected, options) {
83+
let child
84+
let childCount = 0
85+
86+
// rather default to what is more likely to fail than have a false test
87+
let exact = true
88+
if (options && options.exact === false) {
89+
exact = options.exact
90+
} else if (options === false) {
91+
exact = false
92+
}
93+
94+
function getChildren(_parent) {
95+
const children = trace.getChildren(_parent.id)
96+
return children.filter(function (item) {
97+
if (exact && options && options.exclude) {
98+
return options.exclude.indexOf(item.name) === -1
99+
}
100+
return true
101+
})
102+
}
103+
104+
const children = getChildren(parent)
105+
if (exact) {
106+
for (let i = 0; i < expected.length; ++i) {
107+
const sequenceItem = expected[i]
108+
109+
if (typeof sequenceItem === 'string') {
110+
child = children[childCount++]
111+
this.equal(
112+
child ? child.name : undefined,
113+
sequenceItem,
114+
'segment "' +
115+
parent.name +
116+
'" should have child "' +
117+
sequenceItem +
118+
'" in position ' +
119+
childCount
120+
)
121+
122+
// If the next expected item is not array, then check that the current
123+
// child has no children
124+
if (!Array.isArray(expected[i + 1])) {
125+
this.ok(
126+
getChildren(child).length === 0,
127+
'segment "' + child.name + '" should not have any children'
128+
)
129+
}
130+
} else if (typeof sequenceItem === 'object') {
131+
this.assertSegments(trace, child, sequenceItem, options)
132+
}
133+
}
134+
135+
// check if correct number of children was found
136+
this.equal(children.length, childCount)
137+
} else {
138+
for (let i = 0; i < expected.length; i++) {
139+
const sequenceItem = expected[i]
140+
141+
if (typeof sequenceItem === 'string') {
142+
// find corresponding child in parent
143+
for (let j = 0; j < children.length; j++) {
144+
if (children[j].name === sequenceItem) {
145+
child = children[j]
146+
}
147+
}
148+
this.ok(child, 'segment "' + parent.name + '" should have child "' + sequenceItem + '"')
149+
if (typeof expected[i + 1] === 'object') {
150+
this.assertSegments(trace, child, expected[i + 1], exact)
151+
}
152+
}
153+
}
154+
}
155+
}
156+
157+
66158
module.exports = {
67159
getErrorTraces,
68160
getSpanEvents,

tests/create-apollo-server-setup.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@
66
'use strict'
77

88
const tap = require('tap')
9-
109
const utils = require('@newrelic/test-utilities')
1110
utils.assert.extendTap(tap)
12-
11+
require('./agent-testing')
1312
const { getTypeDefs, resolvers } = require('./data-definitions')
1413
const setupErrorSchema = require('./versioned/error-setup')
1514
const { clearCachedModules } = require('./utils')
@@ -78,7 +77,7 @@ function setupApolloServerTests(loadApolloServer, testDir, options, agentConfig)
7877
serverUrl = null
7978
helper = null
8079
clearCachedModules(
81-
['express', 'apollo-server', '@apollo/server', '@apollo/server/express4'],
80+
['express', 'apollo-server', '@apollo/server', '@apollo/server/express4', '@apollo/server/standalone'],
8281
testDir
8382
)
8483
})

tests/integration/config-capture-scalars.test.js

+56-84
Original file line numberDiff line numberDiff line change
@@ -62,51 +62,37 @@ function createNoScalarTests(t) {
6262
helper.agent.once('transactionFinished', (transaction) => {
6363
const operationPart = `query/${expectedName}/${path}`
6464
const expectedSegments = [
65-
{
66-
name: `${TRANSACTION_PREFIX}//${operationPart}`,
67-
children: [
68-
{ name: 'Nodejs/Middleware/Expressjs/query' },
69-
{ name: 'Nodejs/Middleware/Expressjs/expressInit' },
70-
{
71-
name: 'Expressjs/Router: /',
72-
children: [
73-
{ name: 'Nodejs/Middleware/Expressjs/corsMiddleware' },
74-
{ name: 'Nodejs/Middleware/Expressjs/jsonParser' },
75-
{
76-
name: 'Nodejs/Middleware/Expressjs/<anonymous>',
77-
children: [
78-
{
79-
name: `${OPERATION_PREFIX}/${operationPart}`,
80-
children: [
81-
{
82-
name: `${RESOLVE_PREFIX}/library`,
83-
children: [
84-
{
85-
name: 'timers.setTimeout',
86-
children: [
87-
{
88-
name: 'Callback: <anonymous>'
89-
}
90-
]
91-
}
92-
]
93-
},
94-
{ name: `${RESOLVE_PREFIX}/library.books` },
95-
{ name: `${RESOLVE_PREFIX}/library.books.author` },
96-
{ name: `${RESOLVE_PREFIX}/library.books.author` },
97-
{ name: `${RESOLVE_PREFIX}/library.magazines` }
98-
]
99-
}
65+
`${TRANSACTION_PREFIX}//${operationPart}`,
66+
[
67+
'Nodejs/Middleware/Expressjs/query',
68+
'Nodejs/Middleware/Expressjs/expressInit',
69+
'Expressjs/Router: /',
70+
[
71+
'Nodejs/Middleware/Expressjs/corsMiddleware',
72+
'Nodejs/Middleware/Expressjs/jsonParser',
73+
'Nodejs/Middleware/Expressjs/<anonymous>',
74+
[
75+
`${OPERATION_PREFIX}/${operationPart}`,
76+
[
77+
`${RESOLVE_PREFIX}/library`,
78+
[
79+
'timers.setTimeout',
80+
[
81+
'Callback: <anonymous>'
10082
]
101-
}
83+
],
84+
`${RESOLVE_PREFIX}/library.books`,
85+
`${RESOLVE_PREFIX}/library.books.author`,
86+
`${RESOLVE_PREFIX}/library.books.author`,
87+
`${RESOLVE_PREFIX}/library.magazines`
10288
]
103-
}
89+
]
10490
]
105-
}
91+
]
10692
]
10793

10894
// Exact match to ensure no extra fields snuck in
109-
t.exactSegments(transaction.trace.root, expectedSegments)
95+
t.assertSegments(transaction.trace, transaction.trace.root, expectedSegments, { exact: true })
11096
})
11197

11298
executeQuery(serverUrl, query, (err) => {
@@ -143,57 +129,43 @@ function createScalarTests(t) {
143129
helper.agent.once('transactionFinished', (transaction) => {
144130
const operationPart = `query/${expectedName}/${path}`
145131
const expectedSegments = [
146-
{
147-
name: `${TRANSACTION_PREFIX}//${operationPart}`,
148-
children: [
149-
{ name: 'Nodejs/Middleware/Expressjs/query' },
150-
{ name: 'Nodejs/Middleware/Expressjs/expressInit' },
151-
{
152-
name: 'Expressjs/Router: /',
153-
children: [
154-
{ name: 'Nodejs/Middleware/Expressjs/corsMiddleware' },
155-
{ name: 'Nodejs/Middleware/Expressjs/jsonParser' },
156-
{
157-
name: 'Nodejs/Middleware/Expressjs/<anonymous>',
158-
children: [
159-
{
160-
name: `${OPERATION_PREFIX}/${operationPart}`,
161-
children: [
162-
{
163-
name: `${RESOLVE_PREFIX}/library`,
164-
children: [
165-
{
166-
name: 'timers.setTimeout',
167-
children: [
168-
{
169-
name: 'Callback: <anonymous>'
170-
}
171-
]
172-
}
173-
]
174-
},
175-
{ name: `${RESOLVE_PREFIX}/library.books` },
176-
{ name: `${RESOLVE_PREFIX}/library.books.title` },
177-
{ name: `${RESOLVE_PREFIX}/library.books.author` },
178-
{ name: `${RESOLVE_PREFIX}/library.books.author.name` },
179-
{ name: `${RESOLVE_PREFIX}/library.books.title` },
180-
{ name: `${RESOLVE_PREFIX}/library.books.author` },
181-
{ name: `${RESOLVE_PREFIX}/library.books.author.name` },
182-
{ name: `${RESOLVE_PREFIX}/library.magazines` },
183-
{ name: `${RESOLVE_PREFIX}/library.magazines.title` },
184-
{ name: `${RESOLVE_PREFIX}/library.magazines.issue` }
185-
]
186-
}
132+
`${TRANSACTION_PREFIX}//${operationPart}`,
133+
[
134+
'Nodejs/Middleware/Expressjs/query',
135+
'Nodejs/Middleware/Expressjs/expressInit',
136+
'Expressjs/Router: /',
137+
[
138+
'Nodejs/Middleware/Expressjs/corsMiddleware',
139+
'Nodejs/Middleware/Expressjs/jsonParser',
140+
'Nodejs/Middleware/Expressjs/<anonymous>',
141+
[
142+
`${OPERATION_PREFIX}/${operationPart}`,
143+
[
144+
`${RESOLVE_PREFIX}/library`,
145+
[
146+
'timers.setTimeout',
147+
[
148+
'Callback: <anonymous>'
187149
]
188-
}
150+
],
151+
`${RESOLVE_PREFIX}/library.books`,
152+
`${RESOLVE_PREFIX}/library.books.title`,
153+
`${RESOLVE_PREFIX}/library.books.author`,
154+
`${RESOLVE_PREFIX}/library.books.author.name`,
155+
`${RESOLVE_PREFIX}/library.books.title`,
156+
`${RESOLVE_PREFIX}/library.books.author`,
157+
`${RESOLVE_PREFIX}/library.books.author.name`,
158+
`${RESOLVE_PREFIX}/library.magazines`,
159+
`${RESOLVE_PREFIX}/library.magazines.title`,
160+
`${RESOLVE_PREFIX}/library.magazines.issue`
189161
]
190-
}
162+
]
191163
]
192-
}
164+
]
193165
]
194166

195167
// Exact match to ensure no extra fields snuck in
196-
t.exactSegments(transaction.trace.root, expectedSegments)
168+
t.assertSegments(transaction.trace, transaction.trace.root, expectedSegments, { exact: true })
197169
})
198170

199171
executeQuery(serverUrl, query, (err) => {

tests/unit/create-plugin.test.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@ tap.test('createPlugin edge cases', (t) => {
1515

1616
t.beforeEach(function () {
1717
operationSegment = {
18-
start: sinon.stub(),
1918
addAttribute: sinon.stub(),
20-
transaction: { nameState: { setName: sinon.stub() } },
21-
end: sinon.stub()
19+
end: sinon.stub(),
20+
start: sinon.stub()
2221
}
2322

2423
instrumentationApi = {
@@ -34,6 +33,9 @@ tap.test('createPlugin edge cases', (t) => {
3433
}
3534
},
3635
getActiveSegment: sinon.stub().returns({}),
36+
tracer: {
37+
getTransaction: sinon.stub().returns({ nameState: { setName: sinon.stub() }})
38+
},
3739
createSegment: sinon.stub().callsFake((name) => {
3840
operationSegment.name = name
3941
return operationSegment

tests/unit/error-helper.test.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ class MockedInstrumentationApi {
2727
}
2828
}
2929

30-
this.getActiveSegment = () => {}
30+
this.tracer = {
31+
getTransaction: () => {}
32+
}
3133
}
3234
}
3335

tests/versioned/apollo-federation/federated-gateway-server-setup.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
const tap = require('tap')
99

1010
const utils = require('@newrelic/test-utilities')
11-
utils.assert.extendTap(tap)
12-
11+
require('../../agent-testing')
1312
const federatedData = require('./federated-data-definitions')
1413
const { clearCachedModules } = require('../../utils')
1514

0 commit comments

Comments
 (0)