11import { afterEach , beforeEach , describe , expect , it , vi } from 'vitest' ;
22
3- import type { SafeLockReturn } from '../safeLock' ;
43import { SessionCookiePoller } from '../SessionCookiePoller' ;
54
65describe ( 'SessionCookiePoller' , ( ) => {
@@ -13,132 +12,129 @@ describe('SessionCookiePoller', () => {
1312 vi . restoreAllMocks ( ) ;
1413 } ) ;
1514
16- describe ( 'shared lock coordination' , ( ) => {
17- it ( 'accepts an external lock for coordination with other components' , ( ) => {
18- const sharedLock : SafeLockReturn = {
19- acquireLockAndRun : vi . fn ( ) . mockResolvedValue ( undefined ) ,
20- } ;
21-
22- const poller = new SessionCookiePoller ( sharedLock ) ;
15+ describe ( 'startPollingForSessionToken' , ( ) => {
16+ it ( 'executes callback immediately on start' , async ( ) => {
17+ const poller = new SessionCookiePoller ( ) ;
2318 const callback = vi . fn ( ) . mockResolvedValue ( undefined ) ;
2419
2520 poller . startPollingForSessionToken ( callback ) ;
2621
27- // Verify the shared lock is used
28- expect ( sharedLock . acquireLockAndRun ) . toHaveBeenCalledWith ( callback ) ;
22+ // Flush microtasks to let the async run() execute
23+ await Promise . resolve ( ) ;
24+
25+ expect ( callback ) . toHaveBeenCalledTimes ( 1 ) ;
2926
3027 poller . stopPollingForSessionToken ( ) ;
3128 } ) ;
3229
33- it ( 'creates internal lock when none provided (backward compatible)' , ( ) => {
34- // Should not throw when no lock is provided
30+ it ( 'prevents multiple concurrent polling sessions' , async ( ) => {
3531 const poller = new SessionCookiePoller ( ) ;
36- expect ( poller ) . toBeInstanceOf ( SessionCookiePoller ) ;
37- } ) ;
38-
39- it ( 'enables focus handler and poller to share the same lock' , ( ) => {
40- // This test demonstrates the shared lock pattern used in AuthCookieService
41- const sharedLock : SafeLockReturn = {
42- acquireLockAndRun : vi . fn ( ) . mockImplementation ( async ( cb : ( ) => Promise < unknown > ) => {
43- return cb ( ) ;
44- } ) ,
45- } ;
46-
47- const poller = new SessionCookiePoller ( sharedLock ) ;
48- const pollerCallback = vi . fn ( ) . mockResolvedValue ( 'poller-result' ) ;
32+ const callback = vi . fn ( ) . mockResolvedValue ( undefined ) ;
4933
50- // Poller uses the shared lock
51- poller . startPollingForSessionToken ( pollerCallback ) ;
34+ poller . startPollingForSessionToken ( callback ) ;
35+ poller . startPollingForSessionToken ( callback ) ; // Second call should be ignored
5236
53- // Simulate focus handler also using the shared lock (like AuthCookieService does)
54- const focusCallback = vi . fn ( ) . mockResolvedValue ( 'focus-result' ) ;
55- void sharedLock . acquireLockAndRun ( focusCallback ) ;
37+ await Promise . resolve ( ) ;
5638
57- // Both should use the same lock instance
58- expect ( sharedLock . acquireLockAndRun ) . toHaveBeenCalledTimes ( 2 ) ;
59- expect ( sharedLock . acquireLockAndRun ) . toHaveBeenCalledWith ( pollerCallback ) ;
60- expect ( sharedLock . acquireLockAndRun ) . toHaveBeenCalledWith ( focusCallback ) ;
39+ expect ( callback ) . toHaveBeenCalledTimes ( 1 ) ;
6140
6241 poller . stopPollingForSessionToken ( ) ;
6342 } ) ;
6443 } ) ;
6544
66- describe ( 'startPollingForSessionToken' , ( ) => {
67- it ( 'executes callback immediately on start' , ( ) => {
68- const sharedLock : SafeLockReturn = {
69- acquireLockAndRun : vi . fn ( ) . mockResolvedValue ( undefined ) ,
70- } ;
71-
72- const poller = new SessionCookiePoller ( sharedLock ) ;
45+ describe ( 'stopPollingForSessionToken' , ( ) => {
46+ it ( 'stops polling when called' , async ( ) => {
47+ const poller = new SessionCookiePoller ( ) ;
7348 const callback = vi . fn ( ) . mockResolvedValue ( undefined ) ;
7449
7550 poller . startPollingForSessionToken ( callback ) ;
51+ await Promise . resolve ( ) ;
7652
77- expect ( sharedLock . acquireLockAndRun ) . toHaveBeenCalledWith ( callback ) ;
53+ expect ( callback ) . toHaveBeenCalledTimes ( 1 ) ;
7854
7955 poller . stopPollingForSessionToken ( ) ;
80- } ) ;
81-
82- it ( 'prevents multiple concurrent polling sessions' , ( ) => {
83- const sharedLock : SafeLockReturn = {
84- acquireLockAndRun : vi . fn ( ) . mockResolvedValue ( undefined ) ,
85- } ;
86-
87- const poller = new SessionCookiePoller ( sharedLock ) ;
88- const callback = vi . fn ( ) . mockResolvedValue ( undefined ) ;
8956
90- poller . startPollingForSessionToken ( callback ) ;
91- poller . startPollingForSessionToken ( callback ) ; // Second call should be ignored
92-
93- expect ( sharedLock . acquireLockAndRun ) . toHaveBeenCalledTimes ( 1 ) ;
57+ // Advance time - callback should not be called again
58+ await vi . advanceTimersByTimeAsync ( 10000 ) ;
9459
95- poller . stopPollingForSessionToken ( ) ;
60+ expect ( callback ) . toHaveBeenCalledTimes ( 1 ) ;
9661 } ) ;
97- } ) ;
9862
99- describe ( 'stopPollingForSessionToken' , ( ) => {
10063 it ( 'allows restart after stop' , async ( ) => {
101- const sharedLock : SafeLockReturn = {
102- acquireLockAndRun : vi . fn ( ) . mockResolvedValue ( undefined ) ,
103- } ;
104-
105- const poller = new SessionCookiePoller ( sharedLock ) ;
64+ const poller = new SessionCookiePoller ( ) ;
10665 const callback = vi . fn ( ) . mockResolvedValue ( undefined ) ;
10766
10867 // Start and stop
10968 poller . startPollingForSessionToken ( callback ) ;
69+ await Promise . resolve ( ) ;
11070 poller . stopPollingForSessionToken ( ) ;
11171
112- // Clear mock to check restart
113- vi . mocked ( sharedLock . acquireLockAndRun ) . mockClear ( ) ;
72+ expect ( callback ) . toHaveBeenCalledTimes ( 1 ) ;
11473
11574 // Should be able to start again
11675 poller . startPollingForSessionToken ( callback ) ;
117- expect ( sharedLock . acquireLockAndRun ) . toHaveBeenCalledTimes ( 1 ) ;
76+ await Promise . resolve ( ) ;
77+
78+ expect ( callback ) . toHaveBeenCalledTimes ( 2 ) ;
11879
11980 poller . stopPollingForSessionToken ( ) ;
12081 } ) ;
12182 } ) ;
12283
12384 describe ( 'polling interval' , ( ) => {
12485 it ( 'schedules next poll after callback completes' , async ( ) => {
125- const sharedLock : SafeLockReturn = {
126- acquireLockAndRun : vi . fn ( ) . mockResolvedValue ( undefined ) ,
127- } ;
128-
129- const poller = new SessionCookiePoller ( sharedLock ) ;
86+ const poller = new SessionCookiePoller ( ) ;
13087 const callback = vi . fn ( ) . mockResolvedValue ( undefined ) ;
13188
13289 poller . startPollingForSessionToken ( callback ) ;
13390
13491 // Initial call
135- expect ( sharedLock . acquireLockAndRun ) . toHaveBeenCalledTimes ( 1 ) ;
92+ await Promise . resolve ( ) ;
93+ expect ( callback ) . toHaveBeenCalledTimes ( 1 ) ;
13694
13795 // Wait for first interval (5 seconds)
13896 await vi . advanceTimersByTimeAsync ( 5000 ) ;
13997
14098 // Should have scheduled another call
141- expect ( sharedLock . acquireLockAndRun ) . toHaveBeenCalledTimes ( 2 ) ;
99+ expect ( callback ) . toHaveBeenCalledTimes ( 2 ) ;
100+
101+ // Another interval
102+ await vi . advanceTimersByTimeAsync ( 5000 ) ;
103+ expect ( callback ) . toHaveBeenCalledTimes ( 3 ) ;
104+
105+ poller . stopPollingForSessionToken ( ) ;
106+ } ) ;
107+
108+ it ( 'waits for callback to complete before scheduling next poll' , async ( ) => {
109+ const poller = new SessionCookiePoller ( ) ;
110+
111+ let resolveCallback : ( ) => void ;
112+ const callbackPromise = new Promise < void > ( resolve => {
113+ resolveCallback = resolve ;
114+ } ) ;
115+ const callback = vi . fn ( ) . mockReturnValue ( callbackPromise ) ;
116+
117+ poller . startPollingForSessionToken ( callback ) ;
118+
119+ // Let the first call start
120+ await Promise . resolve ( ) ;
121+ expect ( callback ) . toHaveBeenCalledTimes ( 1 ) ;
122+
123+ // Advance time while callback is still running - should NOT schedule next poll
124+ // because the callback promise hasn't resolved yet
125+ await vi . advanceTimersByTimeAsync ( 5000 ) ;
126+
127+ // Should still only be 1 call since previous call hasn't completed
128+ expect ( callback ) . toHaveBeenCalledTimes ( 1 ) ;
129+
130+ // Complete the callback
131+ resolveCallback ! ( ) ;
132+ await Promise . resolve ( ) ;
133+
134+ // Now advance time for the next interval
135+ await vi . advanceTimersByTimeAsync ( 5000 ) ;
136+
137+ expect ( callback ) . toHaveBeenCalledTimes ( 2 ) ;
142138
143139 poller . stopPollingForSessionToken ( ) ;
144140 } ) ;
0 commit comments