namespace Application.Tests.Students; using Application.Students.DTOs; using Application.Students.Queries; using Domain.Exceptions; using Domain.Ports.Repositories; using FluentAssertions; using NSubstitute; using System.Linq.Expressions; using Xunit; public class GetStudentByIdQueryTests { private readonly IStudentRepository _studentRepository; private readonly GetStudentByIdHandler _handler; public GetStudentByIdQueryTests() { _studentRepository = Substitute.For(); _handler = new GetStudentByIdHandler(_studentRepository); } [Fact] public async Task Handle_WhenStudentExists_ShouldReturnStudentDto() { // Arrange var expectedDto = new StudentDto(1, "John Doe", "john@example.com", 0, []); _studentRepository.GetByIdProjectedAsync( 1, Arg.Any>>(), Arg.Any()) .Returns(expectedDto); var query = new GetStudentByIdQuery(1); // Act var result = await _handler.Handle(query, CancellationToken.None); // Assert result.Should().NotBeNull(); result.Id.Should().Be(1); result.Name.Should().Be("John Doe"); result.Email.Should().Be("john@example.com"); } [Fact] public async Task Handle_WhenStudentNotFound_ShouldThrow() { // Arrange _studentRepository.GetByIdProjectedAsync( 999, Arg.Any>>(), Arg.Any()) .Returns((StudentDto?)null); var query = new GetStudentByIdQuery(999); // Act var act = () => _handler.Handle(query, CancellationToken.None); // Assert await act.Should().ThrowAsync(); } [Fact] public async Task Handle_WhenStudentHasEnrollments_ShouldReturnWithEnrollments() { // Arrange var enrollments = new List { new(1, 1, "Math", 3, "Prof A", DateTime.UtcNow), new(2, 2, "Physics", 3, "Prof B", DateTime.UtcNow) }; var expectedDto = new StudentDto(1, "John Doe", "john@example.com", 6, enrollments); _studentRepository.GetByIdProjectedAsync( 1, Arg.Any>>(), Arg.Any()) .Returns(expectedDto); var query = new GetStudentByIdQuery(1); // Act var result = await _handler.Handle(query, CancellationToken.None); // Assert result.Enrollments.Should().HaveCount(2); result.TotalCredits.Should().Be(6); } } public class GetStudentsQueryTests { private readonly IStudentRepository _studentRepository; private readonly GetStudentsHandler _handler; public GetStudentsQueryTests() { _studentRepository = Substitute.For(); _handler = new GetStudentsHandler(_studentRepository); } [Fact] public async Task Handle_ShouldReturnAllStudents() { // Arrange var students = new List { new(1, "John Doe", "john@example.com", 0, []), new(2, "Jane Doe", "jane@example.com", 3, []) }; _studentRepository.GetAllProjectedAsync( Arg.Any>>(), Arg.Any()) .Returns(students); var query = new GetStudentsQuery(); // Act var result = await _handler.Handle(query, CancellationToken.None); // Assert result.Should().HaveCount(2); result[0].Name.Should().Be("John Doe"); result[1].Name.Should().Be("Jane Doe"); } [Fact] public async Task Handle_WhenNoStudents_ShouldReturnEmptyList() { // Arrange _studentRepository.GetAllProjectedAsync( Arg.Any>>(), Arg.Any()) .Returns(new List()); var query = new GetStudentsQuery(); // Act var result = await _handler.Handle(query, CancellationToken.None); // Assert result.Should().BeEmpty(); } } public class GetStudentsPagedQueryTests { private readonly IStudentRepository _studentRepository; private readonly GetStudentsPagedHandler _handler; public GetStudentsPagedQueryTests() { _studentRepository = Substitute.For(); _handler = new GetStudentsPagedHandler(_studentRepository); } [Fact] public async Task Handle_ShouldReturnPagedResult() { // Arrange var items = new List { new(1, "John Doe", "john@example.com", 0), new(2, "Jane Doe", "jane@example.com", 3) }; _studentRepository.GetPagedProjectedAsync( Arg.Any>>(), null, 10, Arg.Any()) .Returns((items, (int?)3, 5)); var query = new GetStudentsPagedQuery(); // Act var result = await _handler.Handle(query, CancellationToken.None); // Assert result.Items.Should().HaveCount(2); result.NextCursor.Should().Be(3); result.TotalCount.Should().Be(5); result.HasNextPage.Should().BeTrue(); } [Fact] public async Task Handle_WithCursor_ShouldPassCursorToRepository() { // Arrange _studentRepository.GetPagedProjectedAsync( Arg.Any>>(), 5, 10, Arg.Any()) .Returns((new List(), (int?)null, 5)); var query = new GetStudentsPagedQuery(AfterCursor: 5); // Act await _handler.Handle(query, CancellationToken.None); // Assert await _studentRepository.Received(1).GetPagedProjectedAsync( Arg.Any>>(), 5, 10, Arg.Any()); } [Fact] public async Task Handle_WithLargePageSize_ShouldLimitTo50() { // Arrange _studentRepository.GetPagedProjectedAsync( Arg.Any>>(), Arg.Any(), 50, Arg.Any()) .Returns((new List(), (int?)null, 0)); var query = new GetStudentsPagedQuery(PageSize: 100); // Act await _handler.Handle(query, CancellationToken.None); // Assert await _studentRepository.Received(1).GetPagedProjectedAsync( Arg.Any>>(), null, 50, Arg.Any()); } [Fact] public async Task Handle_WhenNoNextPage_ShouldReturnHasNextPageFalse() { // Arrange _studentRepository.GetPagedProjectedAsync( Arg.Any>>(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns((new List(), (int?)null, 2)); var query = new GetStudentsPagedQuery(); // Act var result = await _handler.Handle(query, CancellationToken.None); // Assert result.HasNextPage.Should().BeFalse(); result.NextCursor.Should().BeNull(); } }