using Store.Users.Contracts.Enums; using FreeSql; using MAX.Framework.Security; using Store.Users.Contracts; using Store.Users.Entities; using Store.Users.Services; using System.Security.Claims; using YourNamespace; namespace Store.Users.Tests; /// 每个测试独享数据库和身份上下文,不读取宿主连接配置,可并行运行。 internal sealed class UserTestContext : IDisposable { private readonly string _path = Path.Combine(Path.GetTempPath(), $"store-user-test-{Guid.NewGuid():N}.db"); public IFreeSql Db { get; } public TestCurrentUser CurrentUser { get; } = new(); public TestOnlineUsers Sessions { get; } = new(); public IUserQueryService Query { get; } public IUserAuthenticationService Authentication { get; } public UserManagementService Users { get; } public UserFundsService Funds { get; } public UserEntity Admin { get; } public UserEntity Main { get; } public UserEntity Other { get; } public UserEntity Child { get; } public UserTestContext() { Db = new FreeSqlBuilder() .UseMappingPriority(FreeSql.Internal.MappingPriorityType.Attribute, FreeSql.Internal.MappingPriorityType.Aop, FreeSql.Internal.MappingPriorityType.FluentApi) .UseConnectionString(DataType.Sqlite, $"Data Source={_path};Pooling=False") .Build(); // 覆盖业务库 schema,只在临时库建立实体测试表。 Db.CodeFirst.ConfigEntity(e => e.Name("users")); Db.CodeFirst.ConfigEntity(e => e.Name("points")); Db.CodeFirst.ConfigEntity(e => e.Name("payments")); Db.CodeFirst.ConfigEntity(e => e.Name("shops")); Db.CodeFirst.ConfigEntity(e => e.Name("oems")); Db.CodeFirst.ConfigEntity(e => e.Name("messages")); Db.CodeFirst.ConfigEntity(e => e.Name("qrcodes")); Db.CodeFirst.SyncStructure(typeof(UserEntity), typeof(UserPointsRecordEntity), typeof(UserPaymentRecordEntity), typeof(ShopEntity), typeof(OEMEntity), typeof(UserTopUpMessageEntity), typeof(UserTopUpQrcodeEntity)); // 模拟部署脚本提供的唯一索引。 Db.Ado.ExecuteNonQuery("CREATE UNIQUE INDEX ux_user_name ON users(user_name)"); Admin = Seed("admin", 1); Main = Seed("main"); Other = Seed("other"); Child = Seed("child", parent: Main.UserName); CurrentUser.UserName = Admin.UserName; // 生产实现为 internal;通过反射构造真实仓储和查询服务,无需放宽业务代码可见性。 var assembly = typeof(UserManagementService).Assembly; var repository = Activator.CreateInstance(assembly.GetType("Store.Users.Repositories.UserRepository", true)!, Db)!; Query = (IUserQueryService)Activator.CreateInstance(assembly.GetType("Store.Users.Services.UserQueryService", true)!, repository)!; Authentication = (IUserAuthenticationService)Activator.CreateInstance( assembly.GetType("Store.Users.Services.UserAuthenticationService", true)!, Db, Query)!; Users = new(Db, CurrentUser, Query, Sessions); Funds = new(Db, CurrentUser); } public UserEntity Seed(string name, short level = 0, string parent = "") { var user = new UserEntity { UserName = name, NickName = name, Password = "legacy-password", ParentUserName = parent, GroupId = string.IsNullOrEmpty(parent) ? UserGroup.MainAccount : UserGroup.CustomerService, SaLevel = (UserAdminLevel)level, Mobile = "", CompanyName = "", Notes = "", CardId = "", Authority = "积分用户,订单", ForbiddenPage = "", OemId = "", Balance = 0, Points = 0, ShopPointsPerMonth = 30, PostagePerOrder = 1m, RefundPostagePerOrder = 1m }; user.Id = checked((int)Db.Insert(user).ExecuteIdentity()); return user; } public Task Read(string name) => Db.Select().Where(u => u.UserName == name).FirstAsync(); public void Dispose() { Db.Dispose(); if (File.Exists(_path)) File.Delete(_path); } } internal sealed class TestCurrentUser : ICurrentUser { public bool IsAuthenticated { get; set; } = true; public string? UserName { get; set; } public string? NickName => UserName; public IEnumerable Claims => []; public string? GetClaimValue(string claimType) => null; public T? GetClaimValue(string claimType) => default; } internal sealed class TestOnlineUsers : IOnlineUserStore { public HashSet SignedOut { get; } = []; public void SignOutSession(string userName, string jti) => SignedOut.Add(userName); public void SignOut(string userName) => SignedOut.Add(userName); public void SignIn(OnlineUserInfo info) { } public bool IsTokenActive(string userName, string jti) => false; public IReadOnlyList GetOnlineUsers() => []; public int Count => 0; }