huangye-parking/server/tests/HuangyanParking.Tests/AdminCrudPropertyTests.cs
2026-02-28 17:35:49 +08:00

309 lines
10 KiB
C#

using FsCheck;
using FsCheck.Xunit;
using HuangyanParking.Domain.Entities;
using HuangyanParking.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
namespace HuangyanParking.Tests;
/// <summary>
/// 管理后台CRUD Round-Trip 属性测试
/// </summary>
public class AdminCrudPropertyTests
{
/// <summary>创建内存数据库上下文</summary>
private static AppDbContext CreateDbContext()
{
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
return new AppDbContext(options);
}
/// <summary>
/// Feature: huangyan-parking-points, Property 20: 管理后台CRUD Round-Trip — Banner
/// 创建/更新后查询应得到与输入一致的结果
/// Validates: Requirements 18.2
/// </summary>
[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;
}
/// <summary>
/// Feature: huangyan-parking-points, Property 20: 管理后台CRUD Round-Trip — 门店
/// 创建/更新后查询应得到与输入一致的结果
/// Validates: Requirements 18.4
/// </summary>
[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;
}
/// <summary>
/// Feature: huangyan-parking-points, Property 20: 管理后台CRUD Round-Trip — 优惠券模板
/// 创建/更新后查询应得到与输入一致的结果
/// Validates: Requirements 18.6
/// </summary>
[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;
}
/// <summary>
/// Feature: huangyan-parking-points, Property 20: 管理后台CRUD Round-Trip — 弹窗内容
/// 创建/更新后查询应得到与输入一致的结果
/// Validates: Requirements 18.3
/// </summary>
[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;
}
/// <summary>
/// Feature: huangyan-parking-points, Property 20: 管理后台CRUD Round-Trip — 协议内容
/// 创建/更新后查询应得到与输入一致的结果
/// Validates: Requirements 18.11
/// </summary>
[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;
}
/// <summary>
/// Feature: huangyan-parking-points, Property 20: 管理后台CRUD Round-Trip — 积分规则
/// 创建/更新后查询应得到与输入一致的结果
/// Validates: Requirements 18.10
/// </summary>
[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;
}
/// <summary>
/// Feature: huangyan-parking-points, Property 20: 管理后台CRUD Round-Trip — 商户员工
/// 绑定/解绑后查询应得到一致的结果
/// Validates: Requirements 18.5
/// </summary>
[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;
}
}