diff --git a/src/app_controller.py b/src/app_controller.py index c40a4aaa..02342559 100644 --- a/src/app_controller.py +++ b/src/app_controller.py @@ -3893,6 +3893,14 @@ class AppController: self.ui_ai_input = "" self.ui_manual_approve = False self.ui_auto_add_history = False + # Reset ui_files_base_dir to a sane default so a prior live_gui test + # that set a temp/relative path via set_value('files_base_dir', ...) + # does not pollute the session-scoped subprocess with a dead path. + # See tests/test_rag_visual_sim.py and tests/test_visual_sim_mma_v2.py + # for the original polluters. Per Tier 1 investigation: + # docs/reports/INVESTIGATION_rag_phase4_final_verify_20260627.md + self.ui_files_base_dir = self.project.get("files", {}).get("base_dir", ".") + self.ui_shots_base_dir = self.project.get("screenshots", {}).get("base_dir", ".") self.active_track = None self.active_tier = None self.mma_status = 'idle' diff --git a/tests/test_rag_visual_sim.py b/tests/test_rag_visual_sim.py index 3d780abe..a43f9e3a 100644 --- a/tests/test_rag_visual_sim.py +++ b/tests/test_rag_visual_sim.py @@ -15,26 +15,33 @@ from src import api_hook_client def test_rag_full_lifecycle_sim(live_gui): client = api_hook_client.ApiHookClient() assert client.wait_for_server(timeout=15), "Hook server did not start" - + # 1. Setup mock project data - test_dir = tempfile.mkdtemp() + # Per Tier 1 investigation (docs/reports/INVESTIGATION_rag_phase4_final_verify_20260627.md): + # Use a temp dir under tests/artifacts/ (NOT tempfile.mkdtemp() which defaults + # to C:\Users\Ed\AppData\Local\Temp\tmpXXXX and pollutes the session-scoped + # subprocess's ui_files_base_dir with a dead path that persists across + # live_gui tests). Also restore files_base_dir and rag_enabled in finally + # to prevent state leakage into subsequent live_gui tests. + test_dir = tempfile.mkdtemp(dir="tests/artifacts", prefix="rag_visual_sim_") + previous_files_base_dir = client.get_value('files_base_dir') try: (Path(test_dir) / "test_file.txt").write_text("This is a test file about RAG integration. It should be indexed.") (Path(test_dir) / "other_file.py").write_text("# This is another file\ndef hello():\n print('world')") - + # 2. Configure project through Hook API - client.set_value('files_base_dir', test_dir) + client.set_value('files_base_dir', str(Path(test_dir).resolve())) client.set_value('rag_enabled', True) client.set_value('rag_source', 'mock') # Use mock to avoid sentence-transformers dependency in CI - + # 3. Verify initial status status = client.get_value('rag_status') assert status in ['idle', 'ready', 'initializing...'], f"Unexpected initial status: {status}" - + # 4. Trigger Rebuild Index print("[SIM] Triggering index rebuild...") client.click('btn_rebuild_rag_index') - + # 5. Wait for status transition # Wait for 'indexing...' found_indexing = False @@ -44,9 +51,9 @@ def test_rag_full_lifecycle_sim(live_gui): found_indexing = True break time.sleep(0.1) - + print(f"[SIM] Found indexing: {found_indexing}") - + # Wait for 'ready' success = False for _ in range(50): @@ -57,35 +64,48 @@ def test_rag_full_lifecycle_sim(live_gui): if "error" in status.lower(): pytest.fail(f"RAG indexing failed: {status}") time.sleep(0.2) - + assert success, f"RAG indexing timed out. Final status: {status}" print("[SIM] RAG indexing SUCCESS.") - + # 6. Verify retrieval visualization # We simulate a response that has RAG context prepended # Since we are testing GUI visualization, we'll manually inject a message into the discussion # that contains the RAG marker, then verify the GUI state if possible. # However, verifying ImGui internal render state via Hook API is limited to exposed fields. # We've already verified the wiring of settings and status. - + # One final check: toggle RAG off and verify client.set_value('rag_enabled', False) assert client.get_value('rag_enabled') is False - + finally: - shutil.rmtree(test_dir) + # Restore state to prevent pollution of session-scoped subprocess. + # ui_files_base_dir and rag_enabled are sticky; without restoration, + # the next live_gui test (e.g. test_rag_phase4_final_verify) inherits + # the dead temp path and its RAG search silently no-ops on missing files. + try: + client.set_value('rag_enabled', False) + except Exception: + pass + if previous_files_base_dir is not None: + try: + client.set_value('files_base_dir', previous_files_base_dir) + except Exception: + pass + shutil.rmtree(test_dir, ignore_errors=True) @pytest.mark.integration def test_rag_settings_persistence_sim(live_gui): client = api_hook_client.ApiHookClient() assert client.wait_for_server(timeout=15) - + # Change settings client.set_value('rag_chunk_size', 1234) client.set_value('rag_chunk_overlap', 56) - + # Verify they were set in the controller assert client.get_value('rag_chunk_size') == 1234 assert client.get_value('rag_chunk_overlap') == 56 - + print("[SIM] RAG settings persistence simulation PASSED.") diff --git a/tests/test_visual_sim_mma_v2.py b/tests/test_visual_sim_mma_v2.py index 4d568b52..66b109d3 100644 --- a/tests/test_visual_sim_mma_v2.py +++ b/tests/test_visual_sim_mma_v2.py @@ -71,10 +71,15 @@ def test_mma_complete_lifecycle(live_gui) -> None: time.sleep(0.3) client.set_value('gcli_path', f'"{sys.executable}" "{os.path.abspath("tests/mock_gemini_cli.py")}"') time.sleep(0.3) - client.set_value('files_base_dir', 'tests/artifacts/temp_workspace') + # Per Tier 1 investigation: do NOT change files_base_dir here and do NOT + # click btn_project_save. The previous version set files_base_dir to + # 'tests/artifacts/temp_workspace' and persisted it via btn_project_save, + # which polluted the session-scoped subprocess's ui_files_base_dir (and + # the workspace's manual_slop.toml) with a test-only path that persisted + # into subsequent live_gui tests (e.g. test_rag_phase4_final_verify). + # The MMA lifecycle does not depend on a specific files_base_dir — the + # mock_gemini_cli returns canned responses regardless. time.sleep(0.3) - client.click('btn_project_save') - time.sleep(1.0) # one full second — let GUI process all set_value tasks # ------------------------------------------------------------------ # Stage 2: Start epic planning