using Health.Application.Admin; using Health.Domain.Entities; using Health.Infrastructure.Admin; using Health.Infrastructure.Data; using Microsoft.EntityFrameworkCore; namespace Health.Tests; public sealed class AdminServiceTests { private static AppDbContext CreateDbContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; return new AppDbContext(options); } [Fact] public async Task AddDoctor_CreatesDoctorLoginAndProfileTogether() { await using var db = CreateDbContext(); var service = new AdminService(db); var result = await service.AddDoctorAsync( new AddDoctorCommand("13800138001", "王医生", "主任医师", "心内科", "高血压"), CancellationToken.None); Assert.Equal(0, result.Code); var doctor = await db.Doctors.SingleAsync(); var user = await db.Users.SingleAsync(); var profile = await db.DoctorProfiles.SingleAsync(); Assert.Equal("Doctor", user.Role); Assert.Equal(doctor.Id, profile.DoctorId); Assert.Equal(user.Id, profile.UserId); Assert.Equal(doctor.Name, user.Name); Assert.Equal(doctor.Name, profile.Name); Assert.Equal(doctor.Title, profile.Title); Assert.Equal(doctor.Department, profile.Department); } [Fact] public async Task AddDoctor_RejectsAPhoneAlreadyUsedByAnyAccount() { await using var db = CreateDbContext(); db.Users.Add(new User { Id = Guid.NewGuid(), Phone = "13800138002", Role = "User", CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow, }); await db.SaveChangesAsync(); var service = new AdminService(db); var result = await service.AddDoctorAsync( new AddDoctorCommand("13800138002", "李医生", null, null, null), CancellationToken.None); Assert.NotEqual(0, result.Code); Assert.Empty(db.Doctors); Assert.Empty(db.DoctorProfiles); } [Fact] public async Task UpdateDoctor_SynchronizesDoctorLoginAndProfile() { await using var db = CreateDbContext(); var (doctor, user, profile) = await SeedDoctorAsync(db); var service = new AdminService(db); var result = await service.UpdateDoctorAsync( doctor.Id, new UpdateDoctorCommand("13800138999", "新姓名", "副主任医师", "神经内科", "脑血管"), CancellationToken.None); Assert.Equal(0, result.Code); Assert.Equal("13800138999", doctor.Phone); Assert.Equal("13800138999", user.Phone); Assert.Equal("新姓名", doctor.Name); Assert.Equal("新姓名", user.Name); Assert.Equal("新姓名", profile.Name); Assert.Equal("副主任医师", profile.Title); Assert.Equal("神经内科", profile.Department); } [Fact] public async Task ToggleDoctor_SynchronizesAvailabilityButKeepsLoginEnabled() { await using var db = CreateDbContext(); var (doctor, user, profile) = await SeedDoctorAsync(db); var service = new AdminService(db); var result = await service.ToggleDoctorAsync(doctor.Id, CancellationToken.None); Assert.Equal(0, result.Code); Assert.False(doctor.IsActive); Assert.False(profile.IsActive); Assert.Equal("Doctor", user.Role); Assert.NotNull(await db.Users.FindAsync(user.Id)); } [Fact] public async Task DeleteDoctor_IsRejectedWhilePatientsAreBound() { await using var db = CreateDbContext(); var (doctor, user, profile) = await SeedDoctorAsync(db); db.Users.Add(new User { Id = Guid.NewGuid(), Phone = "13800138088", Name = "患者", Role = "User", DoctorId = doctor.Id, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow, }); await db.SaveChangesAsync(); var service = new AdminService(db); var result = await service.DeleteDoctorAsync(doctor.Id, CancellationToken.None); Assert.NotEqual(0, result.Code); Assert.NotNull(await db.Doctors.FindAsync(doctor.Id)); Assert.NotNull(await db.Users.FindAsync(user.Id)); Assert.NotNull(await db.DoctorProfiles.FindAsync(profile.Id)); } [Fact] public async Task DeleteUnusedDoctor_RemovesLoginProfileAndRefreshTokens() { await using var db = CreateDbContext(); var (doctor, user, _) = await SeedDoctorAsync(db); db.RefreshTokens.Add(new RefreshToken { Id = Guid.NewGuid(), UserId = user.Id, Token = "refresh-token", ExpiresAt = DateTime.UtcNow.AddDays(1), }); await db.SaveChangesAsync(); var service = new AdminService(db); var result = await service.DeleteDoctorAsync(doctor.Id, CancellationToken.None); Assert.Equal(0, result.Code); Assert.Empty(db.Doctors); Assert.Empty(db.DoctorProfiles); Assert.Empty(db.Users); Assert.Empty(db.RefreshTokens); } private static async Task<(Doctor Doctor, User User, DoctorProfile Profile)> SeedDoctorAsync(AppDbContext db) { var doctor = new Doctor { Id = Guid.NewGuid(), Phone = "13800138003", Name = "原姓名", Title = "医师", Department = "内科", IsActive = true, CreatedAt = DateTime.UtcNow, }; var user = new User { Id = Guid.NewGuid(), Phone = doctor.Phone, Name = doctor.Name, Role = "Doctor", CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow, }; var profile = new DoctorProfile { Id = Guid.NewGuid(), DoctorId = doctor.Id, UserId = user.Id, Name = doctor.Name, Title = doctor.Title, Department = doctor.Department, IsActive = true, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow, }; db.AddRange(doctor, user, profile); await db.SaveChangesAsync(); return (doctor, user, profile); } }