veza/apps/web/src/services/tokenRefresh.test.ts

209 lines
6.1 KiB
TypeScript
Raw Normal View History

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { TokenStorage } from './tokenStorage';
// Mock dependencies
vi.mock('./tokenStorage');
const mockTokenStorage = vi.mocked(TokenStorage);
// Mock axios
const mockPost = vi.fn();
const mockAxiosInstance = {
post: mockPost,
get: vi.fn(),
put: vi.fn(),
delete: vi.fn(),
patch: vi.fn(),
interceptors: {
request: { use: vi.fn(), eject: vi.fn() },
response: { use: vi.fn(), eject: vi.fn() },
},
};
vi.mock('axios', () => ({
default: {
create: vi.fn(() => mockAxiosInstance),
},
}));
// Import after mocks are set up
import { refreshToken } from './tokenRefresh';
describe('tokenRefresh', () => {
beforeEach(() => {
vi.clearAllMocks();
mockPost.mockReset();
});
describe('refreshToken', () => {
it('should refresh token successfully', async () => {
const mockRefreshToken = 'test-refresh-token';
const mockAccessToken = 'new-access-token';
const mockNewRefreshToken = 'new-refresh-token';
// Mock TokenStorage.getRefreshToken
mockTokenStorage.getRefreshToken.mockReturnValue(mockRefreshToken);
// Mock axios post response
mockPost.mockResolvedValue({
data: {
access_token: mockAccessToken,
refresh_token: mockNewRefreshToken,
expires_in: 900,
},
});
await refreshToken();
// Verify refresh token was retrieved
expect(mockTokenStorage.getRefreshToken).toHaveBeenCalledTimes(1);
// Verify API call was made with correct parameters
expect(mockPost).toHaveBeenCalledWith('/auth/refresh', {
refresh_token: mockRefreshToken,
});
// Verify tokens were updated
expect(mockTokenStorage.setTokens).toHaveBeenCalledWith(
mockAccessToken,
2025-12-13 02:34:34 +00:00
mockNewRefreshToken,
);
// Verify clearTokens was not called on success
expect(mockTokenStorage.clearTokens).not.toHaveBeenCalled();
});
it('should throw error if no refresh token available', async () => {
// Mock TokenStorage.getRefreshToken to return null
mockTokenStorage.getRefreshToken.mockReturnValue(null);
2025-12-13 02:34:34 +00:00
await expect(refreshToken()).rejects.toThrow(
'No refresh token available',
);
// Verify API was not called
expect(mockPost).not.toHaveBeenCalled();
// Verify tokens were not updated
expect(mockTokenStorage.setTokens).not.toHaveBeenCalled();
});
it('should clear tokens and throw error on API failure', async () => {
const mockRefreshToken = 'test-refresh-token';
const apiError = new Error('API Error');
// Mock TokenStorage.getRefreshToken
mockTokenStorage.getRefreshToken.mockReturnValue(mockRefreshToken);
mockPost.mockRejectedValue(apiError);
await expect(refreshToken()).rejects.toThrow('API Error');
// Verify API call was made
expect(mockPost).toHaveBeenCalledWith('/auth/refresh', {
refresh_token: mockRefreshToken,
});
// Verify tokens were cleared on error
expect(mockTokenStorage.clearTokens).toHaveBeenCalledTimes(1);
// Verify tokens were not updated
expect(mockTokenStorage.setTokens).not.toHaveBeenCalled();
});
it('should clear tokens on 401 Unauthorized error', async () => {
const mockRefreshToken = 'test-refresh-token';
const axiosError = {
response: {
status: 401,
data: { error: 'Invalid refresh token' },
},
isAxiosError: true,
};
// Mock TokenStorage.getRefreshToken
mockTokenStorage.getRefreshToken.mockReturnValue(mockRefreshToken);
mockPost.mockRejectedValue(axiosError);
await expect(refreshToken()).rejects.toEqual(axiosError);
// Verify tokens were cleared on 401 error
expect(mockTokenStorage.clearTokens).toHaveBeenCalledTimes(1);
});
it('should clear tokens on 400 Bad Request error', async () => {
const mockRefreshToken = 'test-refresh-token';
const axiosError = {
response: {
status: 400,
data: { error: 'Invalid request' },
},
isAxiosError: true,
};
// Mock TokenStorage.getRefreshToken
mockTokenStorage.getRefreshToken.mockReturnValue(mockRefreshToken);
mockPost.mockRejectedValue(axiosError);
await expect(refreshToken()).rejects.toEqual(axiosError);
// Verify tokens were cleared on error
expect(mockTokenStorage.clearTokens).toHaveBeenCalledTimes(1);
});
it('should clear tokens on network error', async () => {
const mockRefreshToken = 'test-refresh-token';
const networkError = new Error('Network Error');
// Mock TokenStorage.getRefreshToken
mockTokenStorage.getRefreshToken.mockReturnValue(mockRefreshToken);
mockPost.mockRejectedValue(networkError);
await expect(refreshToken()).rejects.toThrow('Network Error');
// Verify tokens were cleared on error
expect(mockTokenStorage.clearTokens).toHaveBeenCalledTimes(1);
});
it('should update tokens with new values from API response', async () => {
const mockRefreshToken = 'old-refresh-token';
const newAccessToken = 'updated-access-token';
const newRefreshToken = 'updated-refresh-token';
// Mock TokenStorage.getRefreshToken
mockTokenStorage.getRefreshToken.mockReturnValue(mockRefreshToken);
mockPost.mockResolvedValue({
data: {
access_token: newAccessToken,
refresh_token: newRefreshToken,
expires_in: 900,
},
});
await refreshToken();
// Verify tokens were updated with new values
expect(mockTokenStorage.setTokens).toHaveBeenCalledWith(
newAccessToken,
2025-12-13 02:34:34 +00:00
newRefreshToken,
);
});
it('should handle empty refresh token string', async () => {
// Mock TokenStorage.getRefreshToken to return empty string
mockTokenStorage.getRefreshToken.mockReturnValue('');
2025-12-13 02:34:34 +00:00
await expect(refreshToken()).rejects.toThrow(
'No refresh token available',
);
// Verify API was not called
expect(mockPost).not.toHaveBeenCalled();
});
});
});