feat(tenpayv3): 升级公共组件

This commit is contained in:
Fu Diwei
2024-01-29 23:12:37 +08:00
committed by RHQYZ
parent 84ee19d614
commit a6763ca683
348 changed files with 4397 additions and 3878 deletions

View File

@@ -0,0 +1,59 @@
using System;
using System.IO;
using System.Reflection;
using SKIT.FlurlHttpClient.Tools.CodeAnalyzer;
using Xunit;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
{
public class CodeAnalyzeTests
{
[Fact(DisplayName = "代码质量分析")]
public void CodeAnalyze()
{
Assert.Null(Record.Exception(() =>
{
var options = new TypeDeclarationAnalyzerOptions()
{
SdkAssembly = Assembly.GetAssembly(typeof(WechatTenpayClient))!,
SdkRequestModelDeclarationNamespace = "SKIT.FlurlHttpClient.Wechat.TenpayV3.Models",
SdkResponseModelDeclarationNamespace = "SKIT.FlurlHttpClient.Wechat.TenpayV3.Models",
SdkExecutingExtensionDeclarationNamespace = "SKIT.FlurlHttpClient.Wechat.TenpayV3",
SdkWebhookEventDeclarationNamespace = "SKIT.FlurlHttpClient.Wechat.TenpayV3.Events",
ThrowOnNotFoundRequestModelTypes = true,
ThrowOnNotFoundResponseModelTypes = true,
ThrowOnNotFoundExecutingExtensionTypes = true,
ThrowOnNotFoundWebhookEventTypes = true
};
new TypeDeclarationAnalyzer(options).AssertNoIssues();
}));
Assert.Null(Record.Exception(() =>
{
string workdir = Environment.CurrentDirectory;
string projdir = Path.Combine(workdir, "../../../../../");
var options = new SourceFileAnalyzerOptions()
{
SdkAssembly = Assembly.GetAssembly(typeof(WechatTenpayClient))!,
SdkRequestModelDeclarationNamespace = "SKIT.FlurlHttpClient.Wechat.TenpayV3.Models",
SdkResponseModelDeclarationNamespace = "SKIT.FlurlHttpClient.Wechat.TenpayV3.Models",
SdkWebhookEventDeclarationNamespace = "SKIT.FlurlHttpClient.Wechat.TenpayV3.Events",
ProjectSourceRootDirectory = Path.Combine(projdir, "./src/SKIT.FlurlHttpClient.Wechat.TenpayV3/"),
ProjectTestRootDirectory = Path.Combine(projdir, "./test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/"),
ThrowOnNotFoundRequestModelClassCodeFiles = true,
ThrowOnNotFoundResponseModelClassCodeFiles = true,
ThrowOnNotFoundExecutingExtensionClassCodeFiles = true,
ThrowOnNotFoundWebhookEventClassCodeFiles = true,
ThrowOnNotFoundRequestModelSerializationSampleFiles = true,
ThrowOnNotFoundResponseModelSerializationSampleFiles = true,
ThrowOnNotFoundWebhookEventSerializationSampleFiles = true
};
new SourceFileAnalyzer(options)
.SetFileScanner(SourceFileContentKinds.WebhookEventClassCode, (directory) => directory.GetAllFiles("*.cs"))
.SetFileScanner(SourceFileContentKinds.WebhookEventSerializationSample, (directory) => directory.GetAllFiles("*.json"))
.AssertNoIssues();
}));
}
}
}

View File

@@ -11,22 +11,22 @@
<ItemGroup>
<None Remove=".gitignore" />
<Content Include="appsettings.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="appsettings.*.json" Condition="'$(Configuration)' == 'Debug'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
</Content>
<Content Include="ModelSamples/**/*.json" />
<Content Include="EventSamples/**/*.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Tools.CodeAnalyzer" Version="0.1.0-alpha.1" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Tools.CodeAnalyzer" Version="0.3.0-preview.1" />
<PackageReference Include="xunit" Version="2.6.6" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Threading.Tasks;
using Xunit;
@@ -46,7 +46,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
var response = await TestClients.InstanceUseRSA.ExecuteDownloadBillFileAsync(request);
Assert.True(response.IsSuccessful());
Assert.NotEmpty(response.RawBytes);
Assert.NotEmpty(response.GetRawBytes());
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Threading.Tasks;
using Xunit;
@@ -135,7 +135,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
Assert.NotNull(response.CombineAppId);
Assert.NotNull(response.CombineMerchantId);
Assert.NotNull(response.CombineOutTradeNumber);
Assert.NotEmpty(response.SubOrderList);
Assert.NotEmpty(response.SubOrderList!);
}
[Fact(DisplayName = "测试用例:调用 API [POST] /combine-transactions/out-trade-no/{combine_out_trade_no}/close")]

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Threading.Tasks;
using Xunit;

