""" Tests for structured failure behavior of The Librarian entry points. run_librarian and run_librarian_stream must raise AgentError on failure instead of returning/yielding error text as if it were research output, and the raised error must not leak exception detail (internal URLs etc.). """ from unittest.mock import AsyncMock, MagicMock, patch import pytest from src.agents.librarian.agent import run_librarian, run_librarian_stream from src.agents.protocol import AgentError @pytest.mark.unit class TestRunLibrarianFailures: """run_librarian raises structured errors instead of returning text.""" @pytest.mark.asyncio async def test_run_librarian_raises_agent_error(self): """Failures raise AgentError rather than returning error prose.""" mock_agent = MagicMock() mock_agent.run = AsyncMock( side_effect=RuntimeError("Connection refused to http://internal:8089") ) with patch( "src.agents.librarian.agent.get_librarian_agent", return_value=mock_agent, ): with pytest.raises(AgentError) as exc_info: await run_librarian(task="Find Docker docs") assert exc_info.value.agent_name == "librarian" # Exception detail stays in logs only assert "internal" not in str(exc_info.value) assert "Connection refused" not in str(exc_info.value) @pytest.mark.asyncio async def test_run_librarian_stream_raises_agent_error(self): """Streaming failures raise instead of yielding error text.""" mock_agent = MagicMock() mock_agent.run_stream = MagicMock( side_effect=RuntimeError("Connection refused to http://internal:8089") ) with patch( "src.agents.librarian.agent.get_librarian_agent", return_value=mock_agent, ): collected: list[str] = [] with pytest.raises(AgentError) as exc_info: async for delta in run_librarian_stream(task="Find Docker docs"): collected.append(delta) assert exc_info.value.agent_name == "librarian" assert collected == [], "no error text may be yielded as output" assert "internal" not in str(exc_info.value)