279 lines
9.4 KiB
C#
279 lines
9.4 KiB
C#
namespace Application.Tests.Subjects;
|
|
|
|
using Application.Subjects.DTOs;
|
|
using Application.Subjects.Queries;
|
|
using Domain.Ports.Repositories;
|
|
using FluentAssertions;
|
|
using NSubstitute;
|
|
using System.Linq.Expressions;
|
|
using Xunit;
|
|
|
|
public class GetSubjectsQueryTests
|
|
{
|
|
private readonly ISubjectRepository _subjectRepository;
|
|
private readonly GetSubjectsHandler _handler;
|
|
|
|
public GetSubjectsQueryTests()
|
|
{
|
|
_subjectRepository = Substitute.For<ISubjectRepository>();
|
|
_handler = new GetSubjectsHandler(_subjectRepository);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Handle_ShouldReturnAllSubjects()
|
|
{
|
|
// Arrange
|
|
var subjects = new List<SubjectDto>
|
|
{
|
|
new(1, "Math", 3, 1, "Prof A"),
|
|
new(2, "Physics", 3, 1, "Prof A"),
|
|
new(3, "Chemistry", 3, 2, "Prof B")
|
|
};
|
|
|
|
_subjectRepository.GetAllProjectedAsync(
|
|
Arg.Any<Expression<Func<Domain.Entities.Subject, SubjectDto>>>(),
|
|
Arg.Any<CancellationToken>())
|
|
.Returns(subjects);
|
|
|
|
var query = new GetSubjectsQuery();
|
|
|
|
// Act
|
|
var result = await _handler.Handle(query, CancellationToken.None);
|
|
|
|
// Assert
|
|
result.Should().HaveCount(3);
|
|
result[0].Name.Should().Be("Math");
|
|
result[0].ProfessorName.Should().Be("Prof A");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Handle_WhenNoSubjects_ShouldReturnEmptyList()
|
|
{
|
|
// Arrange
|
|
_subjectRepository.GetAllProjectedAsync(
|
|
Arg.Any<Expression<Func<Domain.Entities.Subject, SubjectDto>>>(),
|
|
Arg.Any<CancellationToken>())
|
|
.Returns(new List<SubjectDto>());
|
|
|
|
var query = new GetSubjectsQuery();
|
|
|
|
// Act
|
|
var result = await _handler.Handle(query, CancellationToken.None);
|
|
|
|
// Assert
|
|
result.Should().BeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Handle_ShouldReturnCorrectCredits()
|
|
{
|
|
// Arrange
|
|
var subjects = new List<SubjectDto>
|
|
{
|
|
new(1, "Math", 3, 1, "Prof A")
|
|
};
|
|
|
|
_subjectRepository.GetAllProjectedAsync(
|
|
Arg.Any<Expression<Func<Domain.Entities.Subject, SubjectDto>>>(),
|
|
Arg.Any<CancellationToken>())
|
|
.Returns(subjects);
|
|
|
|
var query = new GetSubjectsQuery();
|
|
|
|
// Act
|
|
var result = await _handler.Handle(query, CancellationToken.None);
|
|
|
|
// Assert
|
|
result[0].Credits.Should().Be(3);
|
|
}
|
|
}
|
|
|
|
public class GetAvailableSubjectsQueryTests
|
|
{
|
|
private readonly IStudentRepository _studentRepository;
|
|
private readonly ISubjectRepository _subjectRepository;
|
|
private readonly GetAvailableSubjectsHandler _handler;
|
|
|
|
public GetAvailableSubjectsQueryTests()
|
|
{
|
|
_studentRepository = Substitute.For<IStudentRepository>();
|
|
_subjectRepository = Substitute.For<ISubjectRepository>();
|
|
_handler = new GetAvailableSubjectsHandler(_studentRepository, _subjectRepository);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Handle_WhenStudentNotFound_ShouldThrow()
|
|
{
|
|
// Arrange
|
|
_studentRepository.GetByIdWithEnrollmentsAsync(999, Arg.Any<CancellationToken>())
|
|
.Returns((Domain.Entities.Student?)null);
|
|
|
|
var query = new GetAvailableSubjectsQuery(999);
|
|
|
|
// Act
|
|
var act = () => _handler.Handle(query, CancellationToken.None);
|
|
|
|
// Assert
|
|
await act.Should().ThrowAsync<Domain.Exceptions.StudentNotFoundException>();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Handle_WhenNoEnrollments_AllSubjectsShouldBeAvailable()
|
|
{
|
|
// Arrange
|
|
var student = new Domain.Entities.Student("John", Domain.ValueObjects.Email.Create("john@test.com"));
|
|
|
|
var professor = new Domain.Entities.Professor("Prof A");
|
|
SetEntityId(professor, 1);
|
|
|
|
var subjects = new List<Domain.Entities.Subject>
|
|
{
|
|
CreateSubject(1, "Math", 1, professor),
|
|
CreateSubject(2, "Physics", 1, professor)
|
|
};
|
|
|
|
_studentRepository.GetByIdWithEnrollmentsAsync(1, Arg.Any<CancellationToken>())
|
|
.Returns(student);
|
|
_subjectRepository.GetAllWithProfessorsAsync(Arg.Any<CancellationToken>())
|
|
.Returns(subjects);
|
|
|
|
var query = new GetAvailableSubjectsQuery(1);
|
|
|
|
// Act
|
|
var result = await _handler.Handle(query, CancellationToken.None);
|
|
|
|
// Assert
|
|
result.Should().HaveCount(2);
|
|
result.Should().OnlyContain(s => s.IsAvailable);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Handle_WhenAlreadyEnrolled_SubjectShouldNotBeAvailable()
|
|
{
|
|
// Arrange
|
|
var student = new Domain.Entities.Student("John", Domain.ValueObjects.Email.Create("john@test.com"));
|
|
SetEntityId(student, 1);
|
|
|
|
var professor = new Domain.Entities.Professor("Prof A");
|
|
SetEntityId(professor, 1);
|
|
|
|
var subject = CreateSubject(1, "Math", 1, professor);
|
|
|
|
var enrollment = new Domain.Entities.Enrollment(1, 1);
|
|
SetNavigationProperty(enrollment, "Subject", subject);
|
|
student.AddEnrollment(enrollment);
|
|
|
|
_studentRepository.GetByIdWithEnrollmentsAsync(1, Arg.Any<CancellationToken>())
|
|
.Returns(student);
|
|
_subjectRepository.GetAllWithProfessorsAsync(Arg.Any<CancellationToken>())
|
|
.Returns(new List<Domain.Entities.Subject> { subject });
|
|
|
|
var query = new GetAvailableSubjectsQuery(1);
|
|
|
|
// Act
|
|
var result = await _handler.Handle(query, CancellationToken.None);
|
|
|
|
// Assert
|
|
result[0].IsAvailable.Should().BeFalse();
|
|
result[0].UnavailableReason.Should().Be("Already enrolled");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Handle_WhenSameProfessor_SubjectShouldNotBeAvailable()
|
|
{
|
|
// Arrange
|
|
var student = new Domain.Entities.Student("John", Domain.ValueObjects.Email.Create("john@test.com"));
|
|
SetEntityId(student, 1);
|
|
|
|
var professor = new Domain.Entities.Professor("Prof A");
|
|
SetEntityId(professor, 1);
|
|
|
|
var subject1 = CreateSubject(1, "Math", 1, professor);
|
|
var subject2 = CreateSubject(2, "Physics", 1, professor);
|
|
|
|
var enrollment = new Domain.Entities.Enrollment(1, 1);
|
|
SetNavigationProperty(enrollment, "Subject", subject1);
|
|
student.AddEnrollment(enrollment);
|
|
|
|
_studentRepository.GetByIdWithEnrollmentsAsync(1, Arg.Any<CancellationToken>())
|
|
.Returns(student);
|
|
_subjectRepository.GetAllWithProfessorsAsync(Arg.Any<CancellationToken>())
|
|
.Returns(new List<Domain.Entities.Subject> { subject1, subject2 });
|
|
|
|
var query = new GetAvailableSubjectsQuery(1);
|
|
|
|
// Act
|
|
var result = await _handler.Handle(query, CancellationToken.None);
|
|
|
|
// Assert
|
|
var physics = result.First(s => s.Name == "Physics");
|
|
physics.IsAvailable.Should().BeFalse();
|
|
physics.UnavailableReason.Should().Be("Already have a subject with this professor");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Handle_WhenMaxEnrollmentsReached_AllSubjectsShouldBeUnavailable()
|
|
{
|
|
// Arrange
|
|
var student = new Domain.Entities.Student("John", Domain.ValueObjects.Email.Create("john@test.com"));
|
|
SetEntityId(student, 1);
|
|
|
|
var prof1 = new Domain.Entities.Professor("Prof A");
|
|
var prof2 = new Domain.Entities.Professor("Prof B");
|
|
var prof3 = new Domain.Entities.Professor("Prof C");
|
|
var prof4 = new Domain.Entities.Professor("Prof D");
|
|
SetEntityId(prof1, 1);
|
|
SetEntityId(prof2, 2);
|
|
SetEntityId(prof3, 3);
|
|
SetEntityId(prof4, 4);
|
|
|
|
var subject1 = CreateSubject(1, "Math", 1, prof1);
|
|
var subject2 = CreateSubject(2, "Physics", 2, prof2);
|
|
var subject3 = CreateSubject(3, "Chemistry", 3, prof3);
|
|
var subject4 = CreateSubject(4, "Biology", 4, prof4);
|
|
|
|
student.AddEnrollment(CreateEnrollmentWithSubject(1, 1, subject1));
|
|
student.AddEnrollment(CreateEnrollmentWithSubject(1, 2, subject2));
|
|
student.AddEnrollment(CreateEnrollmentWithSubject(1, 3, subject3));
|
|
|
|
_studentRepository.GetByIdWithEnrollmentsAsync(1, Arg.Any<CancellationToken>())
|
|
.Returns(student);
|
|
_subjectRepository.GetAllWithProfessorsAsync(Arg.Any<CancellationToken>())
|
|
.Returns(new List<Domain.Entities.Subject> { subject4 });
|
|
|
|
var query = new GetAvailableSubjectsQuery(1);
|
|
|
|
// Act
|
|
var result = await _handler.Handle(query, CancellationToken.None);
|
|
|
|
// Assert
|
|
result[0].IsAvailable.Should().BeFalse();
|
|
result[0].UnavailableReason.Should().Be("Maximum 3 subjects reached");
|
|
}
|
|
|
|
private static void SetEntityId<T>(T entity, int id) where T : class
|
|
{
|
|
typeof(T).GetProperty("Id")?.SetValue(entity, id);
|
|
}
|
|
|
|
private static void SetNavigationProperty<T>(T entity, string propertyName, object value) where T : class
|
|
{
|
|
typeof(T).GetProperty(propertyName)?.SetValue(entity, value);
|
|
}
|
|
|
|
private static Domain.Entities.Subject CreateSubject(int id, string name, int professorId, Domain.Entities.Professor professor)
|
|
{
|
|
var subject = new Domain.Entities.Subject(name, professorId);
|
|
SetEntityId(subject, id);
|
|
SetNavigationProperty(subject, "Professor", professor);
|
|
return subject;
|
|
}
|
|
|
|
private static Domain.Entities.Enrollment CreateEnrollmentWithSubject(int studentId, int subjectId, Domain.Entities.Subject subject)
|
|
{
|
|
var enrollment = new Domain.Entities.Enrollment(studentId, subjectId);
|
|
SetNavigationProperty(enrollment, "Subject", subject);
|
|
return enrollment;
|
|
}
|
|
}
|