GFramework/tools/gframework-config-tool/test/configValidation.enum.test.js
GeWuYou 33dd108697 feat(config): 添加配置验证工具功能
- 实现了JSON Schema解析和YAML验证功能
- 添加了对象和数组枚举值的比较验证逻辑
- 实现了配置文件的采样生成功能
- 添加了批量编辑器的数值更新功能
- 实现了配置路径和注释提取功能
- 添加了多种数据格式验证支持包括日期、邮箱、UUID等
- 实现了常量和枚举值的元数据处理功能
- 添加了配置验证诊断信息生成功能
- 实现了表单更新应用到YAML的功能
- 添加了字符串排序比较算法确保工具一致性
2026-04-16 15:12:02 +08:00

228 lines
6.5 KiB
JavaScript

const test = require("node:test");
const assert = require("node:assert/strict");
const {
createSampleConfigYaml,
parseSchemaContent,
parseTopLevelYaml,
validateParsedConfig
} = require("../src/configValidation");
test("parseSchemaContent should capture object and array enum comparable metadata", () => {
const schema = parseSchemaContent(`
{
"type": "object",
"properties": {
"reward": {
"type": "object",
"properties": {
"gold": { "type": "integer" },
"itemId": { "type": "string" }
},
"enum": [
{ "gold": 10, "itemId": "potion" }
]
},
"dropItemIds": {
"type": "array",
"items": { "type": "string" },
"enum": [
["fire", "ice"]
]
}
}
}
`);
assert.deepEqual(schema.properties.reward.enumDisplayValues, ["{\"gold\":10,\"itemId\":\"potion\"}"]);
assert.match(schema.properties.reward.enumComparableValues[0], /^4:gold=/u);
assert.deepEqual(schema.properties.dropItemIds.enumDisplayValues, ["[\"fire\",\"ice\"]"]);
assert.equal(schema.properties.dropItemIds.enumComparableValues[0], "[13:string:4:fire,12:string:3:ice]");
});
test("validateParsedConfig should reject object values not declared in object enum", () => {
const schema = parseSchemaContent(`
{
"type": "object",
"required": ["reward"],
"properties": {
"reward": {
"type": "object",
"required": ["gold", "itemId"],
"properties": {
"gold": { "type": "integer" },
"itemId": { "type": "string" }
},
"enum": [
{ "gold": 10, "itemId": "potion" }
]
}
}
}
`);
const yaml = parseTopLevelYaml(`
reward:
gold: 10
itemId: elixir
`);
const diagnostics = validateParsedConfig(schema, yaml);
assert.equal(diagnostics.length, 1);
assert.match(diagnostics[0].message, /reward/u);
assert.match(diagnostics[0].message, /"itemId":"potion"/u);
});
test("validateParsedConfig should treat array enum candidates as order-sensitive", () => {
const schema = parseSchemaContent(`
{
"type": "object",
"required": ["dropItemIds"],
"properties": {
"dropItemIds": {
"type": "array",
"items": { "type": "string" },
"enum": [
["fire", "ice"]
]
}
}
}
`);
const yaml = parseTopLevelYaml(`
dropItemIds:
- ice
- fire
`);
const diagnostics = validateParsedConfig(schema, yaml);
assert.equal(diagnostics.length, 1);
assert.match(diagnostics[0].message, /dropItemIds/u);
assert.match(diagnostics[0].message, /\["fire","ice"\]/u);
});
test("validateParsedConfig should not add parent object enumMismatch after child diagnostics", () => {
const schema = parseSchemaContent(`
{
"type": "object",
"required": ["reward"],
"properties": {
"reward": {
"type": "object",
"required": ["gold", "itemId"],
"properties": {
"gold": { "type": "integer" },
"itemId": { "type": "string" }
},
"enum": [
{ "gold": 10, "itemId": "potion" }
]
}
}
}
`);
const yaml = parseTopLevelYaml(`
reward:
gold: wrong
itemId: potion
`);
const diagnostics = validateParsedConfig(schema, yaml);
assert.equal(diagnostics.length, 1);
assert.match(diagnostics[0].message, /reward\.gold/u);
assert.doesNotMatch(diagnostics[0].message, /must be one of|必须是以下值之一/u);
});
test("validateParsedConfig should not add parent array enumMismatch after item diagnostics", () => {
const schema = parseSchemaContent(`
{
"type": "object",
"required": ["dropLevels"],
"properties": {
"dropLevels": {
"type": "array",
"items": { "type": "integer" },
"enum": [
[1, 2]
]
}
}
}
`);
const yaml = parseTopLevelYaml(`
dropLevels:
- 1
- two
`);
const diagnostics = validateParsedConfig(schema, yaml);
assert.equal(diagnostics.length, 1);
assert.match(diagnostics[0].message, /dropLevels\[1\]/u);
assert.doesNotMatch(diagnostics[0].message, /must be one of|必须是以下值之一/u);
});
test("createSampleConfigYaml should reuse object and array enum payloads for valid samples", () => {
const schema = parseSchemaContent(`
{
"type": "object",
"required": ["reward", "phases"],
"properties": {
"reward": {
"type": "object",
"required": ["gold", "itemId"],
"properties": {
"gold": { "type": "integer" },
"itemId": { "type": "string" }
},
"enum": [
{ "gold": 10, "itemId": "potion" }
]
},
"phases": {
"type": "array",
"items": {
"type": "object",
"required": ["wave", "monsterId"],
"properties": {
"wave": { "type": "integer" },
"monsterId": { "type": "string" }
},
"enum": [
{ "wave": 1, "monsterId": "slime" }
]
}
}
}
}
`);
const sample = createSampleConfigYaml(schema);
const yaml = parseTopLevelYaml(sample);
const diagnostics = validateParsedConfig(schema, yaml);
assert.equal(diagnostics.length, 0);
assert.match(sample, /^reward:$/mu);
assert.match(sample, /^ gold: 10$/mu);
assert.match(sample, /^ itemId: potion$/mu);
assert.match(sample, /^phases:$/mu);
assert.match(sample, /^ -$/mu);
assert.match(sample, /^ wave: 1$/mu);
assert.match(sample, /^ monsterId: slime$/mu);
});
test("parseSchemaContent should reject empty enum arrays", () => {
assert.throws(() => parseSchemaContent(`
{
"type": "object",
"properties": {
"rarity": {
"type": "string",
"enum": []
}
}
}
`), /must declare 'enum' with at least one value/u);
});