Skip to content

Commit e4aea85

Browse files
committed
test: add diagnostics_channel suppression coverage
Refs: #63623 Refs: #63651 Signed-off-by: Divyanshu Sharma <divyanshu88999@gmail.com>
1 parent a786501 commit e4aea85

1 file changed

Lines changed: 255 additions & 0 deletions

File tree

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const { channel, suppressed } = require('node:diagnostics_channel');
6+
const { AsyncLocalStorage } = require('async_hooks');
7+
8+
// Helper to run a function and capture whether a handler was called
9+
function makeHandler() {
10+
let called = false;
11+
const handler = (msg) => { called = true; };
12+
return {
13+
handler,
14+
called: () => called,
15+
reset: () => { called = false; }
16+
};
17+
}
18+
19+
// Test 1: Basic suppression - subscriber with suppressedBy is skipped inside suppressed()
20+
(function testBasicSuppression() {
21+
const key = Symbol('tracer');
22+
const ch = channel('test-suppression-basic');
23+
const h = makeHandler();
24+
ch.subscribe(h.handler, { suppressedBy: key });
25+
26+
suppressed(key, () => {
27+
ch.publish({});
28+
});
29+
30+
assert.strictEqual(h.called(), false, 'suppressed subscriber should not be called');
31+
// cleanup
32+
ch.unsubscribe(h.handler);
33+
})();
34+
35+
// Test 2: Non-opted subscriber fires even inside suppressed() scope
36+
(function testNonOptedFires() {
37+
const key = Symbol('tracer2');
38+
const ch = channel('test-suppression-nonopted');
39+
const h1 = makeHandler();
40+
const h2 = makeHandler();
41+
ch.subscribe(h1.handler, { suppressedBy: key });
42+
ch.subscribe(h2.handler); // no suppression
43+
44+
suppressed(key, () => {
45+
ch.publish({});
46+
});
47+
48+
assert.strictEqual(h1.called(), false, 'opted subscriber should be skipped');
49+
assert.strictEqual(h2.called(), true, 'non-opted subscriber should be called');
50+
51+
ch.unsubscribe(h1.handler);
52+
ch.unsubscribe(h2.handler);
53+
})();
54+
55+
// Test 3: Two APMs with different keys don't suppress each other
56+
(function testTwoKeysIndependent() {
57+
const k1 = Symbol('k1');
58+
const k2 = Symbol('k2');
59+
const ch = channel('test-suppression-two-keys');
60+
const h1 = makeHandler();
61+
const h2 = makeHandler();
62+
ch.subscribe(h1.handler, { suppressedBy: k1 });
63+
ch.subscribe(h2.handler, { suppressedBy: k2 });
64+
65+
suppressed(k1, () => {
66+
ch.publish({});
67+
});
68+
69+
assert.strictEqual(h1.called(), false);
70+
assert.strictEqual(h2.called(), true);
71+
72+
h1.reset(); h2.reset();
73+
74+
suppressed(k2, () => {
75+
ch.publish({});
76+
});
77+
78+
assert.strictEqual(h1.called(), true);
79+
assert.strictEqual(h2.called(), false);
80+
81+
ch.unsubscribe(h1.handler);
82+
ch.unsubscribe(h2.handler);
83+
})();
84+
85+
// Test 4: Nested suppressed() calls (same key, different keys)
86+
(function testNestedSuppressed() {
87+
const k1 = Symbol('nested1');
88+
const k2 = Symbol('nested2');
89+
const ch = channel('test-suppression-nested');
90+
const h1 = makeHandler();
91+
const h2 = makeHandler();
92+
ch.subscribe(h1.handler, { suppressedBy: k1 });
93+
ch.subscribe(h2.handler, { suppressedBy: k2 });
94+
95+
suppressed(k1, () => {
96+
// inside k1, h1 skipped, h2 runs
97+
ch.publish({});
98+
assert.strictEqual(h1.called(), false);
99+
assert.strictEqual(h2.called(), true);
100+
h2.reset();
101+
102+
suppressed(k2, () => {
103+
// inside both, both skipped
104+
ch.publish({});
105+
assert.strictEqual(h1.called(), false);
106+
assert.strictEqual(h2.called(), false);
107+
});
108+
109+
// back to only k1
110+
ch.publish({});
111+
assert.strictEqual(h1.called(), false);
112+
assert.strictEqual(h2.called(), true);
113+
});
114+
115+
ch.unsubscribe(h1.handler);
116+
ch.unsubscribe(h2.handler);
117+
})();
118+
119+
// Test 5: suppressed() across a Promise boundary (async/await)
120+
(async function testSuppressedAcrossPromise() {
121+
const key = Symbol('promise');
122+
const ch = channel('test-suppression-promise');
123+
const h = makeHandler();
124+
ch.subscribe(h.handler, { suppressedBy: key });
125+
126+
await suppressed(key, async () => {
127+
await Promise.resolve();
128+
ch.publish({});
129+
});
130+
131+
assert.strictEqual(h.called(), false);
132+
ch.unsubscribe(h.handler);
133+
})();
134+
135+
// Test 6: suppressed() across setImmediate and queueMicrotask
136+
(async function testSuppressedAcrossTimers() {
137+
const key = Symbol('timers');
138+
const ch = channel('test-suppression-timers');
139+
const h = makeHandler();
140+
ch.subscribe(h.handler, { suppressedBy: key });
141+
142+
await suppressed(key, async () => {
143+
await new Promise((resolve) => {
144+
setImmediate(() => {
145+
ch.publish({});
146+
assert.strictEqual(h.called(), false);
147+
h.reset();
148+
149+
queueMicrotask(() => {
150+
ch.publish({});
151+
assert.strictEqual(h.called(), false);
152+
153+
ch.unsubscribe(h.handler);
154+
resolve();
155+
});
156+
});
157+
});
158+
});
159+
})();
160+
161+
// Test 7: unsubscribe() works correctly after using suppressedBy
162+
(function testUnsubscribeCleansUp() {
163+
const key = Symbol('unsub');
164+
const ch = channel('test-suppression-unsubscribe');
165+
const h = makeHandler();
166+
ch.subscribe(h.handler, { suppressedBy: key });
167+
ch.unsubscribe(h.handler);
168+
169+
// Should not throw and should not be called
170+
suppressed(key, () => {
171+
ch.publish({});
172+
});
173+
174+
assert.strictEqual(h.called(), false);
175+
})();
176+
177+
// Test 8: bindStore with suppressedBy is skipped inside suppressed()
178+
(function testBindStoreSuppression() {
179+
const key = Symbol('store');
180+
const ch = channel('test-suppression-store');
181+
const als = new AsyncLocalStorage();
182+
183+
let transformCalls = 0;
184+
const handler = common.mustCall(() => {
185+
assert.strictEqual(als.getStore(), undefined);
186+
});
187+
188+
ch.subscribe(handler);
189+
ch.bindStore(als, (d) => {
190+
transformCalls++;
191+
return { foo: d };
192+
}, { suppressedBy: key });
193+
194+
suppressed(key, () => {
195+
ch.publish({});
196+
});
197+
198+
assert.strictEqual(transformCalls, 0);
199+
ch.unsubscribe(handler);
200+
ch.unbindStore(als);
201+
})();
202+
203+
// Test 9: Wrong type for suppressedBy throws ERR_INVALID_ARG_TYPE
204+
(function testWrongTypeThrows() {
205+
const ch = channel('test-suppression-wrong-type');
206+
const bad = 'not-allowed';
207+
assert.throws(() => ch.subscribe(() => {}, { suppressedBy: bad }), {
208+
name: 'TypeError'
209+
});
210+
const als = new AsyncLocalStorage();
211+
assert.throws(() => ch.bindStore(als, (d) => d, { suppressedBy: bad }), {
212+
name: 'TypeError'
213+
});
214+
})();
215+
216+
// Test 10: suppressed() return value passes through fn's return value
217+
(function testSuppressedReturnValueAndContext() {
218+
const key = Symbol('return');
219+
const receiver = { value: 41 };
220+
const result = suppressed(key, function(a, b) {
221+
assert.strictEqual(this, receiver);
222+
assert.strictEqual(a, 'a');
223+
assert.strictEqual(b, 'b');
224+
return this.value + 1;
225+
}, receiver, 'a', 'b');
226+
assert.strictEqual(result, 42);
227+
})();
228+
229+
// Test 11: null suppressedBy behaves like no suppression opt-in
230+
(function testNullSuppressedByIsIgnored() {
231+
const key = Symbol('null-suppressed-by');
232+
const ch = channel('test-suppression-null-suppressed-by');
233+
const h = makeHandler();
234+
235+
ch.subscribe(h.handler, { suppressedBy: null });
236+
237+
suppressed(key, () => {
238+
ch.publish({});
239+
});
240+
241+
assert.strictEqual(h.called(), true);
242+
ch.unsubscribe(h.handler);
243+
})();
244+
245+
// Test 12: suppressed() rejects null/undefined keys consistently
246+
(function testKeyValidation() {
247+
assert.throws(() => suppressed(null, () => {}), {
248+
name: 'TypeError'
249+
});
250+
assert.throws(() => suppressed(undefined, () => {}), {
251+
name: 'TypeError'
252+
});
253+
})();
254+
255+
console.log('ok - diagnostics_channel suppression tests loaded');

0 commit comments

Comments
 (0)