""" Test suite using pytest's capsys for output capture with Agency. This approach uses pytest's built-in capsys fixture to capture output of the terminal. """ from unittest.mock import patch import pytest from agents import ModelSettings from prompt_toolkit.document import Document from agency_swarm import Agency, Agent from agency_swarm.tools.send_message import Handoff from agency_swarm.ui.demos import terminal as terminal_module from agency_swarm.ui.demos.terminal import start_terminal class MockInputProvider: """Provides sequential input responses for testing.""" def __init__(self, inputs: list[str]): self.inputs = inputs self.index = 0 def __call__(self, prompt=""): """Mock input function that returns sequential inputs.""" if self.index < len(self.inputs): result = self.inputs[self.index] self.index += 1 # Echo input to simulate terminal behavior print(f"{prompt}{result}") return result return "/exit" async def async_call(self, prompt="", **kwargs): """Mock async prompt for prompt_toolkit.""" return self(prompt) def _application_factory(provider: MockInputProvider): """Return a PromptToolkit Application stub that replays provider inputs.""" class _ApplicationStub: def __init__(self, *args, **kwargs): self._provider = provider self._layout = kwargs.get("layout") def invalidate(self) -> None: # noqa: D401 - simple no-op """No-op invalidate used by dropdown integration.""" async def run_async(self): # noqa: ANN201 - signature mirrors prompt_toolkit value = await self._provider.async_call() if value is None: value = "/exit" buffer = getattr(self._layout, "current_buffer", None) if buffer is not None: buffer.document = Document(value, len(value)) return value return _ApplicationStub @pytest.fixture def agency(): """Create an agency for testing.""" test_agent = Agent( name="TestAgent", description="A test agent for terminal testing", instructions=( "You are a helpful test assistant. Keep responses very brief (max 10 words). " "Always respond with 'Test response: [user message]'. " "You can hand off to the Developer agent by using transfer_to_ tool." ), model_settings=ModelSettings(temperature=0), ) developer_agent = Agent( name="Developer", description="A developer agent", instructions="You are a developer. Respond with 'Dev response: [message]'.", model_settings=ModelSettings(temperature=0), ) # Agent with a bad formatted name security_expert_agent = Agent( name="SecUrity ExperT_Agent", description="A security expert agent", instructions="You are a security expert. Respond with 'Security expert response: [message]'.", model_settings=ModelSettings(temperature=0), ) agency = Agency( test_agent, developer_agent, security_expert_agent, communication_flows=[ (security_expert_agent < test_agent > developer_agent, Handoff), ], name="TestAgency", shared_instructions="This is a test agency. Keep all responses very brief.", ) return agency class TestTerminalCapsys: """Test terminal demo using capsys for output capture.""" def test_help_command_output(self, agency, capsys): """Test /help command output capture.""" input_provider = MockInputProvider(["/help", "/exit"]) with patch("builtins.input", input_provider): with patch("agency_swarm.ui.demos.terminal.Application", new=_application_factory(input_provider)): start_terminal(agency, reload=False) captured = capsys.readouterr() output = captured.out assert "/help" in output assert "/new" in output assert "/compact" in output assert "/resume" in output assert "/status" in output assert "/exit" in output def test_status_command_output(self, agency, capsys): """Test /status command output capture.""" input_provider = MockInputProvider(["/status", "/exit"]) with patch("builtins.input", input_provider): with patch("agency_swarm.ui.demos.terminal.Application", new=_application_factory(input_provider)): start_terminal(agency, reload=False) captured = capsys.readouterr() output = captured.out assert "Agency: TestAgency" in output assert "Entry Points: TestAgent" in output assert "Default Recipient: TestAgent" in output assert "cwd:" in output def test_new_command_functionality(self, agency, capsys): """Test /new command functionality.""" input_provider = MockInputProvider(["/new", "/exit"]) with patch("builtins.input", input_provider): with patch("agency_swarm.ui.demos.terminal.Application", new=_application_factory(input_provider)): start_terminal(agency, reload=False) captured = capsys.readouterr() output = captured.out assert "Started a new chat session" in output def test_message_sending_and_response(self, agency, capsys): """Test sending message and receiving response from agent.""" input_provider = MockInputProvider(["Hello world", "/exit"]) with patch("builtins.input", input_provider): with patch("agency_swarm.ui.demos.terminal.Application", new=_application_factory(input_provider)): start_terminal(agency, reload=False) captured = capsys.readouterr() output = captured.out assert "Hello world" in output assert "πŸ€– TestAgent β†’ πŸ‘€ user" in output assert "Test response:" in output def test_agent_mention_parsing(self, agency, capsys): """Test @agent mention parsing with multi-agent setup.""" input_provider = MockInputProvider(["@Developer help me", "/exit"]) with patch("builtins.input", input_provider): with patch("agency_swarm.ui.demos.terminal.Application", new=_application_factory(input_provider)): start_terminal(agency, reload=False) captured = capsys.readouterr() output = captured.out assert "πŸ€– Developer β†’ πŸ‘€ user" in output assert "Dev response:" in output def test_empty_input_handling(self, agency, capsys): """Test handling of empty input.""" input_provider = MockInputProvider(["", " ", "/exit"]) with patch("builtins.input", input_provider): with patch("agency_swarm.ui.demos.terminal.Application", new=_application_factory(input_provider)): start_terminal(agency, reload=False) captured = capsys.readouterr() assert "message cannot be empty" in captured.out def test_handoff_chat_transfer(self, agency, capsys): """Test handoff chat transfer.""" input_provider = MockInputProvider(["Transfer to Developer", "Hi", "/exit"]) with patch("builtins.input", input_provider): with patch("agency_swarm.ui.demos.terminal.Application", new=_application_factory(input_provider)): start_terminal(agency, reload=False) captured = capsys.readouterr() output = captured.out # Verify that initial input is sent to main agent assert "πŸ€– TestAgent πŸ› οΈ Executing Function" assert "Calling transfer_to_Developer tool with:" in output # Verify that after handoff, response is sent by developer assert "πŸ€– Developer β†’ πŸ‘€ user" in output assert "Dev response:" in output # Verify that next input went to developer assert "πŸ€– Developer β†’ πŸ‘€ user" in output.split("USER: Hi")[-1] assert "Dev response:" in output.split("USER: Hi")[-1] def test_slash_dropdown_populates_commands(self, agency): """Ensure slash input populates dropdown items.""" input_provider = MockInputProvider(["/", "/exit"]) captured_labels: list[list[str]] = [] original_set_items = terminal_module.DropdownMenu.set_items def _capture(self, items): # noqa: ANN001 captured_labels.append([item.label for item in items]) original_set_items(self, items) with patch("builtins.input", input_provider): with patch("agency_swarm.ui.demos.terminal.Application", new=_application_factory(input_provider)): with patch.object(terminal_module.DropdownMenu, "set_items", _capture): start_terminal(agency, reload=False) assert captured_labels, "Expected dropdown items to be set" assert any(label.startswith("/") for label in captured_labels[0]) class TestTerminalEdgeCases: """Test edge cases and error handling with agency.""" def test_invalid_agent_mention_logs_error(self, agency, capsys, caplog): """Test that invalid @agent mentions are logged as errors.""" input_provider = MockInputProvider(["@NonExistentAgent help", "/exit"]) with patch("builtins.input", input_provider): with patch("agency_swarm.ui.demos.terminal.Application", new=_application_factory(input_provider)): start_terminal(agency, reload=False) # Check that error was logged assert "Recipient agent NonExistentAgent not found" in caplog.text def test_very_long_message(self, agency, capsys): """Test handling of very long messages.""" long_message = "This is a very long message. " * 50 input_provider = MockInputProvider([long_message, "/exit"]) with patch("builtins.input", input_provider): with patch("agency_swarm.ui.demos.terminal.Application", new=_application_factory(input_provider)): start_terminal(agency, reload=False) captured = capsys.readouterr() assert "πŸ€– TestAgent β†’ πŸ‘€ user" in captured.out assert "Test response:" in captured.out def test_special_characters_in_message(self, agency, capsys): """Test handling of special characters in messages.""" special_message = "Test with Γ©mojis πŸŽ‰ and symbols @#$%!" input_provider = MockInputProvider([special_message, "/exit"]) with patch("builtins.input", input_provider): with patch("agency_swarm.ui.demos.terminal.Application", new=_application_factory(input_provider)): start_terminal(agency, reload=False) captured = capsys.readouterr() assert "πŸ€– TestAgent β†’ πŸ‘€ user" in captured.out assert "Test response: Test with Γ©mojis πŸŽ‰ and symbols @#$%!" in captured.out def test_bad_formatted_agent_name(self, agency, capsys): """Test handling of bad formatted agent name.""" input_provider = MockInputProvider(["@SecUrity ExperT_Agent hi", "/exit"]) with patch("builtins.input", input_provider): with patch("agency_swarm.ui.demos.terminal.Application", new=_application_factory(input_provider)): start_terminal(agency, reload=False) captured = capsys.readouterr() assert "πŸ€– SecUrity ExperT_Agent β†’ πŸ‘€ user" in captured.out assert "Security expert response:" in captured.out def test_bad_formatted_agent_name_handoff(self, agency, capsys): """Test handling of bad formatted agent name.""" # Use the exact tool name so this test validates UI handoff rendering, not # the live model's ability to normalize a mismatched tool name. input_provider = MockInputProvider(["Use the transfer_to_SecUrity_ExperT_Agent tool", "Hi", "/exit"]) with patch("builtins.input", input_provider): with patch("agency_swarm.ui.demos.terminal.Application", new=_application_factory(input_provider)): start_terminal(agency, reload=False) captured = capsys.readouterr() output = captured.out # Handoffs will replace spaces with underscores assert "πŸ€– TestAgent πŸ› οΈ Executing Function" assert "Calling transfer_to_SecUrity_ExperT_Agent tool with:" in output # Verify that after handoff, name is shown correctly assert "πŸ€– SecUrity ExperT_Agent β†’ πŸ‘€ user" in output assert "Security expert response:" in output # Verify that next input went didn't cause any errors assert "πŸ€– SecUrity ExperT_Agent β†’ πŸ‘€ user" in output.split("USER: Hi")[-1] assert "Security expert response:" in output.split("USER: Hi")[-1] if __name__ == "__main__": pytest.main([__file__, "-v"])