using Microsoft.EntityFrameworkCore; using NSubstitute; using UniVerse.Application.DTOs.Lectures; using UniVerse.Application.DTOs.Notifications; using UniVerse.Application.Interfaces; using UniVerse.Domain.Entities; using UniVerse.Infrastructure.Data; using UniVerse.Infrastructure.Services; using Xunit; namespace UniVerse.Api.Tests.Lectures; public class LectureServiceTests { [Fact] public async Task GetAllAsync_MarksLecturesEnrolledByCurrentUser() { await using var db = CreateDbContext(); var service = new LectureService(db, Substitute.For(), Substitute.For()); var startsAt = DateTime.UtcNow.AddDays(1); db.Users.Add(new User { Id = 1, Email = "student@test.local" }); db.Courses.Add(new Course { Id = 1, Name = "Course" }); db.Lectures.AddRange( Lecture(1, startsAt), Lecture(2, startsAt.AddDays(1))); db.LectureEnrollments.Add(new LectureEnrollment { LectureId = 1, UserId = 1 }); await db.SaveChangesAsync(); var result = await service.GetAllAsync(new LectureFilterRequest(null, null, null, null, null, null, null, null), 1); Assert.True(result.Items.Single(item => item.Id == 1).IsEnrolled); Assert.False(result.Items.Single(item => item.Id == 2).IsEnrolled); } [Fact] public async Task EnrollAsync_SchedulesLectureReminders() { await using var db = CreateDbContext(); var scheduler = Substitute.For(); var service = new LectureService(db, Substitute.For(), scheduler); var startsAt = DateTime.UtcNow.AddHours(4); db.Users.Add(new User { Id = 1, Email = "student@test.local", DisplayName = "Student" }); db.Courses.Add(new Course { Id = 1, Name = "Course" }); db.Lectures.Add(Lecture(1, startsAt)); await db.SaveChangesAsync(); await service.EnrollAsync(1, 1); await scheduler.Received(1).ScheduleAsync( Arg.Is(m => m.Recipient == "student@test.local" && m.Subject.Contains("через 3 часа")), Arg.Is(d => d == new DateTimeOffset(startsAt.AddHours(-3))), "lecture-1-user-1-starts-in-3-hours", Arg.Any()); await scheduler.Received(1).ScheduleAsync( Arg.Is(m => m.Recipient == "student@test.local" && m.Subject.Contains("через 1 час")), Arg.Is(d => d == new DateTimeOffset(startsAt.AddHours(-1))), "lecture-1-user-1-starts-in-1-hour", Arg.Any()); await scheduler.Received(1).ScheduleAsync( Arg.Is(m => m.Recipient == "student@test.local" && m.Subject.Contains("Оцените")), Arg.Is(d => d == new DateTimeOffset(startsAt.AddHours(2))), "lecture-1-user-1-ended", Arg.Any()); } [Fact] public async Task EnrollAsync_SkipsPastLectureReminders() { await using var db = CreateDbContext(); var scheduler = Substitute.For(); var service = new LectureService(db, Substitute.For(), scheduler); var startsAt = DateTime.UtcNow.AddMinutes(90); db.Users.Add(new User { Id = 1, Email = "student@test.local" }); db.Courses.Add(new Course { Id = 1, Name = "Course" }); db.Lectures.Add(Lecture(1, startsAt)); await db.SaveChangesAsync(); await service.EnrollAsync(1, 1); await scheduler.DidNotReceive().ScheduleAsync( Arg.Any(), Arg.Any(), "lecture-1-user-1-starts-in-3-hours", Arg.Any()); await scheduler.Received(2).ScheduleAsync( Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); } [Fact] public async Task UnenrollAsync_CancelsLectureReminders() { await using var db = CreateDbContext(); var scheduler = Substitute.For(); var service = new LectureService(db, Substitute.For(), scheduler); var startsAt = DateTime.UtcNow.AddHours(4); db.Users.Add(new User { Id = 1, Email = "student@test.local" }); db.Courses.Add(new Course { Id = 1, Name = "Course" }); db.Lectures.Add(Lecture(1, startsAt)); db.LectureEnrollments.Add(new LectureEnrollment { LectureId = 1, UserId = 1 }); await db.SaveChangesAsync(); await service.UnenrollAsync(1, 1); await scheduler.Received(1).CancelAsync("lecture-1-user-1-starts-in-3-hours", Arg.Any()); await scheduler.Received(1).CancelAsync("lecture-1-user-1-starts-in-1-hour", Arg.Any()); await scheduler.Received(1).CancelAsync("lecture-1-user-1-ended", Arg.Any()); } private static AppDbContext CreateDbContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase($"LectureServiceTests_{Guid.NewGuid()}") .Options; return new AppDbContext(options); } private static Lecture Lecture(int id, DateTime startsAt) => new() { Id = id, CourseId = 1, Title = $"Lecture {id}", StartsAt = startsAt, EndsAt = startsAt.AddHours(2), IsOpen = true, MaxEnrollments = 30 }; }