@@ -173,17 +173,17 @@ describe("AuthService", () => {
173173 expect ( loggedOutSpy ) . toHaveBeenCalledWith ( { previousState : "initializing" } )
174174 } )
175175
176- it ( "should transition to inactive -session when valid credentials exist" , async ( ) => {
176+ it ( "should transition to attempting -session when valid credentials exist" , async ( ) => {
177177 const credentials = { clientToken : "test-token" , sessionId : "test-session" }
178178 mockContext . secrets . get . mockResolvedValue ( JSON . stringify ( credentials ) )
179179
180- const inactiveSessionSpy = vi . fn ( )
181- authService . on ( "inactive -session" , inactiveSessionSpy )
180+ const attemptingSessionSpy = vi . fn ( )
181+ authService . on ( "attempting -session" , attemptingSessionSpy )
182182
183183 await authService . initialize ( )
184184
185- expect ( authService . getState ( ) ) . toBe ( "inactive -session" )
186- expect ( inactiveSessionSpy ) . toHaveBeenCalledWith ( { previousState : "initializing" } )
185+ expect ( authService . getState ( ) ) . toBe ( "attempting -session" )
186+ expect ( attemptingSessionSpy ) . toHaveBeenCalledWith ( { previousState : "initializing" } )
187187 expect ( mockTimer . start ) . toHaveBeenCalled ( )
188188 } )
189189
@@ -213,13 +213,13 @@ describe("AuthService", () => {
213213 const newCredentials = { clientToken : "new-token" , sessionId : "new-session" }
214214 mockContext . secrets . get . mockResolvedValue ( JSON . stringify ( newCredentials ) )
215215
216- const inactiveSessionSpy = vi . fn ( )
217- authService . on ( "inactive -session" , inactiveSessionSpy )
216+ const attemptingSessionSpy = vi . fn ( )
217+ authService . on ( "attempting -session" , attemptingSessionSpy )
218218
219219 onDidChangeCallback ! ( { key : "clerk-auth-credentials" } )
220220 await new Promise ( ( resolve ) => setTimeout ( resolve , 0 ) ) // Wait for async handling
221221
222- expect ( inactiveSessionSpy ) . toHaveBeenCalled ( )
222+ expect ( attemptingSessionSpy ) . toHaveBeenCalled ( )
223223 } )
224224 } )
225225
@@ -451,6 +451,26 @@ describe("AuthService", () => {
451451
452452 expect ( authService . getSessionToken ( ) ) . toBe ( "test-jwt" )
453453 } )
454+
455+ it ( "should return correct values for new methods" , async ( ) => {
456+ await authService . initialize ( )
457+ expect ( authService . hasOrIsAcquiringActiveSession ( ) ) . toBe ( false )
458+
459+ // Create a new service instance with credentials (attempting-session)
460+ const credentials = { clientToken : "test-token" , sessionId : "test-session" }
461+ mockContext . secrets . get . mockResolvedValue ( JSON . stringify ( credentials ) )
462+
463+ const attemptingService = new AuthService ( mockContext as unknown as vscode . ExtensionContext , mockLog )
464+ await attemptingService . initialize ( )
465+
466+ expect ( attemptingService . hasOrIsAcquiringActiveSession ( ) ) . toBe ( true )
467+ expect ( attemptingService . hasActiveSession ( ) ) . toBe ( false )
468+
469+ // Manually set state to active-session for testing
470+ attemptingService [ "state" ] = "active-session"
471+ expect ( attemptingService . hasOrIsAcquiringActiveSession ( ) ) . toBe ( true )
472+ expect ( attemptingService . hasActiveSession ( ) ) . toBe ( true )
473+ } )
454474 } )
455475
456476 describe ( "session refresh" , ( ) => {
@@ -497,7 +517,7 @@ describe("AuthService", () => {
497517 expect ( authService . getState ( ) ) . toBe ( "active-session" )
498518 expect ( authService . hasActiveSession ( ) ) . toBe ( true )
499519 expect ( authService . getSessionToken ( ) ) . toBe ( "new-jwt-token" )
500- expect ( activeSessionSpy ) . toHaveBeenCalledWith ( { previousState : "inactive -session" } )
520+ expect ( activeSessionSpy ) . toHaveBeenCalledWith ( { previousState : "attempting -session" } )
501521 expect ( userInfoSpy ) . toHaveBeenCalledWith ( {
502522 userInfo : {
503523 name : "John Doe" ,
@@ -530,6 +550,82 @@ describe("AuthService", () => {
530550 await expect ( timerCallback ( ) ) . rejects . toThrow ( "Network error" )
531551 expect ( mockLog ) . toHaveBeenCalledWith ( "[auth] Failed to refresh session" , expect . any ( Error ) )
532552 } )
553+
554+ it ( "should transition to inactive-session on first attempt failure" , async ( ) => {
555+ // Mock failed token creation response
556+ mockFetch . mockResolvedValue ( {
557+ ok : false ,
558+ status : 500 ,
559+ statusText : "Internal Server Error" ,
560+ } )
561+
562+ const inactiveSessionSpy = vi . fn ( )
563+ authService . on ( "inactive-session" , inactiveSessionSpy )
564+
565+ // Verify we start in attempting-session state
566+ expect ( authService . getState ( ) ) . toBe ( "attempting-session" )
567+ expect ( authService [ "isFirstRefreshAttempt" ] ) . toBe ( true )
568+
569+ const timerCallback = vi . mocked ( RefreshTimer ) . mock . calls [ 0 ] [ 0 ] . callback
570+
571+ await expect ( timerCallback ( ) ) . rejects . toThrow ( )
572+
573+ // Should transition to inactive-session after first failure
574+ expect ( authService . getState ( ) ) . toBe ( "inactive-session" )
575+ expect ( authService [ "isFirstRefreshAttempt" ] ) . toBe ( false )
576+ expect ( inactiveSessionSpy ) . toHaveBeenCalledWith ( { previousState : "attempting-session" } )
577+ } )
578+
579+ it ( "should not transition to inactive-session on subsequent failures" , async ( ) => {
580+ // First, transition to inactive-session by failing the first attempt
581+ mockFetch . mockResolvedValue ( {
582+ ok : false ,
583+ status : 500 ,
584+ statusText : "Internal Server Error" ,
585+ } )
586+
587+ const timerCallback = vi . mocked ( RefreshTimer ) . mock . calls [ 0 ] [ 0 ] . callback
588+ await expect ( timerCallback ( ) ) . rejects . toThrow ( )
589+
590+ // Verify we're now in inactive-session
591+ expect ( authService . getState ( ) ) . toBe ( "inactive-session" )
592+ expect ( authService [ "isFirstRefreshAttempt" ] ) . toBe ( false )
593+
594+ const inactiveSessionSpy = vi . fn ( )
595+ authService . on ( "inactive-session" , inactiveSessionSpy )
596+
597+ // Subsequent failure should not trigger another transition
598+ await expect ( timerCallback ( ) ) . rejects . toThrow ( )
599+
600+ expect ( authService . getState ( ) ) . toBe ( "inactive-session" )
601+ expect ( inactiveSessionSpy ) . not . toHaveBeenCalled ( )
602+ } )
603+
604+ it ( "should clear credentials on 401 during first refresh attempt (bug fix)" , async ( ) => {
605+ // Mock 401 response during first refresh attempt
606+ mockFetch . mockResolvedValue ( {
607+ ok : false ,
608+ status : 401 ,
609+ statusText : "Unauthorized" ,
610+ } )
611+
612+ const loggedOutSpy = vi . fn ( )
613+ authService . on ( "logged-out" , loggedOutSpy )
614+
615+ const timerCallback = vi . mocked ( RefreshTimer ) . mock . calls [ 0 ] [ 0 ] . callback
616+ await expect ( timerCallback ( ) ) . rejects . toThrow ( )
617+
618+ // Should clear credentials (not just transition to inactive-session)
619+ expect ( mockContext . secrets . delete ) . toHaveBeenCalledWith ( "clerk-auth-credentials" )
620+ expect ( mockLog ) . toHaveBeenCalledWith ( "[auth] Invalid/Expired client token: clearing credentials" )
621+
622+ // Simulate credentials cleared event
623+ mockContext . secrets . get . mockResolvedValue ( undefined )
624+ await authService [ "handleCredentialsChange" ] ( )
625+
626+ expect ( authService . getState ( ) ) . toBe ( "logged-out" )
627+ expect ( loggedOutSpy ) . toHaveBeenCalledWith ( { previousState : "attempting-session" } )
628+ } )
533629 } )
534630
535631 describe ( "user info" , ( ) => {
@@ -654,16 +750,16 @@ describe("AuthService", () => {
654750 expect ( loggedOutSpy ) . toHaveBeenCalledWith ( { previousState : "initializing" } )
655751 } )
656752
657- it ( "should emit inactive -session event" , async ( ) => {
753+ it ( "should emit attempting -session event" , async ( ) => {
658754 const credentials = { clientToken : "test-token" , sessionId : "test-session" }
659755 mockContext . secrets . get . mockResolvedValue ( JSON . stringify ( credentials ) )
660756
661- const inactiveSessionSpy = vi . fn ( )
662- authService . on ( "inactive -session" , inactiveSessionSpy )
757+ const attemptingSessionSpy = vi . fn ( )
758+ authService . on ( "attempting -session" , attemptingSessionSpy )
663759
664760 await authService . initialize ( )
665761
666- expect ( inactiveSessionSpy ) . toHaveBeenCalledWith ( { previousState : "initializing" } )
762+ expect ( attemptingSessionSpy ) . toHaveBeenCalledWith ( { previousState : "initializing" } )
667763 } )
668764
669765 it ( "should emit active-session event" , async ( ) => {
@@ -701,7 +797,7 @@ describe("AuthService", () => {
701797 // Wait for async operations to complete
702798 await new Promise ( ( resolve ) => setTimeout ( resolve , 0 ) )
703799
704- expect ( activeSessionSpy ) . toHaveBeenCalledWith ( { previousState : "inactive -session" } )
800+ expect ( activeSessionSpy ) . toHaveBeenCalledWith ( { previousState : "attempting -session" } )
705801 } )
706802
707803 it ( "should emit user-info event" , async ( ) => {
@@ -803,7 +899,7 @@ describe("AuthService", () => {
803899 expect ( mockTimer . stop ) . toHaveBeenCalled ( )
804900 } )
805901
806- it ( "should start timer on inactive -session transition" , async ( ) => {
902+ it ( "should start timer on attempting -session transition" , async ( ) => {
807903 const credentials = { clientToken : "test-token" , sessionId : "test-session" }
808904 mockContext . secrets . get . mockResolvedValue ( JSON . stringify ( credentials ) )
809905
@@ -892,13 +988,13 @@ describe("AuthService", () => {
892988 const newCredentials = { clientToken : "new-token" , sessionId : "new-session" }
893989 mockContext . secrets . get . mockResolvedValue ( JSON . stringify ( newCredentials ) )
894990
895- const inactiveSessionSpy = vi . fn ( )
896- service . on ( "inactive -session" , inactiveSessionSpy )
991+ const attemptingSessionSpy = vi . fn ( )
992+ service . on ( "attempting -session" , attemptingSessionSpy )
897993
898994 onDidChangeCallback ! ( { key : `clerk-auth-credentials-${ customUrl } ` } )
899995 await new Promise ( ( resolve ) => setTimeout ( resolve , 0 ) ) // Wait for async handling
900996
901- expect ( inactiveSessionSpy ) . toHaveBeenCalled ( )
997+ expect ( attemptingSessionSpy ) . toHaveBeenCalled ( )
902998 } )
903999
9041000 it ( "should not respond to changes on different scoped keys" , async ( ) => {
0 commit comments