namespace Application.Tests.Enrollments; using Application.Enrollments.Commands; using Domain.Entities; using Domain.Exceptions; using Domain.Ports.Repositories; using Domain.Services; using Domain.ValueObjects; using FluentAssertions; using NSubstitute; using Xunit; public class EnrollStudentCommandTests { private readonly IStudentRepository _studentRepo = Substitute.For(); private readonly ISubjectRepository _subjectRepo = Substitute.For(); private readonly IEnrollmentRepository _enrollmentRepo = Substitute.For(); private readonly IUnitOfWork _unitOfWork = Substitute.For(); private readonly EnrollmentDomainService _enrollmentService = new(); private readonly EnrollStudentHandler _sut; public EnrollStudentCommandTests() { _sut = new EnrollStudentHandler( _studentRepo, _subjectRepo, _enrollmentRepo, _enrollmentService, _unitOfWork); } [Fact] public async Task Handle_WhenValid_ShouldEnrollStudent() { var student = CreateStudent(); var subject = CreateSubjectWithProfessor(1, "Math", 1, "Prof. Smith"); _studentRepo.GetByIdWithEnrollmentsAsync(1, Arg.Any()) .Returns(student); _subjectRepo.GetByIdWithProfessorAsync(1, Arg.Any()) .Returns(subject); var command = new EnrollStudentCommand(1, 1); var result = await _sut.Handle(command, CancellationToken.None); result.Should().NotBeNull(); result.SubjectName.Should().Be("Math"); result.ProfessorName.Should().Be("Prof. Smith"); _enrollmentRepo.Received(1).Add(Arg.Any()); await _unitOfWork.Received(1).SaveChangesAsync(Arg.Any()); } [Fact] public async Task Handle_WhenStudentNotFound_ShouldThrow() { _studentRepo.GetByIdWithEnrollmentsAsync(999, Arg.Any()) .Returns((Student?)null); var command = new EnrollStudentCommand(999, 1); var act = () => _sut.Handle(command, CancellationToken.None); await act.Should().ThrowAsync(); } [Fact] public async Task Handle_WhenSubjectNotFound_ShouldThrow() { var student = CreateStudent(); _studentRepo.GetByIdWithEnrollmentsAsync(1, Arg.Any()) .Returns(student); _subjectRepo.GetByIdWithProfessorAsync(999, Arg.Any()) .Returns((Subject?)null); var command = new EnrollStudentCommand(1, 999); var act = () => _sut.Handle(command, CancellationToken.None); await act.Should().ThrowAsync(); } [Fact] public async Task Handle_WhenMaxEnrollmentsReached_ShouldThrow() { var student = CreateStudentWithEnrollments(3); var subject = CreateSubjectWithProfessor(10, "Physics", 99, "New Prof"); _studentRepo.GetByIdWithEnrollmentsAsync(1, Arg.Any()) .Returns(student); _subjectRepo.GetByIdWithProfessorAsync(10, Arg.Any()) .Returns(subject); var command = new EnrollStudentCommand(1, 10); var act = () => _sut.Handle(command, CancellationToken.None); await act.Should().ThrowAsync(); _enrollmentRepo.DidNotReceive().Add(Arg.Any()); } [Fact] public async Task Handle_WhenSameProfessor_ShouldThrow() { var student = CreateStudent(); var existingSubject = CreateSubjectWithProfessor(1, "Math", 1, "Prof. Smith"); AddEnrollmentToStudent(student, existingSubject); var newSubject = CreateSubjectWithProfessor(2, "Algebra", 1, "Prof. Smith"); _studentRepo.GetByIdWithEnrollmentsAsync(1, Arg.Any()) .Returns(student); _subjectRepo.GetByIdWithProfessorAsync(2, Arg.Any()) .Returns(newSubject); var command = new EnrollStudentCommand(1, 2); var act = () => _sut.Handle(command, CancellationToken.None); await act.Should().ThrowAsync(); } private static Student CreateStudent() { var student = new Student("John Doe", Email.Create("john@test.com")); typeof(Student).GetProperty("Id")!.SetValue(student, 1); return student; } private static Student CreateStudentWithEnrollments(int count) { var student = CreateStudent(); for (int i = 0; i < count; i++) { var subject = CreateSubjectWithProfessor(i + 1, $"Subject{i}", i + 1, $"Prof{i}"); AddEnrollmentToStudent(student, subject); } return student; } private static Subject CreateSubjectWithProfessor(int id, string name, int profId, string profName) { var professor = new Professor(profName); typeof(Professor).GetProperty("Id")!.SetValue(professor, profId); var subject = new Subject(name, profId); typeof(Subject).GetProperty("Id")!.SetValue(subject, id); typeof(Subject).GetProperty("Professor")!.SetValue(subject, professor); return subject; } private static void AddEnrollmentToStudent(Student student, Subject subject) { var enrollment = new Enrollment(student.Id, subject.Id); typeof(Enrollment).GetProperty("Subject")!.SetValue(enrollment, subject); student.AddEnrollment(enrollment); } }