using System.Text.Json;
using FsCheck;
using FsCheck.Xunit;
using HoneyBox.Admin.Business.Models.Config;
using Xunit;
namespace HoneyBox.Tests.Services;
///
/// 微信支付 V3 配置属性测试
/// **Feature: wechat-pay-v3-upgrade**
///
public class WechatPayV3ConfigPropertyTests
{
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = null, // 使用原始属性名
WriteIndented = false
};
#region Property 1: 配置序列化 Round-Trip
///
/// **Feature: wechat-pay-v3-upgrade, Property 1: 配置序列化 Round-Trip**
/// *For any* 有效的 WeixinPayMerchant 配置对象(包含 V2 或 V3 字段),
/// 序列化为 JSON 后再反序列化,应该得到与原始对象等价的配置。
/// **Validates: Requirements 1.4, 1.5**
///
[Property(MaxTest = 100)]
public bool WeixinPayMerchant_V3Config_RoundTrip_ShouldPreserveAllFields(
NonEmptyString name,
NonEmptyString mchId,
NonEmptyString orderPrefix,
NonEmptyString apiKey,
NonEmptyString apiV3Key,
NonEmptyString certSerialNo,
NonEmptyString privateKeyPath,
NonEmptyString wechatPublicKeyId,
NonEmptyString wechatPublicKeyPath,
bool isV3)
{
// 创建包含 V3 字段的配置
var original = new WeixinPayMerchant
{
Name = name.Get,
MchId = mchId.Get,
OrderPrefix = orderPrefix.Get.Length >= 3 ? orderPrefix.Get.Substring(0, 3) : "ABC",
ApiKey = apiKey.Get,
IsEnabled = "1",
PayVersion = isV3 ? "V3" : "V2",
ApiV3Key = isV3 ? apiV3Key.Get : null,
CertSerialNo = isV3 ? certSerialNo.Get : null,
PrivateKeyPath = isV3 ? privateKeyPath.Get : null,
WechatPublicKeyId = isV3 ? wechatPublicKeyId.Get : null,
WechatPublicKeyPath = isV3 ? wechatPublicKeyPath.Get : null
};
// 序列化
var json = JsonSerializer.Serialize(original, JsonOptions);
// 反序列化
var deserialized = JsonSerializer.Deserialize(json, JsonOptions);
if (deserialized == null) return false;
// 验证所有字段
return original.Name == deserialized.Name &&
original.MchId == deserialized.MchId &&
original.OrderPrefix == deserialized.OrderPrefix &&
original.ApiKey == deserialized.ApiKey &&
original.IsEnabled == deserialized.IsEnabled &&
original.PayVersion == deserialized.PayVersion &&
original.ApiV3Key == deserialized.ApiV3Key &&
original.CertSerialNo == deserialized.CertSerialNo &&
original.PrivateKeyPath == deserialized.PrivateKeyPath &&
original.WechatPublicKeyId == deserialized.WechatPublicKeyId &&
original.WechatPublicKeyPath == deserialized.WechatPublicKeyPath;
}
///
/// **Feature: wechat-pay-v3-upgrade, Property 1: 配置序列化 Round-Trip**
/// *For any* 有效的 WeixinPaySetting 配置对象(包含多个商户),
/// 序列化为 JSON 后再反序列化,应该得到与原始对象等价的配置。
/// **Validates: Requirements 1.4, 1.5**
///
[Property(MaxTest = 100)]
public bool WeixinPaySetting_RoundTrip_ShouldPreserveAllMerchants(PositiveInt seed)
{
// 创建包含多个商户的配置
var merchantCount = (seed.Get % 3) + 1; // 1-3 个商户
var merchants = new List();
for (int i = 0; i < merchantCount; i++)
{
var isV3 = i % 2 == 0; // 交替 V2/V3
merchants.Add(new WeixinPayMerchant
{
Name = $"商户{i + 1}",
MchId = $"mch{seed.Get + i}",
OrderPrefix = $"M{i:D2}",
ApiKey = $"key{seed.Get + i}",
IsEnabled = "1",
PayVersion = isV3 ? "V3" : "V2",
ApiV3Key = isV3 ? $"v3key{seed.Get + i}" : null,
CertSerialNo = isV3 ? $"serial{seed.Get + i}" : null,
PrivateKeyPath = isV3 ? $"certs/{seed.Get + i}/key.pem" : null,
WechatPublicKeyId = isV3 ? $"pubkeyid{seed.Get + i}" : null,
WechatPublicKeyPath = isV3 ? $"certs/{seed.Get + i}/pub.pem" : null
});
}
var original = new WeixinPaySetting { Merchants = merchants };
// 序列化
var json = JsonSerializer.Serialize(original, JsonOptions);
// 反序列化
var deserialized = JsonSerializer.Deserialize(json, JsonOptions);
if (deserialized == null || deserialized.Merchants == null) return false;
if (original.Merchants.Count != deserialized.Merchants.Count) return false;
// 验证每个商户
for (int i = 0; i < original.Merchants.Count; i++)
{
var orig = original.Merchants[i];
var deser = deserialized.Merchants[i];
if (orig.Name != deser.Name ||
orig.MchId != deser.MchId ||
orig.OrderPrefix != deser.OrderPrefix ||
orig.ApiKey != deser.ApiKey ||
orig.IsEnabled != deser.IsEnabled ||
orig.PayVersion != deser.PayVersion ||
orig.ApiV3Key != deser.ApiV3Key ||
orig.CertSerialNo != deser.CertSerialNo ||
orig.PrivateKeyPath != deser.PrivateKeyPath ||
orig.WechatPublicKeyId != deser.WechatPublicKeyId ||
orig.WechatPublicKeyPath != deser.WechatPublicKeyPath)
{
return false;
}
}
return true;
}
///
/// **Feature: wechat-pay-v3-upgrade, Property 1: 配置序列化 Round-Trip**
/// *For any* V2 配置,PayVersion 默认值应该是 "V2"。
/// **Validates: Requirements 1.3**
///
[Fact]
public void WeixinPayMerchant_DefaultPayVersion_ShouldBeV2()
{
var merchant = new WeixinPayMerchant();
Assert.Equal("V2", merchant.PayVersion);
}
///
/// **Feature: wechat-pay-v3-upgrade, Property 1: 配置序列化 Round-Trip**
/// *For any* V3 配置 JSON,反序列化后应该正确读取所有 V3 字段。
/// **Validates: Requirements 1.1, 1.2**
///
[Fact]
public void WeixinPayMerchant_V3JsonDeserialization_ShouldReadAllV3Fields()
{
var json = @"{
""name"": ""测试商户"",
""mch_id"": ""1738725801"",
""order_prefix"": ""MYH"",
""api_key"": ""v2key"",
""is_enabled"": ""1"",
""pay_version"": ""V3"",
""api_v3_key"": ""d1cxc0vXCUH2984901DxddPJMYqcwcnd"",
""cert_serial_no"": ""SERIAL123456"",
""private_key_path"": ""certs/1738725801/apiclient_key.pem"",
""wechat_public_key_id"": ""PUB_KEY_ID_0117387258012026012500291641000801"",
""wechat_public_key_path"": ""certs/1738725801/pub_key.pem""
}";
var merchant = JsonSerializer.Deserialize(json, JsonOptions);
Assert.NotNull(merchant);
Assert.Equal("测试商户", merchant.Name);
Assert.Equal("1738725801", merchant.MchId);
Assert.Equal("MYH", merchant.OrderPrefix);
Assert.Equal("v2key", merchant.ApiKey);
Assert.Equal("1", merchant.IsEnabled);
Assert.Equal("V3", merchant.PayVersion);
Assert.Equal("d1cxc0vXCUH2984901DxddPJMYqcwcnd", merchant.ApiV3Key);
Assert.Equal("SERIAL123456", merchant.CertSerialNo);
Assert.Equal("certs/1738725801/apiclient_key.pem", merchant.PrivateKeyPath);
Assert.Equal("PUB_KEY_ID_0117387258012026012500291641000801", merchant.WechatPublicKeyId);
Assert.Equal("certs/1738725801/pub_key.pem", merchant.WechatPublicKeyPath);
}
///
/// **Feature: wechat-pay-v3-upgrade, Property 1: 配置序列化 Round-Trip**
/// *For any* V2 配置 JSON(不包含 V3 字段),反序列化后 V3 字段应该为 null。
/// **Validates: Requirements 1.3**
///
[Fact]
public void WeixinPayMerchant_V2JsonDeserialization_ShouldHaveNullV3Fields()
{
var json = @"{
""name"": ""V2商户"",
""mch_id"": ""1234567890"",
""order_prefix"": ""ABC"",
""api_key"": ""v2key"",
""is_enabled"": ""1""
}";
var merchant = JsonSerializer.Deserialize(json, JsonOptions);
Assert.NotNull(merchant);
Assert.Equal("V2商户", merchant.Name);
Assert.Equal("V2", merchant.PayVersion); // 默认值
Assert.Null(merchant.ApiV3Key);
Assert.Null(merchant.CertSerialNo);
Assert.Null(merchant.PrivateKeyPath);
Assert.Null(merchant.WechatPublicKeyId);
Assert.Null(merchant.WechatPublicKeyPath);
}
#endregion
}