test: add tests for TemplateRenderer error handling and edge cases

This commit is contained in:
2025-07-06 18:15:16 +00:00
parent 6fde8193c6
commit b917e8fdff
2 changed files with 270 additions and 0 deletions
@@ -0,0 +1,162 @@
import json
import sys
from unittest.mock import patch
from behave import given
from behave import then
from behave import when
from cleveragents.core.exceptions import TemplateError
from cleveragents.templates.renderer import TemplateEngine
from cleveragents.templates.renderer import TemplateRenderer
@given('I try to initialize a TemplateRenderer with the "{engine}" engine')
def step_impl(context, engine):
context.engine_type_str = engine
if engine == "jinja2":
context.engine_type_enum = TemplateEngine.JINJA2
elif engine == "mustache":
context.engine_type_enum = TemplateEngine.MUSTACHE
@when('the required package "{package}" is not installed')
def step_impl(context, package):
with patch.dict(sys.modules, {package: None}):
try:
TemplateRenderer(context.engine_type_enum)
except TemplateError as e:
context.error = e
@then('a TemplateError should be raised with a message containing "{message}"')
def step_impl(context, message):
assert hasattr(context, "error"), "TemplateError was not raised"
assert isinstance(
context.error, TemplateError
), f"Expected TemplateError, but got {type(context.error)}"
assert message in str(context.error), f"'{message}' not in '{str(context.error)}'"
@given("I have an unsupported template engine type")
def step_impl(context):
context.unsupported_engine = "unsupported_engine_type"
@when("I try to initialize a TemplateRenderer with this engine type")
def step_impl(context):
try:
# We need to bypass the Enum type check for this test
renderer = TemplateRenderer()
renderer.engine_type = context.unsupported_engine
renderer._initialize_engine()
except TemplateError as e:
context.error = e
@given('a TemplateRenderer with the "{engine}" engine')
def step_impl(context, engine):
# This step assumes packages are installed for successful initialization
context.renderer = TemplateRenderer(TemplateEngine(engine))
@when("I attempt to register a template with an empty name")
def step_impl(context):
try:
context.renderer.register_template("", "some content")
except TemplateError as e:
context.error = e
@when('I try to register a template with invalid Jinja2 syntax "{syntax}"')
def step_impl(context, syntax):
try:
context.renderer.register_template("invalid", syntax)
except TemplateError as e:
context.error = e
@given('a template named "{name}" with content "{content}"')
def step_impl(context, name, content):
context.renderer.register_template(name, content)
@when('I try to render the "{template_name}" template with an empty context')
def step_impl(context, template_name):
try:
context.renderer.render(template_name, {})
except TemplateError as e:
context.error = e
@when('I try to render the string "{template_str}" with an empty context')
def step_impl(context, template_str):
try:
context.renderer.render_string(template_str, {})
except TemplateError as e:
context.error = e
@when("I render the \"{template_name}\" template with context '{json_context}'")
def step_impl(context, template_name, json_context):
ctx = json.loads(json_context)
context.result = context.renderer.render(template_name, ctx)
@then('the result should be "{expected_result}"')
def step_impl(context, expected_result):
assert (
context.result == expected_result
), f"Expected '{expected_result}', got '{context.result}'"
@when('I try to render a template named "{template_name}" with any context')
@when('I try to render the "{template_name}" template with any context')
def step_impl(context, template_name):
try:
context.renderer.render(template_name, {"foo": "bar"})
except TemplateError as e:
context.error = e
@when('I try to render the string "{template_str}" with any context')
def step_impl(context, template_str):
try:
context.renderer.render_string(template_str, {"foo": "bar"})
except TemplateError as e:
context.error = e
@when('I try to get a template named "{template_name}"')
def step_impl(context, template_name):
try:
context.renderer.get_template(template_name)
except TemplateError as e:
context.error = e
@given("I have manually modified the engine to be invalid")
def step_impl(context):
# Replace engine with an object that doesn't have the required methods
context.renderer.engine = object()
@when('I try to register a template named "{name}" with content "{content}"')
def step_impl(context, name, content):
try:
context.renderer.register_template(name, content)
except TemplateError as e:
context.error = e
@given(
'I have manually registered a template named "{template_name}" that is not a valid Jinja2 template object'
)
def step_impl(context, template_name):
# Manually insert a non-template object into the templates dict
context.renderer.templates[template_name] = "just a string"
@given("I have manually set the engine type to an unsupported value")
def step_impl(context):
context.renderer.engine_type = "unsupported"
@@ -0,0 +1,108 @@
Feature: Template Renderer Error Handling and Edge Cases
Scenario Outline: Template renderer initialization failures for missing packages
Given I try to initialize a TemplateRenderer with the "<engine>" engine
When the required package "<package>" is not installed
Then a TemplateError should be raised with a message containing "<message>"
Examples:
| engine | package | message |
| jinja2 | jinja2 | Jinja2 is not installed. Install it with 'pip install jinja2'. |
| mustache | pystache | Pystache is not installed. Install it with 'pip install pystache'. |
Scenario: Template renderer initialization with unsupported engine
Given I have an unsupported template engine type
When I try to initialize a TemplateRenderer with this engine type
Then a TemplateError should be raised with a message containing "Unsupported template engine"
Scenario: Registering a template with an empty name
Given a TemplateRenderer with the "simple" engine
When I attempt to register a template with an empty name
Then a TemplateError should be raised with a message containing "Template name cannot be empty"
Scenario: Registering an invalid Jinja2 template
Given a TemplateRenderer with the "jinja2" engine
When I try to register a template with invalid Jinja2 syntax "Hello, {{ name"
Then a TemplateError should be raised with a message containing "Failed to register Jinja2 template 'invalid'"
Scenario: Rendering a simple template with missing format variables
Given a TemplateRenderer with the "simple" engine
And a template named "test" with content "Hello, {name}"
When I try to render the "test" template with an empty context
Then a TemplateError should be raised with a message containing "Missing template variable: name"
Scenario: Rendering a simple string with missing format variables
Given a TemplateRenderer with the "simple" engine
When I try to render the string "Hello, {name}" with an empty context
Then a TemplateError should be raised with a message containing "Missing template variable: name"
Scenario: Rendering a simple template with a placeholder evaluating to None
Given a TemplateRenderer with the "simple" engine
And a template named "test" with content "Value: {{novalue}}"
When I render the "test" template with context '{"novalue": null}'
Then the result should be "Value: "
Scenario: Rendering a simple template with an invalid expression
Given a TemplateRenderer with the "simple" engine
And a template named "test" with content "Value: {{ 1 +/ 2 }}"
When I try to render the "test" template with any context
Then a TemplateError should be raised with a message containing "Failed to render template 'test'"
Scenario: Rendering a simple string with an invalid expression
Given a TemplateRenderer with the "simple" engine
When I try to render the string "Value: {{ 1 +/ 2 }}" with any context
Then a TemplateError should be raised with a message containing "Failed to render template string"
Scenario: Getting a non-existent template
Given a TemplateRenderer with the "simple" engine
When I try to get a template named "nonexistent"
Then a TemplateError should be raised with a message containing "Template 'nonexistent' not found"
Scenario: Rendering a non-existent template
Given a TemplateRenderer with the "simple" engine
When I try to render a template named "nonexistent" with any context
Then a TemplateError should be raised with a message containing "Template 'nonexistent' not found"
Scenario: Registration failure with Jinja2 due to bad engine object
Given a TemplateRenderer with the "jinja2" engine
And I have manually modified the engine to be invalid
When I try to register a template named "test" with content "Hello"
Then a TemplateError should be raised with a message containing "Jinja2 environment has no from_string method"
Scenario: Rendering failure with Jinja2 due to bad template object
Given a TemplateRenderer with the "jinja2" engine
And I have manually registered a template named "bad_template" that is not a valid Jinja2 template object
When I try to render the "bad_template" template with any context
Then a TemplateError should be raised with a message containing "Jinja2 template has no render method"
Scenario: String rendering failure with Jinja2 due to bad engine object
Given a TemplateRenderer with the "jinja2" engine
And I have manually modified the engine to be invalid
When I try to render the string "Hello, {{name}}" with any context
Then a TemplateError should be raised with a message containing "Jinja2 environment has no from_string method"
Scenario: Rendering failure with Mustache due to bad engine object
Given a TemplateRenderer with the "mustache" engine
And I have manually modified the engine to be invalid
And a template named "test" with content "Hello, {{name}}"
When I try to render the "test" template with any context
Then a TemplateError should be raised with a message containing "Mustache renderer has no render method"
Scenario: String rendering failure with Mustache due to bad engine object
Given a TemplateRenderer with the "mustache" engine
And I have manually modified the engine to be invalid
When I try to render the string "Hello, {{name}}" with any context
Then a TemplateError should be raised with a message containing "Mustache renderer has no render method"
Scenario: Rendering with an unsupported engine type
Given a TemplateRenderer with the "simple" engine
And I have manually set the engine type to an unsupported value
And a template named "test" with content "Hello"
When I try to render the "test" template with any context
Then a TemplateError should be raised with a message containing "Unsupported template engine"
Scenario: String rendering with an unsupported engine type
Given a TemplateRenderer with the "simple" engine
And I have manually set the engine type to an unsupported value
When I try to render the string "Hello" with any context
Then a TemplateError should be raised with a message containing "Unsupported template engine"