tududi/frontend/__tests__/utils/authUtils.test.ts
Chris 3c1209a5a9
Express migration (#80)
* Initial migration

* Cleanup and create migration scripts

* Introduce test suite

* Fix test issues

* Correct CORS issue and update paths

* Update README
2025-06-16 21:50:44 +03:00

310 lines
No EOL
8.5 KiB
TypeScript

import { User } from '../../entities/User';
// Mock authentication utilities
const login = async (email: string, password: string) => {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ email, password })
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || 'Login failed');
}
return response.json();
};
const logout = async () => {
const response = await fetch('/api/logout', {
method: 'POST',
credentials: 'include'
});
if (!response.ok) {
throw new Error('Logout failed');
}
return response.json();
};
const getCurrentUser = async (): Promise<User | null> => {
try {
const response = await fetch('/api/user', {
credentials: 'include'
});
if (!response.ok) {
return null;
}
return response.json();
} catch {
return null;
}
};
const validateEmail = (email: string): boolean => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
const validatePassword = (password: string): boolean => {
return password.length >= 8;
};
const isAuthenticated = (user: User | null): boolean => {
return user !== null && user.email !== undefined;
};
describe('Authentication Utils', () => {
beforeEach(() => {
(global.fetch as jest.Mock).mockClear();
});
describe('login', () => {
it('successfully logs in with valid credentials', async () => {
const mockResponse = {
success: true,
user: { id: 1, email: 'test@example.com' }
};
(global.fetch as jest.Mock).mockResolvedValueOnce({
ok: true,
json: async () => mockResponse
});
const result = await login('test@example.com', 'password123');
expect(global.fetch).toHaveBeenCalledWith('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({
email: 'test@example.com',
password: 'password123'
})
});
expect(result).toEqual(mockResponse);
});
it('throws error on invalid credentials', async () => {
(global.fetch as jest.Mock).mockResolvedValueOnce({
ok: false,
json: async () => ({ error: 'Invalid credentials' })
});
await expect(login('test@example.com', 'wrongpassword'))
.rejects.toThrow('Invalid credentials');
});
it('throws error on network failure', async () => {
(global.fetch as jest.Mock).mockRejectedValueOnce(new Error('Network error'));
await expect(login('test@example.com', 'password123'))
.rejects.toThrow('Network error');
});
it('handles server error response', async () => {
(global.fetch as jest.Mock).mockResolvedValueOnce({
ok: false,
json: async () => ({})
});
await expect(login('test@example.com', 'password123'))
.rejects.toThrow('Login failed');
});
});
describe('logout', () => {
it('successfully logs out', async () => {
const mockResponse = { success: true };
(global.fetch as jest.Mock).mockResolvedValueOnce({
ok: true,
json: async () => mockResponse
});
const result = await logout();
expect(global.fetch).toHaveBeenCalledWith('/api/logout', {
method: 'POST',
credentials: 'include'
});
expect(result).toEqual(mockResponse);
});
it('throws error on logout failure', async () => {
(global.fetch as jest.Mock).mockResolvedValueOnce({
ok: false
});
await expect(logout()).rejects.toThrow('Logout failed');
});
it('handles network error during logout', async () => {
(global.fetch as jest.Mock).mockRejectedValueOnce(new Error('Network error'));
await expect(logout()).rejects.toThrow('Network error');
});
});
describe('getCurrentUser', () => {
it('returns user when authenticated', async () => {
const mockUser = { id: 1, email: 'test@example.com' };
(global.fetch as jest.Mock).mockResolvedValueOnce({
ok: true,
json: async () => mockUser
});
const result = await getCurrentUser();
expect(global.fetch).toHaveBeenCalledWith('/api/user', {
credentials: 'include'
});
expect(result).toEqual(mockUser);
});
it('returns null when not authenticated', async () => {
(global.fetch as jest.Mock).mockResolvedValueOnce({
ok: false
});
const result = await getCurrentUser();
expect(result).toBeNull();
});
it('returns null on network error', async () => {
(global.fetch as jest.Mock).mockRejectedValueOnce(new Error('Network error'));
const result = await getCurrentUser();
expect(result).toBeNull();
});
});
describe('validateEmail', () => {
it('validates correct email formats', () => {
expect(validateEmail('test@example.com')).toBe(true);
expect(validateEmail('user.name@domain.co.uk')).toBe(true);
expect(validateEmail('test+tag@example.org')).toBe(true);
});
it('rejects invalid email formats', () => {
expect(validateEmail('invalid-email')).toBe(false);
expect(validateEmail('@example.com')).toBe(false);
expect(validateEmail('test@')).toBe(false);
expect(validateEmail('test.example.com')).toBe(false);
expect(validateEmail('')).toBe(false);
});
});
describe('validatePassword', () => {
it('validates passwords with minimum length', () => {
expect(validatePassword('password123')).toBe(true);
expect(validatePassword('12345678')).toBe(true);
expect(validatePassword('very-long-password')).toBe(true);
});
it('rejects passwords below minimum length', () => {
expect(validatePassword('1234567')).toBe(false);
expect(validatePassword('short')).toBe(false);
expect(validatePassword('')).toBe(false);
});
});
describe('isAuthenticated', () => {
it('returns true for valid user', () => {
const user: User = {
id: 1,
email: 'test@example.com',
language: 'en',
appearance: 'light',
timezone: 'UTC'
};
expect(isAuthenticated(user)).toBe(true);
});
it('returns false for null user', () => {
expect(isAuthenticated(null)).toBe(false);
});
it('returns false for user without email', () => {
const invalidUser = {
id: 1,
language: 'en',
appearance: 'light',
timezone: 'UTC'
} as User;
expect(isAuthenticated(invalidUser)).toBe(false);
});
});
describe('integration scenarios', () => {
it('handles complete login flow', async () => {
// Mock successful login
(global.fetch as jest.Mock)
.mockResolvedValueOnce({
ok: true,
json: async () => ({ success: true, user: { id: 1, email: 'test@example.com' } })
})
// Mock getting current user
.mockResolvedValueOnce({
ok: true,
json: async () => ({ id: 1, email: 'test@example.com' })
});
// Login
const loginResult = await login('test@example.com', 'password123');
expect(loginResult.success).toBe(true);
// Get current user
const currentUser = await getCurrentUser();
expect(currentUser).toEqual({ id: 1, email: 'test@example.com' });
// Check authentication status
expect(isAuthenticated(currentUser)).toBe(true);
});
it('handles complete logout flow', async () => {
// Mock successful logout
(global.fetch as jest.Mock)
.mockResolvedValueOnce({
ok: true,
json: async () => ({ success: true })
})
// Mock user no longer authenticated
.mockResolvedValueOnce({
ok: false
});
// Logout
const logoutResult = await logout();
expect(logoutResult.success).toBe(true);
// Get current user (should be null)
const currentUser = await getCurrentUser();
expect(currentUser).toBeNull();
// Check authentication status
expect(isAuthenticated(currentUser)).toBe(false);
});
it('validates user input before authentication', () => {
// Valid inputs
expect(validateEmail('user@example.com')).toBe(true);
expect(validatePassword('securepassword')).toBe(true);
// Invalid inputs
expect(validateEmail('invalid')).toBe(false);
expect(validatePassword('short')).toBe(false);
});
});
});