import json import subprocess from unittest.mock import patch, MagicMock from src.gemini_cli_adapter import GeminiCliAdapter class TestGeminiCliAdapter: def setUp(self) -> None: pass @patch('subprocess.Popen') def test_send_starts_subprocess_with_correct_args(self, mock_popen: MagicMock) -> None: """Verify that send(message) correctly starts the subprocess with expected flags, excluding binary_path itself.""" adapter = GeminiCliAdapter(binary_path="gemini") # Mock Popen behavior mock_process = MagicMock() mock_process.stdout = [b'{"kind": "message", "payload": "hello"}'] mock_process.stderr = [] mock_process.returncode = 0 mock_popen.return_value = mock_process adapter.send("test prompt") # Verify Popen called 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: """Verify that it correctly parses multiple JSONL 'message' events and combines their content.""" adapter = GeminiCliAdapter() mock_process = MagicMock() mock_process.stdout = [ b'{"kind": "message", "payload": "Hello "}\n', b'{"kind": "message", "payload": "world!"}\n' ] mock_process.stderr = [] 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: """Verify that it correctly handles 'tool_use' events in the stream and populates the tool_calls list.""" adapter = GeminiCliAdapter() tool_json = { "kind": "tool_use", "payload": { "id": "call_123", "name": "read_file", "input": {"path": "test.txt"} } } mock_process = MagicMock() mock_process.stdout = [ (json.dumps(tool_json) + "\n").encode('utf-8') ] mock_process.stderr = [] 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: """Verify that usage data is extracted from the 'result' event.""" adapter = GeminiCliAdapter() result_json = { "kind": "result", "payload": { "status": "success", "usage": {"total_tokens": 50} } } mock_process = MagicMock() mock_process.stdout = [ (json.dumps(result_json) + "\n").encode('utf-8') ] mock_process.stderr = [] mock_process.returncode = 0 mock_popen.return_value = mock_process adapter.send("msg") assert adapter.last_usage["total_tokens"] == 50