test(config): 添加YAML配置加载器dependentSchemas约束功能测试

- 实现YAML配置加载器对对象级dependentSchemas约束的运行时行为验证
- 添加dependentSchemas不满足时抛出异常的测试用例
- 添加触发字段缺席时不误触发dependentSchemas检查的测试用例
- 添加dependentSchemas满足时正常加载并保留额外字段的测试用例
- 添加非对象类型dependentSchemas声明时抛出异常的测试用例
- 添加触发字段未在同级properties中声明时的错误处理测试
- 添加dependentSchemas条件子schema非object类型时的验证测试
- 实现临时目录管理以避免不同测试场景间的数据污染
- 提供完整的测试辅助方法包括文件创建、schema构建等功能
This commit is contained in:
GeWuYou 2026-04-17 11:47:06 +08:00
parent 2486352341
commit cde234ddea

View File

@ -10,6 +10,26 @@ namespace GFramework.Game.Tests.Config;
[TestFixture]
public sealed class YamlConfigLoaderDependentSchemasTests
{
private const string DefaultRewardPropertiesJson = """
{
"itemId": { "type": "string" },
"itemCount": { "type": "integer" },
"bonus": { "type": "integer" }
}
""";
private const string DefaultDependentSchemasJson = """
{
"itemId": {
"type": "object",
"required": ["itemCount"],
"properties": {
"itemCount": { "type": "integer" }
}
}
}
""";
private string? _rootPath;
/// <summary>
@ -43,39 +63,13 @@ public sealed class YamlConfigLoaderDependentSchemasTests
{
CreateConfigFile(
"monster/slime.yaml",
"""
id: 1
reward:
itemId: potion
""");
BuildMonsterConfigYaml(
"""
itemId: potion
"""));
CreateSchemaFile(
"schemas/monster.schema.json",
"""
{
"type": "object",
"required": ["id", "reward"],
"properties": {
"id": { "type": "integer" },
"reward": {
"type": "object",
"properties": {
"itemId": { "type": "string" },
"itemCount": { "type": "integer" },
"bonus": { "type": "integer" }
},
"dependentSchemas": {
"itemId": {
"type": "object",
"required": ["itemCount"],
"properties": {
"itemCount": { "type": "integer" }
}
}
}
}
}
}
""");
BuildMonsterSchema(DefaultRewardPropertiesJson, DefaultDependentSchemasJson));
var loader = CreateMonsterRewardLoader();
var registry = CreateRegistry();
@ -101,39 +95,13 @@ public sealed class YamlConfigLoaderDependentSchemasTests
{
CreateConfigFile(
"monster/slime.yaml",
"""
id: 1
reward:
bonus: 2
""");
BuildMonsterConfigYaml(
"""
bonus: 2
"""));
CreateSchemaFile(
"schemas/monster.schema.json",
"""
{
"type": "object",
"required": ["id", "reward"],
"properties": {
"id": { "type": "integer" },
"reward": {
"type": "object",
"properties": {
"itemId": { "type": "string" },
"itemCount": { "type": "integer" },
"bonus": { "type": "integer" }
},
"dependentSchemas": {
"itemId": {
"type": "object",
"required": ["itemCount"],
"properties": {
"itemCount": { "type": "integer" }
}
}
}
}
}
}
""");
BuildMonsterSchema(DefaultRewardPropertiesJson, DefaultDependentSchemasJson));
var loader = CreateMonsterRewardLoader();
var registry = CreateRegistry();
@ -152,41 +120,15 @@ public sealed class YamlConfigLoaderDependentSchemasTests
{
CreateConfigFile(
"monster/slime.yaml",
"""
id: 1
reward:
itemId: potion
itemCount: 3
bonus: 1
""");
BuildMonsterConfigYaml(
"""
itemId: potion
itemCount: 3
bonus: 1
"""));
CreateSchemaFile(
"schemas/monster.schema.json",
"""
{
"type": "object",
"required": ["id", "reward"],
"properties": {
"id": { "type": "integer" },
"reward": {
"type": "object",
"properties": {
"itemId": { "type": "string" },
"itemCount": { "type": "integer" },
"bonus": { "type": "integer" }
},
"dependentSchemas": {
"itemId": {
"type": "object",
"required": ["itemCount"],
"properties": {
"itemCount": { "type": "integer" }
}
}
}
}
}
}
""");
BuildMonsterSchema(DefaultRewardPropertiesJson, DefaultDependentSchemasJson));
var loader = CreateMonsterRewardLoader();
var registry = CreateRegistry();
@ -212,30 +154,22 @@ public sealed class YamlConfigLoaderDependentSchemasTests
{
CreateConfigFile(
"monster/slime.yaml",
"""
id: 1
reward:
itemId: potion
""");
BuildMonsterConfigYaml(
"""
itemId: potion
"""));
CreateSchemaFile(
"schemas/monster.schema.json",
"""
{
"type": "object",
"required": ["id", "reward"],
"properties": {
"id": { "type": "integer" },
"reward": {
"type": "object",
"properties": {
"itemId": { "type": "string" },
"itemCount": { "type": "integer" }
},
"dependentSchemas": ["itemId"]
BuildMonsterSchema(
"""
{
"itemId": { "type": "string" },
"itemCount": { "type": "integer" }
}
}
}
""");
""",
"""
["itemId"]
"""));
var loader = CreateMonsterRewardLoader();
var registry = CreateRegistry();
@ -260,36 +194,28 @@ public sealed class YamlConfigLoaderDependentSchemasTests
{
CreateConfigFile(
"monster/slime.yaml",
"""
id: 1
reward:
itemId: potion
""");
BuildMonsterConfigYaml(
"""
itemId: potion
"""));
CreateSchemaFile(
"schemas/monster.schema.json",
"""
{
"type": "object",
"required": ["id", "reward"],
"properties": {
"id": { "type": "integer" },
"reward": {
"type": "object",
"properties": {
"itemCount": { "type": "integer" }
},
"dependentSchemas": {
"itemId": {
"type": "object",
"properties": {
"itemCount": { "type": "integer" }
}
BuildMonsterSchema(
"""
{
"itemCount": { "type": "integer" }
}
""",
"""
{
"itemId": {
"type": "object",
"properties": {
"itemCount": { "type": "integer" }
}
}
}
}
}
""");
"""));
var loader = CreateMonsterRewardLoader();
var registry = CreateRegistry();
@ -314,34 +240,26 @@ public sealed class YamlConfigLoaderDependentSchemasTests
{
CreateConfigFile(
"monster/slime.yaml",
"""
id: 1
reward:
itemId: potion
""");
BuildMonsterConfigYaml(
"""
itemId: potion
"""));
CreateSchemaFile(
"schemas/monster.schema.json",
"""
{
"type": "object",
"required": ["id", "reward"],
"properties": {
"id": { "type": "integer" },
"reward": {
"type": "object",
"properties": {
"itemId": { "type": "string" }
},
"dependentSchemas": {
"itemId": {
"type": "string",
"const": "potion"
}
BuildMonsterSchema(
"""
{
"itemId": { "type": "string" }
}
""",
"""
{
"itemId": {
"type": "string",
"const": "potion"
}
}
}
}
""");
"""));
var loader = CreateMonsterRewardLoader();
var registry = CreateRegistry();
@ -387,6 +305,48 @@ public sealed class YamlConfigLoaderDependentSchemasTests
CreateConfigFile(relativePath, content);
}
private static string BuildMonsterConfigYaml(string rewardYaml)
{
return $$"""
id: 1
reward:
{{IndentLines(rewardYaml, 2)}}
""";
}
private static string BuildMonsterSchema(
string rewardPropertiesJson,
string dependentSchemasJson)
{
return $$"""
{
"type": "object",
"required": ["id", "reward"],
"properties": {
"id": { "type": "integer" },
"reward": {
"type": "object",
"properties": {{rewardPropertiesJson}},
"dependentSchemas": {{dependentSchemasJson}}
}
}
}
""";
}
private static string IndentLines(string text, int indentLevel)
{
var indentation = new string(' ', indentLevel);
var lines = text
.Trim()
.Split('\n', StringSplitOptions.None)
.Select(static line => line.TrimEnd('\r'));
return string.Join(
Environment.NewLine,
lines.Select(line => $"{indentation}{line}"));
}
/// <summary>
/// 创建用于对象 dependentSchemas 场景的加载器。
/// </summary>