The 21 the automatic pass could not make on its own. `ruff check` and `ruff format --check` are both clean now; typecheck is still red and is next. `in_reasoning` in chat/service.py was a complete state machine that nothing read: initialised False, set True when a reasoning delta arrived, set False when the summary ended — three assignments, zero reads. Ruff reported one at a time, and removing each revealed the next, so what looked like a single stray variable took three passes to bottom out. The branches themselves do real work and are untouched; only the flag is gone. Four `raise HTTPException` inside `except` blocks now chain with `from e`. Until now a failure while handling an error was indistinguishable from the error, which matters most in exactly the situation where the traceback is all you have. In biographer/tools.py the binding was unused but the call is not: MemoryType() is called for the ValueError it raises on an invalid name. The binding is gone and the call and its comment stay, because dropping the line would have removed the validation. The rest are unused bindings in tests where the assertions are on something else (call_args, mostly), plus three unused loop variables and an isinstance tuple. One correction to my own work: removing a dead comprehension in test_error_handling.py left an `if` block with nothing but comments in it, which is a SyntaxError. Ruff caught it immediately. The block now says what the test actually pins — that the stream parses without crashing, which reaching that line demonstrates — rather than computing a list nobody asserts on. `make test` is intermittent here, and it is not this change. test_tatlock_tool_call_logging_calculator failed in two of five full runs across both HEAD and this branch, and passes in the other three; it also fails in isolation at HEAD while passing in isolation here. Order- or timing-dependent. Recorded rather than chased, since tests are not gated in this repo yet. Co-Authored-By: Claude <noreply@anthropic.com>
197 lines
5.5 KiB
Python
197 lines
5.5 KiB
Python
"""
|
|
Tests for Lorem Tester agent.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from src.agents.lorem_tester import LoremTesterAgent
|
|
from src.core.exceptions import (
|
|
APIError,
|
|
ContextLengthError,
|
|
RateLimitError,
|
|
)
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.asyncio
|
|
async def test_lorem_tester_basic_response():
|
|
"""Test basic lorem tester response without reasoning or tools."""
|
|
agent = LoremTesterAgent()
|
|
|
|
messages = [{"role": "user", "content": "Hello"}]
|
|
|
|
items = []
|
|
async for item in agent.generate_response(messages):
|
|
items.append(item)
|
|
|
|
# Should have at least one message item
|
|
assert len(items) >= 1
|
|
|
|
# Last item should be message
|
|
last_item = items[-1]
|
|
assert last_item.type == "message"
|
|
assert last_item.data["role"] == "assistant"
|
|
assert last_item.data["content"][0]["type"] == "output_text"
|
|
assert len(last_item.data["content"][0]["text"]) > 0
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.asyncio
|
|
async def test_lorem_tester_with_reasoning():
|
|
"""Test lorem tester with reasoning enabled."""
|
|
agent = LoremTesterAgent()
|
|
|
|
messages = [{"role": "user", "content": "Explain something"}]
|
|
reasoning = {"summary": "auto", "effort": "medium"}
|
|
|
|
items = []
|
|
async for item in agent.generate_response(messages, reasoning=reasoning):
|
|
items.append(item)
|
|
|
|
# Should have reasoning item and message item
|
|
assert len(items) >= 2
|
|
|
|
# First item should be reasoning
|
|
reasoning_item = items[0]
|
|
assert reasoning_item.type == "reasoning"
|
|
assert "summary" in reasoning_item.data
|
|
assert isinstance(reasoning_item.data["summary"], list)
|
|
assert len(reasoning_item.data["summary"]) > 0
|
|
|
|
# Last item should be message
|
|
message_item = items[-1]
|
|
assert message_item.type == "message"
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.asyncio
|
|
async def test_lorem_tester_reasoning_effort_levels():
|
|
"""Test different reasoning effort levels."""
|
|
agent = LoremTesterAgent()
|
|
messages = [{"role": "user", "content": "Test"}]
|
|
|
|
# Test different effort levels
|
|
efforts = ["minimal", "low", "medium", "high", "xhigh"]
|
|
|
|
for effort in efforts:
|
|
reasoning = {"summary": "auto", "effort": effort}
|
|
|
|
items = []
|
|
async for item in agent.generate_response(messages, reasoning=reasoning):
|
|
if item.type == "reasoning":
|
|
items.append(item)
|
|
|
|
# Should have reasoning item
|
|
assert len(items) >= 1
|
|
reasoning_item = items[0]
|
|
assert reasoning_item.type == "reasoning"
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.asyncio
|
|
async def test_lorem_tester_with_tools():
|
|
"""Test lorem tester with tools (may or may not call them)."""
|
|
agent = LoremTesterAgent()
|
|
|
|
messages = [{"role": "user", "content": "Use a tool"}]
|
|
tools = [
|
|
{"name": "search_knowledge", "description": "Search knowledge base"},
|
|
{"name": "calculate", "description": "Do math"},
|
|
]
|
|
|
|
items = []
|
|
async for item in agent.generate_response(messages, tools=tools):
|
|
items.append(item)
|
|
|
|
# Should have at least message item
|
|
# May have function_call items (randomized)
|
|
assert len(items) >= 1
|
|
|
|
# Check item types
|
|
for item in items:
|
|
assert item.type in ["reasoning", "function_call", "message"]
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.asyncio
|
|
async def test_lorem_tester_capabilities():
|
|
"""Test lorem tester capabilities."""
|
|
agent = LoremTesterAgent()
|
|
|
|
assert await agent.supports_tools() is True
|
|
assert await agent.supports_reasoning() is True
|
|
|
|
capabilities = await agent.get_capabilities()
|
|
assert capabilities["streaming"] is True
|
|
assert capabilities["reasoning"] is True
|
|
assert capabilities["tools"] is True
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.asyncio
|
|
async def test_lorem_tester_rate_limit_trigger():
|
|
"""Test rate limit error trigger."""
|
|
agent = LoremTesterAgent()
|
|
|
|
messages = [{"role": "user", "content": "trigger_rate_limit"}]
|
|
|
|
with pytest.raises(RateLimitError) as exc_info:
|
|
async for _item in agent.generate_response(messages):
|
|
pass
|
|
|
|
assert "rate limit" in str(exc_info.value).lower()
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.asyncio
|
|
async def test_lorem_tester_context_overflow_trigger():
|
|
"""Test context length error trigger."""
|
|
agent = LoremTesterAgent()
|
|
|
|
messages = [{"role": "user", "content": "trigger_context_overflow"}]
|
|
|
|
with pytest.raises(ContextLengthError) as exc_info:
|
|
async for _item in agent.generate_response(messages):
|
|
pass
|
|
|
|
assert "context" in str(exc_info.value).lower()
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.asyncio
|
|
async def test_lorem_tester_invalid_tool_trigger():
|
|
"""Test invalid tool error trigger."""
|
|
agent = LoremTesterAgent()
|
|
|
|
messages = [{"role": "user", "content": "trigger_invalid_tool"}]
|
|
|
|
with pytest.raises(APIError) as exc_info:
|
|
async for _item in agent.generate_response(messages):
|
|
pass
|
|
|
|
assert "tool" in str(exc_info.value).lower()
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.asyncio
|
|
async def test_lorem_tester_temperature_variation():
|
|
"""Test temperature affects response variety."""
|
|
agent = LoremTesterAgent()
|
|
messages = [{"role": "user", "content": "Test"}]
|
|
|
|
# Low temperature
|
|
items_low = []
|
|
async for item in agent.generate_response(messages, temperature=0.1):
|
|
if item.type == "message":
|
|
items_low.append(item)
|
|
|
|
# High temperature
|
|
items_high = []
|
|
async for item in agent.generate_response(messages, temperature=1.5):
|
|
if item.type == "message":
|
|
items_high.append(item)
|
|
|
|
# Both should have responses
|
|
assert len(items_low) >= 1
|
|
assert len(items_high) >= 1
|