9753b31c7e
CI / quality (push) Successful in 36s
CI / lint (push) Successful in 38s
CI / typecheck (push) Successful in 49s
CI / security (push) Successful in 51s
CI / integration_tests (push) Successful in 55s
CI / unit_tests (push) Successful in 3m35s
CI / build (push) Successful in 33s
CI / coverage (push) Successful in 3m36s
CI / status-check (push) Successful in 3s
Add 13 BDD scenarios covering previously uncovered code paths: - SAFE_BUILTINS validation (sandbox.py: 0% → 100%) - ConfigurationError re-raise path (config.py: 99.3% → 100%) - CLI hello/main functions (cli.py: 72.7% → 90.9%) - GraphState message truncation (state.py: 98.7% → 100%) - ProgressBarManager update/context rendering (progress.py: 0% → 87.7%) - MessageRouter regex/exact/contains routing (message_router.py: 0% → 59.4%) - RoutingAdapter parse_routing_command (routing_adapter.py) - DynamicRouterNode pattern-based routing (dynamic_router.py) - EnhancedTemplateRegistry unknown template type (enhanced_registry.py: 99.2%) - CompositeAgent null-graph error path (composite.py: 97.8% → 98.6%) Cover routing_adapter.py (17% → 100%): all GOTO/ROUTE patterns, create_routing_node, create_conditional_router, dynamic config conversion. Cover dynamic_router.py (21% → 87%): execute with empty/dict/string messages, extract_message with colon parsing, config creation, graph extension with edge generation. Cover message_router.py (59% → 78%): regex, exact, contains, prefix, suffix match types, invalid regex handling, non-string message, set_state. Exercise Node._prepare_conversation_history with invalid configs, _runtime error paths, _execute_message_router with rules, _execute_agent with current_message and metadata propagation, _execute_function with dynamic_router, and _execute_conditional with content_contains/content_not_contains/content_starts_with and custom condition types. Improves nodes.py from 71.3% to 72.5%. Exercise PureGraphConfig, PureLangGraph init with dict/config, RxPyLangGraphBridge registration/connection/lookup, ReactiveStreamRouter operator creation and condition functions, ReactiveConfigParser config/route/graph parsing, ToolAgent tool execution with JSON, space-separated, single, file_read, progress_bar invocations, and ReactiveCleverAgentsApp init/dispose/visualization.
364 lines
13 KiB
Gherkin
364 lines
13 KiB
Gherkin
Feature: SmartYAMLLoader Template Detection, Rendering, and TemplateDefinitionStore Operations
|
|
As a developer
|
|
I want SmartYAMLLoader to detect and render Jinja2 templates, and TemplateDefinitionStore to manage definitions with rendering and reconstruction
|
|
So that YAML files with nested Jinja2 templates are loaded, rendered, and reconstructed correctly
|
|
|
|
Background:
|
|
Given the SmartYAMLLoader is available
|
|
|
|
Scenario: Load simple YAML without templates
|
|
Given I have a simple YAML string without templates
|
|
"""
|
|
name: simple_config
|
|
version: 1.0
|
|
settings:
|
|
debug: true
|
|
max_items: 100
|
|
"""
|
|
When I load the YAML string using SmartYAMLLoader
|
|
Then the parsed YAML should be correct
|
|
And the template sections should be empty
|
|
|
|
Scenario: Load YAML file without templates
|
|
Given I have a YAML file "simple.yaml" without templates
|
|
"""
|
|
database:
|
|
host: localhost
|
|
port: 5432
|
|
name: testdb
|
|
features:
|
|
- authentication
|
|
- logging
|
|
- caching
|
|
"""
|
|
When I load the YAML file using SmartYAMLLoader
|
|
Then the parsed YAML should match the file content
|
|
And no template sections should be extracted
|
|
|
|
Scenario: Load YAML with inline Jinja2 template in value
|
|
Given I have a YAML string with inline template
|
|
"""
|
|
app:
|
|
name: myapp
|
|
debug: true
|
|
welcome_message: "Hello {{ user.name }}, welcome to {{ app.title }}!"
|
|
port: 8080
|
|
"""
|
|
When I load the YAML string using SmartYAMLLoader
|
|
Then the template should be extracted from welcome_message
|
|
And the parsed YAML should have template placeholder
|
|
And the template sections should contain the original template
|
|
|
|
Scenario: Load YAML with multiple inline templates
|
|
Given I have a YAML string with multiple inline templates
|
|
"""
|
|
config:
|
|
title: "{{ site.title | default('My Site') }}"
|
|
description: "{{ site.description }}"
|
|
footer: "Copyright {{ current_year }}"
|
|
normal_field: "no template here"
|
|
"""
|
|
When I load the YAML string using SmartYAMLLoader
|
|
Then multiple template sections should be extracted
|
|
And each template should have unique identifiers
|
|
And the parsed YAML should have multiple template placeholders
|
|
|
|
Scenario: Load YAML with Jinja2 for loop block
|
|
Given I have a YAML string with for loop template
|
|
"""
|
|
navigation:
|
|
items:
|
|
{% for item in menu_items %}
|
|
- name: "{{ item.name }}"
|
|
url: "{{ item.url }}"
|
|
active: {{ item.active }}
|
|
{% endfor %}
|
|
content:
|
|
static: "This is static content"
|
|
"""
|
|
When I load the YAML string using SmartYAMLLoader
|
|
Then the for loop template should be extracted
|
|
And the template should preserve proper indentation
|
|
And the key should be correctly identified
|
|
|
|
Scenario: Load YAML with Jinja2 if-else block
|
|
Given I have a YAML string with if block template
|
|
"""
|
|
config:
|
|
auth_section:
|
|
{% if enable_auth %}
|
|
authentication:
|
|
type: oauth2
|
|
provider: google
|
|
{% endif %}
|
|
other_config:
|
|
logging:
|
|
level: info
|
|
"""
|
|
When I load the YAML string using SmartYAMLLoader
|
|
Then the if block template should be extracted
|
|
And the template block should include endif
|
|
And the template key should be identified correctly
|
|
|
|
Scenario: Load YAML with Jinja2 macro block
|
|
Given I have a YAML string with macro template
|
|
"""
|
|
helpers:
|
|
{% macro render_button(text, class) %}
|
|
button:
|
|
text: "{{ text }}"
|
|
class: "{{ class }}"
|
|
type: submit
|
|
{% endmacro %}
|
|
forms:
|
|
login: "Standard form"
|
|
"""
|
|
When I load the YAML string using SmartYAMLLoader
|
|
Then the macro template should be extracted
|
|
And the macro definition should be preserved
|
|
And the endmacro should be included
|
|
|
|
Scenario: Load YAML with nested template blocks
|
|
Given I have a YAML string with nested templates
|
|
"""
|
|
pages:
|
|
{% for page in site.pages %}
|
|
- title: "{{ page.title }}"
|
|
content:
|
|
{% if page.has_sidebar %}
|
|
sidebar:
|
|
widgets:
|
|
{% for widget in page.widgets %}
|
|
- type: "{{ widget.type }}"
|
|
config: {{ widget.config | tojson }}
|
|
{% endfor %}
|
|
{% endif %}
|
|
footer: "{{ page.footer }}"
|
|
{% endfor %}
|
|
"""
|
|
When I load the YAML string using SmartYAMLLoader
|
|
Then nested templates should be handled correctly
|
|
And proper indentation should be maintained
|
|
And all template blocks should be extracted
|
|
|
|
Scenario: Load YAML with mixed templates and regular content
|
|
Given I have a YAML string with mixed content
|
|
"""
|
|
app:
|
|
name: "{{ app.name }}"
|
|
version: "1.0.0"
|
|
features:
|
|
{% for feature in app.features %}
|
|
- name: "{{ feature.name }}"
|
|
enabled: {{ feature.enabled }}
|
|
{% endfor %}
|
|
database:
|
|
host: localhost
|
|
port: 5432
|
|
api:
|
|
base_url: "{{ api.base_url }}"
|
|
timeout: 30
|
|
"""
|
|
When I load the YAML string using SmartYAMLLoader
|
|
Then both inline and block templates should be extracted
|
|
And regular YAML content should remain unchanged
|
|
And template placeholders should be properly inserted
|
|
|
|
Scenario: Handle YAML parsing errors gracefully
|
|
Given I have invalid YAML with templates
|
|
"""
|
|
app:
|
|
name: "{{ app.name }}"
|
|
invalid_yaml: [unclosed list
|
|
port: 8080
|
|
"""
|
|
When I load the invalid YAML string
|
|
Then a YAML parsing error should be raised
|
|
And the error should include debug information
|
|
And the error should be logged
|
|
|
|
Scenario: TemplateDefinitionStore initialization
|
|
Given I need to test TemplateDefinitionStore
|
|
When I create a new TemplateDefinitionStore instance
|
|
Then it should have a SmartYAMLLoader instance
|
|
And it should have empty definitions and template sections
|
|
|
|
Scenario: TemplateDefinitionStore load_config without templates
|
|
Given I have a TemplateDefinitionStore instance
|
|
And I have a config without templates section
|
|
"""
|
|
app:
|
|
name: myapp
|
|
version: 1.0
|
|
database:
|
|
host: localhost
|
|
"""
|
|
When I load the config using TemplateDefinitionStore
|
|
Then the config should be returned unchanged
|
|
And no template definitions should be stored
|
|
|
|
Scenario: TemplateDefinitionStore load_config with templates
|
|
Given I have a TemplateDefinitionStore instance
|
|
And I have a config with templates section
|
|
"""
|
|
templates:
|
|
agents:
|
|
chat_agent:
|
|
type: llm
|
|
config:
|
|
model: "{{ model_name }}"
|
|
temperature: __TEMPLATE:_template_0__
|
|
routes:
|
|
main_route:
|
|
type: stream
|
|
operators: __TEMPLATE:_template_1__
|
|
other:
|
|
data: value
|
|
"""
|
|
When I load the config using TemplateDefinitionStore
|
|
Then templates should be processed correctly
|
|
And template definitions should be marked for deferred processing
|
|
And definition IDs should be generated
|
|
|
|
Scenario: TemplateDefinitionStore contains_template_markers detection
|
|
Given I have a TemplateDefinitionStore instance
|
|
When I check various data types for template markers
|
|
| data_type | value | has_markers |
|
|
| string | __TEMPLATE:_template_0__ | true |
|
|
| string | normal string | false |
|
|
| dict | {"key": "__TEMPLATE:_template_0__"} | true |
|
|
| dict | {"key": "normal"} | false |
|
|
| list | ["__TEMPLATE:_template_0__"] | true |
|
|
| list | ["normal", "values"] | false |
|
|
| int | 123 | false |
|
|
Then template marker detection should work correctly
|
|
|
|
Scenario: TemplateDefinitionStore get_definition
|
|
Given I have a TemplateDefinitionStore instance
|
|
And I have stored a template definition with ID "agents:test"
|
|
When I get the definition for ID "agents:test"
|
|
Then the stored definition should be returned
|
|
When I get a definition for non-existent ID "missing:id"
|
|
Then None should be returned
|
|
|
|
Scenario: TemplateDefinitionStore render_definition
|
|
Given I have a TemplateDefinitionStore instance
|
|
And I have a stored template definition with templates
|
|
And I have template sections with original content
|
|
And I have a template rendering context
|
|
When I render the definition with context
|
|
Then the template should be properly rendered
|
|
And Jinja2 expressions should be evaluated
|
|
|
|
Scenario: TemplateDefinitionStore render_definition with missing ID
|
|
Given I have a TemplateDefinitionStore instance
|
|
When I try to render a definition with non-existent ID
|
|
Then a ValueError should be raised
|
|
And the error message should mention the missing definition
|
|
|
|
Scenario: TemplateDefinitionStore reconstruct_yaml with string templates
|
|
Given I have a TemplateDefinitionStore instance
|
|
And I have data with string template markers
|
|
And I have corresponding template sections
|
|
When I reconstruct the YAML
|
|
Then the original template syntax should be restored
|
|
And inline templates should be properly formatted
|
|
|
|
Scenario: TemplateDefinitionStore reconstruct_yaml with block templates
|
|
Given I have a TemplateDefinitionStore instance
|
|
And I have data with block template markers
|
|
And I have multi-line template sections
|
|
When I reconstruct the YAML
|
|
Then block templates should be properly indented
|
|
And template keys should be correctly formatted
|
|
|
|
Scenario: TemplateDefinitionStore reconstruct_yaml with complex structures
|
|
Given I have a TemplateDefinitionStore instance
|
|
And I have nested data structures with templates
|
|
"""
|
|
{
|
|
"level1": {
|
|
"level2": {
|
|
"template_field": "__TEMPLATE:_template_0__",
|
|
"normal_field": "value"
|
|
},
|
|
"list_field": [
|
|
"__TEMPLATE:_template_1__",
|
|
"normal_item"
|
|
]
|
|
},
|
|
"empty_field": null
|
|
}
|
|
"""
|
|
When I reconstruct the YAML with proper template sections
|
|
Then nested structures should be handled correctly
|
|
And lists should be properly formatted
|
|
And null values should be handled
|
|
|
|
Scenario: SmartYAMLLoader regex pattern matching
|
|
Given I have a SmartYAMLLoader instance
|
|
When I test the template pattern regex
|
|
| text | should_match |
|
|
| "{{ variable }}" | true |
|
|
| "{% for item %}" | true |
|
|
| "{# comment #}" | false |
|
|
| "{{ nested.value }}" | true |
|
|
| "{% if condition %}" | true |
|
|
| "normal text" | false |
|
|
| "{{ complex\|filter }}" | true |
|
|
Then the regex should match Jinja2 templates correctly
|
|
|
|
Scenario: SmartYAMLLoader template block detection edge cases
|
|
Given I have a YAML string with edge case templates
|
|
"""
|
|
config:
|
|
# This should not be detected as template
|
|
comment: "# Not a template"
|
|
# This should be detected
|
|
template_value: "{{ value }}"
|
|
nested:
|
|
{% if condition %}
|
|
inner:
|
|
value: "{{ inner.value }}"
|
|
{% endif %}
|
|
after: "normal value"
|
|
"""
|
|
When I load the YAML string using SmartYAMLLoader
|
|
Then only actual templates should be extracted
|
|
And comments should be ignored
|
|
And nested templates should be properly handled
|
|
|
|
Scenario: SmartYAMLLoader with deeply nested template blocks
|
|
Given I have a YAML string with deeply nested templates
|
|
"""
|
|
levels:
|
|
level1:
|
|
level2:
|
|
level3:
|
|
{% for item in deep.items %}
|
|
- name: "{{ item.name }}"
|
|
details:
|
|
{% if item.has_details %}
|
|
description: "{{ item.description }}"
|
|
{% endif %}
|
|
{% endfor %}
|
|
"""
|
|
When I load the YAML string using SmartYAMLLoader
|
|
Then deep nesting should be handled correctly
|
|
And indentation should be preserved accurately
|
|
And template structure should be maintained
|
|
|
|
Scenario: SmartYAMLLoader template block without proper key association
|
|
Given I have a YAML string with orphaned template block
|
|
"""
|
|
{% for item in orphaned %}
|
|
- value: "{{ item }}"
|
|
{% endfor %}
|
|
normal:
|
|
key: value
|
|
"""
|
|
When I load the YAML string using SmartYAMLLoader
|
|
Then orphaned templates should be handled gracefully
|
|
And processing should not fail
|
|
And valid YAML parts should still be parsed
|