refactor(tests): Add strict type hints to fourth batch of test files

This commit is contained in:
2026-02-28 19:20:41 -05:00
parent e8513d563b
commit ee2d6f4234
9 changed files with 34 additions and 27 deletions

View File

@@ -18,7 +18,7 @@ class TestCliToolBridgeMapping(unittest.TestCase):
@patch('sys.stdin', new_callable=io.StringIO) @patch('sys.stdin', new_callable=io.StringIO)
@patch('sys.stdout', new_callable=io.StringIO) @patch('sys.stdout', new_callable=io.StringIO)
@patch('api_hook_client.ApiHookClient.request_confirmation') @patch('api_hook_client.ApiHookClient.request_confirmation')
def test_mapping_from_api_format(self, mock_request, mock_stdout, mock_stdin): def test_mapping_from_api_format(self, mock_request: MagicMock, mock_stdout: MagicMock, mock_stdin: MagicMock) -> None:
""" """
Verify that bridge correctly maps 'id', 'name', 'input' (Gemini API format) Verify that bridge correctly maps 'id', 'name', 'input' (Gemini API format)
into tool_name and tool_input for the hook client. into tool_name and tool_input for the hook client.

View File

@@ -1,3 +1,4 @@
from typing import Any
import pytest import pytest
import os import os
import tomllib import tomllib
@@ -11,7 +12,7 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
import ai_client import ai_client
import project_manager import project_manager
def test_credentials_error_mentions_deepseek(monkeypatch): def test_credentials_error_mentions_deepseek(monkeypatch: pytest.MonkeyPatch) -> None:
""" """
Verify that the error message shown when credentials.toml is missing Verify that the error message shown when credentials.toml is missing
includes deepseek instructions. includes deepseek instructions.
@@ -48,7 +49,7 @@ def test_deepseek_model_listing() -> None:
assert "deepseek-chat" in models assert "deepseek-chat" in models
assert "deepseek-reasoner" in models assert "deepseek-reasoner" in models
def test_gui_provider_list_via_hooks(live_gui): def test_gui_provider_list_via_hooks(live_gui: Any) -> None:
""" """
Verify 'deepseek' is present in the GUI provider list using API hooks. Verify 'deepseek' is present in the GUI provider list using API hooks.
""" """

View File

@@ -1,3 +1,4 @@
from typing import Any
import pytest import pytest
import time import time
import os import os
@@ -5,7 +6,7 @@ import sys
import requests import requests
from api_hook_client import ApiHookClient from api_hook_client import ApiHookClient
def test_gemini_cli_full_integration(live_gui): def test_gemini_cli_full_integration(live_gui: Any) -> None:
""" """
Integration test for the Gemini CLI provider and tool bridge. Integration test for the Gemini CLI provider and tool bridge.
Handles 'ask_received' events from the bridge and any other approval requests. Handles 'ask_received' events from the bridge and any other approval requests.
@@ -70,7 +71,7 @@ def test_gemini_cli_full_integration(live_gui):
assert approved_count > 0, "No approval events were processed" assert approved_count > 0, "No approval events were processed"
assert found_final, "Final message from mock CLI was not found in the GUI history" assert found_final, "Final message from mock CLI was not found in the GUI history"
def test_gemini_cli_rejection_and_history(live_gui): def test_gemini_cli_rejection_and_history(live_gui: Any) -> None:
""" """
Integration test for the Gemini CLI provider: Rejection flow and history. Integration test for the Gemini CLI provider: Rejection flow and history.
""" """

View File

@@ -1,9 +1,10 @@
from typing import Generator
import pytest import pytest
from unittest.mock import patch from unittest.mock import patch
from gui_2 import App from gui_2 import App
@pytest.fixture @pytest.fixture
def app_instance() -> None: def app_instance() -> Generator[App, None, None]:
with ( with (
patch('gui_2.load_config', return_value={'gui': {'show_windows': {}}}), patch('gui_2.load_config', return_value={'gui': {'show_windows': {}}}),
patch('gui_2.save_config'), patch('gui_2.save_config'),
@@ -17,7 +18,7 @@ def app_instance() -> None:
): ):
yield App() yield App()
def test_gui2_hubs_exist_in_show_windows(app_instance): def test_gui2_hubs_exist_in_show_windows(app_instance: App) -> None:
""" """
Verifies that the new consolidated Hub windows are defined in the App's show_windows. Verifies that the new consolidated Hub windows are defined in the App's show_windows.
This ensures they will be available in the 'Windows' menu. This ensures they will be available in the 'Windows' menu.
@@ -33,7 +34,7 @@ def test_gui2_hubs_exist_in_show_windows(app_instance):
for hub in expected_hubs: for hub in expected_hubs:
assert hub in app_instance.show_windows, f"Expected hub window '{hub}' not found in show_windows" assert hub in app_instance.show_windows, f"Expected hub window '{hub}' not found in show_windows"
def test_gui2_old_windows_removed_from_show_windows(app_instance): def test_gui2_old_windows_removed_from_show_windows(app_instance: App) -> None:
""" """
Verifies that the old fragmented windows are removed from show_windows. Verifies that the old fragmented windows are removed from show_windows.
""" """

