refactor(tests): Update test suite and API hooks for AppController architecture

This commit is contained in:
2026-03-04 11:38:36 -05:00
parent 8642277ef4
commit f2b25757eb
6 changed files with 214 additions and 141 deletions

View File

@@ -88,7 +88,7 @@ def mock_app() -> Generator[App, None, None]:
Mock version of the App for simple unit tests that don't need a loop.
"""
with (
patch('gui_2.load_config', return_value={
patch('src.models.load_config', return_value={
'ai': {'provider': 'gemini', 'model': 'gemini-2.5-flash-lite'},
'projects': {'paths': [], 'active': ''},
'gui': {'show_windows': {}}
@@ -97,16 +97,19 @@ def mock_app() -> Generator[App, None, None]:
patch('gui_2.project_manager'),
patch('gui_2.session_logger'),
patch('gui_2.immapp.run'),
patch.object(App, '_load_active_project'),
patch.object(App, '_fetch_models'),
patch('src.app_controller.AppController._load_active_project'),
patch('src.app_controller.AppController._fetch_models'),
patch.object(App, '_load_fonts'),
patch.object(App, '_post_init'),
patch.object(App, '_prune_old_logs'),
patch.object(App, '_init_ai_and_hooks'),
patch('src.app_controller.AppController._prune_old_logs'),
patch('src.app_controller.AppController.start_services'),
patch('src.app_controller.AppController._init_ai_and_hooks'),
patch('gui_2.PerformanceMonitor')
):
app = App()
yield app
if hasattr(app, 'controller'):
app.controller.stop_services()
if hasattr(app, 'shutdown'):
app.shutdown()
@@ -117,7 +120,7 @@ def app_instance() -> Generator[App, None, None]:
Matches the pattern used in test_token_viz.py and test_gui_phase4.py.
"""
with (
patch('gui_2.load_config', return_value={
patch('src.models.load_config', return_value={
'ai': {'provider': 'gemini', 'model': 'gemini-2.5-flash-lite'},
'projects': {'paths': [], 'active': ''},
'gui': {'show_windows': {}}
@@ -126,22 +129,28 @@ def app_instance() -> Generator[App, None, None]:
patch('gui_2.project_manager'),
patch('gui_2.session_logger'),
patch('gui_2.immapp.run'),
patch.object(App, '_load_active_project'),
patch.object(App, '_fetch_models'),
patch('src.app_controller.AppController._load_active_project'),
patch('src.app_controller.AppController._fetch_models'),
patch.object(App, '_load_fonts'),
patch.object(App, '_post_init'),
patch.object(App, '_prune_old_logs'),
patch.object(App, '_init_ai_and_hooks'),
patch('src.app_controller.AppController._prune_old_logs'),
patch('src.app_controller.AppController.start_services'),
patch('src.app_controller.AppController._init_ai_and_hooks'),
patch('gui_2.PerformanceMonitor')
):
app = App()
yield app
# Cleanup: Ensure background threads and asyncio loop are stopped
if hasattr(app, 'controller'):
app.controller.stop_services()
if hasattr(app, 'shutdown'):
app.shutdown()
if hasattr(app, '_loop') and not app._loop.is_closed():
tasks = [t for t in asyncio.all_tasks(app._loop) if not t.done()]
# Use controller._loop for cleanup
loop = getattr(app.controller, '_loop', None) if hasattr(app, 'controller') else None
if loop and not loop.is_closed():
tasks = [t for t in asyncio.all_tasks(loop) if not t.done()]
if tasks:
# Cancel tasks so they can be gathered
for task in tasks:
@@ -149,14 +158,14 @@ def app_instance() -> Generator[App, None, None]:
# We can't really run the loop if it's already stopping or thread is dead,
# but we try to be clean.
try:
if app._loop.is_running():
app._loop.call_soon_threadsafe(app._loop.stop)
if loop.is_running():
loop.call_soon_threadsafe(loop.stop)
except: pass
# Finally close the loop if we can
try:
if not app._loop.is_running():
app._loop.close()
if not loop.is_running():
loop.close()
except: pass
@pytest.fixture(scope="session")