@@ -5,7 +5,18 @@ import { EmailChannel, type EmailChannelConfig } from "../email.ts";
55const mockConnect = mock ( ( ) => Promise . resolve ( ) ) ;
66const mockLogout = mock ( ( ) => Promise . resolve ( ) ) ;
77const mockGetMailboxLock = mock ( ( ) => Promise . resolve ( { release : ( ) => { } } ) ) ;
8- const mockIdle = mock ( ( ) => new Promise ( ( ) => { } ) ) ;
8+ const mockIdle = mock (
9+ ( opts ?: { abort ?: AbortSignal } ) =>
10+ new Promise < void > ( ( _resolve , reject ) => {
11+ if ( opts ?. abort ) {
12+ if ( opts . abort . aborted ) {
13+ reject ( new Error ( "abort" ) ) ;
14+ return ;
15+ }
16+ opts . abort . addEventListener ( "abort" , ( ) => reject ( new Error ( "abort" ) ) , { once : true } ) ;
17+ }
18+ } ) ,
19+ ) ;
920const mockFetch = mock ( function * ( ) {
1021 // Empty generator - no unread messages
1122} ) ;
@@ -141,6 +152,34 @@ describe("EmailChannel", () => {
141152 expect ( callArgs . text ) . toBe ( "Plain text content" ) ;
142153 } ) ;
143154
155+ test ( "disconnect awaits IDLE loop before logout" , async ( ) => {
156+ const channel = new EmailChannel ( testConfig ) ;
157+ await channel . connect ( ) ;
158+
159+ // disconnect should complete without hanging — the IDLE loop
160+ // must terminate before logout is called
161+ await channel . disconnect ( ) ;
162+
163+ // Verify logout was called (meaning IDLE loop finished first)
164+ expect ( mockLogout ) . toHaveBeenCalledTimes ( 1 ) ;
165+ expect ( channel . isConnected ( ) ) . toBe ( false ) ;
166+ } ) ;
167+
168+ test ( "rapid disconnect and reconnect does not leak IDLE loops" , async ( ) => {
169+ const channel = new EmailChannel ( testConfig ) ;
170+ await channel . connect ( ) ;
171+
172+ await channel . disconnect ( ) ;
173+ mockGetMailboxLock . mockClear ( ) ;
174+
175+ // Reconnect should work cleanly without competing for the lock
176+ await channel . connect ( ) ;
177+ expect ( channel . isConnected ( ) ) . toBe ( true ) ;
178+ expect ( mockGetMailboxLock ) . toHaveBeenCalledTimes ( 1 ) ;
179+
180+ await channel . disconnect ( ) ;
181+ } ) ;
182+
144183 test ( "send generates unique message ID" , async ( ) => {
145184 const channel = new EmailChannel ( testConfig ) ;
146185 await channel . connect ( ) ;
0 commit comments