gohorsejobs/frontend/src/lib/__tests__/auth.test.ts
Yamamoto 31fadc1b11 feat(auth): migrate sessionStorage to localStorage and add refreshSession()
- Replace sessionStorage with localStorage for user data persistence
- Add refreshSession() function to restore session from HTTPOnly cookie via /users/me
- Update tests to use localStorage mocks
- Add 3 new tests for refreshSession() functionality
- Update superadmin credentials in README.md and DEVOPS.md
2026-01-03 09:33:55 -03:00

259 lines
8.5 KiB
TypeScript

import { RegisterCandidateData } from '../auth';
// Mock fetch globally
const mockFetch = jest.fn();
global.fetch = mockFetch;
// Mock localStorage
const localStorageMock = {
getItem: jest.fn(),
setItem: jest.fn(),
removeItem: jest.fn(),
clear: jest.fn(),
};
Object.defineProperty(window, 'localStorage', { value: localStorageMock });
// Mock config module to avoid initConfig fetching
jest.mock('../config', () => ({
getApiUrl: jest.fn().mockReturnValue('http://test-api.com'),
initConfig: jest.fn().mockResolvedValue({}),
getConfig: jest.fn().mockReturnValue({}),
}));
describe('Auth Module', () => {
let authModule: typeof import('../auth');
beforeEach(() => {
jest.resetModules();
mockFetch.mockReset();
localStorageMock.getItem.mockReset();
localStorageMock.setItem.mockReset();
localStorageMock.removeItem.mockReset();
// Re-import the module fresh
authModule = require('../auth');
});
describe('registerCandidate', () => {
const validData: RegisterCandidateData = {
name: 'John Doe',
email: 'john@example.com',
password: 'password123',
username: 'johndoe',
phone: '+5511999999999',
};
it('should register candidate successfully', async () => {
const mockResponse = {
token: 'mock-jwt-token',
user: {
id: '123',
name: 'John Doe',
email: 'john@example.com',
roles: ['CANDIDATE'],
},
};
mockFetch.mockResolvedValueOnce({
ok: true,
json: async () => mockResponse,
});
await expect(authModule.registerCandidate(validData)).resolves.not.toThrow();
expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining('/auth/register'),
expect.objectContaining({
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(validData),
})
);
});
it('should throw error when email already exists', async () => {
const errorResponse = { message: 'email already registered' };
mockFetch.mockResolvedValueOnce({
ok: false,
status: 409,
json: async () => errorResponse,
});
await expect(authModule.registerCandidate(validData)).rejects.toThrow(
'email already registered'
);
});
it('should throw error with status code when no message provided', async () => {
mockFetch.mockResolvedValueOnce({
ok: false,
status: 500,
json: async () => ({}),
});
await expect(authModule.registerCandidate(validData)).rejects.toThrow(
'Erro no registro: 500'
);
});
it('should handle network errors gracefully', async () => {
mockFetch.mockRejectedValueOnce(new Error('Network error'));
await expect(authModule.registerCandidate(validData)).rejects.toThrow(
'Network error'
);
});
it('should send all required fields in request body', async () => {
mockFetch.mockResolvedValueOnce({
ok: true,
json: async () => ({}),
});
await authModule.registerCandidate(validData);
const callArgs = mockFetch.mock.calls[0];
const body = JSON.parse(callArgs[1].body);
expect(body).toEqual({
name: 'John Doe',
email: 'john@example.com',
password: 'password123',
username: 'johndoe',
phone: '+5511999999999',
});
});
});
describe('login', () => {
it('should login and store user in localStorage', async () => {
const mockResponse = {
token: 'mock-jwt-token',
user: {
id: '123',
name: 'Test User',
email: 'test@example.com',
roles: ['CANDIDATE'],
status: 'active',
created_at: '2023-01-01T00:00:00Z',
},
};
mockFetch.mockResolvedValueOnce({
ok: true,
json: async () => mockResponse,
});
const user = await authModule.login('test@example.com', 'password123');
expect(user).toBeDefined();
expect(user?.email).toBe('test@example.com');
expect(localStorageMock.setItem).toHaveBeenCalledWith(
'job-portal-auth',
expect.any(String)
);
// Token is in cookie, not in storage
expect(localStorageMock.setItem).not.toHaveBeenCalledWith(
'auth_token',
expect.any(String)
);
});
it('should throw error on invalid credentials', async () => {
mockFetch.mockResolvedValueOnce({
ok: false,
status: 401,
});
await expect(
authModule.login('test@example.com', 'wrongpassword')
).rejects.toThrow('Credenciais inválidas');
});
});
describe('logout', () => {
it('should remove auth data from localStorage', () => {
authModule.logout();
expect(localStorageMock.removeItem).toHaveBeenCalledWith('job-portal-auth');
});
});
describe('refreshSession', () => {
it('should restore user from /users/me endpoint', async () => {
mockFetch.mockResolvedValueOnce({
ok: true,
json: async () => ({
id: '123',
name: 'Test User',
email: 'test@example.com',
roles: ['candidate'],
}),
});
const user = await authModule.refreshSession();
expect(user).toBeDefined();
expect(user?.email).toBe('test@example.com');
expect(localStorageMock.setItem).toHaveBeenCalled();
});
it('should clear storage on 401', async () => {
mockFetch.mockResolvedValueOnce({ ok: false, status: 401 });
const user = await authModule.refreshSession();
expect(user).toBeNull();
expect(localStorageMock.removeItem).toHaveBeenCalledWith('job-portal-auth');
});
it('should return null on network error', async () => {
mockFetch.mockRejectedValueOnce(new Error('Network error'));
const user = await authModule.refreshSession();
expect(user).toBeNull();
});
});
describe('getCurrentUser', () => {
it('should return user from localStorage', () => {
const storedUser = { id: '123', name: 'Test', email: 'test@test.com', role: 'candidate' };
localStorageMock.getItem.mockReturnValueOnce(JSON.stringify(storedUser));
const user = authModule.getCurrentUser();
expect(user).toEqual(storedUser);
});
it('should return null when no user stored', () => {
localStorageMock.getItem.mockReturnValueOnce(null);
const user = authModule.getCurrentUser();
expect(user).toBeNull();
});
});
describe('isAdminUser', () => {
it('should return true for admin role', () => {
const adminUser = { id: '1', name: 'Admin', email: 'a@a.com', role: 'admin' as const, roles: ['admin'] };
expect(authModule.isAdminUser(adminUser)).toBe(true);
});
it('should return true for SUPERADMIN in roles array', () => {
const superAdmin = { id: '1', name: 'Super', email: 's@s.com', role: 'candidate' as const, roles: ['SUPERADMIN'] };
expect(authModule.isAdminUser(superAdmin)).toBe(true);
});
it('should return false for regular candidate', () => {
const candidate = { id: '1', name: 'User', email: 'u@u.com', role: 'candidate' as const, roles: ['CANDIDATE'] };
expect(authModule.isAdminUser(candidate)).toBe(false);
});
it('should return false for null user', () => {
expect(authModule.isAdminUser(null)).toBe(false);
});
});
});