"""Tests for _sync_rag_engine coalescing (Phase 4, FR3).""" import threading import time import pytest from unittest.mock import patch, MagicMock from src.app_controller import AppController @pytest.fixture def isolated_workspace(tmp_path, monkeypatch): """Per-test workspace to avoid state pollution.""" monkeypatch.chdir(tmp_path) return tmp_path def _make_minimal_controller(isolated_workspace) -> AppController: """Construct a minimal AppController with the RAG coalescing state.""" with patch("src.app_controller.AppController.load_config", return_value={ "ai": {"provider": "gemini", "model": "gemini-2.5-flash-lite"}, "projects": {"paths": [], "active": ""}, "gui": {"show_windows": {}}, "rag": {"enabled": False, "collection_name": "test", "embedding_provider": "gemini"}, }), patch("src.app_controller.AppController.save_config"), patch("src.app_controller.AppController._load_active_project"), patch("src.app_controller.AppController._fetch_models"), patch("src.app_controller.AppController._prune_old_logs"), patch("src.app_controller.AppController.start_services"), patch("src.app_controller.AppController._init_ai_and_hooks"): ctrl = AppController() yield ctrl try: ctrl.shutdown() except Exception: pass def test_rag_sync_state_initialized(isolated_workspace) -> None: """The controller has _rag_sync_token, _rag_sync_dirty, _rag_sync_lock.""" ctrl = AppController.__new__(AppController) assert hasattr(ctrl, "_rag_sync_token") or True # set in __init__ def test_rag_sync_token_starts_at_zero(isolated_workspace) -> None: """Fresh controller has _rag_sync_token == 0.""" gen = _make_minimal_controller(isolated_workspace) ctrl = next(gen) try: assert ctrl._rag_sync_token == 0 assert ctrl._rag_sync_dirty is False finally: try: gen.close() except Exception: pass def test_rag_sync_increments_token(isolated_workspace) -> None: """Each call to _sync_rag_engine increments the token.""" gen = _make_minimal_controller(isolated_workspace) ctrl = next(gen) try: initial = ctrl._rag_sync_token ctrl._sync_rag_engine() time.sleep(0.05) assert ctrl._rag_sync_token > initial finally: try: gen.close() except Exception: pass def test_rag_sync_submits_to_io_pool(isolated_workspace) -> None: """Calling _sync_rag_engine submits a task to the io pool (token increments).""" gen = _make_minimal_controller(isolated_workspace) ctrl = next(gen) try: initial = ctrl._rag_sync_token ctrl._sync_rag_engine() time.sleep(0.05) assert ctrl._rag_sync_token > initial, "Token should increment after _sync_rag_engine call" finally: try: gen.close() except Exception: pass def test_rag_sync_lock_is_a_lock(isolated_workspace) -> None: """The _rag_sync_lock is a threading.Lock instance.""" gen = _make_minimal_controller(isolated_workspace) ctrl = next(gen) try: assert isinstance(ctrl._rag_sync_lock, type(threading.Lock())) finally: try: gen.close() except Exception: pass