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

824 lines
29 KiB
Python

"""
Step definitions for template store coverage tests.
"""
from unittest.mock import patch
from behave import given, then, when
from cleveragents.templates.template_store import TemplateDefinition, 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"]