229 lines
9.0 KiB
C#
229 lines
9.0 KiB
C#
using System.Text.Json;
|
||
using FsCheck;
|
||
using FsCheck.Xunit;
|
||
using HoneyBox.Admin.Business.Models.Config;
|
||
using Xunit;
|
||
|
||
namespace HoneyBox.Tests.Services;
|
||
|
||
/// <summary>
|
||
/// 微信支付 V3 配置属性测试
|
||
/// **Feature: wechat-pay-v3-upgrade**
|
||
/// </summary>
|
||
public class WechatPayV3ConfigPropertyTests
|
||
{
|
||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||
{
|
||
PropertyNameCaseInsensitive = true,
|
||
PropertyNamingPolicy = null, // 使用原始属性名
|
||
WriteIndented = false
|
||
};
|
||
|
||
#region Property 1: 配置序列化 Round-Trip
|
||
|
||
/// <summary>
|
||
/// **Feature: wechat-pay-v3-upgrade, Property 1: 配置序列化 Round-Trip**
|
||
/// *For any* 有效的 WeixinPayMerchant 配置对象(包含 V2 或 V3 字段),
|
||
/// 序列化为 JSON 后再反序列化,应该得到与原始对象等价的配置。
|
||
/// **Validates: Requirements 1.4, 1.5**
|
||
/// </summary>
|
||
[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<WeixinPayMerchant>(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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// **Feature: wechat-pay-v3-upgrade, Property 1: 配置序列化 Round-Trip**
|
||
/// *For any* 有效的 WeixinPaySetting 配置对象(包含多个商户),
|
||
/// 序列化为 JSON 后再反序列化,应该得到与原始对象等价的配置。
|
||
/// **Validates: Requirements 1.4, 1.5**
|
||
/// </summary>
|
||
[Property(MaxTest = 100)]
|
||
public bool WeixinPaySetting_RoundTrip_ShouldPreserveAllMerchants(PositiveInt seed)
|
||
{
|
||
// 创建包含多个商户的配置
|
||
var merchantCount = (seed.Get % 3) + 1; // 1-3 个商户
|
||
var merchants = new List<WeixinPayMerchant>();
|
||
|
||
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<WeixinPaySetting>(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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// **Feature: wechat-pay-v3-upgrade, Property 1: 配置序列化 Round-Trip**
|
||
/// *For any* V2 配置,PayVersion 默认值应该是 "V2"。
|
||
/// **Validates: Requirements 1.3**
|
||
/// </summary>
|
||
[Fact]
|
||
public void WeixinPayMerchant_DefaultPayVersion_ShouldBeV2()
|
||
{
|
||
var merchant = new WeixinPayMerchant();
|
||
Assert.Equal("V2", merchant.PayVersion);
|
||
}
|
||
|
||
/// <summary>
|
||
/// **Feature: wechat-pay-v3-upgrade, Property 1: 配置序列化 Round-Trip**
|
||
/// *For any* V3 配置 JSON,反序列化后应该正确读取所有 V3 字段。
|
||
/// **Validates: Requirements 1.1, 1.2**
|
||
/// </summary>
|
||
[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<WeixinPayMerchant>(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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// **Feature: wechat-pay-v3-upgrade, Property 1: 配置序列化 Round-Trip**
|
||
/// *For any* V2 配置 JSON(不包含 V3 字段),反序列化后 V3 字段应该为 null。
|
||
/// **Validates: Requirements 1.3**
|
||
/// </summary>
|
||
[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<WeixinPayMerchant>(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
|
||
}
|