View File

@@ -41,7 +41,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
[Fact(DisplayName = "测试用例:`CertificateEntry` 构造器")]
public void TestCertificateEntryConstructor()
{
var certRSA = new Settings.CertificateEntry(
var certRSA = Settings.CertificateEntry.Parse(
new Models.QueryCertificatesResponse.Types.Certificate()
{
SerialNumber = CERT_RSA_SN,
@@ -55,7 +55,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
);
Assert.Equal(Settings.CertificateEntry.ALGORITHM_TYPE_RSA, certRSA.AlgorithmType);
var certSM2 = new Settings.CertificateEntry(
var certSM2 = Settings.CertificateEntry.Parse(
new Models.QueryCertificatesResponse.Types.Certificate()
{
SerialNumber = CERT_SM2_SN,

View File

@@ -1,28 +0,0 @@
using SKIT.FlurlHttpClient.Tools.CodeAnalyzer;
using Xunit;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
{
public class TestCase_CodeReview
{
[Fact(DisplayName = "测试用例:代码质量分析")]
public void TestCodeAnalyzer()
{
Assert.Null(Record.Exception(() =>
{
CodeAnalyzerOptions options = new CodeAnalyzerOptions()
{
AssemblyName = "SKIT.FlurlHttpClient.Wechat.TenpayV3",
WorkDirectoryForSourceCode = TestConfigs.WorkDirectoryForSdk,
WorkDirectoryForTestSample = TestConfigs.WorkDirectoryForTest,
AllowNotFoundEventTypes = true,
AllowNotFoundEventSamples = true
};
CodeAnalyzer analyzer = new CodeAnalyzer(options);
analyzer.Start();
analyzer.Assert();
analyzer.Flush();
}));
}
}
}

View File

@@ -0,0 +1,92 @@
using Xunit;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
{
public class TestCase_JsonConverterOfYesOrNoBooleanBooleanTest
{
private sealed class MockObject
{
[Newtonsoft.Json.JsonProperty(Order = 1)]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Internal.YesOrNoBooleanConverter))]
[System.Text.Json.Serialization.JsonPropertyOrder(1)]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Internal.YesOrNoBooleanConverter))]
public bool Property { get; set; }
[Newtonsoft.Json.JsonProperty(Order = 2)]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Internal.YesOrNoBooleanConverter))]
[System.Text.Json.Serialization.JsonPropertyOrder(2)]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Internal.YesOrNoBooleanConverter))]
public bool? NullableProperty { get; set; }
}
private static void TestCustomJsonConverter(IJsonSerializer jsonSerializer)
{
Assert.Multiple(() =>
{
var expectObj = new MockObject() { NullableProperty = null };
var actualJson = jsonSerializer.Serialize(expectObj);
var actualObj = jsonSerializer.Deserialize<MockObject>(actualJson);
Assert.Equal("{\"Property\":\"N\"}", actualJson);
Assert.Equal(expectObj.Property, actualObj.Property);
Assert.Equal(expectObj.Property, jsonSerializer.Deserialize<MockObject>("{\"Property\":\"N\"}").Property);
Assert.Equal(expectObj.Property, jsonSerializer.Deserialize<MockObject>("{}").Property);
Assert.Equal(expectObj.NullableProperty, actualObj.NullableProperty);
Assert.Equal(expectObj.NullableProperty, jsonSerializer.Deserialize<MockObject>("{\"NullableProperty\":null}").NullableProperty);
Assert.Equal(expectObj.NullableProperty, jsonSerializer.Deserialize<MockObject>("{}").NullableProperty);
});
Assert.Multiple(() =>
{
var expectObj = new MockObject() { Property = false, NullableProperty = false };
var actualJson = jsonSerializer.Serialize(expectObj);
var actualObj = jsonSerializer.Deserialize<MockObject>(actualJson);
Assert.Equal("{\"Property\":\"N\",\"NullableProperty\":\"N\"}", actualJson);
Assert.Equal(expectObj.Property, actualObj.Property);
Assert.Equal(expectObj.Property, jsonSerializer.Deserialize<MockObject>("{\"Property\":false}").Property);
Assert.Equal(expectObj.NullableProperty, actualObj.NullableProperty);
Assert.Equal(expectObj.NullableProperty, jsonSerializer.Deserialize<MockObject>("{\"NullableProperty\":false}").NullableProperty);
});
Assert.Multiple(() =>
{
var expectObj = new MockObject() { Property = true, NullableProperty = true };
var actualJson = jsonSerializer.Serialize(expectObj);
var actualObj = jsonSerializer.Deserialize<MockObject>(actualJson);
Assert.Equal("{\"Property\":\"Y\",\"NullableProperty\":\"Y\"}", actualJson);
Assert.Equal(expectObj.Property, actualObj.Property);
Assert.Equal(expectObj.Property, jsonSerializer.Deserialize<MockObject>("{\"Property\":true}").Property);
Assert.Equal(expectObj.NullableProperty, actualObj.NullableProperty);
Assert.Equal(expectObj.NullableProperty, jsonSerializer.Deserialize<MockObject>("{\"NullableProperty\":true}").NullableProperty);
});
}
[Fact(DisplayName = "测试用例:自定义 Newtosoft.Json.JsonConverter 之 YesOrNoBooleanConverter")]
public void TestNewtosoftJsonConverter()
{
var jsonSettings = NewtonsoftJsonSerializer.GetDefaultSerializerSettings();
jsonSettings.Formatting = Newtonsoft.Json.Formatting.None;
jsonSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
TestCustomJsonConverter(new NewtonsoftJsonSerializer(jsonSettings));
}
[Fact(DisplayName = "测试用例:自定义 System.Text.Json.Serialization.JsonConverter 之 YesOrNoBooleanConverter")]
public void TestSystemTextJsonConverter()
{
var jsonOptions = SystemTextJsonSerializer.GetDefaultSerializerOptions();
jsonOptions.WriteIndented = false;
jsonOptions.DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull;
TestCustomJsonConverter(new SystemTextJsonSerializer(jsonOptions));
}
}
}

