import unittest import json import subprocess from unittest.mock import patch, MagicMock from src.gemini_cli_adapter import GeminiCliAdapter class TestGeminiCliAdapterParity(unittest.TestCase): def setUp(self) -> None: """Set up a fresh adapter instance and reset session state for each test.""" self.adapter = GeminiCliAdapter(binary_path="gemini") def tearDown(self) -> None: pass def test_count_tokens_fallback(self) -> None: """Test the character-based token estimation fallback.""" contents = ["Hello", "world!"] estimated = self.adapter.count_tokens(contents) # "Hello\nworld!" is 12 chars. 12 // 4 = 3 self.assertEqual(estimated, 3) @patch('subprocess.Popen') def test_send_starts_subprocess_with_model(self, mock_popen: MagicMock) -> None: """Test that the send method correctly adds the -m flag when a model is specified.""" mock_process = MagicMock() mock_process.stdout = [b'{"kind": "message", "payload": "hi"}'] mock_process.stderr = [] mock_process.returncode = 0 mock_popen.return_value = mock_process self.adapter.send("test", model="gemini-2.0-flash") args, _ = mock_popen.call_args cmd_list = args[0] self.assertIn("-m", cmd_list) self.assertIn("gemini-2.0-flash", cmd_list) @patch('subprocess.Popen') def test_send_parses_tool_calls_from_streaming_json(self, mock_popen: MagicMock) -> None: """Test that tool_use messages in the streaming JSON are correctly parsed.""" tool_call_json = { "kind": "tool_use", "payload": { "id": "call_abc", "name": "list_directory", "input": {"path": "."} } } mock_process = MagicMock() mock_process.stdout = [ (json.dumps(tool_call_json) + "\n").encode('utf-8'), b'{"kind": "message", "payload": "I listed the files."}' ] mock_process.stderr = [] mock_process.returncode = 0 mock_popen.return_value = mock_process result = self.adapter.send("msg") self.assertEqual(len(result["tool_calls"]), 1) self.assertEqual(result["tool_calls"][0]["name"], "list_directory") self.assertEqual(result["text"], "I listed the files.")