using Jiaowu.Api.Controllers; using Jiaowu.Api.Domain.Academic; using Jiaowu.Api.Domain.Identity; using Jiaowu.Api.Infrastructure.Auth; using Jiaowu.Api.Infrastructure.Persistence; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.DependencyInjection; namespace Jiaowu.Api.Tests; public sealed class AuthControllerTests { [Fact] public async Task ActivateStudent_WorksWithRetryingExecutionStrategy() { await using var connection = new SqliteConnection("Data Source=:memory:"); await connection.OpenAsync(); var services = new ServiceCollection(); services.AddLogging(); services.AddDbContext(options => options .UseSqlite(connection) .ReplaceService()); services .AddIdentityCore(options => { options.Password.RequiredLength = 8; options.Password.RequireDigit = true; options.Password.RequireLowercase = true; options.Password.RequireUppercase = true; options.Password.RequireNonAlphanumeric = true; }) .AddRoles() .AddEntityFrameworkStores(); await using var provider = services.BuildServiceProvider(); await using var scope = provider.CreateAsyncScope(); var db = scope.ServiceProvider.GetRequiredService(); await db.Database.EnsureCreatedAsync(); var roleManager = scope.ServiceProvider .GetRequiredService>(); Assert.True((await roleManager.CreateAsync(new ApplicationRole { Name = SystemRoles.Student, Description = "学生" })).Succeeded); var college = new College { Code = "CS", Name = "计算机学院" }; var major = new Major { Code = "SE", Name = "软件工程", CollegeId = college.Id, DegreeType = "工学" }; var administrativeClass = new AdministrativeClass { Code = "SE202601", Name = "软件工程 2026 级 1 班", MajorId = major.Id, Grade = 2026 }; var student = new Student { StudentNumber = "202601999", Name = "测试学生", AdministrativeClassId = administrativeClass.Id, EnrollmentYear = 2026, EnrollmentDate = new DateOnly(2026, 9, 1), Status = StudentStatus.Active }; db.AddRange(college, major, administrativeClass, student); await db.SaveChangesAsync(); var userManager = scope.ServiceProvider .GetRequiredService>(); Assert.True(db.Database.CreateExecutionStrategy().RetriesOnFailure); var controller = new AuthController(db, userManager, new StubTokenService()); var request = new StudentActivationRequest( student.Name, student.StudentNumber, college.Id, major.Id, administrativeClass.Grade, administrativeClass.Id, "Student@123"); var result = await controller.ActivateStudent(request, CancellationToken.None); Assert.IsType(result); var user = await userManager.FindByNameAsync(student.StudentNumber); Assert.NotNull(user); Assert.Equal(student.Name, user.DisplayName); Assert.Equal(college.Id, user.CollegeId); Assert.True(await userManager.IsInRoleAsync(user, SystemRoles.Student)); await db.Entry(student).ReloadAsync(); Assert.Equal(user.Id, student.UserId); } private sealed class RetryingExecutionStrategyFactory( ExecutionStrategyDependencies dependencies) : IExecutionStrategyFactory { public IExecutionStrategy Create() => new TestRetryingExecutionStrategy(dependencies); } private sealed class TestRetryingExecutionStrategy( ExecutionStrategyDependencies dependencies) : ExecutionStrategy(dependencies, 1, TimeSpan.Zero) { protected override bool ShouldRetryOn(Exception exception) => false; } private sealed class StubTokenService : ITokenService { public string Create(ApplicationUser user, IEnumerable roles) => string.Empty; } }