|
1 | 1 | import * as sentryCore from '@sentry/core'; |
2 | 2 | import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; |
| 3 | +import { makeFlushLock } from '../src/flush'; |
3 | 4 | import { getInstrumented } from '../src/instrument'; |
4 | 5 | import * as sdk from '../src/sdk'; |
5 | 6 | import { wrapMethodWithSentry } from '../src/wrapMethodWithSentry'; |
@@ -694,3 +695,53 @@ describe('wrapMethodWithSentry', () => { |
694 | 695 | }); |
695 | 696 | }); |
696 | 697 | }); |
| 698 | + |
| 699 | +describe('wrapMethodWithSentry waitUntil teardown (hibernation regression)', () => { |
| 700 | + beforeEach(() => { |
| 701 | + vi.clearAllMocks(); |
| 702 | + }); |
| 703 | + |
| 704 | + afterEach(() => { |
| 705 | + vi.restoreAllMocks(); |
| 706 | + }); |
| 707 | + |
| 708 | + // Regression for #22328 |
| 709 | + it('does not deadlock teardown against a concurrent waitUntil task', async () => { |
| 710 | + const waitUntilPromises: Array<Promise<unknown>> = []; |
| 711 | + const context = { |
| 712 | + waitUntil: vi.fn((promise: Promise<unknown>) => { |
| 713 | + waitUntilPromises.push(promise); |
| 714 | + }), |
| 715 | + } as unknown as ExecutionContext; |
| 716 | + |
| 717 | + // A prior invocation instrumented context.waitUntil |
| 718 | + // (installs the flush lock) |
| 719 | + const lock = makeFlushLock(context); |
| 720 | + |
| 721 | + // A concurrent, in-flight waitUntil task holds the flush lock |
| 722 | + let resolveUserTask!: () => void; |
| 723 | + const userTask = new Promise<void>(resolve => { |
| 724 | + resolveUserTask = resolve; |
| 725 | + }); |
| 726 | + context.waitUntil(userTask); |
| 727 | + |
| 728 | + // flush waits for the flush lock to drain. |
| 729 | + mocks.flush.mockImplementationOnce(async () => { |
| 730 | + await lock.finalize(); |
| 731 | + return true; |
| 732 | + }); |
| 733 | + |
| 734 | + const wrapped = wrapMethodWithSentry( |
| 735 | + { options: { dsn: 'https://test@sentry.io/123' }, context, spanName: 'webSocketMessage' }, |
| 736 | + vi.fn().mockResolvedValue('ok'), |
| 737 | + ); |
| 738 | + |
| 739 | + await wrapped(); |
| 740 | + |
| 741 | + // Releasing the concurrent task drains the lock |
| 742 | + resolveUserTask(); |
| 743 | + |
| 744 | + await expect(Promise.all(waitUntilPromises)).resolves.toBeDefined(); |
| 745 | + expect(mocks.flush).toHaveBeenCalled(); |
| 746 | + }); |
| 747 | +}); |
0 commit comments