import { describe, it, expect, vi, type Mock } from 'vitest'; import { LoginWithEmailUseCase, type LoginCommandDTO } from './LoginWithEmailUseCase'; import type { IUserRepository, StoredUser } from '../../domain/repositories/IUserRepository'; import type { IdentitySessionPort } from '../ports/IdentitySessionPort'; import type { AuthSessionDTO } from '../dto/AuthSessionDTO'; describe('LoginWithEmailUseCase', () => { let userRepository: { findByEmail: Mock; }; let sessionPort: { createSession: Mock; getCurrentSession: Mock; clearSession: Mock; }; let useCase: LoginWithEmailUseCase; beforeEach(() => { userRepository = { findByEmail: vi.fn(), }; sessionPort = { createSession: vi.fn(), getCurrentSession: vi.fn(), clearSession: vi.fn(), }; useCase = new LoginWithEmailUseCase( userRepository as unknown as IUserRepository, sessionPort as unknown as IdentitySessionPort, ); }); it('creates a session for valid credentials', async () => { const command: LoginCommandDTO = { email: 'Test@Example.com', password: 'password123', }; const storedUser: StoredUser = { id: 'user-1', email: 'test@example.com', displayName: 'Test User', passwordHash: 'hashed-password', salt: 'salt', createdAt: new Date(), }; const session: AuthSessionDTO = { user: { id: storedUser.id, email: storedUser.email, displayName: storedUser.displayName, }, issuedAt: Date.now(), expiresAt: Date.now() + 1000, token: 'token-123', }; userRepository.findByEmail.mockResolvedValue(storedUser); sessionPort.createSession.mockResolvedValue(session); const result = await useCase.execute(command); expect(userRepository.findByEmail).toHaveBeenCalledWith('test@example.com'); expect(sessionPort.createSession).toHaveBeenCalledWith({ id: storedUser.id, email: storedUser.email, displayName: storedUser.displayName, }); expect(result).toEqual(session); }); it('throws when email or password is missing', async () => { await expect(useCase.execute({ email: '', password: 'x' })).rejects.toThrow('Email and password are required'); await expect(useCase.execute({ email: 'a@example.com', password: '' })).rejects.toThrow('Email and password are required'); }); it('throws when user does not exist', async () => { const command: LoginCommandDTO = { email: 'missing@example.com', password: 'password', }; userRepository.findByEmail.mockResolvedValue(null); await expect(useCase.execute(command)).rejects.toThrow('Invalid email or password'); }); it('throws when password is invalid', async () => { const command: LoginCommandDTO = { email: 'test@example.com', password: 'wrong', }; const storedUser: StoredUser = { id: 'user-1', email: 'test@example.com', displayName: 'Test User', passwordHash: 'different-hash', salt: 'salt', createdAt: new Date(), }; userRepository.findByEmail.mockResolvedValue(storedUser); await expect(useCase.execute(command)).rejects.toThrow('Invalid email or password'); }); });