using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging.Abstractions; using NSubstitute; using UniVerse.Application.Interfaces; using UniVerse.Domain.Entities; using UniVerse.Domain.Enums; using UniVerse.Infrastructure.Data; using UniVerse.Infrastructure.Services; using Xunit; namespace UniVerse.Api.Tests.Reviews; public class LlmAnalysisServiceTests { [Fact] public async Task AnalyzeReviewAsync_SavesParsedAnalysisResult() { await using var db = CreateDbContext(); await SeedPendingReviewAsync(db); var llm = Substitute.For(); llm.AnalyzeReviewAsync(Arg.Any(), Arg.Any()) .Returns(new LlmReviewAnalysis( 0.76, "Положительный", ["lecture structure", "practical examples"], true, "{\"quality_score\":0.76,\"sentiment\":\"Положительный\"}")); var gamification = Substitute.For(); gamification.AwardCoinsAsync( Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(Task.CompletedTask); gamification.CheckAndAwardAchievementsAsync(Arg.Any()).Returns(Task.CompletedTask); var service = new LlmAnalysisService(db, llm, gamification, NullLogger.Instance); await service.AnalyzeReviewAsync(1); var review = await db.Reviews.SingleAsync(r => r.Id == 1); Assert.Equal(ReviewLlmStatus.Analyzed, review.LlmStatus); Assert.Equal(ReviewSentiment.Positive, review.Sentiment); Assert.Equal(0.76, review.QualityScore); Assert.True(review.IsInformative); Assert.Equal(["lecture structure", "practical examples"], review.LlmTags!); Assert.Equal("{\"quality_score\":0.76,\"sentiment\":\"Положительный\"}", review.LlmRawOutput); await gamification.Received(1).AwardCoinsAsync( 1, 10, CoinTransactionType.ReviewReward, 1, null, "Informative review reward"); } private static async Task SeedPendingReviewAsync(AppDbContext db) { 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(new Lecture { Id = 1, CourseId = 1, Title = "Lecture", StartsAt = DateTime.UtcNow.AddDays(-1), EndsAt = DateTime.UtcNow.AddDays(-1).AddHours(2), IsOpen = true, MaxEnrollments = 30 }); db.Reviews.Add(new Review { Id = 1, LectureId = 1, UserId = 1, Rating = ReviewRating.Like, Text = "Useful review", LlmStatus = ReviewLlmStatus.Pending }); await db.SaveChangesAsync(); } private static AppDbContext CreateDbContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase($"LlmAnalysisServiceTests_{Guid.NewGuid()}") .Options; return new AppDbContext(options); } }