import { JWTService, TokenPayload } from '../../../src/Application/Services/JWTService'; import { Request, Response } from 'express'; import { UserState } from '../../../src/Domain/User/UserAggregate'; describe('JWTService - Token Refresh Logic', () => { let jwtService: JWTService; let mockRequest: Partial; let mockResponse: Partial; let dateNowSpy: jest.SpyInstance; beforeEach(() => { jwtService = new JWTService(); mockRequest = { cookies: {} }; mockResponse = { cookie: jest.fn() }; // Create a fresh spy for Date.now in each test dateNowSpy = jest.spyOn(Date, 'now'); }); afterEach(() => { // Always restore Date.now after each test dateNowSpy.mockRestore(); }); describe('shouldRefreshToken', () => { it('should return true when token is 75% through its lifetime', () => { // Token issued at time 100, expires at 900 (lifetime: 800) // 75% of 800 = 600, so at time 700 (100 + 600), it should refresh const payload: TokenPayload = { userId: 'test-user', authLevel: 0 as 0 | 1, userStatus: UserState.VERIFIED_REGULAR, orgId: 'test-org', iat: 100, exp: 900 }; // Mock current time as 700 (which is 75% through the token lifetime) dateNowSpy.mockReturnValue(700 * 1000); const result = jwtService.shouldRefreshToken(payload); expect(result).toBe(true); }); it('should return true when token is more than 75% through its lifetime', () => { const payload: TokenPayload = { userId: 'test-user', authLevel: 0 as 0 | 1, userStatus: UserState.VERIFIED_REGULAR, orgId: 'test-org', iat: 100, exp: 900 }; // Mock current time as 750 (which is 81.25% through the token lifetime) dateNowSpy.mockReturnValue(750 * 1000); const result = jwtService.shouldRefreshToken(payload); expect(result).toBe(true); }); it('should return false when token is less than 75% through its lifetime', () => { const payload: TokenPayload = { userId: 'test-user', authLevel: 0 as 0 | 1, userStatus: UserState.VERIFIED_REGULAR, orgId: 'test-org', iat: 100, exp: 900 }; // Mock current time as 600 (which is 62.5% through the token lifetime) dateNowSpy.mockReturnValue(600 * 1000); const result = jwtService.shouldRefreshToken(payload); expect(result).toBe(false); }); it('should return false when payload does not have required timestamp fields', () => { const payload: TokenPayload = { userId: 'test-user', authLevel: 0 as 0 | 1, userStatus: UserState.VERIFIED_REGULAR, orgId: 'test-org' }; const result = jwtService.shouldRefreshToken(payload); expect(result).toBe(false); }); }); describe('refreshIfNeeded', () => { it('should return new token when refresh is needed', () => { // Setup a payload that needs refresh (75% through lifetime) const payload: TokenPayload = { userId: 'test-user', authLevel: 0 as 0 | 1, userStatus: UserState.VERIFIED_REGULAR, orgId: 'test-org', iat: 100, exp: 900 }; // Mock current time as 700 (75% through the token lifetime) dateNowSpy.mockReturnValue(700 * 1000); const result = jwtService.refreshIfNeeded(payload, mockResponse as Response); expect(result).toBe(true); expect(mockResponse.cookie).toHaveBeenCalled(); }); it('should return false when refresh is not needed', () => { // Setup a payload that doesn't need refresh (less than 75% through lifetime) const payload: TokenPayload = { userId: 'test-user', authLevel: 0 as 0 | 1, userStatus: UserState.VERIFIED_REGULAR, orgId: 'test-org', iat: 100, exp: 900 }; // Mock current time as 600 (62.5% through the token lifetime) dateNowSpy.mockReturnValue(600 * 1000); const result = jwtService.refreshIfNeeded(payload, mockResponse as Response); expect(result).toBe(false); expect(mockResponse.cookie).not.toHaveBeenCalled(); }); }); });