Files
UniVerse/backend/UniVerse.Api.Tests/Gamification/GamificationServiceTests.cs
T
serega404 811b6ef51a
Backend CI / build-and-test (push) Successful in 52s
Frontend CI / build-and-check (push) Failing after 5m15s
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Successful in 16s
🚀 Create and publish a Docker image / Build & publish backend image (push) Successful in 1m0s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Successful in 32s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 13s
fix: перенёс уровни в бд и пофиксид их отображение на фронте
2026-05-18 02:28:05 +03:00

184 lines
6.9 KiB
C#

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging.Abstractions;
using NSubstitute;
using UniVerse.Application.DTOs.Notifications;
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.Gamification;
public class GamificationServiceTests
{
[Fact]
public async Task CheckAndAwardAchievementsAsync_AwardsModernConditionsOnce()
{
await using var db = CreateDbContext();
SeedLevelThresholds(db);
var service = CreateService(db);
db.Users.Add(new User
{
Id = 1,
Email = "student@test.local",
DisplayName = "Student",
AvatarUrl = "avatar.png",
Xp = 100,
Coins = 510
});
db.Courses.Add(new Course { Id = 1, Name = "Course" });
db.Lectures.Add(new Lecture
{
Id = 1,
CourseId = 1,
Title = "Future lecture",
StartsAt = DateTime.UtcNow.AddDays(1),
EndsAt = DateTime.UtcNow.AddDays(1).AddHours(2),
IsOpen = true
});
db.LectureEnrollments.Add(new LectureEnrollment { UserId = 1, LectureId = 1 });
db.Reviews.AddRange(
new Review { Id = 1, UserId = 1, LectureId = 1, Rating = ReviewRating.Like },
new Review { Id = 2, UserId = 1, LectureId = 1, Rating = ReviewRating.Neutral },
new Review { Id = 3, UserId = 1, LectureId = 1, Rating = ReviewRating.Dislike });
db.CoinTransactions.Add(new CoinTransaction
{
UserId = 1,
Amount = 510,
Type = CoinTransactionType.AdminAdjustment,
Description = "Initial coins"
});
db.Achievements.AddRange(
Achievement(1001, "First activity", "first_activity:1", 10),
Achievement(1002, "Reviews", "reviews_written:3", 20),
Achievement(1003, "Active registrations", "active_registrations:1", 30),
Achievement(1004, "Coins earned", "coins_earned:500", 40),
Achievement(1005, "Level reached", "level_reached:2", 50),
Achievement(1006, "Profile completed", "profile_completed:1", 60),
Achievement(1007, "Old condition", "reviews_1", 100));
await db.SaveChangesAsync();
await service.CheckAndAwardAchievementsAsync(1);
await service.CheckAndAwardAchievementsAsync(1);
var user = await db.Users.FindAsync(1);
Assert.NotNull(user);
Assert.Equal(720, user!.Coins);
Assert.Equal(310, user.Xp);
Assert.Equal(6, await db.UserAchievements.CountAsync(ua => ua.UserId == 1));
Assert.False(await db.UserAchievements.AnyAsync(ua => ua.AchievementId == 1007));
Assert.Equal(6, await db.CoinTransactions.CountAsync(ct =>
ct.UserId == 1 && ct.Type == CoinTransactionType.AchievementReward));
}
[Fact]
public async Task CheckAndAwardAchievementsAsync_CountsConsecutiveIsoWeeksAcrossYears()
{
await using var db = CreateDbContext();
SeedLevelThresholds(db);
var service = CreateService(db);
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, new DateTime(2025, 12, 29, 10, 0, 0, DateTimeKind.Utc)),
Lecture(2, new DateTime(2026, 1, 5, 10, 0, 0, DateTimeKind.Utc)),
Lecture(3, new DateTime(2026, 1, 12, 10, 0, 0, DateTimeKind.Utc)));
db.LectureEnrollments.AddRange(
new LectureEnrollment { UserId = 1, LectureId = 1, Attended = true },
new LectureEnrollment { UserId = 1, LectureId = 2, Attended = true },
new LectureEnrollment { UserId = 1, LectureId = 3, Attended = true });
db.Achievements.Add(Achievement(1001, "Streak", "attendance_streak_weeks:3", 10));
await db.SaveChangesAsync();
await service.CheckAndAwardAchievementsAsync(1);
Assert.True(await db.UserAchievements.AnyAsync(ua => ua.UserId == 1 && ua.AchievementId == 1001));
}
[Theory]
[InlineData(0, 1)]
[InlineData(99, 1)]
[InlineData(100, 2)]
[InlineData(299, 2)]
[InlineData(300, 3)]
public async Task CalculateLevelAsync_UsesDatabaseThresholds(int xp, int expectedLevel)
{
await using var db = CreateDbContext();
SeedLevelThresholds(db);
var service = CreateService(db);
var level = await service.CalculateLevelAsync(xp);
Assert.Equal(expectedLevel, level);
}
[Theory]
[InlineData(120, 100, 300)]
[InlineData(350, 300, null)]
public async Task GetLevelProgressAsync_ReturnsCurrentAndNextThresholds(int xp, int currentLevelXp, int? nextLevelXp)
{
await using var db = CreateDbContext();
SeedLevelThresholds(db);
var service = CreateService(db);
var progress = await service.GetLevelProgressAsync(xp);
Assert.Equal(currentLevelXp, progress.CurrentLevelXp);
Assert.Equal(nextLevelXp, progress.NextLevelXp);
}
private static AppDbContext CreateDbContext()
{
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase($"GamificationTests_{Guid.NewGuid()}")
.Options;
return new AppDbContext(options);
}
private static GamificationService CreateService(AppDbContext db)
{
var notifications = Substitute.For<INotificationService>();
notifications.CreateUserNotificationAsync(
Arg.Any<int>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<string>(),
Arg.Any<CancellationToken>())
.Returns(callInfo => new UserNotificationDto(1, callInfo.ArgAt<string>(1), callInfo.ArgAt<string>(2), callInfo.ArgAt<string>(3), false, DateTime.UtcNow));
notifications.SendAsync(Arg.Any<NotificationMessage>(), Arg.Any<CancellationToken>())
.Returns(Task.CompletedTask);
return new GamificationService(db, notifications, NullLogger<GamificationService>.Instance);
}
private static void SeedLevelThresholds(AppDbContext db)
{
db.LevelThresholds.AddRange(
new LevelThreshold { Level = 1, RequiredXp = 0 },
new LevelThreshold { Level = 2, RequiredXp = 100 },
new LevelThreshold { Level = 3, RequiredXp = 300 });
db.SaveChanges();
}
private static Achievement Achievement(int id, string name, string condition, int coinReward) => new()
{
Id = id,
Name = name,
Condition = condition,
CoinReward = coinReward
};
private static Lecture Lecture(int id, DateTime startsAt) => new()
{
Id = id,
CourseId = 1,
Title = $"Lecture {id}",
StartsAt = startsAt,
EndsAt = startsAt.AddHours(2)
};
}