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 }