WIP: I HATE PYTHON

This commit is contained in:
2026-03-05 13:55:40 -05:00
parent 107608cd76
commit 5e69617f88
43 changed files with 1854 additions and 1671 deletions

View File

@@ -1,112 +1,102 @@
import unittest
from typing import Any
from unittest.mock import patch, MagicMock
import json
import subprocess
import sys
import os
from unittest.mock import patch, MagicMock
from src.gemini_cli_adapter import GeminiCliAdapter
# Ensure the project root is in sys.path to resolve imports correctly
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "src")))
class TestGeminiCliAdapter:
def setUp(self) -> None:
pass
from gemini_cli_adapter import GeminiCliAdapter
@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
class TestGeminiCliAdapter(unittest.TestCase):
def setUp(self) -> None:
self.adapter = GeminiCliAdapter(binary_path="gemini")
@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_starts_subprocess_with_correct_args(self, mock_popen: Any) -> None:
"""
Verify that send(message) correctly starts the subprocess with
--output-format stream-json and the provided message via stdin.
"""
# Setup mock process
process_mock = MagicMock()
jsonl_output = json.dumps({"type": "result", "usage": {}}) + "\n"
process_mock.communicate.return_value = (jsonl_output, "")
mock_popen.return_value = process_mock
message = "Hello Gemini CLI"
self.adapter.send(message)
# Verify subprocess.Popen call
mock_popen.assert_called_once()
args, kwargs = mock_popen.call_args
cmd_list = args[0]
# Check mandatory CLI components
self.assertIn("gemini", cmd_list)
self.assertIn("--output-format", cmd_list)
self.assertIn("stream-json", cmd_list)
# Verify message was passed to communicate
process_mock.communicate.assert_called_with(input=message)
# Check process configuration
self.assertEqual(kwargs.get('stdout'), subprocess.PIPE)
self.assertEqual(kwargs.get('stdin'), subprocess.PIPE)
self.assertEqual(kwargs.get('text'), True)
@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_parses_jsonl_output(self, mock_popen: Any) -> None:
"""
Verify that it correctly parses multiple JSONL 'message' events
and returns the combined text.
"""
jsonl_output = (
json.dumps({"type": "message", "role": "model", "text": "The quick brown "}) + "\n" +
json.dumps({"type": "message", "role": "model", "text": "fox jumps."}) + "\n" +
json.dumps({"type": "result", "usage": {"prompt_tokens": 5, "candidates_tokens": 5}}) + "\n"
)
process_mock = MagicMock()
process_mock.communicate.return_value = (jsonl_output, "")
mock_popen.return_value = process_mock
result = self.adapter.send("test message")
self.assertEqual(result["text"], "The quick brown fox jumps.")
self.assertEqual(result["tool_calls"], [])
@patch('subprocess.Popen')
def test_send_handles_tool_use_events(self, mock_popen: Any) -> None:
"""
Verify that it correctly handles 'tool_use' events in the stream
by continuing to read until the final 'result' event.
"""
jsonl_output = (
json.dumps({"type": "message", "role": "assistant", "text": "Calling tool..."}) + "\n" +
json.dumps({"type": "tool_use", "name": "read_file", "args": {"path": "test.txt"}}) + "\n" +
json.dumps({"type": "message", "role": "assistant", "text": "\nFile read successfully."}) + "\n" +
json.dumps({"type": "result", "usage": {}}) + "\n"
)
process_mock = MagicMock()
process_mock.communicate.return_value = (jsonl_output, "")
mock_popen.return_value = process_mock
result = self.adapter.send("read test.txt")
# Result should contain the combined text from all 'message' events
self.assertEqual(result["text"], "Calling tool...\nFile read successfully.")
self.assertEqual(len(result["tool_calls"]), 1)
self.assertEqual(result["tool_calls"][0]["name"], "read_file")
@patch('subprocess.Popen')
def test_send_captures_usage_metadata(self, mock_popen: Any) -> None:
"""
Verify that usage data is extracted from the 'result' event.
"""
usage_data = {"total_tokens": 42}
jsonl_output = (
json.dumps({"type": "message", "text": "Finalizing"}) + "\n" +
json.dumps({"type": "result", "usage": usage_data}) + "\n"
)
process_mock = MagicMock()
process_mock.communicate.return_value = (jsonl_output, "")
mock_popen.return_value = process_mock
self.adapter.send("usage test")
# Verify the usage was captured in the adapter instance
self.assertEqual(self.adapter.last_usage, usage_data)
if __name__ == '__main__':
unittest.main()
@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