fix(tests): Fix gemini_cli tests - proper mocking of subprocess.Popen
This commit is contained in:
@@ -3,26 +3,21 @@ 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."""
|
||||
class TestGeminiCliAdapter:
|
||||
@patch("subprocess.Popen")
|
||||
def test_send_starts_subprocess_with_correct_args(
|
||||
self, mock_popen: MagicMock
|
||||
) -> None:
|
||||
adapter = GeminiCliAdapter(binary_path="gemini")
|
||||
|
||||
# Mock Popen behavior
|
||||
mock_process = MagicMock()
|
||||
mock_process.stdout = [b'{"kind": "message", "payload": "hello"}']
|
||||
mock_process.stderr = []
|
||||
mock_process.communicate.return_value = (
|
||||
'{"type": "message", "content": "hello"}',
|
||||
"",
|
||||
)
|
||||
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]
|
||||
@@ -31,72 +26,61 @@ class TestGeminiCliAdapter:
|
||||
assert "--output-format" in cmd_list
|
||||
assert "stream-json" in cmd_list
|
||||
|
||||
@patch('subprocess.Popen')
|
||||
@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()
|
||||
|
||||
stdout_str = '{"type": "message", "content": "Hello "}\n{"type": "message", "content": "world!"}\n'
|
||||
mock_process = MagicMock()
|
||||
mock_process.stdout = [
|
||||
b'{"kind": "message", "payload": "Hello "}\n',
|
||||
b'{"kind": "message", "payload": "world!"}\n'
|
||||
]
|
||||
mock_process.stderr = []
|
||||
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')
|
||||
@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"}
|
||||
}
|
||||
"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.stdout = [
|
||||
(json.dumps(tool_json) + "\n").encode('utf-8')
|
||||
]
|
||||
mock_process.stderr = []
|
||||
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')
|
||||
@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}
|
||||
}
|
||||
}
|
||||
|
||||
result_json = {"type": "result", "stats": {"total_tokens": 50}}
|
||||
stdout_str = json.dumps(result_json) + "\n"
|
||||
mock_process = MagicMock()
|
||||
mock_process.stdout = [
|
||||
(json.dumps(result_json) + "\n").encode('utf-8')
|
||||
]
|
||||
mock_process.stderr = []
|
||||
mock_process.communicate.return_value = (stdout_str, "")
|
||||
mock_process.returncode = 0
|
||||
mock_popen.return_value = mock_process
|
||||
|
||||
adapter.send("msg")
|
||||
assert adapter.last_usage["total_tokens"] == 50
|
||||
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"]
|
||||
|
||||
Reference in New Issue
Block a user