View File

@@ -1,3 +1,4 @@
from typing import Generator
import pytest import pytest
from unittest.mock import MagicMock, patch, AsyncMock from unittest.mock import MagicMock, patch, AsyncMock
import asyncio import asyncio
@@ -7,7 +8,7 @@ from events import UserRequestEvent
import ai_client import ai_client
@pytest.fixture @pytest.fixture
def mock_app() -> None: def mock_app() -> Generator[App, None, None]:
with ( with (
patch('gui_2.load_config', return_value={ patch('gui_2.load_config', return_value={
"ai": {"provider": "gemini", "model": "model-1", "temperature": 0.0, "max_tokens": 100, "history_trunc_limit": 1000}, "ai": {"provider": "gemini", "model": "model-1", "temperature": 0.0, "max_tokens": 100, "history_trunc_limit": 1000},
@@ -33,7 +34,7 @@ def mock_app() -> None:
# so we just let it daemon-exit. # so we just let it daemon-exit.
@pytest.mark.timeout(10) @pytest.mark.timeout(10)
def test_user_request_integration_flow(mock_app): def test_user_request_integration_flow(mock_app: App) -> None:
""" """
Verifies that pushing a UserRequestEvent to the event_queue: Verifies that pushing a UserRequestEvent to the event_queue:
1. Triggers ai_client.send 1. Triggers ai_client.send
@@ -83,7 +84,7 @@ def test_user_request_integration_flow(mock_app):
assert app.ai_status == "done" assert app.ai_status == "done"
@pytest.mark.timeout(10) @pytest.mark.timeout(10)
def test_user_request_error_handling(mock_app): def test_user_request_error_handling(mock_app: App) -> None:
""" """
Verifies that if ai_client.send raises an exception, the UI is updated with the error state. Verifies that if ai_client.send raises an exception, the UI is updated with the error state.
""" """

View File

@@ -1,3 +1,4 @@
from typing import Tuple
import os import os
import shutil import shutil
import pytest import pytest
@@ -7,7 +8,7 @@ from log_registry import LogRegistry
from log_pruner import LogPruner from log_pruner import LogPruner
@pytest.fixture @pytest.fixture
def pruner_setup(tmp_path): def pruner_setup(tmp_path: Path) -> Tuple[LogPruner, LogRegistry, Path]:
logs_dir = tmp_path / "logs" logs_dir = tmp_path / "logs"
logs_dir.mkdir() logs_dir.mkdir()
registry_path = logs_dir / "log_registry.toml" registry_path = logs_dir / "log_registry.toml"
@@ -15,7 +16,7 @@ def pruner_setup(tmp_path):
pruner = LogPruner(registry, str(logs_dir)) pruner = LogPruner(registry, str(logs_dir))
return pruner, registry, logs_dir return pruner, registry, logs_dir
def test_prune_old_insignificant_logs(pruner_setup): def test_prune_old_insignificant_logs(pruner_setup: Tuple[LogPruner, LogRegistry, Path]) -> None:
pruner, registry, logs_dir = pruner_setup pruner, registry, logs_dir = pruner_setup
# 1. Old and small (insignificant) -> should be pruned # 1. Old and small (insignificant) -> should be pruned
session_id_old_small = "old_small" session_id_old_small = "old_small"

View File

@@ -1,10 +1,11 @@
from typing import Generator
import pytest import pytest
from unittest.mock import patch, MagicMock from unittest.mock import patch, MagicMock
import asyncio import asyncio
from gui_2 import App from gui_2 import App
@pytest.fixture @pytest.fixture
def app_instance() -> None: def app_instance() -> Generator[App, None, None]:
with ( with (
patch('gui_2.load_config', return_value={'ai': {}, 'projects': {}}), patch('gui_2.load_config', return_value={'ai': {}, 'projects': {}}),
patch('gui_2.save_config'), patch('gui_2.save_config'),
@@ -21,7 +22,7 @@ def app_instance() -> None:
app._loop = MagicMock() app._loop = MagicMock()
yield app yield app
def test_cb_ticket_retry(app_instance): def test_cb_ticket_retry(app_instance: App) -> None:
ticket_id = "test_ticket_1" ticket_id = "test_ticket_1"
app_instance.active_tickets = [{"id": ticket_id, "status": "failed"}] app_instance.active_tickets = [{"id": ticket_id, "status": "failed"}]
with patch('asyncio.run_coroutine_threadsafe') as mock_run_safe: with patch('asyncio.run_coroutine_threadsafe') as mock_run_safe:
@@ -34,7 +35,7 @@ def test_cb_ticket_retry(app_instance):
args, _ = mock_run_safe.call_args args, _ = mock_run_safe.call_args
assert args[1] == app_instance._loop assert args[1] == app_instance._loop
def test_cb_ticket_skip(app_instance): def test_cb_ticket_skip(app_instance: App) -> None:
ticket_id = "test_ticket_1" ticket_id = "test_ticket_1"
app_instance.active_tickets = [{"id": ticket_id, "status": "todo"}] app_instance.active_tickets = [{"id": ticket_id, "status": "todo"}]
with patch('asyncio.run_coroutine_threadsafe') as mock_run_safe: with patch('asyncio.run_coroutine_threadsafe') as mock_run_safe:

View File

@@ -1,10 +1,11 @@
from typing import Generator
import pytest import pytest
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
import ai_client import ai_client
from gui_2 import App from gui_2 import App
@pytest.fixture @pytest.fixture
def app_instance() -> None: def app_instance() -> Generator[App, None, None]:
with ( with (
patch('gui_2.load_config', return_value={'ai': {'provider': 'gemini', 'model': 'gemini-2.5-flash-lite'}, 'projects': {}}), patch('gui_2.load_config', return_value={'ai': {'provider': 'gemini', 'model': 'gemini-2.5-flash-lite'}, 'projects': {}}),
patch('gui_2.save_config'), patch('gui_2.save_config'),
@@ -21,8 +22,7 @@ def app_instance() -> None:
app = App() app = App()
yield app yield app
def test_redundant_calls_in_process_pending_gui_tasks(app_instance): def test_redundant_calls_in_process_pending_gui_tasks(app_instance: App) -> None:
# Setup
app_instance._pending_gui_tasks = [ app_instance._pending_gui_tasks = [
{'action': 'set_value', 'item': 'current_provider', 'value': 'anthropic'} {'action': 'set_value', 'item': 'current_provider', 'value': 'anthropic'}
] ]
@@ -40,8 +40,7 @@ def test_redundant_calls_in_process_pending_gui_tasks(app_instance):
assert mock_set_provider.call_count == 1 assert mock_set_provider.call_count == 1
assert mock_reset_session.call_count == 1 assert mock_reset_session.call_count == 1
def test_gcli_path_updates_adapter(app_instance): def test_gcli_path_updates_adapter(app_instance: App) -> None:
# Setup
app_instance.current_provider = 'gemini_cli' app_instance.current_provider = 'gemini_cli'
app_instance._pending_gui_tasks = [ app_instance._pending_gui_tasks = [
{'action': 'set_value', 'item': 'gcli_path', 'value': '/new/path/to/gemini'} {'action': 'set_value', 'item': 'gcli_path', 'value': '/new/path/to/gemini'}

View File

@@ -1,14 +1,15 @@
import os
import shutil
import pytest import pytest
import shutil
import os
import tomllib
from pathlib import Path from pathlib import Path
from datetime import datetime from datetime import datetime
from unittest.mock import patch from unittest.mock import patch
from typing import Generator
import session_logger import session_logger
import tomllib
@pytest.fixture @pytest.fixture
def temp_logs(tmp_path, monkeypatch): def temp_logs(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Generator[Path, None, None]:
# Ensure closed before starting # Ensure closed before starting
session_logger.close_session() session_logger.close_session()
monkeypatch.setattr(session_logger, "_comms_fh", None) monkeypatch.setattr(session_logger, "_comms_fh", None)
@@ -28,7 +29,8 @@ def temp_logs(tmp_path, monkeypatch):
session_logger._LOG_DIR = original_log_dir session_logger._LOG_DIR = original_log_dir
session_logger._SCRIPTS_DIR = original_scripts_dir session_logger._SCRIPTS_DIR = original_scripts_dir
def test_open_session_creates_subdir_and_registry(temp_logs): def test_open_session_creates_subdir_and_registry(temp_logs: Path) -> None:
label = "test-label" label = "test-label"
# We can't easily mock datetime.datetime.now() because it's a built-in # We can't easily mock datetime.datetime.now() because it's a built-in
# but we can check the resulting directory name pattern # but we can check the resulting directory name pattern