mirror of
https://gitee.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat.git
synced 2025-09-20 02:29:40 +08:00
test: 优化 CodeStyle 测试流程
This commit is contained in:
@@ -11,7 +11,10 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
{
|
||||
public static class CodeStyleUtil
|
||||
{
|
||||
private static bool TryJsonize(string json, Type type, out Exception exception)
|
||||
private static readonly Regex CODESTYLE_RULE_REGEX_NO_JSONABLE_PROPERTY_IN_REQUEST = new Regex(@"\/\* \@codestyle-disable[a-zA-Z0-9,\-\s]*no-jsonable-property-in-request-get[a-zA-Z0-9,\-\s]*\*\/", RegexOptions.Compiled);
|
||||
private static readonly Regex CODESTYLE_RULE_REGEX_NO_INSTANTIATED_PROPERTY_IN_RESPONSE = new Regex(@"\/\* \@codestyle-disable[a-zA-Z0-9,\-\s]*no-instantiated-property-in-response[a-zA-Z0-9,\-\s]*\*\/", RegexOptions.Compiled);
|
||||
|
||||
private static bool TryJsonize(Type type, string json, out Exception exception)
|
||||
{
|
||||
exception = null;
|
||||
|
||||
@@ -29,11 +32,13 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is Newtonsoft.Json.JsonException)
|
||||
exception = new Exception($"通过 Newtonsoft.Json 反序列化 `{type.Name}` 失败。", ex);
|
||||
exception = new Exception($"[模型] 通过 Newtonsoft.Json 反序列化 `{type.Name}` 失败。", ex);
|
||||
else if (ex is System.Text.Json.JsonException)
|
||||
exception = new Exception($"通过 System.Text.Json 反序列化 `{type.Name}` 失败。", ex);
|
||||
exception = new Exception($"[模型] 通过 System.Text.Json 反序列化 `{type.Name}` 失败。", ex);
|
||||
else
|
||||
exception = new Exception($"JSON 反序列化 `{type.Name}` 遇到问题。", ex);
|
||||
exception = new Exception($"[模型] JSON 反序列化 `{type.Name}` 遇到问题。", ex);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
@@ -47,20 +52,97 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is Newtonsoft.Json.JsonException)
|
||||
exception = new Exception($"通过 Newtonsoft.Json 序列化 `{type.Name}` 失败。", ex);
|
||||
exception = new Exception($"[模型] 通过 Newtonsoft.Json 序列化 `{type.Name}` 失败。", ex);
|
||||
else if (ex is System.Text.Json.JsonException)
|
||||
exception = new Exception($"通过 System.Text.Json 序列化 `{type.Name}` 失败。", ex);
|
||||
exception = new Exception($"[模型] 通过 System.Text.Json 序列化 `{type.Name}` 失败。", ex);
|
||||
else
|
||||
exception = new Exception($"JSON 序列化 `{type.Name}` 遇到问题。", ex);
|
||||
exception = new Exception($"[模型] JSON 序列化 `{type.Name}` 遇到问题。", ex);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
PropertyInfo[] lstPropInfo = TestReflectionUtil.GetAllProperties(type);
|
||||
foreach (PropertyInfo propInfo in lstPropInfo)
|
||||
try
|
||||
{
|
||||
var newtonsoftJsonAttribute = propInfo.GetCustomAttribute<Newtonsoft.Json.JsonPropertyAttribute>();
|
||||
var systemTextJsonAttribute = propInfo.GetCustomAttribute<System.Text.Json.Serialization.JsonPropertyNameAttribute>();
|
||||
if (newtonsoftJsonAttribute?.PropertyName != systemTextJsonAttribute?.Name)
|
||||
exception = new Exception($"类型 `{type.Name}` 的可 JSON 序列化字段声明不一致:`{newtonsoftJsonAttribute.PropertyName}` & `{systemTextJsonAttribute.Name}`。");
|
||||
PropertyInfo[] lstPropInfo = TestReflectionUtil.GetAllProperties(type);
|
||||
foreach (PropertyInfo propInfo in lstPropInfo)
|
||||
{
|
||||
var newtonsoftJsonAttribute = propInfo.GetCustomAttribute<Newtonsoft.Json.JsonPropertyAttribute>(inherit: true);
|
||||
var systemTextJsonAttribute = propInfo.GetCustomAttribute<System.Text.Json.Serialization.JsonPropertyNameAttribute>(inherit: true);
|
||||
if (newtonsoftJsonAttribute?.PropertyName != systemTextJsonAttribute?.Name)
|
||||
{
|
||||
exception = new Exception($"[模型] 类型 `{type.Name}` 的可 JSON 序列化字段 `{propInfo.Name}` 声明不一致:`{newtonsoftJsonAttribute.PropertyName}` & `{systemTextJsonAttribute.Name}`。");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = new Exception($"[模型] JSON 分析 `{type.Name}` 遇到问题。", ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return exception == null;
|
||||
}
|
||||
|
||||
private static bool TryXmlize(Type type, string xml, out Exception exception)
|
||||
{
|
||||
exception = null;
|
||||
|
||||
try
|
||||
{
|
||||
using var reader = new StringReader(xml);
|
||||
var xmlSerializer = new XmlSerializer(type, new XmlRootAttribute("xml"));
|
||||
xmlSerializer.Deserialize(reader);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = new Exception($"[模型] XML 反序列化 `{type.Name}` 遇到问题。", ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var xmlDoc = new System.Xml.XmlDocument();
|
||||
xmlDoc.LoadXml(xml);
|
||||
|
||||
Func<System.Xml.XmlNode, Exception> fn = default;
|
||||
fn = new Func<System.Xml.XmlNode, Exception>((xmlNode) =>
|
||||
{
|
||||
if (xmlNode.NodeType == System.Xml.XmlNodeType.Element)
|
||||
{
|
||||
PropertyInfo[] lstPropInfo = TestReflectionUtil.GetAllProperties(type);
|
||||
var xmlElementAttributes = lstPropInfo.SelectMany(e => e.GetCustomAttributes<XmlElementAttribute>(inherit: true)).ToArray();
|
||||
var xmlArrayAttributes = lstPropInfo.SelectMany(e => e.GetCustomAttributes<XmlArrayAttribute>(inherit: true)).ToArray();
|
||||
var xmlArrayItemAttributes = lstPropInfo.SelectMany(e => e.GetCustomAttributes<XmlArrayItemAttribute>(inherit: true)).ToArray();
|
||||
if (!xmlElementAttributes.Any(e => e.ElementName == xmlNode.Name) &&
|
||||
!xmlArrayAttributes.Any(e => e.ElementName == xmlNode.Name) &&
|
||||
!xmlArrayItemAttributes.Any(e => e.ElementName == xmlNode.Name))
|
||||
{
|
||||
return new Exception($"[模型] 类型 `{type.Name}` 的可 XML 序列化字段缺失项:`{xmlNode.Name}`。");
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < xmlNode.ChildNodes.Count; i++)
|
||||
{
|
||||
Exception ex = fn.Invoke(xmlNode.ChildNodes[i]);
|
||||
if (ex != null)
|
||||
return ex;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
for (int i = 0; i < xmlDoc.DocumentElement.ChildNodes.Count; i++)
|
||||
{
|
||||
exception = fn.Invoke(xmlDoc.DocumentElement.ChildNodes[i]);
|
||||
if (exception != null)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = new Exception($"[模型] XML 分析 `{type.Name}` 遇到问题。", ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return exception == null;
|
||||
@@ -79,7 +161,7 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
|
||||
if (!name.EndsWith("Request") && !name.EndsWith("Response"))
|
||||
{
|
||||
lstError.Add(new Exception($"`{name}` 类名结尾应为 \"Request\" 或 \"Response\"。"));
|
||||
lstError.Add(new Exception($"[模型] `{name}` 类名结尾应为 \"Request\" 或 \"Response\"。"));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -87,13 +169,13 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
{
|
||||
if (!typeof(ICommonRequest).IsAssignableFrom(modelType))
|
||||
{
|
||||
lstError.Add(new Exception($"`{name}` 类需实现自 `ICommonRequest`。"));
|
||||
lstError.Add(new Exception($"[模型] `{name}` 是请求模型,需实现自 `{nameof(ICommonRequest)}`。"));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!lstModelType.Any(e => e.Name == $"{name.Substring(0, name.Length - "Request".Length)}Response"))
|
||||
{
|
||||
lstError.Add(new Exception($"`{name}` 是请求模型,但不存在对应的响应模型。"));
|
||||
lstError.Add(new Exception($"[模型] `{name}` 是请求模型,但不存在对应的响应模型。"));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -102,13 +184,13 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
{
|
||||
if (!typeof(ICommonResponse).IsAssignableFrom(modelType))
|
||||
{
|
||||
lstError.Add(new Exception($"`{name}` 类需实现自 `ICommonResponse`。"));
|
||||
lstError.Add(new Exception($"[模型] `{name}` 是响应模型,需实现自 `{nameof(ICommonResponse)}`。"));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!lstModelType.Any(e => e.Name == $"{name.Substring(0, name.Length - "Response".Length)}Request"))
|
||||
{
|
||||
lstError.Add(new Exception($"`{name}` 是响应模型,但不存在对应的请求模型。"));
|
||||
lstError.Add(new Exception($"[模型] `{name}` 是响应模型,但不存在对应的请求模型。"));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -132,29 +214,32 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
var lstModelType = TestReflectionUtil.GetAllApiModelsTypes(assembly);
|
||||
var lstError = new List<Exception>();
|
||||
|
||||
var lstFile = TestIOUtil.GetAllFiles(workdir)
|
||||
var lstFilePath = TestIOUtil.GetAllFiles(workdir)
|
||||
.Where(e => string.Equals(Path.GetExtension(e), ".json", StringComparison.InvariantCultureIgnoreCase))
|
||||
.ToList();
|
||||
if (!lstFile.Any())
|
||||
if (!lstFilePath.Any())
|
||||
{
|
||||
lstError.Add(new Exception($"路径 \"{workdir}\" 下不存在 JSON 格式的模型示例文件,请检查路径是否正确。"));
|
||||
lstError.Add(new Exception($"[模型] \"{workdir}\" 下不存在示例文件,请检查配置的扫描路径是否正确。"));
|
||||
}
|
||||
|
||||
foreach (string file in lstFile)
|
||||
foreach (string filePath in lstFilePath)
|
||||
{
|
||||
string json = File.ReadAllText(file);
|
||||
string name = Path.GetFileNameWithoutExtension(file).Split('.')[0];
|
||||
string name = Path.GetFileNameWithoutExtension(filePath).Split('.')[0];
|
||||
|
||||
Type type = assembly.GetTypes().SingleOrDefault(e => e.FullName.StartsWith($"{assembly.GetName().Name}.Models.") && e.Name == name);
|
||||
Type type = assembly.GetTypes().FirstOrDefault(e => e.FullName.StartsWith($"{assembly.GetName().Name}.Models.") && e.Name == name);
|
||||
if (type == null)
|
||||
{
|
||||
lstError.Add(new Exception($"类型 `{name}`不存在。"));
|
||||
lstError.Add(new Exception($"[模型] 扫描到示例文件 \"{filePath}\",但类型 `{name}` 不存在。"));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!TryJsonize(json, type, out Exception ex))
|
||||
if (string.Equals(Path.GetExtension(filePath), ".json", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
lstError.Add(ex);
|
||||
string json = File.ReadAllText(filePath);
|
||||
if (!TryJsonize(type, json, out Exception ex))
|
||||
{
|
||||
lstError.Add(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,56 +261,45 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
var lstModelType = TestReflectionUtil.GetAllApiModelsTypes(assembly);
|
||||
var lstError = new List<Exception>();
|
||||
|
||||
var lstJsonFile = TestIOUtil.GetAllFiles(workdir)
|
||||
.Where(e => string.Equals(Path.GetExtension(e), ".json", StringComparison.InvariantCultureIgnoreCase))
|
||||
var lstFilePath = TestIOUtil.GetAllFiles(workdir)
|
||||
.Where(e =>
|
||||
string.Equals(Path.GetExtension(e), ".json", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
string.Equals(Path.GetExtension(e), ".xml", StringComparison.InvariantCultureIgnoreCase)
|
||||
)
|
||||
.ToArray();
|
||||
var lstXmlFile = TestIOUtil.GetAllFiles(workdir)
|
||||
.Where(e => string.Equals(Path.GetExtension(e), ".xml", StringComparison.InvariantCultureIgnoreCase))
|
||||
.ToArray();
|
||||
if (!lstJsonFile.Any() && !lstXmlFile.Any())
|
||||
if (!lstFilePath.Any())
|
||||
{
|
||||
lstError.Add(new Exception($"路径 \"{workdir}\" 下不存在 JSON 或 XML 格式的事件示例文件,请检查路径是否正确。"));
|
||||
lstError.Add(new Exception($"[模型] \"{workdir}\" 下不存在示例文件,请检查配置的扫描路径是否正确。"));
|
||||
}
|
||||
|
||||
foreach (string file in lstJsonFile)
|
||||
foreach (string filePath in lstFilePath)
|
||||
{
|
||||
string json = File.ReadAllText(file);
|
||||
string name = Path.GetFileNameWithoutExtension(file).Split('.')[0];
|
||||
string name = Path.GetFileNameWithoutExtension(filePath).Split('.')[0];
|
||||
|
||||
Type type = assembly.GetTypes().SingleOrDefault(e => e.FullName.StartsWith($"{assembly.GetName().Name}.Events.") && e.Name == name);
|
||||
if (type == null)
|
||||
{
|
||||
lstError.Add(new Exception($"类型 `{name}`不存在。"));
|
||||
lstError.Add(new Exception($"[模型] 扫描到示例文件 \"{filePath}\",但类型 `{name}` 不存在。"));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!TryJsonize(json, type, out Exception ex))
|
||||
if (string.Equals(Path.GetExtension(filePath), ".json", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
lstError.Add(ex);
|
||||
}
|
||||
}
|
||||
string json = File.ReadAllText(filePath);
|
||||
|
||||
foreach (string file in lstXmlFile)
|
||||
{
|
||||
string xml = File.ReadAllText(file);
|
||||
string name = Path.GetFileNameWithoutExtension(file).Split('.')[0];
|
||||
|
||||
Type type = assembly.GetTypes().SingleOrDefault(e => e.FullName.StartsWith($"{assembly.GetName().Name}.Events.") && e.Name == name);
|
||||
if (type == null)
|
||||
{
|
||||
lstError.Add(new Exception($"类型 `{name}`不存在。"));
|
||||
continue;
|
||||
if (!TryJsonize(type, json, out Exception ex))
|
||||
{
|
||||
lstError.Add(ex);
|
||||
}
|
||||
}
|
||||
else if (string.Equals(Path.GetExtension(filePath), ".xml", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
string xml = File.ReadAllText(filePath);
|
||||
|
||||
try
|
||||
{
|
||||
using StringReader reader = new StringReader(xml);
|
||||
XmlSerializer xmlSerializer = new XmlSerializer(type, new XmlRootAttribute("xml"));
|
||||
xmlSerializer.Deserialize(reader);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
lstError.Add(new Exception($"XML 反序列化 `{type.Name}` 遇到问题。", ex));
|
||||
if (!TryXmlize(type, xml, out Exception ex))
|
||||
{
|
||||
lstError.Add(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,12 +320,11 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
var lstExtType = TestReflectionUtil.GetAllApiExtensionsTypes(assembly);
|
||||
var lstError = new List<Exception>();
|
||||
|
||||
foreach (Type extType in lstExtType)
|
||||
foreach (Type extensionsType in lstExtType)
|
||||
{
|
||||
MethodInfo[] lstMethod = extType.GetMethods()
|
||||
MethodInfo[] lstMethod = extensionsType.GetMethods()
|
||||
.Where(e =>
|
||||
e.IsPublic &&
|
||||
e.IsStatic &&
|
||||
e.IsPublic && e.IsStatic &&
|
||||
typeof(ICommonClient).IsAssignableFrom(e.GetParameters().FirstOrDefault().ParameterType)
|
||||
)
|
||||
.ToArray();
|
||||
@@ -260,17 +333,15 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
{
|
||||
ParameterInfo[] lstParamInfo = methodInfo.GetParameters();
|
||||
|
||||
// 参数签名必为 this client + request + cancelToken
|
||||
if (lstParamInfo.Length != 3)
|
||||
{
|
||||
lstError.Add(new Exception($"`{extType.Name}.{methodInfo.Name}` 方法需有且仅有 3 个入参。"));
|
||||
lstError.Add(new Exception($"[接口] `{extensionsType.Name}.{methodInfo.Name}` 方法需有且仅有 3 个入参。"));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 第二个参数必为 ICommonRequest 子类
|
||||
if (!typeof(ICommonRequest).IsAssignableFrom(lstParamInfo[1].ParameterType))
|
||||
{
|
||||
lstError.Add(new Exception($"`{extType.Name}.{methodInfo.Name}` 方法第 1 个入参需实现自 `ICommonRequest`。"));
|
||||
lstError.Add(new Exception($"[接口] `{extensionsType.Name}.{methodInfo.Name}` 方法第 1 个入参未实现 `{nameof(ICommonRequest)}`。"));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -280,25 +351,25 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
string retv = methodInfo.ReturnType.GenericTypeArguments.FirstOrDefault()?.Name;
|
||||
if (para == null || !para.EndsWith("Request"))
|
||||
{
|
||||
lstError.Add(new Exception($"`{extType.Name}.{methodInfo.Name}` 方法第 1 个入参类名应以 `Request` 结尾。"));
|
||||
lstError.Add(new Exception($"[接口] `{extensionsType.Name}.{methodInfo.Name}` 方法第 1 个入参类名未以 `Request` 结尾。"));
|
||||
continue;
|
||||
}
|
||||
else if (retv == null || !retv.EndsWith("Response"))
|
||||
{
|
||||
if (!methodInfo.ReturnType.GenericTypeArguments.First().IsGenericType)
|
||||
{
|
||||
lstError.Add(new Exception($"`{extType.Name}.{methodInfo.Name}` 方法返回值类名应以 `Response` 结尾。"));
|
||||
lstError.Add(new Exception($"[接口] `{extensionsType.Name}.{methodInfo.Name}` 方法返回值类名未以 `Response` 结尾。"));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (!string.Equals(func, $"Execute{para.Substring(0, para.Length - "Request".Length)}Async"))
|
||||
{
|
||||
lstError.Add(new Exception($"`{extType.Name}.{methodInfo.Name}` 方法与请求模型应同名。"));
|
||||
lstError.Add(new Exception($"[接口] `{extensionsType.Name}.{methodInfo.Name}` 方法与请求模型不同名。"));
|
||||
continue;
|
||||
}
|
||||
else if (!string.Equals(func, $"Execute{retv.Substring(0, retv.Length - "Response".Length)}Async"))
|
||||
{
|
||||
lstError.Add(new Exception($"`{extType.Name}.{methodInfo.Name}` 方法与响应模型应同名。"));
|
||||
lstError.Add(new Exception($"[接口] `{extensionsType.Name}.{methodInfo.Name}` 方法与响应模型不同名。"));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -323,7 +394,6 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
var lstExtensionsCodeFile = TestIOUtil.GetAllFiles(workdir)
|
||||
.Where(e => string.Equals(Path.GetExtension(e), ".cs", StringComparison.InvariantCultureIgnoreCase))
|
||||
.Where(e => Path.GetDirectoryName(e).StartsWith(Path.Combine(workdir, "Extensions")))
|
||||
.Where(e => Path.GetFileNameWithoutExtension(e).StartsWith("Wechat"))
|
||||
.Where(e => Path.GetFileNameWithoutExtension(e).Contains("ClientExecute"))
|
||||
.Where(e => Path.GetFileNameWithoutExtension(e).EndsWith("Extensions"))
|
||||
.ToArray();
|
||||
@@ -334,18 +404,18 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
.ToArray();
|
||||
if (!lstExtensionsCodeFile.Any() || !lstModelsCodeFile.Any())
|
||||
{
|
||||
lstError.Add(new Exception($"路径 \"{workdir}\" 下不存在 CSharp 格式的源代码文件,请检查路径是否正确。"));
|
||||
lstError.Add(new Exception($"[风格] \"{workdir}\" 下不存在源代码文件,请检查配置的扫描路径是否正确。"));
|
||||
}
|
||||
|
||||
foreach (string extCodeFilePath in lstExtensionsCodeFile)
|
||||
{
|
||||
string extCodeFileName = Path.GetFileName(extCodeFilePath);
|
||||
|
||||
string[] array = File.ReadAllText(extCodeFilePath)
|
||||
string[] segments = File.ReadAllText(extCodeFilePath)
|
||||
.Split("<summary>", StringSplitOptions.RemoveEmptyEntries)
|
||||
.Where(e => e.Contains("Async") && !e.Contains("public static class"))
|
||||
.ToArray();
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
for (int i = 0; i < segments.Length; i++)
|
||||
{
|
||||
bool TryCheckExtensionSourceCode(string sourceCode, out (string Name, string Method, string Url)? request)
|
||||
{
|
||||
@@ -355,7 +425,7 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
var regexPara = new Regex("<para(([\\s\\S])*?)</para>").Match(sourceCode);
|
||||
if (!regexPara.Success)
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{extCodeFileName}\" 下第 {i + 1} 段文档注释不齐全,未能匹配到 \"<para> ... </para>\"。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{extCodeFileName}\" 下第 {i + 1} 段文档注释不齐全,未能匹配到 \"<para> ... </para>\"。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -363,7 +433,7 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
var regexApi = new Regex("\\[(\\S*)\\]\\s*(\\S*)").Match(regexPara.Groups[1].Value);
|
||||
if (!regexApi.Success)
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{extCodeFileName}\" 下第 {i + 1} 段文档注释不齐全,未能匹配到 \"异步调用 ... 接口\"。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{extCodeFileName}\" 下第 {i + 1} 段文档注释不齐全,未能匹配到 \"异步调用 ... 接口\"。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -377,7 +447,7 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
string.Empty;
|
||||
if (!string.Equals(expectedMethod, actualMethod, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{extCodeFileName}\" 下第 {i + 1} 段文档注释有误,`[{expectedMethod}] {expectedUrl}` 与实际接口谓词不一致。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{extCodeFileName}\" 下第 {i + 1} 段文档注释有误,`[{expectedMethod}] {expectedUrl}` 与实际接口谓词不一致。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -393,7 +463,7 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
string[] actualUrlSegments = actualUrl.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(e => e.Trim()).ToArray();
|
||||
if (expectedUrlSegments.Length != actualUrlSegments.Length)
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{extCodeFileName}\" 下第 {i + 1} 段文档注释有误,`[{expectedMethod}] {expectedUrl}` 与实际接口路由不一致。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{extCodeFileName}\" 下第 {i + 1} 段文档注释有误,`[{expectedMethod}] {expectedUrl}` 与实际接口路由不一致。"));
|
||||
return false;
|
||||
}
|
||||
else
|
||||
@@ -406,7 +476,7 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
{
|
||||
if (actualUrlSegment.StartsWith("\""))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{extCodeFileName}\" 下第 {i + 1} 段文档注释有误,`[{expectedMethod}] {expectedUrl}` 与实际接口路由不一致。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{extCodeFileName}\" 下第 {i + 1} 段文档注释有误,`[{expectedMethod}] {expectedUrl}` 与实际接口路由不一致。"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -415,7 +485,7 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
actualUrlSegment = actualUrlSegment.Replace("\"", string.Empty).Trim('/');
|
||||
if (!string.Equals(expectedUrlSegment, actualUrlSegment))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{extCodeFileName}\" 下第 {i + 1} 段文档注释有误,`[{expectedMethod}] {expectedUrl}` 与实际接口路由不一致。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{extCodeFileName}\" 下第 {i + 1} 段文档注释有误,`[{expectedMethod}] {expectedUrl}` 与实际接口路由不一致。"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -427,7 +497,7 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
{
|
||||
if (sourceCode.Contains("flurlReq, cancellationToken"))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{extCodeFileName}\" 下第 {i + 1} 段代码有误,`[{expectedMethod}] {expectedUrl}` 为非简单请求但不包含请求正文。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{extCodeFileName}\" 下第 {i + 1} 段代码有误,`[{expectedMethod}] {expectedUrl}` 为非简单请求但不包含请求正文。"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -438,6 +508,12 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
}
|
||||
bool TryCheckRequestModelSourceCode(string filePath, string expectedRequestMethod, string expectedRequestUrl)
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{filePath}\" 不存在。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
string reqCodeFileName = Path.GetFileName(filePath);
|
||||
string reqCodeSourceCode = File.ReadAllText(filePath);
|
||||
|
||||
@@ -445,7 +521,7 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
var regexPara = new Regex("<para(([\\s\\S])*?)</para>").Match(reqCodeSourceCode);
|
||||
if (!regexPara.Success)
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{reqCodeFileName}\" 下文档注释不齐全,未能匹配到 \"<para> ... </para>\"。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{reqCodeFileName}\" 下文档注释不齐全,未能匹配到 \"<para> ... </para>\"。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -453,7 +529,7 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
var regexApi = new Regex("\\[(\\S*)\\]\\s*(\\S*)").Match(regexPara.Groups[1].Value);
|
||||
if (!regexApi.Success)
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{reqCodeFileName}\" 下文档注释不齐全,未能匹配到 \"异步调用 ... 接口\"。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{reqCodeFileName}\" 下文档注释不齐全,未能匹配到 \"异步调用 ... 接口\"。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -461,7 +537,7 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
string actualMethod = regexApi.Groups[1].Value.Trim();
|
||||
if (!string.Equals(expectedRequestMethod, actualMethod, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{reqCodeFileName}\" 下文档注释有误,与实际接口谓词不一致。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{reqCodeFileName}\" 下文档注释有误,与实际接口谓词不一致。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -469,45 +545,45 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
string actualUrl = regexApi.Groups[2].Value.Split('?')[0].Trim();
|
||||
if (!string.Equals(expectedRequestUrl, actualUrl, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{reqCodeFileName}\" 下文档注释有误,与实际接口路由不一致。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{reqCodeFileName}\" 下文档注释有误,与实际接口路由不一致。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检验是否包含 `default!` 的赋值
|
||||
if (new Regex("=\\s*default!").IsMatch(reqCodeSourceCode))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{reqCodeFileName}\" 下代码有误,请求模型不应包含 `= default!` 赋值。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{reqCodeFileName}\" 下代码有误,请求模型不应包含 `= default!` 赋值。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检验是否包含数组类型字段
|
||||
if (new Regex("public\\s*[a-zA-Z0-9.]*\\[\\]\\s*[a-zA-Z0-9]*\\s*{\\s*get;\\s*set;\\s*}").IsMatch(reqCodeSourceCode.Replace("byte[]", string.Empty).Replace("Byte[]", string.Empty)))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{reqCodeFileName}\" 下代码有误,请求模型不应包含数组类型字段。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{reqCodeFileName}\" 下代码有误,请求模型不应包含数组类型字段。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 校验是否包含可空字段的初始化
|
||||
if (new Regex("\\? [a-zA-Z0-9]* \\{ get; set; \\} =").IsMatch(reqCodeSourceCode))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{reqCodeFileName}\" 下代码有误,可空字段不应初始化。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{reqCodeFileName}\" 下代码有误,可空字段不应初始化。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果是 GET 请求,检查是否包含 JSON 序列化字段
|
||||
if ("GET".Equals(expectedRequestMethod, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!(reqCodeSourceCode.Contains("/* @codestyle-disable") && reqCodeSourceCode.Contains("no-jsonable-property-in-request-get")))
|
||||
if (!(CODESTYLE_RULE_REGEX_NO_JSONABLE_PROPERTY_IN_REQUEST.IsMatch(reqCodeSourceCode)))
|
||||
{
|
||||
if (new Regex("\\[Newtonsoft.Json.JsonProperty\\(\"[a-zA-Z0-9_]*\"\\)\\]").IsMatch(reqCodeSourceCode))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{reqCodeFileName}\" 下代码有误,请求模型为简单请求、不应包含可 JSON 序列化字段。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{reqCodeFileName}\" 下代码有误,请求模型为简单请求、不应包含可 JSON 序列化字段。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (new Regex("\\[System.Text.Json.Serialization.JsonPropertyName\\(\"[a-zA-Z0-9_]*\"\\)\\]").IsMatch(reqCodeSourceCode))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{reqCodeFileName}\" 下代码有误,请求模型为简单请求、不应包含可 JSON 序列化字段。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{reqCodeFileName}\" 下代码有误,请求模型为简单请求、不应包含可 JSON 序列化字段。"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -517,6 +593,12 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
}
|
||||
bool TryCheckResponseModelSourceCode(string filePath, string expectedRequestMethod, string expectedRequestUrl)
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{filePath}\" 不存在。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
string resCodeFileName = Path.GetFileName(filePath);
|
||||
string resCodeSourceCode = File.ReadAllText(filePath);
|
||||
|
||||
@@ -524,7 +606,7 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
var regexPara = new Regex("<para(([\\s\\S])*?)</para>").Match(resCodeSourceCode);
|
||||
if (!regexPara.Success)
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{resCodeFileName}\" 下文档注释不齐全,未能匹配到 \"<para> ... </para>\"。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{resCodeFileName}\" 下文档注释不齐全,未能匹配到 \"<para> ... </para>\"。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -532,7 +614,7 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
var regexApi = new Regex("\\[(\\S*)\\]\\s*(\\S*)").Match(regexPara.Groups[1].Value);
|
||||
if (!regexApi.Success)
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{resCodeFileName}\" 下文档注释不齐全,未能匹配到 \"异步调用 ... 接口\"。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{resCodeFileName}\" 下文档注释不齐全,未能匹配到 \"异步调用 ... 接口\"。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -540,7 +622,7 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
string actualMethod = regexApi.Groups[1].Value.Trim();
|
||||
if (!string.Equals(expectedRequestMethod, actualMethod, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{resCodeFileName}\" 下文档注释有误,与实际接口谓词不一致。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{resCodeFileName}\" 下文档注释有误,与实际接口谓词不一致。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -548,23 +630,23 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
string actualUrl = regexApi.Groups[2].Value.Split('?')[0].Trim();
|
||||
if (!string.Equals(expectedRequestUrl, actualUrl, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{resCodeFileName}\" 下文档注释有误,与实际接口路由不一致。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{resCodeFileName}\" 下文档注释有误,与实际接口路由不一致。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检验是否包含 `string.Empty` 的赋值
|
||||
if (new Regex("=\\s*string.Empty").IsMatch(resCodeSourceCode))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{resCodeFileName}\" 下代码有误,响应模型不应包含 `= string.Empty` 赋值。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{resCodeFileName}\" 下代码有误,响应模型不应包含 `= string.Empty` 赋值。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检验是否包含 `new class()` 的赋值
|
||||
if (!(resCodeSourceCode.Contains("/* @codestyle-disable") && resCodeSourceCode.Contains("no-instantiated-property-in-response")))
|
||||
if (new Regex("=\\s*new\\s[a-zA-Z0-9.]*\\(\\)").IsMatch(resCodeSourceCode))
|
||||
{
|
||||
if (new Regex("=\\s*new\\s[a-zA-Z0-9.]*\\(\\)").IsMatch(resCodeSourceCode))
|
||||
if (!(CODESTYLE_RULE_REGEX_NO_INSTANTIATED_PROPERTY_IN_RESPONSE.IsMatch(resCodeSourceCode)))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{resCodeFileName}\" 下代码有误,响应模型不应包含 `= new class()` 赋值。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{resCodeFileName}\" 下代码有误,响应模型不应包含 `= new class()` 赋值。"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -572,23 +654,23 @@ namespace SKIT.FlurlHttpClient.Wechat
|
||||
// 检验是否包含列表类型字段
|
||||
if (new Regex("public\\s*IList<[a-zA-Z0-9.]*>\\s*[a-zA-Z0-9]*\\s*{\\s*get;\\s*set;\\s*}").IsMatch(resCodeSourceCode))
|
||||
{
|
||||
lstError.Add(new Exception($"源代码 \"{resCodeFileName}\" 下代码有误,响应模型不应包含列表类型字段。"));
|
||||
lstError.Add(new Exception($"[风格] 源代码 \"{resCodeFileName}\" 下代码有误,响应模型不应包含列表类型字段。"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!TryCheckExtensionSourceCode(array[i], out var request))
|
||||
if (!TryCheckExtensionSourceCode(segments[i], out var request))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
string requestModelFileName = lstModelsCodeFile.Single(e => string.Equals(Path.GetFileNameWithoutExtension(e), $"{request.Value.Name}Request"));
|
||||
string responseModelFileName = lstModelsCodeFile.Single(e => string.Equals(Path.GetFileNameWithoutExtension(e), $"{request.Value.Name}Response"));
|
||||
bool isValidReq = TryCheckRequestModelSourceCode(requestModelFileName, request.Value.Method, request.Value.Url);
|
||||
bool isValidRes = TryCheckResponseModelSourceCode(responseModelFileName, request.Value.Method, request.Value.Url);
|
||||
string reqModelFileName = lstModelsCodeFile.FirstOrDefault(e => string.Equals(Path.GetFileNameWithoutExtension(e), $"{request.Value.Name}Request"));
|
||||
string resModelFileName = lstModelsCodeFile.FirstOrDefault(e => string.Equals(Path.GetFileNameWithoutExtension(e), $"{request.Value.Name}Response"));
|
||||
bool isValidReq = TryCheckRequestModelSourceCode(reqModelFileName, request.Value.Method, request.Value.Url);
|
||||
bool isValidRes = TryCheckResponseModelSourceCode(resModelFileName, request.Value.Method, request.Value.Url);
|
||||
if (!isValidReq || !isValidRes)
|
||||
{
|
||||
continue;
|
||||
|
Reference in New Issue
Block a user