GFramework/GFramework.Game.Tests/Config/YamlConfigSchemaValidatorTests.cs
GeWuYou e40703c202 feat(config): 添加 YAML 配置文件 JSON Schema 校验器
- 实现了 YAML 配置与 JSON Schema 的运行时校验功能
- 支持嵌套对象、对象数组、标量数组的递归校验
- 集成了 enum 和引用约束的深度校验机制
- 实现了 multipleOf、uniqueItems 等扩展约束规则
- 添加了跨表引用收集和校验能力
- 提供了异步和同步两种加载校验接口
- 支持 minContains/maxContains 数组匹配计数规则
- 实现了 minProperties/maxProperties 对象属性数量校验
- 集成了日期时间、邮箱、URI 等字符串格式校验
- 提供了详细的错误诊断信息和定位功能
2026-04-12 14:28:31 +08:00

94 lines
2.8 KiB
C#

using System.IO;
using GFramework.Game.Config;
namespace GFramework.Game.Tests.Config;
/// <summary>
/// 验证内部 schema 解析器会输出稳定且可预期的运行时依赖元数据。
/// </summary>
[TestFixture]
public sealed class YamlConfigSchemaValidatorTests
{
private string _rootPath = null!;
/// <summary>
/// 为每个测试准备独立临时目录。
/// </summary>
[SetUp]
public void SetUp()
{
_rootPath = Path.Combine(Path.GetTempPath(), "GFramework.SchemaValidatorTests", Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(_rootPath);
}
/// <summary>
/// 清理测试临时目录。
/// </summary>
[TearDown]
public void TearDown()
{
if (Directory.Exists(_rootPath))
{
Directory.Delete(_rootPath, true);
}
}
/// <summary>
/// 验证 schema 中声明的跨表引用名称会以序数排序形式输出,
/// 避免热重载依赖推导与测试快照受哈希集合枚举顺序影响。
/// </summary>
[Test]
public void Load_Should_Return_Referenced_Table_Names_In_Ordinal_Sorted_Order()
{
var schemaPath = CreateSchemaFile(
"schemas/monster.schema.json",
"""
{
"type": "object",
"properties": {
"weaponId": {
"type": "string",
"x-gframework-ref-table": "weapon"
},
"allies": {
"type": "array",
"items": {
"type": "integer",
"x-gframework-ref-table": "ally"
}
},
"itemId": {
"type": "string",
"x-gframework-ref-table": "item"
}
}
}
""");
var schema = YamlConfigSchemaValidator.Load("monster", schemaPath);
Assert.That(schema.ReferencedTableNames, Is.EqualTo(new[] { "ally", "item", "weapon" }));
}
/// <summary>
/// 在临时目录中创建 schema 文件。
/// </summary>
/// <param name="relativePath">相对根目录的路径。</param>
/// <param name="content">文件内容。</param>
/// <returns>写入后的绝对路径。</returns>
private string CreateSchemaFile(
string relativePath,
string content)
{
var fullPath = Path.Combine(_rootPath, relativePath.Replace('/', Path.DirectorySeparatorChar));
var directoryPath = Path.GetDirectoryName(fullPath);
if (!string.IsNullOrWhiteSpace(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
File.WriteAllText(fullPath, content.Replace("\n", Environment.NewLine, StringComparison.Ordinal));
return fullPath;
}
}