mirror of
https://gitee.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat.git
synced 2025-04-05 17:37:54 +08:00
test: 设计代码分析工具,以规避拼写错误导致的 API 定义问题
This commit is contained in:
parent
d136eb3d09
commit
1b20769099
@ -11,15 +11,9 @@
|
||||
<Content Include="appsettings.local.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="ModelSamples/**/*.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="EventSamples/**/*.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="EventSamples/**/*.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="ModelSamples/**/*.json" />
|
||||
<Content Include="EventSamples/**/*.json" />
|
||||
<Content Include="EventSamples/**/*.xml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -13,16 +13,23 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests
|
||||
|
||||
using var stream = File.OpenRead("appsettings.local.json");
|
||||
using var json = JsonDocument.Parse(stream);
|
||||
|
||||
var config = json.RootElement.GetProperty("WechatConfig");
|
||||
WechatAppId = config.GetProperty("AppId").GetString();
|
||||
WechatAppSecret = config.GetProperty("AppSecret").GetString();
|
||||
WechatAccessToken = config.GetProperty("AccessToken").GetString();
|
||||
WechatOpenId = config.GetProperty("OpenId").GetString();
|
||||
|
||||
ProjectSourceDirectory = json.RootElement.GetProperty("ProjectSourceDirectory").GetString();
|
||||
ProjectTestDirectory = json.RootElement.GetProperty("ProjectTestDirectory").GetString();
|
||||
}
|
||||
|
||||
public static readonly string WechatAppId;
|
||||
public static readonly string WechatAppSecret;
|
||||
public static readonly string WechatAccessToken;
|
||||
public static readonly string WechatOpenId;
|
||||
|
||||
public static readonly string ProjectSourceDirectory;
|
||||
public static readonly string ProjectTestDirectory;
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests
|
||||
[Fact(DisplayName = "验证 API 模型定义")]
|
||||
public void ApiModelsDefinitionTest()
|
||||
{
|
||||
string workdir = Path.Combine(Environment.CurrentDirectory, "ModelSamples");
|
||||
string workdir = Path.Combine(TestConfigs.ProjectTestDirectory, "ModelSamples");
|
||||
Assert.True(Directory.Exists(workdir));
|
||||
|
||||
TestAssertUtil.VerifyApiModelsDefinition(_assembly, workdir, out var ex);
|
||||
@ -41,7 +41,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests
|
||||
[Fact(DisplayName = "验证 API 事件定义")]
|
||||
public void ApiEventsDefinitionTest()
|
||||
{
|
||||
string workdir = Path.Combine(Environment.CurrentDirectory, "EventSamples");
|
||||
string workdir = Path.Combine(TestConfigs.ProjectTestDirectory, "EventSamples");
|
||||
Assert.True(Directory.Exists(workdir));
|
||||
|
||||
TestAssertUtil.VerifyApiEventsDefinition(_assembly, workdir, out var ex);
|
||||
@ -62,5 +62,19 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests
|
||||
|
||||
Assert.Null(ex);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "验证 API 接口文档注释")]
|
||||
public void ApiExtensionsDocumentationTest()
|
||||
{
|
||||
string workdir = Path.Combine(TestConfigs.ProjectSourceDirectory, "Extensions");
|
||||
Assert.True(Directory.Exists(workdir));
|
||||
|
||||
TestAssertUtil.VerifyApiExtensionsSourceCodeStyle(workdir, out var ex);
|
||||
|
||||
if (ex != null)
|
||||
throw ex;
|
||||
|
||||
Assert.Null(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,5 +4,7 @@
|
||||
"AppSecret": "请在此填写用于测试的微信 AppSecret",
|
||||
"AccessToken": "请在此填写用于测试的微信 AccessToken",
|
||||
"OpenId": "请在此填写用于测试的微信用户唯一标识"
|
||||
}
|
||||
},
|
||||
"ProjectSourceDirectory": "请输入当前 SDK 项目所在的目录完整路径,如 C:\\Project\\src\\SKIT.FlurlHttpClient.Wechat.Api\\",
|
||||
"ProjectTestDirectory": "请输入当前测试项目所在的目录完整路径,如 C:\\Project\\test\\SKIT.FlurlHttpClient.Wechat.Api.UnitTests\\"
|
||||
}
|
@ -11,12 +11,8 @@
|
||||
<Content Include="appsettings.local.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="ModelSamples/**/*.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="EventSamples/**/*.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="ModelSamples/**/*.json" />
|
||||
<Content Include="EventSamples/**/*.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -13,6 +13,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
|
||||
|
||||
using var stream = File.OpenRead("appsettings.local.json");
|
||||
using var json = JsonDocument.Parse(stream);
|
||||
|
||||
var config = json.RootElement.GetProperty("WechatConfig");
|
||||
WechatAppId = config.GetProperty("AppId").GetString();
|
||||
WechatMerchantId = config.GetProperty("MerchantId").GetString();
|
||||
@ -20,6 +21,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
|
||||
WechatMerchantCertSerialNumber = config.GetProperty("MerchantCertSerialNumber").GetString();
|
||||
WechatMerchantCertPrivateKey = config.GetProperty("MerchantCertPrivateKey").GetString();
|
||||
WechatOpenId = config.GetProperty("OpenId").GetString();
|
||||
|
||||
ProjectSourceDirectory = json.RootElement.GetProperty("ProjectSourceDirectory").GetString();
|
||||
ProjectTestDirectory = json.RootElement.GetProperty("ProjectTestDirectory").GetString();
|
||||
}
|
||||
|
||||
public static readonly string WechatAppId;
|
||||
@ -28,5 +32,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
|
||||
public static readonly string WechatMerchantCertSerialNumber;
|
||||
public static readonly string WechatMerchantCertPrivateKey;
|
||||
public static readonly string WechatOpenId;
|
||||
|
||||
public static readonly string ProjectSourceDirectory;
|
||||
public static readonly string ProjectTestDirectory;
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
|
||||
[Fact(DisplayName = "验证 API 模型定义")]
|
||||
public void ApiModelsDefinitionTest()
|
||||
{
|
||||
string workdir = Path.Combine(Environment.CurrentDirectory, "ModelSamples");
|
||||
string workdir = Path.Combine(TestConfigs.ProjectTestDirectory, "ModelSamples");
|
||||
Assert.True(Directory.Exists(workdir));
|
||||
|
||||
TestAssertUtil.VerifyApiModelsDefinition(_assembly, workdir, out var ex);
|
||||
@ -41,7 +41,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
|
||||
[Fact(DisplayName = "验证 API 事件定义")]
|
||||
public void ApiEventsDefinitionTest()
|
||||
{
|
||||
string workdir = Path.Combine(Environment.CurrentDirectory, "EventSamples");
|
||||
string workdir = Path.Combine(TestConfigs.ProjectTestDirectory, "EventSamples");
|
||||
Assert.True(Directory.Exists(workdir));
|
||||
|
||||
TestAssertUtil.VerifyApiEventsDefinition(_assembly, workdir, out var ex);
|
||||
@ -62,5 +62,19 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
|
||||
|
||||
Assert.Null(ex);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "验证 API 接口文档注释")]
|
||||
public void ApiExtensionsDocumentationTest()
|
||||
{
|
||||
string workdir = Path.Combine(TestConfigs.ProjectSourceDirectory, "Extensions");
|
||||
Assert.True(Directory.Exists(workdir));
|
||||
|
||||
TestAssertUtil.VerifyApiExtensionsSourceCodeStyle(workdir, out var ex);
|
||||
|
||||
if (ex != null)
|
||||
throw ex;
|
||||
|
||||
Assert.Null(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,5 +6,7 @@
|
||||
"MerchantCertSerialNumber": "请在此填写用于测试的微信商户 API 证书序列号",
|
||||
"MerchantCertPrivateKey": "请在此填写用于测试的微信商户 API 私钥(字符串格式)",
|
||||
"OpenId": "请在此填写用于测试的微信用户唯一标识"
|
||||
}
|
||||
},
|
||||
"ProjectSourceDirectory": "请输入当前 SDK 项目所在的目录完整路径,如 C:\\Project\\src\\SKIT.FlurlHttpClient.Wechat.TenpayV3\\",
|
||||
"ProjectTestDirectory": "请输入当前测试项目所在的目录完整路径,如 C:\\Project\\test\\SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests\\"
|
||||
}
|
@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat
|
||||
@ -78,7 +79,7 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
|
||||
if (!name.EndsWith("Request") && !name.EndsWith("Response"))
|
||||
{
|
||||
lstError.Add(new Exception($"`{name}` 类名结尾应为 \"Request\" 或 \"eponse\"。"));
|
||||
lstError.Add(new Exception($"`{name}` 类名结尾应为 \"Request\" 或 \"Response\"。"));
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -312,5 +313,136 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
exception = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool VerifyApiExtensionsSourceCodeStyle(string workdir, out Exception exception)
|
||||
{
|
||||
if (workdir == null) throw new ArgumentNullException(nameof(workdir));
|
||||
|
||||
var lstError = new List<Exception>();
|
||||
|
||||
var lstCodeFile = TestIOUtil.GetAllFiles(workdir)
|
||||
.Where(e => string.Equals(Path.GetExtension(e), ".cs", StringComparison.InvariantCultureIgnoreCase))
|
||||
.Where(e => Path.GetFileNameWithoutExtension(e).StartsWith("Wechat"))
|
||||
.Where(e => Path.GetFileNameWithoutExtension(e).Contains("ClientExecute"))
|
||||
.Where(e => Path.GetFileNameWithoutExtension(e).EndsWith("Extensions"))
|
||||
.ToArray();
|
||||
if (!lstCodeFile.Any())
|
||||
{
|
||||
lstError.Add(new Exception($"路径 \"{workdir}\" 下不存在 CSharp 格式的源代码文件,请检查路径是否正确。"));
|
||||
}
|
||||
|
||||
foreach (string file in lstCodeFile)
|
||||
{
|
||||
string filename = Path.GetFileName(file);
|
||||
|
||||
string[] array = File.ReadAllText(file)
|
||||
.Split("<summary>", StringSplitOptions.RemoveEmptyEntries)
|
||||
.Where(e => e.Contains("Async"))
|
||||
.ToArray();
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
string sourcecode = array[i];
|
||||
|
||||
var regexPara = new Regex("<para(([\\s\\S])*?)</para>").Match(sourcecode);
|
||||
if (!regexPara.Success)
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{filename}\" 下第 {i + 1} 段文档注释不齐全,未能匹配到 \"<para> ... </para>\"。"));
|
||||
continue;
|
||||
}
|
||||
|
||||
var regexApi = new Regex("\\[(\\S*)\\]\\s*(\\S*)").Match(regexPara.Groups[1].Value);
|
||||
if (!regexApi.Success)
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{filename}\" 下第 {i + 1} 段文档注释不齐全,未能匹配到 \"异步调用 ... 接口\"。"));
|
||||
continue;
|
||||
}
|
||||
|
||||
string expectedMethod = regexApi.Groups[1].Value.Trim();
|
||||
string expectedUrl = regexApi.Groups[2].Value.Split('?')[0].Trim();
|
||||
if (!sourcecode.Contains(".SetOptions"))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{filename}\" 下第 {i + 1} 段文档注释有误,未能匹配到 \".SetOptions( ... )\"。"));
|
||||
continue;
|
||||
}
|
||||
|
||||
string actualMethod = sourcecode.Contains($".{nameof(IWechatClient.CreateRequest)}(new HttpMethod(\"") ?
|
||||
sourcecode.Split($".{nameof(IWechatClient.CreateRequest)}(new HttpMethod(\"")[1].Split("\"")[0] :
|
||||
sourcecode.Contains($".{nameof(IWechatClient.CreateRequest)}(HttpMethod.") ?
|
||||
sourcecode.Split($".{nameof(IWechatClient.CreateRequest)}(HttpMethod.")[1].Split(",")[0] :
|
||||
string.Empty;
|
||||
if (!string.Equals(expectedMethod, actualMethod, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{filename}\" 下第 {i + 1} 段文档注释有误,`[{expectedMethod}] {expectedUrl}` 与实际接口谓词不一致。"));
|
||||
continue;
|
||||
}
|
||||
|
||||
string actualUrl = sourcecode
|
||||
.Split($"{nameof(IWechatClient.CreateRequest)}(", StringSplitOptions.RemoveEmptyEntries)[1]
|
||||
.Substring(sourcecode.Split($"{nameof(IWechatClient.CreateRequest)}(", StringSplitOptions.RemoveEmptyEntries)[1].Split(",")[0].Length + 1)
|
||||
.Split('\n')[0]
|
||||
.Trim()
|
||||
.TrimEnd(')', ';')
|
||||
.Trim();
|
||||
string[] expectedUrlSegments = expectedUrl.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||
string[] actualUrlSegments = actualUrl.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(e => e.Trim()).ToArray();
|
||||
if (expectedUrlSegments.Length != actualUrlSegments.Length)
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{filename}\" 下第 {i + 1} 段文档注释有误,`[{expectedMethod}] {expectedUrl}` 与实际接口路由不一致。"));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int urlSegmentIndex = 0; urlSegmentIndex < expectedUrlSegments.Length; urlSegmentIndex++)
|
||||
{
|
||||
string expectedUrlSegment = expectedUrlSegments[urlSegmentIndex];
|
||||
string actualUrlSegment = actualUrlSegments[urlSegmentIndex];
|
||||
if (expectedUrlSegment.Contains("{"))
|
||||
{
|
||||
if (actualUrlSegment.StartsWith("\""))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{filename}\" 下第 {i + 1} 段文档注释有误,`[{expectedMethod}] {expectedUrl}` 与实际接口路由不一致。"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
actualUrlSegment = actualUrlSegment.Replace("\"", string.Empty);
|
||||
if (!string.Equals(expectedUrlSegment, actualUrlSegment))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{filename}\" 下第 {i + 1} 段文档注释有误,`[{expectedMethod}] {expectedUrl}` 与实际接口路由不一致。"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ("GET".Equals(actualMethod, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!sourcecode.Contains("flurlReq, cancellationToken"))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{filename}\" 下第 {i + 1} 段文档注释有误,`[{expectedMethod}] {expectedUrl}` 为简单请求但包含了请求正文。"));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sourcecode.Contains("flurlReq, cancellationToken"))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{filename}\" 下第 {i + 1} 段文档注释有误,`[{expectedMethod}] {expectedUrl}` 为非简单请求但不包含请求正文。"));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lstError.Any())
|
||||
{
|
||||
exception = new AggregateException(lstError);
|
||||
return false;
|
||||
}
|
||||
|
||||
exception = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,15 +11,9 @@
|
||||
<Content Include="appsettings.local.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="ModelSamples/**/*.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="EventSamples/**/*.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="EventSamples/**/*.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="ModelSamples/**/*.json" />
|
||||
<Content Include="EventSamples/**/*.json" />
|
||||
<Content Include="EventSamples/**/*.xml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -13,14 +13,21 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests
|
||||
|
||||
using var stream = File.OpenRead("appsettings.local.json");
|
||||
using var json = JsonDocument.Parse(stream);
|
||||
|
||||
var config = json.RootElement.GetProperty("WechatConfig");
|
||||
WechatCorpId = config.GetProperty("CorpId").GetString();
|
||||
WechatAgentId = int.Parse(config.GetProperty("AgentId").GetString());
|
||||
WechatAgentSecret = config.GetProperty("AgentSecret").GetString();
|
||||
|
||||
ProjectSourceDirectory = json.RootElement.GetProperty("ProjectSourceDirectory").GetString();
|
||||
ProjectTestDirectory = json.RootElement.GetProperty("ProjectTestDirectory").GetString();
|
||||
}
|
||||
|
||||
public static readonly string WechatCorpId;
|
||||
public static readonly int WechatAgentId;
|
||||
public static readonly string WechatAgentSecret;
|
||||
|
||||
public static readonly string ProjectSourceDirectory;
|
||||
public static readonly string ProjectTestDirectory;
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests
|
||||
[Fact(DisplayName = "验证 API 模型定义")]
|
||||
public void ApiModelsDefinitionTest()
|
||||
{
|
||||
string workdir = Path.Combine(Environment.CurrentDirectory, "ModelSamples");
|
||||
string workdir = Path.Combine(TestConfigs.ProjectTestDirectory, "ModelSamples");
|
||||
Assert.True(Directory.Exists(workdir));
|
||||
|
||||
TestAssertUtil.VerifyApiModelsDefinition(_assembly, workdir, out var ex);
|
||||
@ -41,7 +41,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests
|
||||
[Fact(DisplayName = "验证 API 事件定义")]
|
||||
public void ApiEventsDefinitionTest()
|
||||
{
|
||||
string workdir = Path.Combine(Environment.CurrentDirectory, "EventSamples");
|
||||
string workdir = Path.Combine(TestConfigs.ProjectTestDirectory, "EventSamples");
|
||||
Assert.True(Directory.Exists(workdir));
|
||||
|
||||
TestAssertUtil.VerifyApiEventsDefinition(_assembly, workdir, out var ex);
|
||||
@ -62,5 +62,19 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests
|
||||
|
||||
Assert.Null(ex);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "验证 API 接口文档注释")]
|
||||
public void ApiExtensionsDocumentationTest()
|
||||
{
|
||||
string workdir = Path.Combine(TestConfigs.ProjectSourceDirectory, "Extensions");
|
||||
Assert.True(Directory.Exists(workdir));
|
||||
|
||||
TestAssertUtil.VerifyApiExtensionsSourceCodeStyle(workdir, out var ex);
|
||||
|
||||
if (ex != null)
|
||||
throw ex;
|
||||
|
||||
Assert.Null(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,5 +3,7 @@
|
||||
"CorpId": "请在此填写用于测试的企业微信 CorpId",
|
||||
"AgentId": "请在此填写用于测试的企业微信 AgentId",
|
||||
"AgentSecret": "请在此填写用于测试的企业微信 AgentSecret"
|
||||
}
|
||||
},
|
||||
"ProjectSourceDirectory": "请输入当前 SDK 项目所在的目录完整路径,如 C:\\Project\\src\\SKIT.FlurlHttpClient.Wechat.Work\\",
|
||||
"ProjectTestDirectory": "请输入当前测试项目所在的目录完整路径,如 C:\\Project\\test\\SKIT.FlurlHttpClient.Wechat.TenpayV3.Work\\"
|
||||
}
|
Loading…
Reference in New Issue
Block a user