|
|
@@ -115,4 +115,49 @@ describe('session utilities', () => {
|
|
|
'Session abort timed out after 5ms',
|
|
|
);
|
|
|
});
|
|
|
+
|
|
|
+ test('promptWithTimeout handles late prompt rejection without unhandled rejection', async () => {
|
|
|
+ let deferredReject: ((error: Error) => void) | undefined;
|
|
|
+ const prompt = mock(
|
|
|
+ () =>
|
|
|
+ new Promise<never>((_resolve, reject) => {
|
|
|
+ deferredReject = reject;
|
|
|
+ }),
|
|
|
+ );
|
|
|
+ const abort = mock(async () => ({}));
|
|
|
+ const client = {
|
|
|
+ session: { abort, prompt },
|
|
|
+ } as any;
|
|
|
+
|
|
|
+ let unhandledRejection: Error | null = null;
|
|
|
+ const handler = (err: Error) => {
|
|
|
+ unhandledRejection = err;
|
|
|
+ };
|
|
|
+ process.on('unhandledRejection', handler);
|
|
|
+ try {
|
|
|
+ await expect(
|
|
|
+ promptWithTimeout(
|
|
|
+ client,
|
|
|
+ { path: { id: 's1' }, body: { parts: [] } },
|
|
|
+ 5,
|
|
|
+ ),
|
|
|
+ ).rejects.toThrow('Prompt timed out after 5ms');
|
|
|
+
|
|
|
+ // Timeout behavior is unchanged — abort is called
|
|
|
+ expect(abort).toHaveBeenCalledWith({ path: { id: 's1' } });
|
|
|
+
|
|
|
+ // Simulate a late provider response arriving after timeout
|
|
|
+ if (deferredReject) {
|
|
|
+ deferredReject(new Error('provider error after timeout'));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Yield to the microtask queue so the catch handler runs
|
|
|
+ await new Promise<void>((resolve) => setTimeout(resolve, 0));
|
|
|
+
|
|
|
+ // No unhandled rejection should surface
|
|
|
+ expect(unhandledRejection).toBeNull();
|
|
|
+ } finally {
|
|
|
+ process.off('unhandledRejection', handler);
|
|
|
+ }
|
|
|
+ });
|
|
|
});
|