View File

@@ -1,3 +1,4 @@
using System.Linq;
using System.Threading.Tasks;
using Xunit;
@@ -5,36 +6,42 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
{
public class TestCase_ResponseVerificationTests
{
[Fact(DisplayName = "测试用例:验证响应签名")]
public async Task TestVerifyResponseSignature()
[Fact(DisplayName = "测试用例:验证响应签名(基于 RSA")]
public async Task TestVerifyResponseSignatureUseRSA()
{
await TestClients.InitializeCertificateManagerAsync();
var request1 = new Models.QueryCertificatesRequest() { AlgorithmType = "RSA" };
var response1 = await TestClients.InstanceUseRSA.ExecuteQueryCertificatesAsync(request1);
Assert.True(response1.RawStatus == 200);
Assert.True(response1.RawHeaders.Count > 0);
Assert.True(response1.RawBytes.Length > 0);
Assert.True(TestClients.InstanceUseRSA.VerifyResponseSignature(response1));
Assert.NotNull(response1.WechatpayRequestId);
Assert.NotNull(response1.WechatpayNonce);
Assert.NotNull(response1.WechatpayTimestamp);
Assert.NotNull(response1.WechatpaySignature);
Assert.NotNull(response1.WechatpaySignatureType);
Assert.NotNull(response1.WechatpayCertificateSerialNumber);
var request = new Models.QueryCertificatesRequest() { AlgorithmType = "RSA" };
var response = await TestClients.InstanceUseRSA.ExecuteQueryCertificatesAsync(request);
Assert.True(response.GetRawStatus() == 200);
Assert.True(response.GetRawHeaders().Any());
Assert.True(response.GetRawBytes().Any());
Assert.True(TestClients.InstanceUseRSA.VerifyResponseSignature(response));
Assert.NotNull(response.WechatpayRequestId);
Assert.NotNull(response.WechatpayNonce);
Assert.NotNull(response.WechatpayTimestamp);
Assert.NotNull(response.WechatpaySignature);
Assert.NotNull(response.WechatpaySignatureType);
Assert.NotNull(response.WechatpayCertificateSerialNumber);
}
var request2 = new Models.QueryCertificatesRequest() { AlgorithmType = "SM2" };
var response2 = await TestClients.InstanceUseSM2.ExecuteQueryCertificatesAsync(request2);
Assert.True(response2.RawStatus == 200);
Assert.True(response2.RawHeaders.Count > 0);
Assert.True(response2.RawBytes.Length > 0);
Assert.True(TestClients.InstanceUseSM2.VerifyResponseSignature(response2));
Assert.NotNull(response2.WechatpayRequestId);
Assert.NotNull(response2.WechatpayNonce);
Assert.NotNull(response2.WechatpayTimestamp);
Assert.NotNull(response2.WechatpaySignature);
Assert.NotNull(response2.WechatpaySignatureType);
Assert.NotNull(response2.WechatpayCertificateSerialNumber);
[Fact(DisplayName = "测试用例:验证响应签名(基于 SM2")]
public async Task TestVerifyResponseSignatureUseSM2()
{
await TestClients.InitializeCertificateManagerAsync();
var request = new Models.QueryCertificatesRequest() { AlgorithmType = "SM2" };
var response = await TestClients.InstanceUseSM2.ExecuteQueryCertificatesAsync(request);
Assert.True(response.GetRawStatus() == 200);
Assert.True(response.GetRawHeaders().Any());
Assert.True(response.GetRawBytes().Any());
Assert.True(TestClients.InstanceUseSM2.VerifyResponseSignature(response));
Assert.NotNull(response.WechatpayRequestId);
Assert.NotNull(response.WechatpayNonce);
Assert.NotNull(response.WechatpayTimestamp);
Assert.NotNull(response.WechatpaySignature);
Assert.NotNull(response.WechatpaySignatureType);
Assert.NotNull(response.WechatpayCertificateSerialNumber);
}
}
}

