""" 模板系统单元测试 测试 Template 类的初始化、变量解析、条件渲染和模板渲染功能 """ import pytest from pathlib import Path from loaders.yaml_loader import YAMLError from core.template import Template # ============= 模板初始化测试 ============= class TestTemplateInit: """Template 初始化测试类""" def test_init_without_template_dir_raises_error(self, temp_dir): """测试未指定模板目录会引发错误""" with pytest.raises(YAMLError, match="未指定模板目录"): Template("title-slide", templates_dir=None) def test_init_with_path_separator_in_name_raises_error(self, temp_dir): """测试模板名称包含路径分隔符会引发错误""" with pytest.raises(YAMLError, match="模板名称不能包含路径分隔符"): Template("../etc/passwd", templates_dir=temp_dir) def test_init_with_nonexistent_template_raises_error(self, temp_dir): """测试模板文件不存在会引发错误""" with pytest.raises(YAMLError, match="模板文件不存在"): Template("nonexistent", templates_dir=temp_dir) def test_init_with_valid_template(self, sample_template): """测试使用有效模板初始化""" template = Template("title-slide", templates_dir=sample_template) assert template.data is not None assert "elements" in template.data assert "vars" in template.data # ============= 变量解析测试 ============= class TestResolveValue: """resolve_value 方法测试类""" def test_resolve_value_simple_variable(self, sample_template): """测试解析简单变量""" template = Template("title-slide", templates_dir=sample_template) result = template.resolve_value("{title}", {"title": "My Title"}) assert result == "My Title" def test_resolve_value_multiple_variables(self, sample_template): """测试解析多个变量""" template = Template("title-slide", templates_dir=sample_template) result = template.resolve_value( "{title} - {subtitle}", {"title": "Main", "subtitle": "Sub"} ) assert result == "Main - Sub" def test_resolve_value_undefined_variable_raises_error(self, sample_template): """测试未定义变量会引发错误""" template = Template("title-slide", templates_dir=sample_template) with pytest.raises(YAMLError, match="未定义的变量"): template.resolve_value("{undefined}", {"title": "Test"}) def test_resolve_value_preserves_non_string(self, sample_template): """测试非字符串值保持原样""" template = Template("title-slide", templates_dir=sample_template) assert template.resolve_value(123, {}) == 123 assert template.resolve_value(None, {}) is None assert template.resolve_value(["list"], {}) == ["list"] def test_resolve_value_converts_to_integer(self, sample_template): """测试结果转换为整数""" template = Template("title-slide", templates_dir=sample_template) result = template.resolve_value("{value}", {"value": "42"}) assert result == 42 assert isinstance(result, int) def test_resolve_value_converts_to_float(self, sample_template): """测试结果转换为浮点数""" template = Template("title-slide", templates_dir=sample_template) result = template.resolve_value("{value}", {"value": "3.14"}) assert result == 3.14 assert isinstance(result, float) # ============= resolve_element 测试 ============= class TestResolveElement: """resolve_element 方法测试类""" def test_resolve_element_dict(self, sample_template): """测试解析字典元素""" template = Template("title-slide", templates_dir=sample_template) elem = {"content": "{title}", "box": [1, 2, 3, 4]} result = template.resolve_element(elem, {"title": "Test"}) assert result["content"] == "Test" assert result["box"] == [1, 2, 3, 4] def test_resolve_element_list(self, sample_template): """测试解析列表元素""" template = Template("title-slide", templates_dir=sample_template) elem = ["{a}", "{b}", "static"] result = template.resolve_element(elem, {"a": "A", "b": "B"}) assert result == ["A", "B", "static"] def test_resolve_element_nested_structure(self, sample_template): """测试解析嵌套结构""" template = Template("title-slide", templates_dir=sample_template) elem = {"font": {"size": "{size}", "color": "#000000"}} result = template.resolve_element(elem, {"size": "24"}) assert result["font"]["size"] == 24 assert result["font"]["color"] == "#000000" def test_resolve_element_excludes_visible(self, sample_template): """测试 visible 字段被排除""" template = Template("title-slide", templates_dir=sample_template) elem = {"content": "test", "visible": "{condition}"} result = template.resolve_element(elem, {}) assert "visible" not in result # ============= 条件渲染测试 ============= class TestEvaluateCondition: """evaluate_condition 方法测试类""" def test_evaluate_condition_with_non_empty_variable(self, sample_template): """测试非空变量条件为真""" template = Template("title-slide", templates_dir=sample_template) result = template.evaluate_condition( "{subtitle != ''}", {"subtitle": "Test Subtitle"} ) assert result is True def test_evaluate_condition_with_empty_variable(self, sample_template): """测试空变量条件为假""" template = Template("title-slide", templates_dir=sample_template) result = template.evaluate_condition("{subtitle != ''}", {"subtitle": ""}) assert result is False def test_evaluate_condition_with_missing_variable(self, sample_template): """测试缺失变量条件为假""" template = Template("title-slide", templates_dir=sample_template) result = template.evaluate_condition("{subtitle != ''}", {}) assert result is False def test_evaluate_condition_unrecognized_format_returns_true(self, sample_template): """测试无法识别的条件格式默认返回 True""" template = Template("title-slide", templates_dir=sample_template) result = template.evaluate_condition("some other format", {}) assert result is True # ============= 模板渲染测试 ============= class TestRender: """render 方法测试类""" def test_render_with_required_variable(self, sample_template): """测试渲染包含必需变量的模板""" template = Template("title-slide", templates_dir=sample_template) result = template.render({"title": "My Presentation"}) # 由于条件渲染,subtitle元素被跳过,只返回1个元素 assert len(result) == 1 assert result[0]["content"] == "My Presentation" def test_render_with_optional_variable(self, sample_template): """测试渲染包含可选变量的模板""" template = Template("title-slide", templates_dir=sample_template) result = template.render({"title": "Test", "subtitle": "Subtitle"}) assert len(result) == 2 # 两个元素 def test_render_without_optional_variable(self, sample_template): """测试不提供可选变量""" template = Template("title-slide", templates_dir=sample_template) result = template.render({"title": "Test"}) # subtitle 元素应该被跳过(visible 条件) assert len(result) == 1 def test_render_missing_required_variable_raises_error(self, sample_template): """测试缺少必需变量会引发错误""" template = Template("title-slide", templates_dir=sample_template) with pytest.raises(YAMLError, match="缺少必需变量"): template.render({}) # 缺少 title def test_render_with_default_value(self, temp_dir): """测试使用默认值""" # 创建带默认值的模板 template_content = """ vars: - name: title required: true - name: subtitle required: false default: "Default Subtitle" elements: - type: text content: "{title}" - type: text content: "{subtitle}" """ template_file = temp_dir / "templates" / "default-test.yaml" template_file.parent.mkdir(exist_ok=True) template_file.write_text(template_content) template = Template("default-test", templates_dir=temp_dir / "templates") result = template.render({"title": "Test"}) assert len(result) == 2 def test_render_filters_elements_by_visible_condition(self, sample_template): """测试根据 visible 条件过滤元素""" template = Template("title-slide", templates_dir=sample_template) # 有 subtitle - 应该显示两个元素 result_with = template.render({"title": "Test", "subtitle": "Sub"}) assert len(result_with) == 2 # 无 subtitle - 应该只显示一个元素 result_without = template.render({"title": "Test"}) assert len(result_without) == 1 # ============= 边界情况补充测试 ============= class TestTemplateBoundaryCases: """模板系统边界情况测试""" def test_nested_variable_resolution(self, temp_dir): """测试嵌套变量解析""" template_content = """ vars: - name: title required: true - name: full_title required: false default: "{title} - Extended" elements: - type: text content: "{full_title}" box: [0, 0, 1, 1] font: {} """ template_file = temp_dir / "templates" / "nested.yaml" template_file.parent.mkdir(exist_ok=True) template_file.write_text(template_content) template = Template("nested", templates_dir=temp_dir / "templates") result = template.render({"title": "Main"}) # 默认值中的变量应该被解析 assert result[0]["content"] == "Main - Extended" def test_variable_with_special_characters(self, temp_dir): """测试变量包含特殊字符""" template_content = """ vars: - name: title required: true elements: - type: text content: "{title}" box: [0, 0, 1, 1] font: {} """ template_file = temp_dir / "templates" / "special.yaml" template_file.parent.mkdir(exist_ok=True) template_file.write_text(template_content) template = Template("special", templates_dir=temp_dir / "templates") special_values = [ "Test & Data", "Test