from __future__ import annotations
import os
from types import SimpleNamespace
from langchain_core.messages import AIMessage, ToolMessage
from msagent.utils import render as render_module
def test_render_templates_recurses_and_preserves_unrenderable_strings() -> None:
data = {
"title": "Hello {name}",
"items": ["{name}", {"missing": "{unknown}"}],
"count": 3,
}
rendered = render_module.render_templates(data, {"name": "Alice"})
assert rendered == {
"title": "Hello Alice",
"items": ["Alice", {"missing": "{unknown}"}],
"count": 3,
}
def test_format_tool_response_handles_strings_collections_dicts_and_none() -> None:
tool_message = ToolMessage(
content="full text",
tool_call_id="call-1",
name="tool",
short_content="short text",
)
assert render_module.format_tool_response(tool_message) == ("full text", "short text")
assert render_module.format_tool_response(' {"value": 1} ') == ('{\n "value": "1"\n}', None)
assert render_module.format_tool_response("[1, null, 2]") == ("1\n2", None)
assert render_module.format_tool_response("{not json") == ("{not json", None)
assert render_module.format_tool_response(None) == ("", None)
assert render_module.format_tool_response(42) == ("42", None)
def test_truncate_text_handles_zero_and_overflow_lengths() -> None:
assert render_module.truncate_text("", 0) == ""
assert render_module.truncate_text("abcdef", 0) == "... (truncated, original length: 6)"
assert render_module.truncate_text("abc", 5) == "abc"
assert render_module.truncate_text("abcdef", 3) == "abc... (truncated, original length: 6)"
def test_create_tool_message_extracts_metadata_and_short_content() -> None:
result = SimpleNamespace(
is_error=False,
return_direct=True,
additional_kwargs={"source": "base"},
response_metadata={"latency": 1},
)
message = render_module.create_tool_message(
result=result,
tool_name="search",
tool_call_id="call-1",
additional_kwargs={"extra": "value"},
response_metadata={render_module.TOOL_TIMING_RESPONSE_METADATA_KEY: 5},
)
assert message.name == "search"
assert message.tool_call_id == "call-1"
assert message.return_direct is True
assert "namespace(" in message.short_content
assert message.additional_kwargs == {"source": "base", "extra": "value"}
assert message.response_metadata == {"latency": 1, render_module.TOOL_TIMING_RESPONSE_METADATA_KEY: 5}
def test_create_tool_message_uses_ai_message_text_and_error_content() -> None:
message = render_module.create_tool_message(
result=AIMessage(content="assistant result"),
tool_name="tool",
tool_call_id="call-2",
is_error=True,
)
assert message.content == "assistant result"
assert message.short_content == "assistant result"
assert message.is_error is True
def test_generate_diff_and_line_number_helpers_adjust_hunks() -> None:
diff_lines = render_module.generate_diff(
old_content="beta\ngamma",
new_content="beta\nGAMMA",
full_content="alpha\nbeta\ngamma\ndelta",
)
assert diff_lines[0:2] == ["--- ", "+++ "]
assert "@@ -2,2 +2,2 @@" in diff_lines[2]
assert render_module._find_content_line_number(["a", "b", "c"], ["b", "c"]) == 2
assert render_module._find_content_line_number(["a"], ["missing"]) == 0
assert render_module._adjust_diff_line_numbers(["@@ -1,2 +1,2 @@"], 3) == ["@@ -3,2 +3,2 @@"]
def test_wrap_diff_line_and_format_diff_rich_cover_wrapping_and_markers(monkeypatch) -> None:
wrapped = render_module._wrap_diff_line(
code="[bold]" + ("x" * 40),
marker="+",
color="green",
line_num=7,
width=3,
term_width=18,
)
assert len(wrapped) > 1
assert wrapped[0].startswith("[green] 7 + ")
assert "\\[bold]" in wrapped[0]
monkeypatch.setattr(render_module.shutil, "get_terminal_size", lambda: os.terminal_size((40, 20)))
rich_diff = render_module.format_diff_rich(
[
"--- ",
"+++ ",
"@@ -3,2 +3,2 @@",
" context",
"-old",
"+new",
"...",
]
)
assert "No changes detected" not in rich_diff
assert " 3 context" in rich_diff
assert " 4 - old" in rich_diff
assert " 4 + new" in rich_diff
assert "..." in rich_diff
def test_format_diff_rich_handles_empty_diff() -> None:
assert render_module.format_diff_rich([]) == "[muted]No changes detected[/muted]"