Skip to content

Commit 4ad871a

Browse files
committed
test no tracing activity
1 parent 87c9167 commit 4ad871a

4 files changed

Lines changed: 105 additions & 28 deletions

File tree

src/__testUtils__/diagnosticsTestUtils.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* eslint-disable n/no-unsupported-features/node-builtins, import/no-nodejs-modules */
22
import dc from 'node:diagnostics_channel';
33

4+
import { expect } from 'chai';
5+
46
import type { MinimalTracingChannel } from '../diagnostics.js';
57

68
export interface CollectedEvent {
@@ -52,3 +54,68 @@ export function collectEvents(channel: MinimalTracingChannel): {
5254
export function getTracingChannel(name: string): MinimalTracingChannel {
5355
return dc.tracingChannel(name) as unknown as MinimalTracingChannel;
5456
}
57+
58+
/**
59+
* Assert that a graphql tracing channel stays on its zero-subscriber fast path.
60+
* The test installs wrappers around the real tracing methods and verifies none
61+
* of them were touched while `fn` ran.
62+
*/
63+
export async function expectNoTracingActivity<T>(
64+
channel: MinimalTracingChannel,
65+
fn: () => T | Promise<T>,
66+
): Promise<Awaited<T>> {
67+
expect(channel.hasSubscribers).to.equal(false);
68+
expect(channel.start.hasSubscribers).to.equal(false);
69+
expect(channel.end.hasSubscribers).to.equal(false);
70+
expect(channel.asyncStart.hasSubscribers).to.equal(false);
71+
expect(channel.asyncEnd.hasSubscribers).to.equal(false);
72+
expect(channel.error.hasSubscribers).to.equal(false);
73+
74+
const calls: Array<string> = [];
75+
const restore: Array<() => void> = [];
76+
77+
function interceptMethod(
78+
target: { [key: string]: unknown },
79+
key: string,
80+
name: string,
81+
): void {
82+
const original = target[key] as (...args: Array<unknown>) => unknown;
83+
target[key] = function interceptedMethod(
84+
this: unknown,
85+
...args: Array<unknown>
86+
) {
87+
calls.push(name);
88+
return original.apply(this, args);
89+
};
90+
restore.push(() => {
91+
target[key] = original;
92+
});
93+
}
94+
95+
interceptMethod(
96+
channel as unknown as { [key: string]: unknown },
97+
'traceSync',
98+
'traceSync',
99+
);
100+
101+
for (const phase of ['start', 'end', 'asyncStart', 'asyncEnd', 'error']) {
102+
const subChannel = channel[
103+
phase as keyof Pick<
104+
MinimalTracingChannel,
105+
'start' | 'end' | 'asyncStart' | 'asyncEnd' | 'error'
106+
>
107+
] as unknown as { [key: string]: unknown };
108+
interceptMethod(subChannel, 'publish', `${phase}.publish`);
109+
interceptMethod(subChannel, 'runStores', `${phase}.runStores`);
110+
}
111+
112+
try {
113+
const result = await fn();
114+
expect(calls).to.deep.equal([]);
115+
return result;
116+
} finally {
117+
while (restore.length > 0) {
118+
restore.pop()?.();
119+
}
120+
}
121+
}

src/execution/__tests__/tracing-test.ts

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { afterEach, describe, it } from 'mocha';
33

44
import {
55
collectEvents,
6+
expectNoTracingActivity,
67
getTracingChannel,
78
} from '../../__testUtils__/diagnosticsTestUtils.js';
89

@@ -173,13 +174,15 @@ describe('execute diagnostics channel', () => {
173174
}
174175
});
175176

176-
it('does nothing when no subscribers are attached', () => {
177+
it('does not call tracing methods when no subscribers are attached', async () => {
177178
const document = parse('{ sync }');
178-
const result = execute({
179-
schema,
180-
document,
181-
rootValue: { sync: () => 'hello' },
182-
});
179+
const result = await expectNoTracingActivity(executeChannel, () =>
180+
execute({
181+
schema,
182+
document,
183+
rootValue: { sync: () => 'hello' },
184+
}),
185+
);
183186
expect(result).to.deep.equal({ data: { sync: 'hello' } });
184187
});
185188
});
@@ -257,18 +260,19 @@ describe('subscribe diagnostics channel', () => {
257260
expect(active.events.map((e) => e.kind)).to.deep.equal(['start', 'end']);
258261
});
259262

260-
it('does nothing when no subscribers are attached', async () => {
263+
it('does not call tracing methods when no subscribers are attached', async () => {
261264
const document = parse('subscription { tick }');
262265

263-
const result = subscribe({
264-
schema,
265-
document,
266-
rootValue: { tick: twoTicks },
267-
});
268-
const resolved = isPromise(result) ? await result : result;
269-
if (isAsyncIterable(resolved)) {
266+
await expectNoTracingActivity(subscribeChannel, async () => {
267+
const result = subscribe({
268+
schema,
269+
document,
270+
rootValue: { tick: twoTicks },
271+
});
272+
const resolved = isPromise(result) ? await result : result;
273+
assert(isAsyncIterable(resolved));
270274
await resolved.return?.();
271-
}
275+
});
272276
});
273277
});
274278

@@ -449,14 +453,14 @@ describe('resolve diagnostics channel', () => {
449453
]);
450454
});
451455

