"""
测试 ConfigManager 模块
"""
import pytest
import json
import yaml
from pathlib import Path
from core.config_manager import (
PathConfig,
ProcessingConfig,
EngineConfig,
RenpyConfig,
NaninovelConfig,
AppConfig
)
[文档]
class TestPathConfig:
"""测试 PathConfig 类"""
[文档]
def test_default_paths(self):
"""测试默认路径"""
config = PathConfig()
assert config.input_dir == Path("./input")
assert config.output_dir == Path("./output")
assert config.param_config_dir == Path("./param_config")
assert config.log_dir == Path("./logs")
[文档]
def test_custom_paths(self):
"""测试自定义路径"""
config = PathConfig(
input_dir=Path("./custom_input"),
output_dir=Path("./custom_output"),
param_config_dir=Path("./custom_param"),
log_dir=Path("./custom_logs")
)
assert config.input_dir == Path("./custom_input")
assert config.output_dir == Path("./custom_output")
assert config.param_config_dir == Path("./custom_param")
assert config.log_dir == Path("./custom_logs")
[文档]
def test_post_init_converts_to_path(self):
"""测试 __post_init__ 将字符串转换为 Path 对象"""
config = PathConfig(
input_dir="./string_input",
output_dir="./string_output"
)
assert isinstance(config.input_dir, Path)
assert isinstance(config.output_dir, Path)
[文档]
def test_ensure_dirs_exist(self, tmp_path):
"""测试创建目录"""
config = PathConfig(
input_dir=tmp_path / "input",
output_dir=tmp_path / "output",
param_config_dir=tmp_path / "param_config",
log_dir=tmp_path / "logs"
)
# 确保目录不存在
assert not config.input_dir.exists()
assert not config.output_dir.exists()
# 创建目录
config.ensure_dirs_exist()
# 验证目录已创建
assert config.input_dir.exists()
assert config.output_dir.exists()
assert config.param_config_dir.exists()
assert config.log_dir.exists()
[文档]
class TestProcessingConfig:
"""测试 ProcessingConfig 类"""
[文档]
def test_default_config(self):
"""测试默认配置"""
config = ProcessingConfig()
assert config.ignore_mode is True
assert config.ignore_words == [""]
assert config.batch_size == 100
assert config.enable_progress_bar is True
[文档]
def test_custom_config(self):
"""测试自定义配置"""
config = ProcessingConfig(
ignore_mode=False,
ignore_words=["test", "ignore"],
batch_size=50,
enable_progress_bar=False
)
assert config.ignore_mode is False
assert config.ignore_words == ["test", "ignore"]
assert config.batch_size == 50
assert config.enable_progress_bar is False
[文档]
class TestEngineConfig:
"""测试 EngineConfig 基类"""
[文档]
def test_get_output_filename(self):
"""测试生成输出文件名"""
config = EngineConfig(
engine_type="test",
file_extension=".txt"
)
assert config.get_output_filename("chapter1") == "chapter1.txt"
assert config.get_output_filename("scene_01") == "scene_01.txt"
[文档]
class TestRenpyConfig:
"""测试 RenpyConfig 类"""
[文档]
def test_default_config(self):
"""测试默认配置"""
config = RenpyConfig()
assert config.engine_type == "renpy"
assert config.file_extension == ".rpy"
assert config.indent_size == 4
assert config.label_indent is False
assert config.default_transition == "dissolve"
[文档]
def test_get_output_filename(self):
"""测试生成输出文件名"""
config = RenpyConfig()
assert config.get_output_filename("script") == "script.rpy"
[文档]
class TestNaninovelConfig:
"""测试 NaninovelConfig 类"""
[文档]
def test_default_config(self):
"""测试默认配置"""
config = NaninovelConfig()
assert config.engine_type == "naninovel"
assert config.file_extension == ".nani"
assert config.command_prefix == "@"
[文档]
def test_get_output_filename(self):
"""测试生成输出文件名"""
config = NaninovelConfig()
assert config.get_output_filename("chapter1") == "chapter1.nani"
[文档]
class TestAppConfig:
"""测试 AppConfig 类"""
[文档]
def test_default_config(self):
"""测试默认配置"""
config = AppConfig()
assert isinstance(config.paths, PathConfig)
assert isinstance(config.processing, ProcessingConfig)
assert isinstance(config.engine, NaninovelConfig)
[文档]
def test_create_default_naninovel(self):
"""测试创建默认 Naninovel 配置"""
config = AppConfig.create_default("naninovel")
assert isinstance(config.engine, NaninovelConfig)
assert config.engine.engine_type == "naninovel"
[文档]
def test_create_default_renpy(self):
"""测试创建默认 Ren'Py 配置"""
config = AppConfig.create_default("renpy")
assert isinstance(config.engine, RenpyConfig)
assert config.engine.engine_type == "renpy"
[文档]
def test_create_default_invalid_engine(self):
"""测试创建不支持的引擎配置"""
with pytest.raises(ValueError, match="不支持的引擎类型"):
AppConfig.create_default("invalid_engine")
[文档]
def test_from_dict_minimal(self):
"""测试从最小字典创建配置"""
data = {}
config = AppConfig.from_dict(data)
assert isinstance(config.paths, PathConfig)
assert isinstance(config.processing, ProcessingConfig)
assert isinstance(config.engine, NaninovelConfig)
[文档]
def test_from_dict_with_paths(self):
"""测试从包含路径的字典创建配置"""
data = {
'paths': {
'input_dir': './test_input',
'output_dir': './test_output'
}
}
config = AppConfig.from_dict(data)
assert config.paths.input_dir == Path('./test_input')
assert config.paths.output_dir == Path('./test_output')
[文档]
def test_from_dict_with_processing(self):
"""测试从包含处理配置的字典创建配置"""
data = {
'processing': {
'ignore_mode': False,
'batch_size': 50
}
}
config = AppConfig.from_dict(data)
assert config.processing.ignore_mode is False
assert config.processing.batch_size == 50
[文档]
def test_from_dict_with_renpy_engine(self):
"""测试从字典创建 Ren'Py 引擎配置"""
data = {
'engine': {
'engine_type': 'renpy',
'indent_size': 2,
'default_transition': 'fade'
}
}
config = AppConfig.from_dict(data)
assert isinstance(config.engine, RenpyConfig)
assert config.engine.engine_type == 'renpy'
assert config.engine.indent_size == 2
assert config.engine.default_transition == 'fade'
[文档]
def test_from_dict_with_naninovel_engine(self):
"""测试从字典创建 Naninovel 引擎配置"""
data = {
'engine': {
'engine_type': 'naninovel',
'command_prefix': '#'
}
}
config = AppConfig.from_dict(data)
assert isinstance(config.engine, NaninovelConfig)
assert config.engine.command_prefix == '#'
[文档]
def test_from_dict_with_engine_string(self):
"""测试从字符串引擎类型创建配置"""
data = {
'engine': 'renpy'
}
config = AppConfig.from_dict(data)
assert isinstance(config.engine, RenpyConfig)
assert config.engine.engine_type == 'renpy'
[文档]
def test_from_dict_invalid_engine(self):
"""测试从字典创建不支持的引擎配置"""
data = {
'engine': {
'engine_type': 'invalid'
}
}
with pytest.raises(ValueError, match="不支持的引擎类型"):
AppConfig.from_dict(data)
[文档]
def test_from_file_json(self, tmp_path):
"""测试从 JSON 文件加载配置"""
config_file = tmp_path / "config.json"
data = {
'paths': {
'input_dir': './json_input'
},
'engine': {
'engine_type': 'renpy'
}
}
with open(config_file, 'w', encoding='utf-8') as f:
json.dump(data, f)
config = AppConfig.from_file(config_file)
assert config.paths.input_dir == Path('./json_input')
assert isinstance(config.engine, RenpyConfig)
[文档]
def test_from_file_yaml(self, tmp_path):
"""测试从 YAML 文件加载配置"""
config_file = tmp_path / "config.yaml"
data = {
'paths': {
'input_dir': './yaml_input'
},
'engine': {
'engine_type': 'naninovel'
}
}
with open(config_file, 'w', encoding='utf-8') as f:
yaml.dump(data, f)
config = AppConfig.from_file(config_file)
assert config.paths.input_dir == Path('./yaml_input')
assert isinstance(config.engine, NaninovelConfig)
[文档]
def test_from_file_yml_extension(self, tmp_path):
"""测试从 .yml 文件加载配置"""
config_file = tmp_path / "config.yml"
data = {
'engine': {
'engine_type': 'renpy'
}
}
with open(config_file, 'w', encoding='utf-8') as f:
yaml.dump(data, f)
config = AppConfig.from_file(config_file)
assert isinstance(config.engine, RenpyConfig)
[文档]
def test_to_file_json(self, tmp_path):
"""测试保存配置到 JSON 文件"""
config = AppConfig.create_default("renpy")
config.paths.input_dir = Path("./test_input")
config.processing.batch_size = 50
config_file = tmp_path / "output.json"
config.to_file(config_file)
# 验证文件已创建
assert config_file.exists()
# 验证文件内容
with open(config_file, 'r', encoding='utf-8') as f:
data = json.load(f)
assert Path(data['paths']['input_dir']) == Path('./test_input')
assert data['processing']['batch_size'] == 50
assert data['engine']['engine_type'] == 'renpy'
assert data['engine']['label_indent'] is False
assert data['engine']['default_transition'] == 'dissolve'
[文档]
def test_to_file_yaml(self, tmp_path):
"""测试保存配置到 YAML 文件"""
config = AppConfig.create_default("naninovel")
config.paths.output_dir = Path("./test_output")
config.processing.ignore_mode = False
config_file = tmp_path / "output.yaml"
config.to_file(config_file)
# 验证文件已创建
assert config_file.exists()
# 验证文件内容
with open(config_file, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
assert Path(data['paths']['output_dir']) == Path('./test_output')
assert data['processing']['ignore_mode'] is False
assert data['engine']['engine_type'] == 'naninovel'
assert data['engine']['command_prefix'] == '@'
[文档]
def test_to_file_yml_extension(self, tmp_path):
"""测试保存配置到 .yml 文件"""
config = AppConfig.create_default("renpy")
config_file = tmp_path / "output.yml"
config.to_file(config_file)
assert config_file.exists()
with open(config_file, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
assert data['engine']['engine_type'] == 'renpy'
[文档]
def test_roundtrip_json(self, tmp_path):
"""测试 JSON 配置的往返转换"""
# 创建配置
original = AppConfig.create_default("renpy")
original.paths.input_dir = Path("./roundtrip_input")
original.processing.batch_size = 75
# 保存到文件
config_file = tmp_path / "roundtrip.json"
original.to_file(config_file)
# 从文件加载
loaded = AppConfig.from_file(config_file)
# 验证配置一致
assert loaded.paths.input_dir == original.paths.input_dir
assert loaded.processing.batch_size == original.processing.batch_size
assert loaded.engine.engine_type == original.engine.engine_type
[文档]
def test_roundtrip_yaml(self, tmp_path):
"""测试 YAML 配置的往返转换"""
# 创建配置
original = AppConfig.create_default("naninovel")
original.paths.output_dir = Path("./roundtrip_output")
original.processing.enable_progress_bar = False
# 保存到文件
config_file = tmp_path / "roundtrip.yaml"
original.to_file(config_file)
# 从文件加载
loaded = AppConfig.from_file(config_file)
# 验证配置一致
assert loaded.paths.output_dir == original.paths.output_dir
assert loaded.processing.enable_progress_bar == original.processing.enable_progress_bar
assert loaded.engine.engine_type == original.engine.engine_type