@@ -35,7 +35,7 @@ describe('Session', () => {
3535
3636 beforeEach ( ( ) => {
3737 dispatchSpy = vi . spyOn ( eventBus , 'emit' ) ;
38- BaseResource . clerk = clerkMock ( ) as any ;
38+ BaseResource . clerk = clerkMock ( ) ;
3939 } ) ;
4040
4141 afterEach ( ( ) => {
@@ -76,7 +76,7 @@ describe('Session', () => {
7676 it ( 'hydrates token cache from lastActiveToken' , async ( ) => {
7777 BaseResource . clerk = clerkMock ( {
7878 organization : new Organization ( { id : 'activeOrganization' } as OrganizationJSON ) ,
79- } ) as any ;
79+ } ) ;
8080
8181 const session = new Session ( {
8282 status : 'active' ,
@@ -100,10 +100,81 @@ describe('Session', () => {
100100 expect ( dispatchSpy ) . toHaveBeenCalledTimes ( 2 ) ;
101101 } ) ;
102102
103+ it ( 'does not re-cache token when Session is reconstructed with same token' , async ( ) => {
104+ BaseResource . clerk = clerkMock ( {
105+ organization : new Organization ( { id : 'activeOrganization' } as OrganizationJSON ) ,
106+ } ) ;
107+
108+ SessionTokenCache . clear ( ) ;
109+
110+ const session1 = new Session ( {
111+ status : 'active' ,
112+ id : 'session_1' ,
113+ object : 'session' ,
114+ user : createUser ( { } ) ,
115+ last_active_organization_id : 'activeOrganization' ,
116+ last_active_token : { object : 'token' , jwt : mockJwt } ,
117+ actor : null ,
118+ created_at : new Date ( ) . getTime ( ) ,
119+ updated_at : new Date ( ) . getTime ( ) ,
120+ } as SessionJSON ) ;
121+
122+ expect ( SessionTokenCache . size ( ) ) . toBe ( 1 ) ;
123+ const cachedEntry1 = SessionTokenCache . get ( { tokenId : 'session_1-activeOrganization' } ) ;
124+ expect ( cachedEntry1 ) . toBeDefined ( ) ;
125+
126+ const session2 = new Session ( {
127+ status : 'active' ,
128+ id : 'session_1' ,
129+ object : 'session' ,
130+ user : createUser ( { } ) ,
131+ last_active_organization_id : 'activeOrganization' ,
132+ last_active_token : { object : 'token' , jwt : mockJwt } ,
133+ actor : null ,
134+ created_at : new Date ( ) . getTime ( ) ,
135+ updated_at : new Date ( ) . getTime ( ) ,
136+ } as SessionJSON ) ;
137+
138+ expect ( SessionTokenCache . size ( ) ) . toBe ( 1 ) ;
139+
140+ const token1 = await session1 . getToken ( ) ;
141+ const token2 = await session2 . getToken ( ) ;
142+
143+ expect ( token1 ) . toBe ( token2 ) ;
144+ expect ( token1 ) . toEqual ( mockJwt ) ;
145+ expect ( BaseResource . clerk . getFapiClient ( ) . request ) . not . toHaveBeenCalled ( ) ;
146+ } ) ;
147+
148+ it ( 'caches token from cookie during degraded mode recovery' , async ( ) => {
149+ BaseResource . clerk = clerkMock ( ) ;
150+
151+ SessionTokenCache . clear ( ) ;
152+
153+ const sessionFromCookie = new Session ( {
154+ status : 'active' ,
155+ id : 'session_1' ,
156+ object : 'session' ,
157+ user : createUser ( { } ) ,
158+ last_active_organization_id : null ,
159+ last_active_token : { object : 'token' , jwt : mockJwt } ,
160+ actor : null ,
161+ created_at : new Date ( ) . getTime ( ) ,
162+ updated_at : new Date ( ) . getTime ( ) ,
163+ } as SessionJSON ) ;
164+
165+ expect ( SessionTokenCache . size ( ) ) . toBe ( 1 ) ;
166+ const cachedEntry = SessionTokenCache . get ( { tokenId : 'session_1' } ) ;
167+ expect ( cachedEntry ) . toBeDefined ( ) ;
168+
169+ const token = await sessionFromCookie . getToken ( ) ;
170+ expect ( token ) . toEqual ( mockJwt ) ;
171+ expect ( BaseResource . clerk . getFapiClient ( ) . request ) . not . toHaveBeenCalled ( ) ;
172+ } ) ;
173+
103174 it ( 'dispatches token:update event on getToken with active organization' , async ( ) => {
104175 BaseResource . clerk = clerkMock ( {
105176 organization : new Organization ( { id : 'activeOrganization' } as OrganizationJSON ) ,
106- } ) as any ;
177+ } ) ;
107178
108179 const session = new Session ( {
109180 status : 'active' ,
@@ -138,7 +209,7 @@ describe('Session', () => {
138209 it ( 'does not dispatch token:update if template is provided' , async ( ) => {
139210 BaseResource . clerk = clerkMock ( {
140211 organization : new Organization ( { id : 'activeOrganization' } as OrganizationJSON ) ,
141- } ) as any ;
212+ } ) ;
142213
143214 const session = new Session ( {
144215 status : 'active' ,
@@ -159,7 +230,7 @@ describe('Session', () => {
159230 it ( 'dispatches token:update when provided organization ID matches current active organization' , async ( ) => {
160231 BaseResource . clerk = clerkMock ( {
161232 organization : new Organization ( { id : 'activeOrganization' } as OrganizationJSON ) ,
162- } ) as any ;
233+ } ) ;
163234
164235 const session = new Session ( {
165236 status : 'active' ,
@@ -178,7 +249,7 @@ describe('Session', () => {
178249 } ) ;
179250
180251 it ( 'does not dispatch token:update when provided organization ID does not match current active organization' , async ( ) => {
181- BaseResource . clerk = clerkMock ( ) as any ;
252+ BaseResource . clerk = clerkMock ( ) ;
182253
183254 const session = new Session ( {
184255 status : 'active' ,
@@ -240,7 +311,7 @@ describe('Session', () => {
240311 it ( `uses the current session's lastActiveOrganizationId by default, not clerk.organization.id` , async ( ) => {
241312 BaseResource . clerk = clerkMock ( {
242313 organization : new Organization ( { id : 'oldActiveOrganization' } as OrganizationJSON ) ,
243- } ) as any ;
314+ } ) ;
244315
245316 const session = new Session ( {
246317 status : 'active' ,
@@ -261,7 +332,7 @@ describe('Session', () => {
261332 } ) ;
262333
263334 it ( 'deduplicates concurrent getToken calls to prevent multiple API requests' , async ( ) => {
264- BaseResource . clerk = clerkMock ( ) as any ;
335+ BaseResource . clerk = clerkMock ( ) ;
265336
266337 const session = new Session ( {
267338 status : 'active' ,
@@ -286,7 +357,7 @@ describe('Session', () => {
286357 } ) ;
287358
288359 it ( 'deduplicates concurrent getToken calls with same template' , async ( ) => {
289- BaseResource . clerk = clerkMock ( ) as any ;
360+ BaseResource . clerk = clerkMock ( ) ;
290361
291362 const session = new Session ( {
292363 status : 'active' ,
@@ -313,7 +384,7 @@ describe('Session', () => {
313384 } ) ;
314385
315386 it ( 'does not deduplicate getToken calls with different templates' , async ( ) => {
316- BaseResource . clerk = clerkMock ( ) as any ;
387+ BaseResource . clerk = clerkMock ( ) ;
317388
318389 const session = new Session ( {
319390 status : 'active' ,
@@ -335,7 +406,7 @@ describe('Session', () => {
335406 } ) ;
336407
337408 it ( 'does not deduplicate getToken calls with different organization IDs' , async ( ) => {
338- BaseResource . clerk = clerkMock ( ) as any ;
409+ BaseResource . clerk = clerkMock ( ) ;
339410
340411 const session = new Session ( {
341412 status : 'active' ,
@@ -362,7 +433,7 @@ describe('Session', () => {
362433
363434 beforeEach ( ( ) => {
364435 dispatchSpy = vi . spyOn ( eventBus , 'emit' ) ;
365- BaseResource . clerk = clerkMock ( ) as any ;
436+ BaseResource . clerk = clerkMock ( ) ;
366437 } ) ;
367438
368439 afterEach ( ( ) => {
0 commit comments