259 lines
7.7 KiB
C#
259 lines
7.7 KiB
C#
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<IStudentRepository>();
|
|
_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<Expression<Func<Domain.Entities.Student, StudentDto>>>(),
|
|
Arg.Any<CancellationToken>())
|
|
.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<StudentDto>(
|
|
999,
|
|
Arg.Any<Expression<Func<Domain.Entities.Student, StudentDto>>>(),
|
|
Arg.Any<CancellationToken>())
|
|
.Returns((StudentDto?)null);
|
|
|
|
var query = new GetStudentByIdQuery(999);
|
|
|
|
// Act
|
|
var act = () => _handler.Handle(query, CancellationToken.None);
|
|
|
|
// Assert
|
|
await act.Should().ThrowAsync<StudentNotFoundException>();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Handle_WhenStudentHasEnrollments_ShouldReturnWithEnrollments()
|
|
{
|
|
// Arrange
|
|
var enrollments = new List<EnrollmentDto>
|
|
{
|
|
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<Expression<Func<Domain.Entities.Student, StudentDto>>>(),
|
|
Arg.Any<CancellationToken>())
|
|
.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<IStudentRepository>();
|
|
_handler = new GetStudentsHandler(_studentRepository);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Handle_ShouldReturnAllStudents()
|
|
{
|
|
// Arrange
|
|
var students = new List<StudentDto>
|
|
{
|
|
new(1, "John Doe", "john@example.com", 0, []),
|
|
new(2, "Jane Doe", "jane@example.com", 3, [])
|
|
};
|
|
|
|
_studentRepository.GetAllProjectedAsync(
|
|
Arg.Any<Expression<Func<Domain.Entities.Student, StudentDto>>>(),
|
|
Arg.Any<CancellationToken>())
|
|
.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<Expression<Func<Domain.Entities.Student, StudentDto>>>(),
|
|
Arg.Any<CancellationToken>())
|
|
.Returns(new List<StudentDto>());
|
|
|
|
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<IStudentRepository>();
|
|
_handler = new GetStudentsPagedHandler(_studentRepository);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Handle_ShouldReturnPagedResult()
|
|
{
|
|
// Arrange
|
|
var items = new List<StudentPagedDto>
|
|
{
|
|
new(1, "John Doe", "john@example.com", 0),
|
|
new(2, "Jane Doe", "jane@example.com", 3)
|
|
};
|
|
|
|
_studentRepository.GetPagedProjectedAsync(
|
|
Arg.Any<Expression<Func<Domain.Entities.Student, StudentPagedDto>>>(),
|
|
null,
|
|
10,
|
|
Arg.Any<CancellationToken>())
|
|
.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<Expression<Func<Domain.Entities.Student, StudentPagedDto>>>(),
|
|
5,
|
|
10,
|
|
Arg.Any<CancellationToken>())
|
|
.Returns((new List<StudentPagedDto>(), (int?)null, 5));
|
|
|
|
var query = new GetStudentsPagedQuery(AfterCursor: 5);
|
|
|
|
// Act
|
|
await _handler.Handle(query, CancellationToken.None);
|
|
|
|
// Assert
|
|
await _studentRepository.Received(1).GetPagedProjectedAsync(
|
|
Arg.Any<Expression<Func<Domain.Entities.Student, StudentPagedDto>>>(),
|
|
5,
|
|
10,
|
|
Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Handle_WithLargePageSize_ShouldLimitTo50()
|
|
{
|
|
// Arrange
|
|
_studentRepository.GetPagedProjectedAsync(
|
|
Arg.Any<Expression<Func<Domain.Entities.Student, StudentPagedDto>>>(),
|
|
Arg.Any<int?>(),
|
|
50,
|
|
Arg.Any<CancellationToken>())
|
|
.Returns((new List<StudentPagedDto>(), (int?)null, 0));
|
|
|
|
var query = new GetStudentsPagedQuery(PageSize: 100);
|
|
|
|
// Act
|
|
await _handler.Handle(query, CancellationToken.None);
|
|
|
|
// Assert
|
|
await _studentRepository.Received(1).GetPagedProjectedAsync(
|
|
Arg.Any<Expression<Func<Domain.Entities.Student, StudentPagedDto>>>(),
|
|
null,
|
|
50,
|
|
Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Handle_WhenNoNextPage_ShouldReturnHasNextPageFalse()
|
|
{
|
|
// Arrange
|
|
_studentRepository.GetPagedProjectedAsync(
|
|
Arg.Any<Expression<Func<Domain.Entities.Student, StudentPagedDto>>>(),
|
|
Arg.Any<int?>(),
|
|
Arg.Any<int>(),
|
|
Arg.Any<CancellationToken>())
|
|
.Returns((new List<StudentPagedDto>(), (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();
|
|
}
|
|
}
|