|
1 | 1 | import { describe, expect, it, vi } from 'vitest'; |
2 | 2 | import { createTransport } from '../../../src/transports/base'; |
| 3 | +import type { ClientReport } from '../../../src/types-hoist/clientreport'; |
3 | 4 | import type { AttachmentItem, EventEnvelope, EventItem } from '../../../src/types-hoist/envelope'; |
4 | 5 | import type { TransportMakeRequestResponse } from '../../../src/types-hoist/transport'; |
| 6 | +import { createClientReportEnvelope } from '../../../src/utils/clientreport'; |
5 | 7 | import { createEnvelope, serializeEnvelope } from '../../../src/utils/envelope'; |
6 | | -import type { PromiseBuffer } from '../../../src/utils/promisebuffer'; |
| 8 | +import { type PromiseBuffer, SENTRY_BUFFER_FULL_ERROR } from '../../../src/utils/promisebuffer'; |
7 | 9 | import { resolvedSyncPromise } from '../../../src/utils/syncpromise'; |
8 | 10 |
|
9 | 11 | const ERROR_ENVELOPE = createEnvelope<EventEnvelope>({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [ |
@@ -31,6 +33,25 @@ const ATTACHMENT_ENVELOPE = createEnvelope<EventEnvelope>( |
31 | 33 | ], |
32 | 34 | ); |
33 | 35 |
|
| 36 | +const defaultDiscardedEvents: ClientReport['discarded_events'] = [ |
| 37 | + { |
| 38 | + reason: 'before_send', |
| 39 | + category: 'error', |
| 40 | + quantity: 30, |
| 41 | + }, |
| 42 | + { |
| 43 | + reason: 'network_error', |
| 44 | + category: 'transaction', |
| 45 | + quantity: 23, |
| 46 | + }, |
| 47 | +]; |
| 48 | + |
| 49 | +const CLIENT_REPORT_ENVELOPE = createClientReportEnvelope( |
| 50 | + defaultDiscardedEvents, |
| 51 | + |
| 52 | + 123456, |
| 53 | +); |
| 54 | + |
34 | 55 | const transportOptions = { |
35 | 56 | recordDroppedEvent: () => undefined, // noop |
36 | 57 | }; |
@@ -304,5 +325,71 @@ describe('createTransport', () => { |
304 | 325 | expect(recordDroppedEventCallback).not.toHaveBeenCalled(); |
305 | 326 | }); |
306 | 327 | }); |
| 328 | + |
| 329 | + describe('Client Reports', () => { |
| 330 | + it('should not record outcomes when client reports fail to send', async () => { |
| 331 | + expect.assertions(2); |
| 332 | + |
| 333 | + const mockRecordDroppedEventCallback = vi.fn(); |
| 334 | + |
| 335 | + const transport = createTransport({ recordDroppedEvent: mockRecordDroppedEventCallback }, req => { |
| 336 | + expect(req.body).toEqual(serializeEnvelope(CLIENT_REPORT_ENVELOPE)); |
| 337 | + return Promise.reject(new Error('Network error')); |
| 338 | + }); |
| 339 | + |
| 340 | + try { |
| 341 | + await transport.send(CLIENT_REPORT_ENVELOPE); |
| 342 | + } catch (e) { |
| 343 | + // Expected to throw |
| 344 | + } |
| 345 | + |
| 346 | + // recordDroppedEvent should NOT be called when a client report fails |
| 347 | + expect(mockRecordDroppedEventCallback).not.toHaveBeenCalled(); |
| 348 | + }); |
| 349 | + |
| 350 | + it('should not record outcomes when client reports fail due to buffer overflow', async () => { |
| 351 | + expect.assertions(2); |
| 352 | + |
| 353 | + const mockRecordDroppedEventCallback = vi.fn(); |
| 354 | + const mockBuffer: PromiseBuffer<TransportMakeRequestResponse> = { |
| 355 | + $: [], |
| 356 | + add: vi.fn(() => Promise.reject(SENTRY_BUFFER_FULL_ERROR)), |
| 357 | + drain: vi.fn(), |
| 358 | + }; |
| 359 | + |
| 360 | + const transport = createTransport( |
| 361 | + { recordDroppedEvent: mockRecordDroppedEventCallback }, |
| 362 | + _ => resolvedSyncPromise({}), |
| 363 | + mockBuffer, |
| 364 | + ); |
| 365 | + |
| 366 | + const result = await transport.send(CLIENT_REPORT_ENVELOPE); |
| 367 | + |
| 368 | + // Should resolve without throwing |
| 369 | + expect(result).toEqual({}); |
| 370 | + // recordDroppedEvent should NOT be called when a client report fails |
| 371 | + expect(mockRecordDroppedEventCallback).not.toHaveBeenCalled(); |
| 372 | + }); |
| 373 | + |
| 374 | + it('should record outcomes when regular events fail to send', async () => { |
| 375 | + expect.assertions(2); |
| 376 | + |
| 377 | + const mockRecordDroppedEventCallback = vi.fn(); |
| 378 | + |
| 379 | + const transport = createTransport({ recordDroppedEvent: mockRecordDroppedEventCallback }, req => { |
| 380 | + expect(req.body).toEqual(serializeEnvelope(ERROR_ENVELOPE)); |
| 381 | + return Promise.reject(new Error('Network error')); |
| 382 | + }); |
| 383 | + |
| 384 | + try { |
| 385 | + await transport.send(ERROR_ENVELOPE); |
| 386 | + } catch (e) { |
| 387 | + // Expected to throw |
| 388 | + } |
| 389 | + |
| 390 | + // recordDroppedEvent SHOULD be called for regular events |
| 391 | + expect(mockRecordDroppedEventCallback).toHaveBeenCalledWith('network_error', 'error'); |
| 392 | + }); |
| 393 | + }); |
307 | 394 | }); |
308 | 395 | }); |
0 commit comments