forked from HAL9000/cleveragents-core
297 lines
9.9 KiB
Gherkin
297 lines
9.9 KiB
Gherkin
Feature: YAML Template Engine Comprehensive Coverage
|
|
As a developer using CleverAgents
|
|
I want comprehensive coverage of the YAML template engine
|
|
So that all functionality is thoroughly tested
|
|
|
|
Background:
|
|
Given the YAML template engine is initialized
|
|
|
|
@core_functionality
|
|
Scenario: Load YAML file from filesystem
|
|
Given I have a test YAML file "test_template.yaml" with content:
|
|
"""
|
|
config:
|
|
name: {{ project_name }}
|
|
version: {{ version }}
|
|
agents:
|
|
{% for agent in agents %}
|
|
{{ agent.name }}:
|
|
type: {{ agent.type }}
|
|
model: {{ agent.model }}
|
|
{% endfor %}
|
|
"""
|
|
And I have a template context:
|
|
| key | value |
|
|
| project_name | FileProject |
|
|
| version | 2.0 |
|
|
And I have agents data:
|
|
| name | type | model |
|
|
| agent1 | llm | gpt-4 |
|
|
| agent2 | llm | gpt-3.5-turbo |
|
|
When I load the YAML file with context
|
|
Then the result should contain a "config" section with name "FileProject"
|
|
And the result should contain 2 agents
|
|
And agent "agent1" should have model "gpt-4" in comprehensive test
|
|
|
|
@core_functionality
|
|
Scenario: Load YAML string without templates
|
|
Given I have a plain YAML string:
|
|
"""
|
|
config:
|
|
name: PlainProject
|
|
version: 1.0
|
|
agents:
|
|
simple_agent:
|
|
type: llm
|
|
model: gpt-3.5-turbo
|
|
"""
|
|
When I load the YAML string without context
|
|
Then the result should contain a "config" section with name "PlainProject"
|
|
And the result should contain an agent named "simple_agent"
|
|
|
|
@preprocessing
|
|
Scenario: Preprocessing YAML with for loops
|
|
Given I have a YAML string with for loops requiring preprocessing:
|
|
"""
|
|
services:
|
|
{% for i in range(3) %}
|
|
service_{{ i }}:
|
|
port: {{ 8000 + i }}
|
|
replicas: {{ i + 1 }}
|
|
{% endfor %}
|
|
"""
|
|
And I have a simple numeric context
|
|
When I process the YAML with preprocessing
|
|
Then the result should contain 3 services
|
|
And service "service_0" should have port 8000
|
|
And service "service_2" should have port 8002
|
|
|
|
@postprocessing
|
|
Scenario: Postprocessing fixes structural issues
|
|
Given I have a YAML string that will generate structural issues:
|
|
"""
|
|
items:
|
|
{% for item in items %}
|
|
{{ item.key }}: {{ item.value }}
|
|
{% endfor %}
|
|
"""
|
|
And I have a context with structural data:
|
|
| key | value |
|
|
| item1 | val1 |
|
|
| item2 | val2 |
|
|
When I process the YAML with postprocessing
|
|
Then the result should have proper YAML structure
|
|
And the items should be correctly separated
|
|
|
|
@error_handling
|
|
Scenario: Handle YAML parsing errors gracefully
|
|
Given I have a YAML string that will cause parsing errors:
|
|
"""
|
|
config:
|
|
name: {{ name }}
|
|
{% for item in items %}
|
|
{{ item }}: value: {{ item }}: another
|
|
{% endfor %}
|
|
"""
|
|
And I have error-prone template context:
|
|
| key | value |
|
|
| name | ErrorProject |
|
|
| items | [test, demo] |
|
|
When I process the YAML with error handling
|
|
Then the YAML parsing errors should be handled
|
|
And the result should contain fallback structures
|
|
|
|
@filters
|
|
Scenario: YAML filter functionality
|
|
Given I have a YAML string using the yaml filter:
|
|
"""
|
|
data:
|
|
serialized: {{ complex_data | yaml }}
|
|
indented_text: "{{ nested_content | indent(4) }}"
|
|
"""
|
|
And I have complex filter context
|
|
When I process the YAML with filters
|
|
Then the yaml filter should produce valid YAML output
|
|
And the indent filter should add proper spacing
|
|
|
|
@filters
|
|
Scenario: Sum filter with attributes
|
|
Given I have a YAML string using sum filter:
|
|
"""
|
|
totals:
|
|
simple_sum: {{ numbers | sum }}
|
|
attribute_sum: {{ items | sum(attribute='value') }}
|
|
dict_sum: {{ dict_items | sum }}
|
|
"""
|
|
And I have sum filter context
|
|
When I process the YAML with sum filters
|
|
Then the simple_sum should be correct
|
|
And the attribute_sum should be correct
|
|
And the dict_sum should handle dict values
|
|
|
|
@filters
|
|
Scenario: Selectattr filter functionality
|
|
Given I have a YAML string using selectattr filter:
|
|
"""
|
|
filtered:
|
|
greater_than: {{ data | selectattr('score', '>', 80) | list | length }}
|
|
less_than: {{ data | selectattr('score', '<', 50) | list | length }}
|
|
equal_to: {{ data | selectattr('category', '==', 'A') | list | length }}
|
|
not_equal: {{ data | selectattr('status', '!=', 'active') | list | length }}
|
|
"""
|
|
And I have selectattr filter context
|
|
When I process the YAML with selectattr filters
|
|
Then the selectattr results should be correct for all operators
|
|
|
|
@structure_analysis
|
|
Scenario: Analyze YAML structure for templates
|
|
Given I have a complex YAML structure with mixed templates:
|
|
"""
|
|
root:
|
|
static_key: static_value
|
|
{% for section in sections %}
|
|
{{ section.name }}:
|
|
dynamic_value: {{ section.value }}
|
|
nested:
|
|
{% if section.enabled %}
|
|
enabled_feature: true
|
|
{% endif %}
|
|
{% endfor %}
|
|
inline_template: {{ simple_var }}
|
|
"""
|
|
And I have structure analysis context
|
|
When I analyze the YAML structure
|
|
Then template blocks should be identified
|
|
And inline templates should be detected
|
|
And the hierarchy should be mapped correctly
|
|
|
|
@block_handling
|
|
Scenario: Find template block boundaries
|
|
Given I have nested template blocks:
|
|
"""
|
|
outer:
|
|
{% for outer_item in outer_items %}
|
|
{{ outer_item.name }}:
|
|
{% for inner_item in outer_item.children %}
|
|
{{ inner_item.name }}: {{ inner_item.value }}
|
|
{% endfor %}
|
|
{% endfor %}
|
|
"""
|
|
And I have nested block context
|
|
When I find template block boundaries
|
|
Then outer block boundaries should be correct
|
|
And inner block boundaries should be correct
|
|
And nested structure should be preserved
|
|
|
|
@deferred_rendering
|
|
Scenario: Complex deferred template extraction
|
|
Given I have a template requiring complex extraction:
|
|
"""
|
|
workflows:
|
|
{% for workflow in workflows %}
|
|
{{ workflow.name }}:
|
|
steps:
|
|
{% for step in workflow.steps %}
|
|
- name: {{ step.name }}
|
|
action: {{ step.action }}
|
|
{% if step.params %}
|
|
params:
|
|
{% for key, value in step.params.items() %}
|
|
{{ key }}: {{ value }}
|
|
{% endfor %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
{% endfor %}
|
|
"""
|
|
When I extract the template for deferred rendering
|
|
Then the template structure should be preserved
|
|
And template sections should be marked correctly
|
|
And the result should be renderable later
|
|
|
|
@template_reconstruction
|
|
Scenario: Reconstruct YAML from stored structure
|
|
Given I have a stored template structure with template blocks
|
|
And I have template content and metadata
|
|
When I reconstruct YAML from the structure
|
|
Then the reconstructed YAML should match original format
|
|
And template blocks should be properly restored
|
|
And inline templates should be correctly placed
|
|
|
|
@utility_functions
|
|
Scenario: Custom utility functions in context
|
|
Given I have a YAML string using utility functions:
|
|
"""
|
|
computed:
|
|
length: {{ data | length }}
|
|
range_test: {{ range(3) | list }}
|
|
min_val: {{ values | min }}
|
|
max_val: {{ values | max }}
|
|
sum_val: {{ values | sum }}
|
|
rounded: {{ float_val | round(2) }}
|
|
json_output: {{ data | tojson }}
|
|
default_val: {{ none_val | default('fallback') }}
|
|
"""
|
|
And I have utility function context
|
|
When I process the YAML with utility functions
|
|
Then all utility functions should work correctly
|
|
And the output should have expected values
|
|
|
|
@edge_cases
|
|
Scenario: Handle empty and None values
|
|
Given I have a YAML string with empty and None handling:
|
|
"""
|
|
values:
|
|
{% for item in empty_list %}
|
|
- {{ item }}
|
|
{% endfor %}
|
|
{% if none_value %}
|
|
none_section: {{ none_value }}
|
|
{% endif %}
|
|
empty_dict_keys:
|
|
{% for key, value in empty_dict.items() %}
|
|
{{ key }}: {{ value }}
|
|
{% endfor %}
|
|
"""
|
|
And I have empty value context
|
|
When I process the YAML with empty values
|
|
Then empty structures should be handled gracefully
|
|
And None values should not cause errors
|
|
And the result should have valid structure
|
|
|
|
@file_operations
|
|
Scenario: File operations error handling
|
|
Given I have a non-existent file path "missing_file.yaml"
|
|
When I try to load the missing file
|
|
Then a file not found error should be handled appropriately
|
|
And the error should be informative
|
|
|
|
@context_management
|
|
Scenario: Complete render context creation
|
|
Given I have various data types in context
|
|
When I create a complete render context
|
|
Then Python built-ins should be available
|
|
And utility functions should be included
|
|
And custom context should be preserved
|
|
And all functions should be callable
|
|
|
|
@yaml_fixes
|
|
Scenario: Fix common YAML structural issues
|
|
Given I have YAML with multiple structural problems:
|
|
"""
|
|
problematic:
|
|
key1: value1 key2: value2 key3: value3
|
|
key4: value4
|
|
key5: value5: extra: colons
|
|
"""
|
|
When I apply YAML structure fixes
|
|
Then multiple colons should be fixed
|
|
And keys should be properly separated
|
|
And the result should parse correctly
|
|
|
|
@template_storage
|
|
Scenario: Simple template extraction fallback
|
|
Given I have a complex template that fails detailed extraction
|
|
When I use simple template extraction
|
|
Then the template should be stored as raw content
|
|
And template markers should be present
|
|
And the template should be marked for later rendering |