Files
temp/tests/features/steps/template_store_coverage_steps.py
T

853 lines
29 KiB
Python

"""
Step definitions for template store coverage tests.
"""
import logging
from typing import Any
from typing import Dict
from unittest.mock import Mock
from unittest.mock import patch
from behave import given
from behave import then
from behave import when
from cleveragents.templates.template_store import TemplateDefinition
from cleveragents.templates.template_store import TemplateStore
@given("a clean template store test environment")
def step_clean_template_store_environment(context):
"""Initialize clean test environment for template store tests."""
context.template_store = None
context.template_definition = None
context.result = None
context.error = None
context.warning_logged = False
@when("I create a new TemplateStore")
def step_create_new_template_store(context):
"""Create a new TemplateStore instance."""
try:
context.template_store = TemplateStore()
except Exception as e:
context.error = e
@then("the template store should be initialized with empty collections")
def step_template_store_initialized_empty(context):
"""Verify template store is initialized with empty collections."""
assert context.error is None
assert context.template_store is not None
assert isinstance(context.template_store.raw_templates, dict)
assert isinstance(context.template_store.metadata, dict)
# Check all template types are empty
for template_type in ["agents", "graphs", "streams"]:
assert template_type in context.template_store.raw_templates
assert template_type in context.template_store.metadata
assert len(context.template_store.raw_templates[template_type]) == 0
assert len(context.template_store.metadata[template_type]) == 0
@then("the template store should have three template types")
def step_template_store_has_three_types(context):
"""Verify template store has three template types."""
assert len(context.template_store.raw_templates) == 3
assert len(context.template_store.metadata) == 3
assert "agents" in context.template_store.raw_templates
assert "graphs" in context.template_store.raw_templates
assert "streams" in context.template_store.raw_templates
@given("I have a TemplateStore")
def step_have_template_store(context):
"""Create a TemplateStore for testing."""
context.template_store = TemplateStore()
@when("I add a string template definition")
def step_add_string_template_definition(context):
"""Add a string template definition."""
try:
yaml_string = """
type: agent
parameters:
name:
type: string
required: true
config:
model: "{{ name }}"
"""
context.template_store.add_template("agents", "test_agent", yaml_string)
context.template_string = yaml_string
except Exception as e:
context.error = e
@then("the template should be stored as raw YAML")
def step_template_stored_as_raw_yaml(context):
"""Verify template is stored as raw YAML."""
assert context.error is None
stored_template = context.template_store.get_template("agents", "test_agent")
assert stored_template is not None
assert stored_template == context.template_string
@then("metadata should be extracted successfully")
def step_metadata_extracted_successfully(context):
"""Verify metadata was extracted successfully."""
metadata = context.template_store.get_metadata("agents", "test_agent")
assert metadata is not None
assert metadata["type"] == "agent"
assert "parameters" in metadata
@when("I add a dict template definition")
def step_add_dict_template_definition(context):
"""Add a dict template definition."""
try:
template_dict = {
"type": "graph",
"parameters": {"nodes": {"type": "list", "required": True}},
"config": {"nodes": "{{ nodes }}"},
}
context.template_store.add_template("graphs", "test_graph", template_dict)
context.template_dict = template_dict
except Exception as e:
context.error = e
@then("the template should be converted to YAML string")
def step_template_converted_to_yaml(context):
"""Verify template is converted to YAML string."""
assert context.error is None
stored_template = context.template_store.get_template("graphs", "test_graph")
assert stored_template is not None
assert isinstance(stored_template, str)
assert "type: graph" in stored_template
@then("metadata should be extracted from dict")
def step_metadata_extracted_from_dict(context):
"""Verify metadata was extracted from dict."""
metadata = context.template_store.get_metadata("graphs", "test_graph")
assert metadata is not None
assert metadata["type"] == "graph"
assert "parameters" in metadata
assert "nodes" in metadata["parameters"]
@when("I add an invalid YAML string template")
def step_add_invalid_yaml_template(context):
"""Add an invalid YAML string template."""
try:
with patch("cleveragents.templates.template_store.logger") as mock_logger:
invalid_yaml = "invalid: yaml: content: [\nunclosed bracket"
context.template_store.add_template(
"streams", "invalid_stream", invalid_yaml
)
context.invalid_yaml = invalid_yaml
context.mock_logger = mock_logger
except Exception as e:
context.error = e
@then("the template should be stored anyway")
def step_template_stored_anyway(context):
"""Verify template is stored despite being invalid."""
assert context.error is None
stored_template = context.template_store.get_template("streams", "invalid_stream")
assert stored_template == context.invalid_yaml
@then("a warning should be logged for metadata extraction failure")
def step_warning_logged_metadata_failure(context):
"""Verify warning was logged for metadata extraction failure."""
context.mock_logger.warning.assert_called_once()
warning_call = context.mock_logger.warning.call_args[0]
# Check the format string
assert "Could not parse metadata" in warning_call[0]
# Check the arguments (template_type and name)
assert len(warning_call) == 3 # format string + 2 args
assert warning_call[1] == "streams"
assert warning_call[2] == "invalid_stream"
@given("I have a TemplateStore with stored templates")
def step_template_store_with_stored_templates(context):
"""Create TemplateStore with some stored templates."""
context.template_store = TemplateStore()
# Add some test templates
agent_template = {
"type": "agent",
"parameters": {"model": {"type": "string"}},
"config": {"model": "{{ model }}"},
}
graph_template = {
"type": "graph",
"parameters": {"nodes": {"type": "list"}},
"config": {"nodes": "{{ nodes }}"},
}
context.template_store.add_template("agents", "stored_agent", agent_template)
context.template_store.add_template("graphs", "stored_graph", graph_template)
@when("I get an existing template")
def step_get_existing_template(context):
"""Get an existing template."""
try:
context.result = context.template_store.get_template("agents", "stored_agent")
except Exception as e:
context.error = e
@then("the raw template string should be returned")
def step_raw_template_string_returned(context):
"""Verify raw template string is returned."""
assert context.error is None
assert context.result is not None
assert isinstance(context.result, str)
assert "type: agent" in context.result
@when("I get a non-existing template")
def step_get_non_existing_template(context):
"""Get a non-existing template."""
try:
context.result = context.template_store.get_template("agents", "non_existing")
except Exception as e:
context.error = e
@then("None should be returned for template")
def step_none_returned_for_template(context):
"""Verify None is returned for template."""
assert context.error is None
assert context.result is None
@when("I get metadata for an existing template")
def step_get_metadata_existing_template(context):
"""Get metadata for an existing template."""
try:
context.result = context.template_store.get_metadata("graphs", "stored_graph")
except Exception as e:
context.error = e
@then("the template metadata should be returned")
def step_template_metadata_returned(context):
"""Verify template metadata is returned."""
assert context.error is None
assert context.result is not None
assert isinstance(context.result, dict)
assert context.result["type"] == "graph"
assert "parameters" in context.result
@when("I get metadata for a non-existing template")
def step_get_metadata_non_existing_template(context):
"""Get metadata for a non-existing template."""
try:
context.result = context.template_store.get_metadata("streams", "non_existing")
except Exception as e:
context.error = e
@then("None should be returned for metadata")
def step_none_returned_for_metadata(context):
"""Verify None is returned for metadata."""
assert context.error is None
assert context.result is None
@given("I have a TemplateStore with a templated definition")
def step_template_store_with_templated_definition(context):
"""Create TemplateStore with a templated definition."""
context.template_store = TemplateStore()
template_with_jinja = """
type: agent
parameters:
model_name:
type: string
required: true
temperature:
type: float
default: 0.7
config:
model: "{{ model_name }}"
temperature: {{ temperature }}
tools:
{% for i in range(3) %}
- tool_{{ i }}
{% endfor %}
"""
context.template_store.add_template(
"agents", "templated_agent", template_with_jinja
)
@when("I instantiate the template store template with parameters")
def step_instantiate_template_store_template_with_parameters(context):
"""Instantiate template store template with parameters."""
try:
params = {"model_name": "gpt-4", "temperature": 0.8}
context.result = context.template_store.instantiate_template(
"agents", "templated_agent", params
)
context.params = params
except Exception as e:
context.error = e
@then("the template should be processed with parameters")
def step_template_processed_with_parameters(context):
"""Verify template was processed with parameters."""
assert context.error is None
assert context.result is not None
assert isinstance(context.result, dict)
assert context.result["config"]["model"] == "gpt-4"
assert context.result["config"]["temperature"] == 0.8
@then("utility functions should be available in context")
def step_utility_functions_available(context):
"""Verify utility functions were available in template context."""
# The template used range(3) which should have created 3 tools
tools = context.result["config"]["tools"]
assert len(tools) == 3
assert "tool_0" in tools
assert "tool_1" in tools
assert "tool_2" in tools
@when("I try to instantiate a non-existing template")
def step_instantiate_non_existing_template(context):
"""Try to instantiate a non-existing template."""
try:
context.result = context.template_store.instantiate_template(
"agents", "non_existing", {}
)
except Exception as e:
context.error = e
@then("a ValueError should be raised with template not found message")
def step_value_error_template_not_found(context):
"""Verify ValueError is raised with template not found message."""
assert context.error is not None
assert isinstance(context.error, ValueError)
assert "Template agents/non_existing not found" in str(context.error)
@when("I create a TemplateDefinition with a string")
def step_create_template_definition_with_string(context):
"""Create TemplateDefinition with a string."""
try:
yaml_string = """
type: test_type
parameters:
param1: value1
config:
setting: "{{ param1 }}"
"""
context.template_definition = TemplateDefinition(yaml_string)
context.input_string = yaml_string
except Exception as e:
context.error = e
@then("the raw YAML should be stored")
def step_raw_yaml_stored(context):
"""Verify raw YAML is stored."""
assert context.error is None
assert context.template_definition.raw_yaml == context.input_string
@then("the definition should be parsed correctly")
def step_definition_parsed_correctly(context):
"""Verify definition is parsed correctly."""
assert context.template_definition.parsed is not None
assert context.template_definition.parsed["type"] == "test_type"
assert "parameters" in context.template_definition.parsed
@then("template syntax should be detected")
def step_template_syntax_detected(context):
"""Verify template syntax is detected."""
assert context.template_definition.contains_templates is True
@when("I create a TemplateDefinition with a dict")
def step_create_template_definition_with_dict(context):
"""Create TemplateDefinition with a dict."""
try:
input_dict = {
"type": "test_dict_type",
"parameters": {"param2": "value2"},
"config": {"setting": "{{ param2 }}"},
}
context.template_definition = TemplateDefinition(input_dict)
context.input_dict = input_dict
except Exception as e:
context.error = e
@then("the dict should be converted to YAML")
def step_dict_converted_to_yaml(context):
"""Verify dict is converted to YAML."""
assert context.error is None
assert isinstance(context.template_definition.raw_yaml, str)
assert "type: test_dict_type" in context.template_definition.raw_yaml
@then("the parsed definition should match input")
def step_parsed_definition_matches_input(context):
"""Verify parsed definition matches input."""
assert context.template_definition.parsed == context.input_dict
@then("template syntax should be detected in YAML")
def step_template_syntax_detected_in_yaml(context):
"""Verify template syntax is detected in YAML."""
assert context.template_definition.contains_templates is True
@when("I create a TemplateDefinition with invalid YAML")
def step_create_template_definition_with_invalid_yaml(context):
"""Create TemplateDefinition with invalid YAML."""
try:
invalid_yaml = "invalid: yaml: [unclosed"
context.template_definition = TemplateDefinition(invalid_yaml)
context.invalid_yaml = invalid_yaml
context.input_string = invalid_yaml # Set this for the assertion
except Exception as e:
context.error = e
@then("the parsed definition should be empty dict")
def step_parsed_definition_empty_dict(context):
"""Verify parsed definition is empty dict."""
assert context.error is None
assert context.template_definition.parsed == {}
@then("template syntax detection should still work")
def step_template_syntax_detection_still_works(context):
"""Verify template syntax detection still works."""
# Invalid YAML doesn't contain template syntax, so should be False
assert context.template_definition.contains_templates is False
@when("I create a TemplateDefinition with Jinja2 syntax")
def step_create_template_definition_with_jinja2(context):
"""Create TemplateDefinition with Jinja2 syntax."""
try:
yaml_with_jinja = """
type: agent
config:
model: "{{ model_name }}"
{% if use_tools %}
tools: ["tool1", "tool2"]
{% endif %}
"""
context.template_definition = TemplateDefinition(yaml_with_jinja)
except Exception as e:
context.error = e
@then("template syntax should be automatically detected")
def step_template_syntax_auto_detected(context):
"""Verify template syntax is automatically detected."""
assert context.error is None
assert context.template_definition.contains_templates is True
@then("contains_templates should be True")
def step_contains_templates_true(context):
"""Verify contains_templates is True."""
assert context.template_definition.contains_templates is True
@when("I create a TemplateDefinition without Jinja2 syntax")
def step_create_template_definition_without_jinja2(context):
"""Create TemplateDefinition without Jinja2 syntax."""
try:
yaml_without_jinja = """
type: agent
config:
model: "gpt-4"
temperature: 0.7
"""
context.template_definition = TemplateDefinition(yaml_without_jinja)
except Exception as e:
context.error = e
@then("template syntax should not be detected")
def step_template_syntax_not_detected(context):
"""Verify template syntax is not detected."""
assert context.error is None
assert context.template_definition.contains_templates is False
@then("contains_templates should be False")
def step_contains_templates_false(context):
"""Verify contains_templates is False."""
assert context.template_definition.contains_templates is False
@when("I create a TemplateDefinition with explicit template flag")
def step_create_template_definition_explicit_flag(context):
"""Create TemplateDefinition with explicit template flag."""
try:
yaml_without_syntax = """
type: agent
config:
model: "gpt-4"
"""
# Explicitly set contains_templates=True even though no syntax
context.template_definition = TemplateDefinition(
yaml_without_syntax, contains_templates=True
)
except Exception as e:
context.error = e
@then("the explicit flag should be used")
def step_explicit_flag_used(context):
"""Verify explicit flag is used."""
assert context.error is None
assert context.template_definition.contains_templates is True
@then("automatic detection should be skipped")
def step_automatic_detection_skipped(context):
"""Verify automatic detection is skipped."""
# Since we set explicit flag, _check_for_templates should not have been called
# This is verified by the fact that contains_templates is True despite no syntax
assert context.template_definition.contains_templates is True
@given("I have a TemplateDefinition with parameters")
def step_template_definition_with_parameters(context):
"""Create TemplateDefinition with parameters."""
template_dict = {
"type": "agent",
"parameters": {
"model": {"type": "string", "default": "gpt-4"},
"temperature": {"type": "float", "default": 0.7},
},
"config": {"model": "{{ model }}"},
}
context.template_definition = TemplateDefinition(template_dict)
@when("I call get_parameters")
def step_call_get_parameters(context):
"""Call get_parameters method."""
try:
context.result = context.template_definition.get_parameters()
except Exception as e:
context.error = e
@then("the parameters should be returned correctly")
def step_parameters_returned_correctly(context):
"""Verify parameters are returned correctly."""
assert context.error is None
assert context.result is not None
assert "model" in context.result
assert "temperature" in context.result
assert context.result["model"]["type"] == "string"
@given("I have a TemplateDefinition with type")
def step_template_definition_with_type(context):
"""Create TemplateDefinition with type."""
template_dict = {"type": "custom_agent", "config": {"setting": "value"}}
context.template_definition = TemplateDefinition(template_dict)
@when("I call get_type")
def step_call_get_type(context):
"""Call get_type method."""
try:
context.result = context.template_definition.get_type()
except Exception as e:
context.error = e
@then("the type should be returned correctly")
def step_type_returned_correctly(context):
"""Verify type is returned correctly."""
assert context.error is None
assert context.result == "custom_agent"
@given("I have a TemplateDefinition without type")
def step_template_definition_without_type(context):
"""Create TemplateDefinition without type."""
template_dict = {"config": {"setting": "value"}}
context.template_definition = TemplateDefinition(template_dict)
@then('the default type "unknown" should be returned')
def step_default_type_unknown_returned(context):
"""Verify default type 'unknown' is returned."""
assert context.error is None
assert context.result == "unknown"
@given("I have a TemplateDefinition without Jinja2 templates")
def step_template_definition_without_jinja2_templates(context):
"""Create TemplateDefinition without Jinja2 templates."""
template_dict = {
"type": "simple_agent",
"config": {"model": "gpt-4", "temperature": 0.7},
}
context.template_definition = TemplateDefinition(template_dict)
context.original_parsed = template_dict
@when("I call instantiate")
def step_call_instantiate(context):
"""Call instantiate method."""
try:
context.result = context.template_definition.instantiate({})
except Exception as e:
context.error = e
@then("the parsed definition should be returned unchanged")
def step_parsed_definition_returned_unchanged(context):
"""Verify parsed definition is returned unchanged."""
assert context.error is None
assert context.result == context.original_parsed
@given("I have a TemplateDefinition with Jinja2 templates")
def step_template_definition_with_jinja2_templates(context):
"""Create TemplateDefinition with Jinja2 templates."""
yaml_with_templates = """
type: templated_agent
config:
model: "{{ model_name }}"
temperature: {{ temp }}
count: {{ len(items) if items else 0 }}
"""
context.template_definition = TemplateDefinition(yaml_with_templates)
@when("I call instantiate with parameters")
def step_call_instantiate_with_parameters(context):
"""Call instantiate with parameters."""
try:
params = {"model_name": "gpt-4-turbo", "temp": 0.9, "items": ["a", "b", "c"]}
context.result = context.template_definition.instantiate(params)
context.params = params
except Exception as e:
context.error = e
@then("the template should be processed")
def step_template_should_be_processed(context):
"""Verify template is processed."""
assert context.error is None
assert context.result is not None
assert context.result["config"]["model"] == "gpt-4-turbo"
assert context.result["config"]["temperature"] == 0.9
@then("utility functions should be available in the context")
def step_utility_functions_available_in_context(context):
"""Verify utility functions are available in the context."""
# The template used len(items) which should return 3
assert context.result["config"]["count"] == 3
@when("I add multiple template types with dict definitions")
def step_add_multiple_template_types_with_dict_definitions(context):
"""Add multiple template types with dict definitions."""
try:
# Test agents type
agent_dict = {
"type": "agent",
"parameters": {"model": {"type": "string"}},
"config": {"model": "{{ model }}"},
}
context.template_store.add_template("agents", "dict_agent", agent_dict)
# Test graphs type
graph_dict = {
"type": "graph",
"parameters": {"nodes": {"type": "list"}},
"config": {"nodes": "{{ nodes }}"},
}
context.template_store.add_template("graphs", "dict_graph", graph_dict)
# Test streams type
stream_dict = {
"type": "stream",
"parameters": {"buffer_size": {"type": "int"}},
"config": {"buffer_size": "{{ buffer_size }}"},
}
context.template_store.add_template("streams", "dict_stream", stream_dict)
context.template_dicts = {
"agents": agent_dict,
"graphs": graph_dict,
"streams": stream_dict,
}
except Exception as e:
context.error = e
@then("all template types should be converted to YAML correctly")
def step_all_template_types_converted_to_yaml(context):
"""Verify all template types are converted to YAML correctly."""
assert context.error is None
for template_type in ["agents", "graphs", "streams"]:
template_name = f"dict_{template_type[:-1]}" # Remove 's' from type name
stored_template = context.template_store.get_template(
template_type, template_name
)
assert stored_template is not None
assert isinstance(stored_template, str)
assert (
f"type: {context.template_dicts[template_type]['type']}" in stored_template
)
@then("all metadata should be extracted properly")
def step_all_metadata_extracted_properly(context):
"""Verify all metadata is extracted properly."""
for template_type in ["agents", "graphs", "streams"]:
template_name = f"dict_{template_type[:-1]}" # Remove 's' from type name
metadata = context.template_store.get_metadata(template_type, template_name)
assert metadata is not None
assert metadata["type"] == context.template_dicts[template_type]["type"]
assert "parameters" in metadata
@when("I perform comprehensive template store operations")
def step_perform_comprehensive_template_store_operations(context):
"""Perform comprehensive template store operations to hit all code paths."""
try:
# Test 1: Add template with dict (hits lines 65-67)
dict_template = {
"type": "comprehensive",
"parameters": {"value": {"type": "string"}},
"config": {"value": "{{ value }}"},
}
context.template_store.add_template(
"agents", "comprehensive_dict", dict_template
)
# Test 2: Try to instantiate non-existing template (hits line 105)
try:
context.template_store.instantiate_template("agents", "non_existent", {})
context.missing_template_error = None
except ValueError as e:
context.missing_template_error = e
# Test 3: Create TemplateDefinition with string and check all paths
yaml_string_with_templates = """
type: comprehensive_test
parameters:
name:
type: string
required: true
config:
name: "{{ name }}"
condition: "{% if name %}present{% endif %}"
"""
# Test with explicit flag False (should trigger auto-detection)
context.template_def_auto = TemplateDefinition(
yaml_string_with_templates, contains_templates=False
)
# Test with string input that contains templates
context.template_def_string = TemplateDefinition(yaml_string_with_templates)
# Test with dict input that gets converted to YAML
dict_with_templates = {
"type": "dict_test",
"config": {
"template_var": "{{ test_value }}",
"loop": "{% for i in range(3) %}item_{{ i }}{% endfor %}",
},
}
context.template_def_dict = TemplateDefinition(dict_with_templates)
# Test get_parameters and get_type methods
context.params_result = context.template_def_string.get_parameters()
context.type_result = context.template_def_string.get_type()
# Test instantiate without templates (should return parsed directly)
no_template_dict = {"type": "no_templates", "config": {"static": "value"}}
context.template_def_no_templates = TemplateDefinition(no_template_dict)
context.no_template_result = context.template_def_no_templates.instantiate({})
# Test instantiate with templates (should process)
context.template_result = context.template_def_string.instantiate(
{"name": "test_name"}
)
except Exception as e:
context.error = e
@then("all TemplateStore code paths should be exercised")
def step_all_template_store_code_paths_exercised(context):
"""Verify all TemplateStore code paths are exercised."""
assert context.error is None
# Verify dict template was added and converted to YAML (lines 65-67)
stored = context.template_store.get_template("agents", "comprehensive_dict")
assert stored is not None
assert "type: comprehensive" in stored
# Verify ValueError was raised for missing template (line 105)
assert context.missing_template_error is not None
assert isinstance(context.missing_template_error, ValueError)
assert "not found" in str(context.missing_template_error)
@then("all TemplateDefinition code paths should be exercised")
def step_all_template_definition_code_paths_exercised(context):
"""Verify all TemplateDefinition code paths are exercised."""
# Verify template syntax detection worked (lines 157-158, 162)
assert (
context.template_def_auto.contains_templates is True
) # Auto-detected despite False flag
assert context.template_def_string.contains_templates is True
assert context.template_def_dict.contains_templates is True
# Verify string input path (lines 146-151)
assert context.template_def_string.raw_yaml is not None
assert context.template_def_string.parsed is not None
# Verify dict input path (lines 152-154)
assert context.template_def_dict.raw_yaml is not None
assert "{{ test_value }}" in context.template_def_dict.raw_yaml
# Verify get_parameters and get_type (lines 166, 170)
assert context.params_result is not None
assert context.type_result == "comprehensive_test"
# Verify instantiate without templates returns parsed (lines 182-184)
assert context.no_template_result == context.template_def_no_templates.parsed
# Verify instantiate with templates processes (lines 186-203)
assert context.template_result is not None
assert context.template_result["config"]["name"] == "test_name"
assert "present" in context.template_result["config"]["condition"]