Files
aplp.frontend.web/src/features/auth/store/authStore.test.ts
T

85 lines
2.7 KiB
TypeScript
Raw Normal View History

2026-08-11 21:09:32 +07:00
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { tokenStorage } from '@/api/tokenStorage';
import { authApi } from '@/features/auth/api/authApi';
import { useAuthStore } from '@/features/auth/store/authStore';
vi.mock('@/features/auth/api/authApi', () => ({
authApi: {
login: vi.fn(),
logout: vi.fn(),
refresh: vi.fn(),
getMe: vi.fn(),
listUsers: vi.fn(),
},
}));
const mockedAuthApi = vi.mocked(authApi);
describe('authStore', () => {
beforeEach(() => {
tokenStorage.clear();
useAuthStore.setState({ user: null, isInitializing: true });
vi.clearAllMocks();
mockedAuthApi.login.mockResolvedValue({
accessToken: 'access-123',
user: { id: 'u1', email: 'learner@aplp.io', name: 'Learner' },
});
mockedAuthApi.refresh.mockResolvedValue({
accessToken: 'access-refreshed',
user: { id: 'u1', email: 'learner@aplp.io', name: 'Learner' },
});
});
it('login stores token and user', async () => {
await useAuthStore.getState().login('learner@aplp.io', 'secret');
expect(mockedAuthApi.login).toHaveBeenCalledWith({
email: 'learner@aplp.io',
password: 'secret',
});
expect(tokenStorage.get()).toBe('access-123');
expect(useAuthStore.getState().user?.email).toBe('learner@aplp.io');
expect(useAuthStore.getState().isInitializing).toBe(false);
});
it('login failure leaves user signed out', async () => {
mockedAuthApi.login.mockRejectedValue(new Error('invalid credentials'));
await expect(useAuthStore.getState().login('learner@aplp.io', 'wrong')).rejects.toThrow(
'invalid credentials',
);
expect(tokenStorage.get()).toBeNull();
expect(useAuthStore.getState().user).toBeNull();
});
it('logout clears token and user even when API call fails', async () => {
await useAuthStore.getState().login('learner@aplp.io', 'secret');
mockedAuthApi.logout.mockRejectedValue(new Error('network'));
await useAuthStore.getState().logout();
expect(tokenStorage.get()).toBeNull();
expect(useAuthStore.getState().user).toBeNull();
});
it('init restores session from refresh token', async () => {
await useAuthStore.getState().init();
expect(mockedAuthApi.refresh).toHaveBeenCalledOnce();
expect(tokenStorage.get()).toBe('access-refreshed');
expect(useAuthStore.getState().user?.id).toBe('u1');
expect(useAuthStore.getState().isInitializing).toBe(false);
});
it('init handles expired refresh token gracefully', async () => {
mockedAuthApi.refresh.mockRejectedValue(new Error('401'));
await useAuthStore.getState().init();
expect(tokenStorage.get()).toBeNull();
expect(useAuthStore.getState().user).toBeNull();
expect(useAuthStore.getState().isInitializing).toBe(false);
});
});