namespace Application.Tests.Students; using Application.Auth; using Application.Students.Commands; using Application.Students.DTOs; using Domain.Entities; using Domain.Exceptions; using Domain.Ports.Repositories; using Domain.ValueObjects; using FluentAssertions; using Microsoft.Extensions.Configuration; using NSubstitute; using Xunit; public class CreateStudentCommandTests { private readonly IStudentRepository _studentRepository; private readonly IPasswordService _passwordService; private readonly IUnitOfWork _unitOfWork; private readonly IConfiguration _configuration; private readonly CreateStudentHandler _handler; public CreateStudentCommandTests() { _studentRepository = Substitute.For(); _passwordService = Substitute.For(); _unitOfWork = Substitute.For(); _configuration = Substitute.For(); _passwordService.HashPassword(Arg.Any()).Returns("hashed_code"); _configuration["App:BaseUrl"].Returns("http://localhost:4200"); _handler = new CreateStudentHandler(_studentRepository, _passwordService, _unitOfWork, _configuration); } [Fact] public async Task Handle_WithValidData_ShouldCreateStudent() { // Arrange var command = new CreateStudentCommand("John Doe", "john@example.com"); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Should().NotBeNull(); result.Student.Name.Should().Be("John Doe"); result.Student.Email.Should().Be("john@example.com"); result.Student.TotalCredits.Should().Be(0); result.Student.Enrollments.Should().BeEmpty(); result.ActivationCode.Should().NotBeNullOrEmpty(); result.ActivationUrl.Should().Contain("/activate?code="); _studentRepository.Received(1).Add(Arg.Any()); await _unitOfWork.Received(1).SaveChangesAsync(Arg.Any()); } [Fact] public async Task Handle_WithInvalidEmail_ShouldThrow() { // Arrange var command = new CreateStudentCommand("John Doe", "invalid-email"); // Act var act = () => _handler.Handle(command, CancellationToken.None); // Assert await act.Should().ThrowAsync(); } [Fact] public async Task Handle_WithEmptyName_ShouldThrow() { // Arrange var command = new CreateStudentCommand("", "john@example.com"); // Act var act = () => _handler.Handle(command, CancellationToken.None); // Assert await act.Should().ThrowAsync(); } } public class UpdateStudentCommandTests { private readonly IStudentRepository _studentRepository; private readonly IUnitOfWork _unitOfWork; private readonly UpdateStudentHandler _handler; public UpdateStudentCommandTests() { _studentRepository = Substitute.For(); _unitOfWork = Substitute.For(); _handler = new UpdateStudentHandler(_studentRepository, _unitOfWork); } [Fact] public async Task Handle_WhenStudentExists_ShouldUpdateStudent() { // Arrange var student = new Student("Old Name", Email.Create("old@example.com")); SetEntityId(student, 1); _studentRepository.GetByIdWithEnrollmentsAsync(1, Arg.Any()) .Returns(student); var command = new UpdateStudentCommand(1, "New Name", "new@example.com"); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Should().NotBeNull(); result.Name.Should().Be("New Name"); result.Email.Should().Be("new@example.com"); _studentRepository.Received(1).Update(student); await _unitOfWork.Received(1).SaveChangesAsync(Arg.Any()); } [Fact] public async Task Handle_WhenStudentNotFound_ShouldThrow() { // Arrange _studentRepository.GetByIdWithEnrollmentsAsync(999, Arg.Any()) .Returns((Student?)null); var command = new UpdateStudentCommand(999, "Name", "email@test.com"); // Act var act = () => _handler.Handle(command, CancellationToken.None); // Assert await act.Should().ThrowAsync(); } [Fact] public async Task Handle_WithInvalidEmail_ShouldThrow() { // Arrange var student = new Student("Name", Email.Create("old@example.com")); _studentRepository.GetByIdWithEnrollmentsAsync(1, Arg.Any()) .Returns(student); var command = new UpdateStudentCommand(1, "Name", "invalid"); // Act var act = () => _handler.Handle(command, CancellationToken.None); // Assert await act.Should().ThrowAsync(); } private static void SetEntityId(T entity, int id) where T : class { typeof(T).GetProperty("Id")?.SetValue(entity, id); } } public class DeleteStudentCommandTests { private readonly IStudentRepository _studentRepository; private readonly IUnitOfWork _unitOfWork; private readonly DeleteStudentHandler _handler; public DeleteStudentCommandTests() { _studentRepository = Substitute.For(); _unitOfWork = Substitute.For(); _handler = new DeleteStudentHandler(_studentRepository, _unitOfWork); } [Fact] public async Task Handle_WhenStudentExists_ShouldDeleteStudent() { // Arrange var student = new Student("John Doe", Email.Create("john@example.com")); _studentRepository.GetByIdAsync(1, Arg.Any()) .Returns(student); var command = new DeleteStudentCommand(1); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Should().BeTrue(); _studentRepository.Received(1).Delete(student); await _unitOfWork.Received(1).SaveChangesAsync(Arg.Any()); } [Fact] public async Task Handle_WhenStudentNotFound_ShouldThrow() { // Arrange _studentRepository.GetByIdAsync(999, Arg.Any()) .Returns((Student?)null); var command = new DeleteStudentCommand(999); // Act var act = () => _handler.Handle(command, CancellationToken.None); // Assert await act.Should().ThrowAsync(); } }