View File

@@ -37,7 +37,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
foreach (var certificate in InstanceUseRSA.DecryptResponseSensitiveProperty(response).CertificateList)
{
InstanceUseRSA.PlatformCertificateManager.AddEntry(new Settings.CertificateEntry(certificate));
InstanceUseRSA.PlatformCertificateManager.AddEntry(Settings.CertificateEntry.Parse(certificate));
}
}

View File

@@ -16,7 +16,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
using var stream = File.OpenRead("appsettings.local.json");
using var jdoc = JsonDocument.Parse(stream);
var config = jdoc.RootElement.GetProperty("TestConfig");
var config = jdoc.RootElement.GetProperty("TestConfigs");
WechatMerchantId = config.GetProperty("MerchantId").GetString()!;
WechatMerchantSecret = config.GetProperty("MerchantSecret").GetString()!;
WechatMerchantRSACertificateSerialNumber = config.GetProperty("MerchantRSACertificateSerialNumber").GetString()!;
@@ -25,9 +25,6 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
WechatMerchantSM2CertificatePrivateKey = config.GetProperty("MerchantSM2CertificatePrivateKey").GetString();
WechatAppId = config.GetProperty("AppId").GetString()!;
WechatOpenId = config.GetProperty("OpenId").GetString()!;
WorkDirectoryForSdk = jdoc.RootElement.GetProperty("WorkDirectoryForSdk").GetString()!;
WorkDirectoryForTest = jdoc.RootElement.GetProperty("WorkDirectoryForTest").GetString()!;
}
catch (Exception ex)
{
@@ -43,8 +40,5 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
public static readonly string? WechatMerchantSM2CertificatePrivateKey;
public static readonly string WechatAppId;
public static readonly string WechatOpenId;
public static readonly string WorkDirectoryForSdk;
public static readonly string WorkDirectoryForTest;
}
}

View File

@@ -1,5 +1,5 @@
{
"TestConfig": {
"TestConfigs": {
"MerchantId": "请在此填写用于测试的微信商户号",
"MerchantSecret": "请在此填写用于测试的微信商户 API v3 密钥",
"MerchantRSACertificateSerialNumber": "请在此填写用于测试的微信商户基于 RSA 算法的 API 证书序列号",
@@ -8,7 +8,5 @@
"MerchantSM2CertificatePrivateKey": "请在此填写用于测试的微信商户基于 SM2 算法的 API 证书私钥(字符串格式)",
"AppId": "请在此填写用于测试的微信 AppId",
"OpenId": "请在此填写用于测试的微信用户唯一标识"
},
"WorkDirectoryForSdk": "请输入当前 SDK 项目所在的目录完整路径,如 C:\\Project\\src\\SKIT.FlurlHttpClient.Wechat.TenpayV3\\",
"WorkDirectoryForTest": "请输入当前测试项目所在的目录完整路径,如 C:\\Project\\test\\SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests\\"
}
}