Files
goodgo-platform/apps/web/lib/__tests__/auth-store.spec.ts
Ho Ngoc Hai 0c26dd85ef fix: resolve all lint errors across codebase
- Convert CacheTTL enum to const object to fix duplicate value errors
- Fix import ordering in test files (eslint-disable for vi.mock pattern)
- Fix unused variable warnings (prefix with underscore)
- Auto-fix import ordering in subscription page, dashboard layout
- 0 lint errors remaining

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 23:13:35 +07:00

218 lines
6.8 KiB
TypeScript

/* eslint-disable import-x/order */
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { ApiError } from '../api-client';
import { useAuthStore } from '../auth-store';
// Mock auth-api module
vi.mock('../auth-api', () => ({
authApi: {
login: vi.fn(),
register: vi.fn(),
logout: vi.fn(),
refresh: vi.fn(),
exchangeToken: vi.fn(),
getProfile: vi.fn(),
},
}));
// Import mocked module
import { authApi } from '../auth-api';
const mockedAuthApi = vi.mocked(authApi);
const mockUser = {
id: '1',
email: 'test@example.com',
phone: '0912345678',
fullName: 'Nguyen Van A',
avatarUrl: null,
role: 'user',
kycStatus: 'pending',
isActive: true,
createdAt: '2024-01-01',
};
describe('useAuthStore', () => {
beforeEach(() => {
// Reset store state
useAuthStore.setState({
user: null,
isAuthenticated: false,
isLoading: false,
error: null,
});
vi.clearAllMocks();
});
afterEach(() => {
vi.restoreAllMocks();
});
describe('initial state', () => {
it('starts with null user and unauthenticated', () => {
const state = useAuthStore.getState();
expect(state.user).toBeNull();
expect(state.isAuthenticated).toBe(false);
expect(state.isLoading).toBe(false);
expect(state.error).toBeNull();
});
});
describe('login', () => {
it('sets isAuthenticated and fetches profile on success', async () => {
mockedAuthApi.login.mockResolvedValue({ message: 'ok' });
mockedAuthApi.getProfile.mockResolvedValue(mockUser);
await useAuthStore.getState().login({ phone: '0912345678', password: 'pass123' });
const state = useAuthStore.getState();
expect(state.isAuthenticated).toBe(true);
expect(state.user).toEqual(mockUser);
expect(state.isLoading).toBe(false);
expect(state.error).toBeNull();
});
it('sets error on login failure', async () => {
mockedAuthApi.login.mockRejectedValue(new ApiError(401, 'Sai mật khẩu'));
await expect(
useAuthStore.getState().login({ phone: '0912345678', password: 'wrong' }),
).rejects.toThrow();
const state = useAuthStore.getState();
expect(state.isAuthenticated).toBe(false);
expect(state.error).toBe('Sai mật khẩu');
expect(state.isLoading).toBe(false);
});
it('uses default error message for non-ApiError', async () => {
mockedAuthApi.login.mockRejectedValue(new Error('Network error'));
await expect(
useAuthStore.getState().login({ phone: '0912345678', password: 'pass' }),
).rejects.toThrow();
expect(useAuthStore.getState().error).toBe('Đăng nhập thất bại');
});
});
describe('register', () => {
it('sets isAuthenticated and fetches profile on success', async () => {
mockedAuthApi.register.mockResolvedValue({ message: 'ok' });
mockedAuthApi.getProfile.mockResolvedValue(mockUser);
await useAuthStore.getState().register({
phone: '0912345678',
password: 'password123',
fullName: 'Nguyen Van A',
});
const state = useAuthStore.getState();
expect(state.isAuthenticated).toBe(true);
expect(state.user).toEqual(mockUser);
});
it('sets error on register failure', async () => {
mockedAuthApi.register.mockRejectedValue(new ApiError(409, 'Số điện thoại đã tồn tại'));
await expect(
useAuthStore.getState().register({
phone: '0912345678',
password: 'pass',
fullName: 'Test',
}),
).rejects.toThrow();
expect(useAuthStore.getState().error).toBe('Số điện thoại đã tồn tại');
});
});
describe('logout', () => {
it('clears user and auth state', async () => {
useAuthStore.setState({ user: mockUser, isAuthenticated: true });
mockedAuthApi.logout.mockResolvedValue({ message: 'ok' });
await useAuthStore.getState().logout();
const state = useAuthStore.getState();
expect(state.user).toBeNull();
expect(state.isAuthenticated).toBe(false);
});
it('clears state even if API logout fails', async () => {
useAuthStore.setState({ user: mockUser, isAuthenticated: true });
mockedAuthApi.logout.mockRejectedValue(new Error('Network error'));
await useAuthStore.getState().logout();
expect(useAuthStore.getState().user).toBeNull();
expect(useAuthStore.getState().isAuthenticated).toBe(false);
});
});
describe('refreshToken', () => {
it('returns true and sets authenticated on success', async () => {
mockedAuthApi.refresh.mockResolvedValue({ message: 'ok' });
const result = await useAuthStore.getState().refreshToken();
expect(result).toBe(true);
expect(useAuthStore.getState().isAuthenticated).toBe(true);
});
it('returns false and clears state on failure', async () => {
useAuthStore.setState({ user: mockUser, isAuthenticated: true });
mockedAuthApi.refresh.mockRejectedValue(new Error('expired'));
const result = await useAuthStore.getState().refreshToken();
expect(result).toBe(false);
expect(useAuthStore.getState().user).toBeNull();
expect(useAuthStore.getState().isAuthenticated).toBe(false);
});
});
describe('fetchProfile', () => {
it('fetches and sets user profile', async () => {
mockedAuthApi.getProfile.mockResolvedValue(mockUser);
await useAuthStore.getState().fetchProfile();
expect(useAuthStore.getState().user).toEqual(mockUser);
expect(useAuthStore.getState().isAuthenticated).toBe(true);
});
it('attempts refresh on 401 and retries profile', async () => {
mockedAuthApi.getProfile
.mockRejectedValueOnce(new ApiError(401, 'Unauthorized'))
.mockResolvedValueOnce(mockUser);
mockedAuthApi.refresh.mockResolvedValue({ message: 'ok' });
await useAuthStore.getState().fetchProfile();
expect(mockedAuthApi.refresh).toHaveBeenCalled();
expect(useAuthStore.getState().user).toEqual(mockUser);
});
});
describe('handleOAuthCallback', () => {
it('exchanges token and fetches profile', async () => {
mockedAuthApi.exchangeToken.mockResolvedValue({ message: 'ok' });
mockedAuthApi.getProfile.mockResolvedValue(mockUser);
await useAuthStore.getState().handleOAuthCallback('access', 'refresh', 3600);
expect(mockedAuthApi.exchangeToken).toHaveBeenCalledWith('access', 'refresh', 3600);
expect(useAuthStore.getState().isAuthenticated).toBe(true);
expect(useAuthStore.getState().user).toEqual(mockUser);
});
});
describe('clearError', () => {
it('clears the error state', () => {
useAuthStore.setState({ error: 'Some error' });
useAuthStore.getState().clearError();
expect(useAuthStore.getState().error).toBeNull();
});
});
});