Files
temp/tests/features/steps/yaml_template_coverage_gaps_steps.py

519 lines
18 KiB
Python

"""Step definitions for YAML template engine coverage gaps."""
import json
from typing import Any
from typing import Dict
from typing import List
import yaml
from behave import given
from behave import then
from behave import when
from cleveragents.templates.yaml_template_engine import YAMLTemplateEngine
@given("I have YAML content that triggers postprocessing line splitting")
def step_yaml_postprocessing_line_splitting(context):
"""Create YAML content that triggers line splitting."""
context.yaml_content = """config:
combined: value1 key2: value2
normal: single_value"""
@when("I test the postprocessing method directly")
def step_test_postprocessing_directly(context):
"""Test postprocessing method directly."""
context.processed = context.yaml_engine._postprocess_rendered_yaml(
context.yaml_content
)
@then("the line splitting logic should be executed")
def step_check_line_splitting_executed(context):
"""Check line splitting logic is executed."""
# The method should process the content (may or may not change it)
assert isinstance(context.processed, str)
@then("problematic lines should be restructured")
def step_check_problematic_lines_restructured(context):
"""Check problematic lines are restructured."""
# Lines with multiple values should be processed
lines = context.processed.split("\n")
assert len(lines) >= 2
@given("I have YAML with multiple colons that need fixing")
def step_yaml_multiple_colons_fixing(context):
"""Create YAML with multiple colons."""
context.yaml_content = """problematic:
key1: value1: key2: value2: key3: value3
normal_key: normal_value"""
@when("I test the fix common issues method directly")
def step_test_fix_common_issues_directly(context):
"""Test fix common issues method directly."""
context.fixed = context.yaml_engine._fix_common_yaml_issues(context.yaml_content)
@then("multiple colon lines should be processed")
def step_check_multiple_colon_lines_processed(context):
"""Check multiple colon lines are processed."""
# Should attempt to fix multiple colon issues
assert isinstance(context.fixed, str)
lines = context.fixed.split("\n")
assert len(lines) >= 2
@then("tokens should be properly separated")
def step_check_tokens_separated(context):
"""Check tokens are properly separated."""
# Fixed version should handle token separation
assert len(context.fixed) > 0
@then("indentation should be maintained")
def step_check_indentation_maintained(context):
"""Check indentation is maintained."""
lines = context.fixed.split("\n")
has_indentation = any(line.startswith(" ") for line in lines if line.strip())
# Should maintain some form of structure
assert has_indentation or len([l for l in lines if l.strip()]) <= 2
@given("I have data with None values for sum filter")
def step_data_none_values_sum_filter(context):
"""Create data with None values for sum filter."""
context.test_data = [1, 2, None, 4, None, 5]
@when("I test the sum filter directly with None values")
def step_test_sum_filter_none_values(context):
"""Test sum filter directly with None values."""
context.sum_result = context.yaml_engine._sum_filter(context.test_data)
@then("None values should be filtered out by sum filter")
def step_check_none_values_filtered(context):
"""Check None values are filtered out."""
# Sum should be 1+2+4+5 = 12 (None values filtered)
assert context.sum_result == 12
@then("remaining values should be summed correctly")
def step_check_remaining_values_summed(context):
"""Check remaining values are summed correctly."""
assert context.sum_result == 12
@given("I have objects with missing attributes for sum filter")
def step_objects_missing_attributes_sum(context):
"""Create objects with missing attributes."""
context.test_objects = [
{"value": 10},
{"value": 20},
{"name": "no_value"}, # Missing 'value' attribute
{"value": 30},
]
@when("I test the sum filter with attribute parameter")
def step_test_sum_filter_attribute_param(context):
"""Test sum filter with attribute parameter."""
context.sum_attr_result = context.yaml_engine._sum_filter(
context.test_objects, "value"
)
@then("missing attributes should be treated as zero")
def step_check_missing_attributes_zero(context):
"""Check missing attributes are treated as zero."""
# Should sum 10+20+0+30 = 60
assert context.sum_attr_result == 60
@then("available attributes should be summed")
def step_check_available_attributes_summed(context):
"""Check available attributes are summed."""
assert context.sum_attr_result == 60
@given("I have data for testing all selectattr operators")
def step_data_all_selectattr_operators(context):
"""Create data for testing all selectattr operators."""
context.selectattr_data = [
{"score": 95, "status": "active", "level": "A"},
{"score": 45, "status": "inactive", "level": "B"},
{"score": 80, "status": "active", "level": "A"},
{"score": 50, "status": "pending", "level": "C"},
{"score": 85, "status": "active", "level": "A"},
]
@when("I test selectattr filter with greater than operator")
def step_test_selectattr_greater_than(context):
"""Test selectattr with greater than operator."""
context.gt_result = context.yaml_engine._selectattr_filter(
context.selectattr_data, "score", ">", 80
)
@when("I test selectattr filter with less than operator")
def step_test_selectattr_less_than(context):
"""Test selectattr with less than operator."""
context.lt_result = context.yaml_engine._selectattr_filter(
context.selectattr_data, "score", "<", 60
)
@when("I test selectattr filter with greater equal operator")
def step_test_selectattr_greater_equal(context):
"""Test selectattr with greater equal operator."""
context.ge_result = context.yaml_engine._selectattr_filter(
context.selectattr_data, "score", ">=", 80
)
@when("I test selectattr filter with less equal operator")
def step_test_selectattr_less_equal(context):
"""Test selectattr with less equal operator."""
context.le_result = context.yaml_engine._selectattr_filter(
context.selectattr_data, "score", "<=", 50
)
@when("I test selectattr filter with not equal operator")
def step_test_selectattr_not_equal(context):
"""Test selectattr with not equal operator."""
context.ne_result = context.yaml_engine._selectattr_filter(
context.selectattr_data, "status", "!=", "active"
)
@when("I test selectattr filter with unknown operator")
def step_test_selectattr_unknown_operator(context):
"""Test selectattr with unknown operator."""
context.unknown_result = context.yaml_engine._selectattr_filter(
context.selectattr_data, "score", "unknown_op", 50
)
@then("all operators should work correctly")
def step_check_all_operators_work(context):
"""Check all operators work correctly."""
assert len(context.gt_result) == 2 # scores > 80: 95, 85
assert len(context.lt_result) == 2 # scores < 60: 45, 50
assert len(context.ge_result) == 3 # scores >= 80: 95, 80, 85
assert len(context.le_result) == 2 # scores <= 50: 45, 50
assert len(context.ne_result) == 2 # status != 'active': inactive, pending
@then("unknown operators should default to equality")
def step_check_unknown_operators_default(context):
"""Check unknown operators default to equality."""
assert len(context.unknown_result) == 1 # score == 50: only one
@given("I have objects with missing attributes for selectattr")
def step_objects_missing_attributes_selectattr(context):
"""Create objects with missing attributes."""
context.selectattr_missing_data = [
{"name": "obj1", "score": 85},
{"name": "obj2"}, # Missing score
{"name": "obj3", "score": None}, # None score
{"name": "obj4", "score": 90},
]
@when("I test selectattr filter on missing attributes")
def step_test_selectattr_missing_attributes(context):
"""Test selectattr filter on missing attributes."""
# Test with attribute that doesn't exist on all objects
context.missing_attr_result = context.yaml_engine._selectattr_filter(
context.selectattr_missing_data, "missing_attr", "==", "value"
)
# Test filtering on score attribute where some objects don't have it
context.score_filter_result = context.yaml_engine._selectattr_filter(
context.selectattr_missing_data, "score", ">", 80
)
@then("objects without the attribute should be handled gracefully")
def step_check_objects_without_attribute_handled(context):
"""Check objects without attribute are handled gracefully."""
assert len(context.missing_attr_result) == 0 # No objects have 'missing_attr'
@then("None values should be properly filtered")
def step_check_none_values_properly_filtered(context):
"""Check None values are properly filtered."""
# Should only include objects with score > 80 and not None
assert len(context.score_filter_result) == 2 # obj1(85) and obj4(90)
@given("I have YAML with various template blocks")
def step_yaml_various_template_blocks(context):
"""Create YAML with various template blocks."""
context.yaml_content = """root:
static_key: static_value
{% for item in items %}
{{ item.name }}:
value: {{ item.value }}
{% if item.enabled %}
enabled_feature: true
{% endif %}
{% endfor %}
{% macro test_macro() %}
macro_content: value
{% endmacro %}
inline_template: {{ simple_var }}"""
@when("I analyze the YAML structure for templates")
def step_analyze_yaml_structure_templates(context):
"""Analyze YAML structure for templates."""
context.structure = context.yaml_engine._analyze_yaml_structure(
context.yaml_content
)
@then("template blocks should be identified correctly")
def step_check_template_blocks_identified(context):
"""Check template blocks are identified correctly."""
assert "template_blocks" in context.structure
blocks = context.structure["template_blocks"]
assert len(blocks) >= 2 # Should find for, if, and possibly macro blocks
@then("block types should be detected")
def step_check_block_types_detected(context):
"""Check block types are detected."""
blocks = context.structure["template_blocks"]
block_types = [block.get("type") for block in blocks]
assert "for" in block_types
@then("block boundaries should be found")
def step_check_block_boundaries_found(context):
"""Check block boundaries are found."""
blocks = context.structure["template_blocks"]
for block in blocks:
assert "start_line" in block
assert "end_line" in block
assert block["start_line"] < block["end_line"]
@given("I have template blocks with varying indentations")
def step_template_blocks_varying_indentations(context):
"""Create template blocks with varying indentations."""
context.yaml_lines = [
"section1:",
" {% for item in items %}",
" subsection:",
" value: {{ item.value }}",
" {% endfor %}",
"section2:",
" {% if condition %}",
" deep_section:",
" nested: value",
" {% endif %}",
]
@when("I find block end boundaries")
def step_find_block_end_boundaries(context):
"""Find block end boundaries."""
context.block_ends = []
for i, line in enumerate(context.yaml_lines):
if (
"{%" in line
and any(kw in line for kw in ["for", "if"])
and "end" not in line
):
indent = len(line) - len(line.lstrip())
end_line = context.yaml_engine._find_block_end(
context.yaml_lines, i, indent
)
context.block_ends.append((i, end_line, indent))
@then("end markers should respect indentation levels")
def step_check_end_markers_respect_indentation(context):
"""Check end markers respect indentation levels."""
for start, end, indent in context.block_ends:
assert end > start
# Verify the end line has proper end marker
end_line = context.yaml_lines[end] if end < len(context.yaml_lines) else ""
if end_line.strip():
# Should contain end marker or be at proper indentation
assert indent >= 0
@then("nested blocks should be handled correctly")
def step_check_nested_blocks_handled(context):
"""Check nested blocks are handled correctly."""
# Should find at least 2 blocks at different indentation levels
indents = [indent for _, _, indent in context.block_ends]
assert len(set(indents)) >= 1 # Different indentation levels
@given("I have complex templates that may fail extraction")
def step_complex_templates_fail_extraction(context):
"""Create complex templates that may fail extraction."""
context.complex_yaml = """
complex:
{% macro test_macro(param) %}
{{ param }}: value
{% endmacro %}
{% for item in items %}
{{ item }}: {{ test_macro(item) }}
{% endfor %}
nested:
{% if complex_condition %}
{% for sub in sub_items %}
{{ sub.name }}: {{ sub.value }}
{% endfor %}
{% endif %}
"""
@when("I attempt complex template extraction")
def step_attempt_complex_extraction(context):
"""Attempt complex template extraction."""
context.extracted = context.yaml_engine._prepare_for_deferred_rendering(
context.complex_yaml
)
@then("fallback to simple extraction should occur")
def step_check_fallback_simple_extraction(context):
"""Check fallback to simple extraction occurs."""
# Should result in simple template format
assert isinstance(context.extracted, dict)
assert "_raw_template" in context.extracted or "_is_template" in context.extracted
@then("templates should be marked for deferred rendering")
def step_check_templates_marked_deferred(context):
"""Check templates are marked for deferred rendering."""
assert "_is_template" in context.extracted
assert context.extracted["_is_template"] is True
@given("I have stored template structures with various types")
def step_stored_template_structures_various_types(context):
"""Create stored template structures with various types."""
context.stored_structures = [
# Template with block content
{
"section": {
"_template_content": "{% for item in items %}\n{{ item.name }}: {{ item.value }}\n{% endfor %}",
"_template_type": "block",
}
},
# Template with inline content
{
"config": {
"_template_value": "{{ config_value }}",
"_template_type": "inline",
}
},
# Regular nested structure
{
"nested": {
"key1": "value1",
"key2": None,
"subsection": {"inner": "inner_value"},
},
"list_data": ["item1", "item2", {"nested_item": "value"}],
},
]
@when("I reconstruct YAML from the structures")
def step_reconstruct_yaml_structures(context):
"""Reconstruct YAML from structures."""
context.reconstructed = []
for structure in context.stored_structures:
reconstructed = context.yaml_engine._reconstruct_from_structure(structure)
context.reconstructed.append(reconstructed)
@then("template blocks should be restored")
def step_check_template_blocks_restored(context):
"""Check template blocks are restored."""
# First structure should have template block restored
first_reconstructed = context.reconstructed[0]
assert "{% for item in items %}" in first_reconstructed
@then("inline templates should be placed correctly")
def step_check_inline_templates_placed(context):
"""Check inline templates are placed correctly."""
# Second structure should have inline template
second_reconstructed = context.reconstructed[1]
assert "{{ config_value }}" in second_reconstructed
@then("nested structures should be reconstructed")
def step_check_nested_structures_reconstructed(context):
"""Check nested structures are reconstructed."""
# Third structure should have nested content
third_reconstructed = context.reconstructed[2]
assert "nested:" in third_reconstructed
assert "key1:" in third_reconstructed
assert "list_data:" in third_reconstructed
@given("I have templates in raw and structured formats")
def step_templates_raw_structured_formats(context):
"""Create templates in different formats."""
context.raw_template = {
"_raw_template": "config:\n name: {{ name }}\n value: {{ value }}",
"_is_template": True,
}
context.structured_template = {
"config": {
"_template_content": "name: {{ name }}\nvalue: {{ value }}",
"_template_type": "block",
}
}
@when("I render templates from both formats")
def step_render_templates_both_formats(context):
"""Render templates from both formats."""
render_context = {"name": "test_name", "value": "test_value"}
context.raw_rendered = context.yaml_engine.render_template(
context.raw_template, render_context
)
context.structured_rendered = context.yaml_engine.render_template(
context.structured_template, render_context
)
@then("both formats should be supported")
def step_check_both_formats_supported(context):
"""Check both formats are supported."""
assert isinstance(context.raw_rendered, dict)
assert isinstance(context.structured_rendered, dict)
@then("template variables should be substituted correctly")
def step_check_template_variables_substituted_correctly(context):
"""Check template variables are substituted correctly."""
# Raw template should have config section with substituted values
if "config" in context.raw_rendered:
config = context.raw_rendered["config"]
assert config["name"] == "test_name"
assert config["value"] == "test_value"
# Structured template should also have proper values
assert isinstance(context.structured_rendered, dict)