using FsCheck; using FsCheck.Xunit; using HuangyanParking.Domain.Entities; using HuangyanParking.Infrastructure.Data; using Microsoft.EntityFrameworkCore; namespace HuangyanParking.Tests; /// /// 管理后台CRUD Round-Trip 属性测试 /// public class AdminCrudPropertyTests { /// 创建内存数据库上下文 private static AppDbContext CreateDbContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .Options; return new AppDbContext(options); } /// /// Feature: huangyan-parking-points, Property 20: 管理后台CRUD Round-Trip — Banner /// 创建/更新后查询应得到与输入一致的结果 /// Validates: Requirements 18.2 /// [Property(MaxTest = 100)] public bool Banner_CrudRoundTrip_ShouldBeConsistent( NonEmptyString imageUrlWrapper, byte sortOrder, bool isActive) { using var db = CreateDbContext(); var imageUrl = imageUrlWrapper.Get; // 创建 var banner = new Banner { ImageUrl = imageUrl, SortOrder = sortOrder, IsActive = isActive, }; db.Banners.Add(banner); db.SaveChanges(); // 查询验证创建 var queried = db.Banners.Find(banner.Id)!; if (queried.ImageUrl != imageUrl || queried.SortOrder != sortOrder || queried.IsActive != isActive) return false; // 更新 var newUrl = imageUrl + "_updated"; var newSort = (sortOrder + 1) % 256; queried.ImageUrl = newUrl; queried.SortOrder = newSort; queried.IsActive = !isActive; db.SaveChanges(); // 查询验证更新 var updated = db.Banners.Find(banner.Id)!; return updated.ImageUrl == newUrl && updated.SortOrder == newSort && updated.IsActive == !isActive; } /// /// Feature: huangyan-parking-points, Property 20: 管理后台CRUD Round-Trip — 门店 /// 创建/更新后查询应得到与输入一致的结果 /// Validates: Requirements 18.4 /// [Property(MaxTest = 100)] public bool Store_CrudRoundTrip_ShouldBeConsistent( NonEmptyString nameWrapper, NonEmptyString addressWrapper, bool isActive) { using var db = CreateDbContext(); var name = nameWrapper.Get; var address = addressWrapper.Get; // 创建 var store = new Store { Name = name, Address = address, Type = "human", IsActive = isActive, }; db.Stores.Add(store); db.SaveChanges(); // 查询验证创建 var queried = db.Stores.Find(store.Id)!; if (queried.Name != name || queried.Address != address || queried.IsActive != isActive) return false; // 更新 var newName = name + "_updated"; queried.Name = newName; queried.Type = "unmanned"; queried.IsActive = !isActive; db.SaveChanges(); // 查询验证更新 var updated = db.Stores.Find(store.Id)!; return updated.Name == newName && updated.Type == "unmanned" && updated.IsActive == !isActive; } /// /// Feature: huangyan-parking-points, Property 20: 管理后台CRUD Round-Trip — 优惠券模板 /// 创建/更新后查询应得到与输入一致的结果 /// Validates: Requirements 18.6 /// [Property(MaxTest = 100)] public bool CouponTemplate_CrudRoundTrip_ShouldBeConsistent( NonEmptyString nameWrapper, PositiveInt pointsCostWrapper, PositiveInt totalCountWrapper) { using var db = CreateDbContext(); var name = nameWrapper.Get; var pointsCost = pointsCostWrapper.Get % 1000 + 1; var totalCount = totalCountWrapper.Get % 500 + 1; // 先创建门店(外键依赖) var store = new Store { Name = "测试门店", Type = "human" }; db.Stores.Add(store); db.SaveChanges(); // 创建优惠券模板 var template = new CouponTemplate { Name = name, CouponType = "free", StoreId = store.Id, ValidStart = DateTime.UtcNow, ValidEnd = DateTime.UtcNow.AddDays(30), TotalCount = totalCount, RemainingCount = totalCount, PointsCost = pointsCost, Source = "platform", }; db.CouponTemplates.Add(template); db.SaveChanges(); // 查询验证创建 var queried = db.CouponTemplates.Find(template.Id)!; if (queried.Name != name || queried.PointsCost != pointsCost || queried.TotalCount != totalCount || queried.RemainingCount != totalCount) return false; // 更新 var newName = name + "_v2"; var newPointsCost = pointsCost + 10; queried.Name = newName; queried.PointsCost = newPointsCost; queried.CouponType = "discount"; queried.DiscountAmount = 20m; db.SaveChanges(); // 查询验证更新 var updated = db.CouponTemplates.Find(template.Id)!; return updated.Name == newName && updated.PointsCost == newPointsCost && updated.CouponType == "discount" && updated.DiscountAmount == 20m; } /// /// Feature: huangyan-parking-points, Property 20: 管理后台CRUD Round-Trip — 弹窗内容 /// 创建/更新后查询应得到与输入一致的结果 /// Validates: Requirements 18.3 /// [Property(MaxTest = 100)] public bool PopupContent_CrudRoundTrip_ShouldBeConsistent(NonEmptyString contentWrapper) { using var db = CreateDbContext(); var content = contentWrapper.Get; // 创建 var popup = new PopupContent { Type = "verify-flow", Content = content }; db.PopupContents.Add(popup); db.SaveChanges(); // 查询验证创建 var queried = db.PopupContents.FirstOrDefault(p => p.Type == "verify-flow")!; if (queried.Content != content) return false; // 更新 var newContent = content + "_updated"; queried.Content = newContent; db.SaveChanges(); // 查询验证更新 var updated = db.PopupContents.FirstOrDefault(p => p.Type == "verify-flow")!; return updated.Content == newContent; } /// /// Feature: huangyan-parking-points, Property 20: 管理后台CRUD Round-Trip — 协议内容 /// 创建/更新后查询应得到与输入一致的结果 /// Validates: Requirements 18.11 /// [Property(MaxTest = 100)] public bool Agreement_CrudRoundTrip_ShouldBeConsistent(NonEmptyString contentWrapper) { using var db = CreateDbContext(); var content = contentWrapper.Get; // 创建 var agreement = new Agreement { Type = "user-agreement", Content = content }; db.Agreements.Add(agreement); db.SaveChanges(); // 查询验证创建 var queried = db.Agreements.FirstOrDefault(a => a.Type == "user-agreement")!; if (queried.Content != content) return false; // 更新 var newContent = content + "_v2"; queried.Content = newContent; db.SaveChanges(); // 查询验证更新 var updated = db.Agreements.FirstOrDefault(a => a.Type == "user-agreement")!; return updated.Content == newContent; } /// /// Feature: huangyan-parking-points, Property 20: 管理后台CRUD Round-Trip — 积分规则 /// 创建/更新后查询应得到与输入一致的结果 /// Validates: Requirements 18.10 /// [Property(MaxTest = 100)] public bool PointsRule_CrudRoundTrip_ShouldBeConsistent( PositiveInt chargeWrapper, PositiveInt pointsWrapper) { using var db = CreateDbContext(); var chargeAmount = (decimal)(chargeWrapper.Get % 1000 + 1); var pointsValue = pointsWrapper.Get % 500 + 1; // 创建 var rule = new PointsRule { ChargeAmount = chargeAmount, PointsValue = pointsValue, IsActive = true, }; db.PointsRules.Add(rule); db.SaveChanges(); // 查询验证创建 var queried = db.PointsRules.Find(rule.Id)!; if (queried.ChargeAmount != chargeAmount || queried.PointsValue != pointsValue) return false; // 更新 var newCharge = chargeAmount + 10; var newPoints = pointsValue + 5; queried.ChargeAmount = newCharge; queried.PointsValue = newPoints; db.SaveChanges(); // 查询验证更新 var updated = db.PointsRules.Find(rule.Id)!; return updated.ChargeAmount == newCharge && updated.PointsValue == newPoints; } /// /// Feature: huangyan-parking-points, Property 20: 管理后台CRUD Round-Trip — 商户员工 /// 绑定/解绑后查询应得到一致的结果 /// Validates: Requirements 18.5 /// [Property(MaxTest = 100)] public bool Merchant_BindUnbind_ShouldBeConsistent(PositiveInt storeIdSeed) { using var db = CreateDbContext(); // 创建门店 var store = new Store { Name = "测试门店", Type = "human" }; db.Stores.Add(store); db.SaveChanges(); // 创建用户并绑定为商户 var user = new User { Uid = "123456", Phone = "13800001111", IsMerchant = true, StoreId = store.Id, }; db.Users.Add(user); db.SaveChanges(); // 查询验证绑定 var queried = db.Users.Find(user.Id)!; if (!queried.IsMerchant || queried.StoreId != store.Id) return false; // 解绑 queried.IsMerchant = false; queried.StoreId = null; db.SaveChanges(); // 查询验证解绑 var updated = db.Users.Find(user.Id)!; return !updated.IsMerchant && updated.StoreId == null; } }