mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
020957cd76
Signed-off-by: phernandez <paul@basicmachines.co> Co-authored-by: phernandez <paul@basicmachines.co> Co-authored-by: Claude <noreply@anthropic.com>
220 lines
7.8 KiB
Python
220 lines
7.8 KiB
Python
"""Tests for the template loader functionality."""
|
|
|
|
import datetime
|
|
import pytest
|
|
from pathlib import Path
|
|
|
|
from basic_memory.api.template_loader import TemplateLoader
|
|
|
|
|
|
@pytest.fixture
|
|
def temp_template_dir(tmpdir):
|
|
"""Create a temporary directory for test templates."""
|
|
template_dir = tmpdir.mkdir("templates").mkdir("prompts")
|
|
return template_dir
|
|
|
|
|
|
@pytest.fixture
|
|
def custom_template_loader(temp_template_dir):
|
|
"""Return a TemplateLoader instance with a custom template directory."""
|
|
return TemplateLoader(str(temp_template_dir))
|
|
|
|
|
|
@pytest.fixture
|
|
def simple_template(temp_template_dir):
|
|
"""Create a simple test template."""
|
|
template_path = temp_template_dir / "simple.hbs"
|
|
template_path.write_text("Hello, {{name}}!", encoding="utf-8")
|
|
return "simple.hbs"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_render_simple_template(custom_template_loader, simple_template):
|
|
"""Test rendering a simple template."""
|
|
context = {"name": "World"}
|
|
result = await custom_template_loader.render(simple_template, context)
|
|
assert result == "Hello, World!"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_template_cache(custom_template_loader, simple_template):
|
|
"""Test that templates are cached."""
|
|
context = {"name": "World"}
|
|
|
|
# First render, should load template
|
|
await custom_template_loader.render(simple_template, context)
|
|
|
|
# Check that template is in cache
|
|
assert simple_template in custom_template_loader.template_cache
|
|
|
|
# Modify the template file - shouldn't affect the cached version
|
|
template_path = Path(custom_template_loader.template_dir) / simple_template
|
|
template_path.write_text("Goodbye, {{name}}!", encoding="utf-8")
|
|
|
|
# Second render, should use cached template
|
|
result = await custom_template_loader.render(simple_template, context)
|
|
assert result == "Hello, World!"
|
|
|
|
# Clear cache and render again - should use updated template
|
|
custom_template_loader.clear_cache()
|
|
assert simple_template not in custom_template_loader.template_cache
|
|
|
|
result = await custom_template_loader.render(simple_template, context)
|
|
assert result == "Goodbye, World!"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_date_helper(custom_template_loader, temp_template_dir):
|
|
# Test date helper
|
|
date_path = temp_template_dir / "date.hbs"
|
|
date_path.write_text("{{date timestamp}}", encoding="utf-8")
|
|
date_result = await custom_template_loader.render(
|
|
"date.hbs", {"timestamp": datetime.datetime(2023, 1, 1, 12, 30)}
|
|
)
|
|
assert "2023-01-01" in date_result
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_default_helper(custom_template_loader, temp_template_dir):
|
|
# Test default helper
|
|
default_path = temp_template_dir / "default.hbs"
|
|
default_path.write_text("{{default null 'default-value'}}", encoding="utf-8")
|
|
default_result = await custom_template_loader.render("default.hbs", {"null": None})
|
|
assert default_result == "default-value"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_capitalize_helper(custom_template_loader, temp_template_dir):
|
|
# Test capitalize helper
|
|
capitalize_path = temp_template_dir / "capitalize.hbs"
|
|
capitalize_path.write_text("{{capitalize 'test'}}", encoding="utf-8")
|
|
capitalize_result = await custom_template_loader.render("capitalize.hbs", {})
|
|
assert capitalize_result == "Test"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_size_helper(custom_template_loader, temp_template_dir):
|
|
# Test size helper
|
|
size_path = temp_template_dir / "size.hbs"
|
|
size_path.write_text("{{size collection}}", encoding="utf-8")
|
|
size_result = await custom_template_loader.render("size.hbs", {"collection": [1, 2, 3]})
|
|
assert size_result == "3"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_json_helper(custom_template_loader, temp_template_dir):
|
|
# Test json helper
|
|
json_path = temp_template_dir / "json.hbs"
|
|
json_path.write_text("{{json data}}", encoding="utf-8")
|
|
json_result = await custom_template_loader.render("json.hbs", {"data": {"key": "value"}})
|
|
assert json_result == '{"key": "value"}'
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_less_than_helper(custom_template_loader, temp_template_dir):
|
|
# Test lt (less than) helper
|
|
lt_path = temp_template_dir / "lt.hbs"
|
|
lt_path.write_text("{{#if_cond (lt 2 3)}}true{{else}}false{{/if_cond}}", encoding="utf-8")
|
|
lt_result = await custom_template_loader.render("lt.hbs", {})
|
|
assert lt_result == "true"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_file_not_found(custom_template_loader):
|
|
"""Test that FileNotFoundError is raised when a template doesn't exist."""
|
|
with pytest.raises(FileNotFoundError):
|
|
await custom_template_loader.render("non_existent_template.hbs", {})
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_extension_handling(custom_template_loader, temp_template_dir):
|
|
"""Test that template extensions are handled correctly."""
|
|
# Create template with .hbs extension
|
|
template_path = temp_template_dir / "test_extension.hbs"
|
|
template_path.write_text("Template with extension: {{value}}", encoding="utf-8")
|
|
|
|
# Test accessing with full extension
|
|
result = await custom_template_loader.render("test_extension.hbs", {"value": "works"})
|
|
assert result == "Template with extension: works"
|
|
|
|
# Test accessing without extension
|
|
result = await custom_template_loader.render("test_extension", {"value": "also works"})
|
|
assert result == "Template with extension: also works"
|
|
|
|
# Test accessing with wrong extension gets converted
|
|
template_path = temp_template_dir / "liquid_template.hbs"
|
|
template_path.write_text("Liquid template: {{value}}", encoding="utf-8")
|
|
|
|
result = await custom_template_loader.render("liquid_template.liquid", {"value": "converted"})
|
|
assert result == "Liquid template: converted"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_dedent_helper(custom_template_loader, temp_template_dir):
|
|
"""Test the dedent helper for text blocks."""
|
|
dedent_path = temp_template_dir / "dedent.hbs"
|
|
|
|
# Create a template with indented text blocks
|
|
template_content = """Before
|
|
{{#dedent}}
|
|
This is indented text
|
|
with nested indentation
|
|
that should be dedented
|
|
while preserving relative indentation
|
|
{{/dedent}}
|
|
After"""
|
|
|
|
dedent_path.write_text(template_content, encoding="utf-8")
|
|
|
|
# Render the template
|
|
result = await custom_template_loader.render("dedent.hbs", {})
|
|
|
|
# Print the actual output for debugging
|
|
print(f"Dedent helper result: {repr(result)}")
|
|
|
|
# Check that the indentation is properly removed
|
|
assert "This is indented text" in result
|
|
assert "with nested indentation" in result
|
|
assert "that should be dedented" in result
|
|
assert "while preserving relative indentation" in result
|
|
assert "Before" in result
|
|
assert "After" in result
|
|
|
|
# Check that relative indentation is preserved
|
|
assert result.find("with nested indentation") > result.find("This is indented text")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_nested_dedent_helper(custom_template_loader, temp_template_dir):
|
|
"""Test the dedent helper with nested content."""
|
|
dedent_path = temp_template_dir / "nested_dedent.hbs"
|
|
|
|
# Create a template with nested indented blocks
|
|
template_content = """
|
|
{{#each items}}
|
|
{{#dedent}}
|
|
--- Item {{this}}
|
|
|
|
Details for item {{this}}
|
|
- Indented detail 1
|
|
- Indented detail 2
|
|
{{/dedent}}
|
|
{{/each}}"""
|
|
|
|
dedent_path.write_text(template_content, encoding="utf-8")
|
|
|
|
# Render the template
|
|
result = await custom_template_loader.render("nested_dedent.hbs", {"items": [1, 2]})
|
|
|
|
# Print the actual output for debugging
|
|
print(f"Actual result: {repr(result)}")
|
|
|
|
# Use a more flexible assertion that checks individual components
|
|
# instead of exact string matching
|
|
assert "--- Item 1" in result
|
|
assert "Details for item 1" in result
|
|
assert "- Indented detail 1" in result
|
|
assert "--- Item 2" in result
|
|
assert "Details for item 2" in result
|
|
assert "- Indented detail 2" in result
|