|
1 |
| -import type { Debugger, InspectorNotification } from 'inspector'; |
2 |
| - |
3 |
| -import { NodeClient, defaultStackParser } from '../../src'; |
4 | 1 | import { createRateLimiter } from '../../src/integrations/local-variables/common';
|
5 |
| -import type { FrameVariables } from '../../src/integrations/local-variables/common'; |
6 |
| -import type { DebugSession } from '../../src/integrations/local-variables/local-variables-sync'; |
7 |
| -import { LocalVariablesSync, createCallbackList } from '../../src/integrations/local-variables/local-variables-sync'; |
| 2 | +import { createCallbackList } from '../../src/integrations/local-variables/local-variables-sync'; |
8 | 3 | import { NODE_VERSION } from '../../src/nodeVersion';
|
9 |
| -import { getDefaultNodeClientOptions } from '../../test/helper/node-client-options'; |
10 | 4 |
|
11 | 5 | jest.setTimeout(20_000);
|
12 | 6 |
|
13 | 7 | const describeIf = (condition: boolean) => (condition ? describe : describe.skip);
|
14 | 8 |
|
15 |
| -interface ThrowOn { |
16 |
| - configureAndConnect?: boolean; |
17 |
| - getLocalVariables?: boolean; |
18 |
| -} |
19 |
| - |
20 |
| -class MockDebugSession implements DebugSession { |
21 |
| - private _onPause?: (message: InspectorNotification<Debugger.PausedEventDataType>, callback: () => void) => void; |
22 |
| - |
23 |
| - constructor(private readonly _vars: Record<string, Record<string, unknown>>, private readonly _throwOn?: ThrowOn) {} |
24 |
| - |
25 |
| - public configureAndConnect( |
26 |
| - onPause: (message: InspectorNotification<Debugger.PausedEventDataType>, callback: () => void) => void, |
27 |
| - _captureAll: boolean, |
28 |
| - ): void { |
29 |
| - if (this._throwOn?.configureAndConnect) { |
30 |
| - throw new Error('configureAndConnect should not be called'); |
31 |
| - } |
32 |
| - |
33 |
| - this._onPause = onPause; |
34 |
| - } |
35 |
| - |
36 |
| - public setPauseOnExceptions(_: boolean): void {} |
37 |
| - |
38 |
| - public getLocalVariables(objectId: string, callback: (vars: Record<string, unknown>) => void): void { |
39 |
| - if (this._throwOn?.getLocalVariables) { |
40 |
| - throw new Error('getLocalVariables should not be called'); |
41 |
| - } |
42 |
| - |
43 |
| - callback(this._vars[objectId]); |
44 |
| - } |
45 |
| - |
46 |
| - public runPause(message: InspectorNotification<Debugger.PausedEventDataType>): Promise<void> { |
47 |
| - return new Promise(resolve => { |
48 |
| - this._onPause?.(message, resolve); |
49 |
| - }); |
50 |
| - } |
51 |
| -} |
52 |
| - |
53 |
| -interface LocalVariablesPrivate { |
54 |
| - _getCachedFramesCount(): number; |
55 |
| - _getFirstCachedFrame(): FrameVariables[] | undefined; |
56 |
| -} |
57 |
| - |
58 |
| -const exceptionEvent = { |
59 |
| - method: 'Debugger.paused', |
60 |
| - params: { |
61 |
| - reason: 'exception', |
62 |
| - data: { |
63 |
| - description: |
64 |
| - 'Error: Some error\n' + |
65 |
| - ' at two (/dist/javascript/src/main.js:23:9)\n' + |
66 |
| - ' at one (/dist/javascript/src/main.js:19:3)\n' + |
67 |
| - ' at Timeout._onTimeout (/dist/javascript/src/main.js:40:5)\n' + |
68 |
| - ' at listOnTimeout (node:internal/timers:559:17)\n' + |
69 |
| - ' at process.processTimers (node:internal/timers:502:7)', |
70 |
| - }, |
71 |
| - callFrames: [ |
72 |
| - { |
73 |
| - callFrameId: '-6224981551105448869.1.0', |
74 |
| - functionName: 'two', |
75 |
| - location: { scriptId: '134', lineNumber: 22 }, |
76 |
| - url: '', |
77 |
| - scopeChain: [ |
78 |
| - { |
79 |
| - type: 'local', |
80 |
| - object: { |
81 |
| - type: 'object', |
82 |
| - className: 'Object', |
83 |
| - objectId: '-6224981551105448869.1.2', |
84 |
| - }, |
85 |
| - name: 'two', |
86 |
| - }, |
87 |
| - ], |
88 |
| - this: { |
89 |
| - type: 'object', |
90 |
| - className: 'global', |
91 |
| - }, |
92 |
| - }, |
93 |
| - { |
94 |
| - callFrameId: '-6224981551105448869.1.1', |
95 |
| - functionName: 'one', |
96 |
| - location: { scriptId: '134', lineNumber: 18 }, |
97 |
| - url: '', |
98 |
| - scopeChain: [ |
99 |
| - { |
100 |
| - type: 'local', |
101 |
| - object: { |
102 |
| - type: 'object', |
103 |
| - className: 'Object', |
104 |
| - objectId: '-6224981551105448869.1.6', |
105 |
| - }, |
106 |
| - name: 'one', |
107 |
| - }, |
108 |
| - ], |
109 |
| - this: { |
110 |
| - type: 'object', |
111 |
| - className: 'global', |
112 |
| - }, |
113 |
| - }, |
114 |
| - ], |
115 |
| - }, |
116 |
| -}; |
117 |
| - |
118 |
| -const exceptionEvent100Frames = { |
119 |
| - method: 'Debugger.paused', |
120 |
| - params: { |
121 |
| - reason: 'exception', |
122 |
| - data: { |
123 |
| - description: |
124 |
| - 'Error: Some error\n' + |
125 |
| - ' at two (/dist/javascript/src/main.js:23:9)\n' + |
126 |
| - ' at one (/dist/javascript/src/main.js:19:3)\n' + |
127 |
| - ' at Timeout._onTimeout (/dist/javascript/src/main.js:40:5)\n' + |
128 |
| - ' at listOnTimeout (node:internal/timers:559:17)\n' + |
129 |
| - ' at process.processTimers (node:internal/timers:502:7)', |
130 |
| - }, |
131 |
| - callFrames: new Array(100).fill({ |
132 |
| - callFrameId: '-6224981551105448869.1.0', |
133 |
| - functionName: 'two', |
134 |
| - location: { scriptId: '134', lineNumber: 22 }, |
135 |
| - url: '', |
136 |
| - scopeChain: [ |
137 |
| - { |
138 |
| - type: 'local', |
139 |
| - object: { |
140 |
| - type: 'object', |
141 |
| - className: 'Object', |
142 |
| - objectId: '-6224981551105448869.1.2', |
143 |
| - }, |
144 |
| - name: 'two', |
145 |
| - }, |
146 |
| - ], |
147 |
| - this: { |
148 |
| - type: 'object', |
149 |
| - className: 'global', |
150 |
| - }, |
151 |
| - }), |
152 |
| - }, |
153 |
| -}; |
154 |
| - |
155 | 9 | describeIf(NODE_VERSION.major >= 18)('LocalVariables', () => {
|
156 |
| - it('Adds local variables to stack frames', async () => { |
157 |
| - const session = new MockDebugSession({ |
158 |
| - '-6224981551105448869.1.2': { name: 'tim' }, |
159 |
| - '-6224981551105448869.1.6': { arr: [1, 2, 3] }, |
160 |
| - }); |
161 |
| - const localVariables = new LocalVariablesSync({}, session); |
162 |
| - const options = getDefaultNodeClientOptions({ |
163 |
| - stackParser: defaultStackParser, |
164 |
| - includeLocalVariables: true, |
165 |
| - integrations: [], |
166 |
| - }); |
167 |
| - |
168 |
| - const client = new NodeClient(options); |
169 |
| - client.addIntegration(localVariables); |
170 |
| - |
171 |
| - const eventProcessors = client['_eventProcessors']; |
172 |
| - const eventProcessor = eventProcessors.find(processor => processor.id === 'LocalVariables'); |
173 |
| - |
174 |
| - expect(eventProcessor).toBeDefined(); |
175 |
| - |
176 |
| - await session.runPause(exceptionEvent); |
177 |
| - |
178 |
| - expect((localVariables as unknown as LocalVariablesPrivate)._getCachedFramesCount()).toBe(1); |
179 |
| - |
180 |
| - const frames = (localVariables as unknown as LocalVariablesPrivate)._getFirstCachedFrame(); |
181 |
| - |
182 |
| - expect(frames).toBeDefined(); |
183 |
| - |
184 |
| - const vars = frames as FrameVariables[]; |
185 |
| - |
186 |
| - expect(vars).toEqual([ |
187 |
| - { function: 'two', vars: { name: 'tim' } }, |
188 |
| - { function: 'one', vars: { arr: [1, 2, 3] } }, |
189 |
| - ]); |
190 |
| - |
191 |
| - const event = await eventProcessor!( |
192 |
| - { |
193 |
| - event_id: '9cbf882ade9a415986632ac4e16918eb', |
194 |
| - platform: 'node', |
195 |
| - timestamp: 1671113680.306, |
196 |
| - level: 'fatal', |
197 |
| - exception: { |
198 |
| - values: [ |
199 |
| - { |
200 |
| - type: 'Error', |
201 |
| - value: 'Some error', |
202 |
| - stacktrace: { |
203 |
| - frames: [ |
204 |
| - { |
205 |
| - function: 'process.processTimers', |
206 |
| - lineno: 502, |
207 |
| - colno: 7, |
208 |
| - in_app: false, |
209 |
| - }, |
210 |
| - { |
211 |
| - function: 'listOnTimeout', |
212 |
| - lineno: 559, |
213 |
| - colno: 17, |
214 |
| - in_app: false, |
215 |
| - }, |
216 |
| - { |
217 |
| - function: 'Timeout._onTimeout', |
218 |
| - lineno: 40, |
219 |
| - colno: 5, |
220 |
| - in_app: true, |
221 |
| - }, |
222 |
| - { |
223 |
| - function: 'one', |
224 |
| - lineno: 19, |
225 |
| - colno: 3, |
226 |
| - in_app: true, |
227 |
| - }, |
228 |
| - { |
229 |
| - function: 'two', |
230 |
| - lineno: 23, |
231 |
| - colno: 9, |
232 |
| - in_app: true, |
233 |
| - }, |
234 |
| - ], |
235 |
| - }, |
236 |
| - mechanism: { type: 'generic', handled: true }, |
237 |
| - }, |
238 |
| - ], |
239 |
| - }, |
240 |
| - }, |
241 |
| - {}, |
242 |
| - ); |
243 |
| - |
244 |
| - expect(event?.exception?.values?.[0].stacktrace?.frames?.[3]?.vars).toEqual({ arr: [1, 2, 3] }); |
245 |
| - expect(event?.exception?.values?.[0].stacktrace?.frames?.[4]?.vars).toEqual({ name: 'tim' }); |
246 |
| - |
247 |
| - expect((localVariables as unknown as LocalVariablesPrivate)._getCachedFramesCount()).toBe(0); |
248 |
| - }); |
249 |
| - |
250 |
| - it('Only considers the first 5 frames', async () => { |
251 |
| - const session = new MockDebugSession({}); |
252 |
| - const localVariables = new LocalVariablesSync({}, session); |
253 |
| - const options = getDefaultNodeClientOptions({ |
254 |
| - stackParser: defaultStackParser, |
255 |
| - includeLocalVariables: true, |
256 |
| - integrations: [], |
257 |
| - }); |
258 |
| - |
259 |
| - const client = new NodeClient(options); |
260 |
| - client.addIntegration(localVariables); |
261 |
| - |
262 |
| - await session.runPause(exceptionEvent100Frames); |
263 |
| - |
264 |
| - expect((localVariables as unknown as LocalVariablesPrivate)._getCachedFramesCount()).toBe(1); |
265 |
| - |
266 |
| - const frames = (localVariables as unknown as LocalVariablesPrivate)._getFirstCachedFrame(); |
267 |
| - |
268 |
| - expect(frames).toBeDefined(); |
269 |
| - |
270 |
| - const vars = frames as FrameVariables[]; |
271 |
| - |
272 |
| - expect(vars.length).toEqual(5); |
273 |
| - }); |
274 |
| - |
275 |
| - it('Should not lookup variables for non-exception reasons', async () => { |
276 |
| - const session = new MockDebugSession({}, { getLocalVariables: true }); |
277 |
| - const localVariables = new LocalVariablesSync({}, session); |
278 |
| - const options = getDefaultNodeClientOptions({ |
279 |
| - stackParser: defaultStackParser, |
280 |
| - includeLocalVariables: true, |
281 |
| - integrations: [], |
282 |
| - }); |
283 |
| - |
284 |
| - const client = new NodeClient(options); |
285 |
| - client.addIntegration(localVariables); |
286 |
| - |
287 |
| - const nonExceptionEvent = { |
288 |
| - method: exceptionEvent.method, |
289 |
| - params: { ...exceptionEvent.params, reason: 'non-exception-reason' }, |
290 |
| - }; |
291 |
| - |
292 |
| - await session.runPause(nonExceptionEvent); |
293 |
| - |
294 |
| - expect((localVariables as unknown as LocalVariablesPrivate)._getCachedFramesCount()).toBe(0); |
295 |
| - }); |
296 |
| - |
297 |
| - it('Should not initialize when disabled', async () => { |
298 |
| - const session = new MockDebugSession({}, { configureAndConnect: true }); |
299 |
| - const localVariables = new LocalVariablesSync({}, session); |
300 |
| - const options = getDefaultNodeClientOptions({ |
301 |
| - stackParser: defaultStackParser, |
302 |
| - integrations: [], |
303 |
| - }); |
304 |
| - |
305 |
| - const client = new NodeClient(options); |
306 |
| - client.addIntegration(localVariables); |
307 |
| - |
308 |
| - const eventProcessors = client['_eventProcessors']; |
309 |
| - const eventProcessor = eventProcessors.find(processor => processor.id === 'LocalVariables'); |
310 |
| - |
311 |
| - expect(eventProcessor).toBeDefined(); |
312 |
| - }); |
313 |
| - |
314 |
| - it('Should not initialize when inspector not loaded', async () => { |
315 |
| - const localVariables = new LocalVariablesSync({}, undefined); |
316 |
| - const options = getDefaultNodeClientOptions({ |
317 |
| - stackParser: defaultStackParser, |
318 |
| - integrations: [], |
319 |
| - }); |
320 |
| - |
321 |
| - const client = new NodeClient(options); |
322 |
| - client.addIntegration(localVariables); |
323 |
| - |
324 |
| - const eventProcessors = client['_eventProcessors']; |
325 |
| - const eventProcessor = eventProcessors.find(processor => processor.id === 'LocalVariables'); |
326 |
| - |
327 |
| - expect(eventProcessor).toBeDefined(); |
328 |
| - }); |
329 |
| - |
330 |
| - it('Should cache identical uncaught exception events', async () => { |
331 |
| - const session = new MockDebugSession({ |
332 |
| - '-6224981551105448869.1.2': { name: 'tim' }, |
333 |
| - '-6224981551105448869.1.6': { arr: [1, 2, 3] }, |
334 |
| - }); |
335 |
| - const localVariables = new LocalVariablesSync({}, session); |
336 |
| - const options = getDefaultNodeClientOptions({ |
337 |
| - stackParser: defaultStackParser, |
338 |
| - includeLocalVariables: true, |
339 |
| - integrations: [], |
340 |
| - }); |
341 |
| - |
342 |
| - const client = new NodeClient(options); |
343 |
| - client.addIntegration(localVariables); |
344 |
| - |
345 |
| - await session.runPause(exceptionEvent); |
346 |
| - await session.runPause(exceptionEvent); |
347 |
| - await session.runPause(exceptionEvent); |
348 |
| - await session.runPause(exceptionEvent); |
349 |
| - await session.runPause(exceptionEvent); |
350 |
| - |
351 |
| - expect((localVariables as unknown as LocalVariablesPrivate)._getCachedFramesCount()).toBe(1); |
352 |
| - }); |
353 |
| - |
354 | 10 | describe('createCallbackList', () => {
|
355 | 11 | it('Should call callbacks in reverse order', done => {
|
356 | 12 | const log: number[] = [];
|
|
0 commit comments