import json import subprocess from unittest.mock import patch, MagicMock from src.gemini_cli_adapter import GeminiCliAdapter class TestGeminiCliAdapter: @patch("subprocess.Popen") def test_send_starts_subprocess_with_correct_args( self, mock_popen: MagicMock ) -> None: adapter = GeminiCliAdapter(binary_path="gemini") mock_process = MagicMock() mock_process.communicate.return_value = ( '{"type": "message", "content": "hello"}', "", ) mock_process.returncode = 0 mock_popen.return_value = mock_process adapter.send("test prompt") assert mock_popen.called args, kwargs = mock_popen.call_args cmd_list = args[0] assert "gemini" in cmd_list assert "--prompt" in cmd_list assert "--output-format" in cmd_list assert "stream-json" in cmd_list @patch("subprocess.Popen") def test_send_parses_jsonl_output(self, mock_popen: MagicMock) -> None: adapter = GeminiCliAdapter() stdout_str = '{"type": "message", "content": "Hello "}\n{"type": "message", "content": "world!"}\n' mock_process = MagicMock() mock_process.communicate.return_value = (stdout_str, "") mock_process.returncode = 0 mock_popen.return_value = mock_process result = adapter.send("msg") assert result["text"] == "Hello world!" @patch("subprocess.Popen") def test_send_handles_tool_use_events(self, mock_popen: MagicMock) -> None: adapter = GeminiCliAdapter() tool_json = { "type": "tool_use", "tool_name": "read_file", "parameters": {"path": "test.txt"}, "tool_id": "call_123", } stdout_str = json.dumps(tool_json) + "\n" mock_process = MagicMock() mock_process.communicate.return_value = (stdout_str, "") mock_process.returncode = 0 mock_popen.return_value = mock_process result = adapter.send("msg") assert len(result["tool_calls"]) == 1 assert result["tool_calls"][0]["name"] == "read_file" assert result["tool_calls"][0]["args"]["path"] == "test.txt" @patch("subprocess.Popen") def test_send_captures_usage_metadata(self, mock_popen: MagicMock) -> None: adapter = GeminiCliAdapter() result_json = {"type": "result", "stats": {"total_tokens": 50}} stdout_str = json.dumps(result_json) + "\n" mock_process = MagicMock() mock_process.communicate.return_value = (stdout_str, "") mock_process.returncode = 0 mock_popen.return_value = mock_process adapter.send("msg") assert adapter.last_usage is not None assert adapter.last_usage.get("total_tokens") == 50 @patch("subprocess.Popen") def test_full_flow_integration(self, mock_popen: MagicMock) -> None: adapter = GeminiCliAdapter() msg_json = {"type": "message", "content": "Final response"} result_json = { "type": "result", "stats": {"total_tokens": 25, "input_tokens": 10, "output_tokens": 15}, } stdout_str = json.dumps(msg_json) + "\n" + json.dumps(result_json) + "\n" mock_process = MagicMock() mock_process.communicate.return_value = (stdout_str, "") mock_process.returncode = 0 mock_popen.return_value = mock_process result = adapter.send("test") assert "Final response" in result["text"]