HaniBlindBox/server/HoneyBox/tests/HoneyBox.Tests/Services/WechatPayV3ConfigPropertyTests.cs
2026-01-25 20:28:11 +08:00

229 lines
9.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}