Files
cim_summary/backend/src/models/__tests__/UserModel.test.ts
Jon 5a3c961bfc feat: Complete implementation of Tasks 1-5 - CIM Document Processor
Backend Infrastructure:
- Complete Express server setup with security middleware (helmet, CORS, rate limiting)
- Comprehensive error handling and logging with Winston
- Authentication system with JWT tokens and session management
- Database models and migrations for Users, Documents, Feedback, and Processing Jobs
- API routes structure for authentication and document management
- Integration tests for all server components (86 tests passing)

Frontend Infrastructure:
- React application with TypeScript and Vite
- Authentication UI with login form, protected routes, and logout functionality
- Authentication context with proper async state management
- Component tests with proper async handling (25 tests passing)
- Tailwind CSS styling and responsive design

Key Features:
- User registration, login, and authentication
- Protected routes with role-based access control
- Comprehensive error handling and user feedback
- Database schema with proper relationships
- Security middleware and validation
- Production-ready build configuration

Test Coverage: 111/111 tests passing
Tasks Completed: 1-5 (Project setup, Database, Auth system, Frontend UI, Backend infrastructure)

Ready for Task 6: File upload backend infrastructure
2025-07-27 13:29:26 -04:00

227 lines
6.2 KiB
TypeScript

import { UserModel } from '../UserModel';
import { CreateUserInput } from '../types';
// Mock the database pool
jest.mock('../../config/database', () => ({
query: jest.fn()
}));
// Mock the logger
jest.mock('../../utils/logger', () => ({
info: jest.fn(),
error: jest.fn(),
warn: jest.fn()
}));
describe('UserModel', () => {
let mockPool: any;
beforeEach(() => {
jest.clearAllMocks();
mockPool = require('../../config/database');
});
describe('create', () => {
it('should create a new user successfully', async () => {
const userData: CreateUserInput = {
email: 'test@example.com',
name: 'Test User',
password: 'password123',
role: 'user'
};
const mockUser = {
id: '123e4567-e89b-12d3-a456-426614174000',
email: userData.email,
name: userData.name,
password_hash: 'hashed_password',
role: userData.role,
created_at: new Date(),
updated_at: new Date(),
is_active: true
};
mockPool.query.mockResolvedValueOnce({ rows: [mockUser] });
const result = await UserModel.create(userData);
expect(mockPool.query).toHaveBeenCalledWith(
expect.stringContaining('INSERT INTO users'),
[userData.email, userData.name, userData.password, userData.role]
);
expect(result).toEqual(mockUser);
});
it('should handle database errors', async () => {
const userData: CreateUserInput = {
email: 'test@example.com',
name: 'Test User',
password: 'password123'
};
const error = new Error('Database error');
mockPool.query.mockRejectedValueOnce(error);
await expect(UserModel.create(userData)).rejects.toThrow('Database error');
});
});
describe('findById', () => {
it('should find user by ID successfully', async () => {
const userId = '123e4567-e89b-12d3-a456-426614174000';
const mockUser = {
id: userId,
email: 'test@example.com',
name: 'Test User',
password_hash: 'hashed_password',
role: 'user',
created_at: new Date(),
updated_at: new Date(),
is_active: true
};
mockPool.query.mockResolvedValueOnce({ rows: [mockUser] });
const result = await UserModel.findById(userId);
expect(mockPool.query).toHaveBeenCalledWith(
'SELECT * FROM users WHERE id = $1 AND is_active = true',
[userId]
);
expect(result).toEqual(mockUser);
});
it('should return null when user not found', async () => {
const userId = '123e4567-e89b-12d3-a456-426614174000';
mockPool.query.mockResolvedValueOnce({ rows: [] });
const result = await UserModel.findById(userId);
expect(result).toBeNull();
});
});
describe('findByEmail', () => {
it('should find user by email successfully', async () => {
const email = 'test@example.com';
const mockUser = {
id: '123e4567-e89b-12d3-a456-426614174000',
email,
name: 'Test User',
password_hash: 'hashed_password',
role: 'user',
created_at: new Date(),
updated_at: new Date(),
is_active: true
};
mockPool.query.mockResolvedValueOnce({ rows: [mockUser] });
const result = await UserModel.findByEmail(email);
expect(mockPool.query).toHaveBeenCalledWith(
'SELECT * FROM users WHERE email = $1 AND is_active = true',
[email]
);
expect(result).toEqual(mockUser);
});
});
describe('update', () => {
it('should update user successfully', async () => {
const userId = '123e4567-e89b-12d3-a456-426614174000';
const updates = {
name: 'Updated Name',
email: 'updated@example.com'
};
const mockUpdatedUser = {
id: userId,
...updates,
password_hash: 'hashed_password',
role: 'user',
created_at: new Date(),
updated_at: new Date(),
is_active: true
};
mockPool.query.mockResolvedValueOnce({ rows: [mockUpdatedUser] });
const result = await UserModel.update(userId, updates);
expect(mockPool.query).toHaveBeenCalledWith(
expect.stringContaining('UPDATE users'),
expect.arrayContaining([updates.name, updates.email, userId])
);
expect(result).toEqual(mockUpdatedUser);
});
});
describe('delete', () => {
it('should soft delete user successfully', async () => {
const userId = '123e4567-e89b-12d3-a456-426614174000';
mockPool.query.mockResolvedValueOnce({ rows: [{ id: userId }] });
const result = await UserModel.delete(userId);
expect(mockPool.query).toHaveBeenCalledWith(
'UPDATE users SET is_active = false WHERE id = $1 RETURNING id',
[userId]
);
expect(result).toBe(true);
});
it('should return false when user not found', async () => {
const userId = '123e4567-e89b-12d3-a456-426614174000';
mockPool.query.mockResolvedValueOnce({ rows: [] });
const result = await UserModel.delete(userId);
expect(result).toBe(false);
});
});
describe('emailExists', () => {
it('should return true when email exists', async () => {
const email = 'test@example.com';
mockPool.query.mockResolvedValueOnce({ rows: [{ id: '123' }] });
const result = await UserModel.emailExists(email);
expect(mockPool.query).toHaveBeenCalledWith(
'SELECT id FROM users WHERE email = $1 AND is_active = true',
[email]
);
expect(result).toBe(true);
});
it('should return false when email does not exist', async () => {
const email = 'test@example.com';
mockPool.query.mockResolvedValueOnce({ rows: [] });
const result = await UserModel.emailExists(email);
expect(result).toBe(false);
});
});
describe('count', () => {
it('should return correct user count', async () => {
const expectedCount = 5;
mockPool.query.mockResolvedValueOnce({ rows: [{ count: expectedCount.toString() }] });
const result = await UserModel.count();
expect(mockPool.query).toHaveBeenCalledWith(
'SELECT COUNT(*) FROM users WHERE is_active = true'
);
expect(result).toBe(expectedCount);
});
});
});