452-
it('does nothing when no subscribers are attached', () => {
453-
const result = execute({
454-
schema,
455-
document: parse('{ sync }'),
456-
rootValue: { sync: () => 'hello' },
457-
});
458-
if (isPromise(result)) {
459-
throw new Error('expected sync');
460-
}
456+
it('does not call tracing methods when no subscribers are attached', async () => {
457+
const result = await expectNoTracingActivity(resolveChannel, () =>
458+
execute({
459+
schema,
460+
document: parse('{ sync }'),
461+
rootValue: { sync: () => 'hello' },
462+
}),
463+
);
464+
expect(result).to.deep.equal({ data: { sync: 'hello' } });
461465
});
462466
});

src/language/__tests__/tracing-test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { afterEach, describe, it } from 'mocha';
33

44
import {
55
collectEvents,
6+
expectNoTracingActivity,
67
getTracingChannel,
78
} from '../../__testUtils__/diagnosticsTestUtils.js';
89

@@ -39,8 +40,10 @@ describe('parse diagnostics channel', () => {
3940
expect(active.events[1].ctx.error).to.be.instanceOf(Error);
4041
});
4142

42-
it('does nothing when no subscribers are attached', () => {
43-
const doc = parse('{ field }');
43+
it('does not call tracing methods when no subscribers are attached', async () => {
44+
const doc = await expectNoTracingActivity(parseChannel, () =>
45+
parse('{ field }'),
46+
);
4447
expect(doc.kind).to.equal('Document');
4548
});
4649
});

src/validation/__tests__/tracing-test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { afterEach, describe, it } from 'mocha';
33

44
import {
55
collectEvents,
6+
expectNoTracingActivity,
67
getTracingChannel,
78
} from '../../__testUtils__/diagnosticsTestUtils.js';
89

@@ -64,8 +65,10 @@ describe('validate diagnostics channel', () => {
6465
expect(active.events[1].ctx.error).to.be.instanceOf(Error);
6566
});
6667

67-
it('does nothing when no subscribers are attached', () => {
68-
const errors = validate(schema, parse('{ field }'));
68+
it('does not call tracing methods when no subscribers are attached', async () => {
69+
const errors = await expectNoTracingActivity(validateChannel, () =>
70+
validate(schema, parse('{ field }')),
71+
);
6972
expect(errors).to.deep.equal([]);
7073
});
7174
});

0 commit comments

Comments
 (0)