"""Regression test: _handle_reset_session must clear MMA + RAG state. Added in test_infrastructure_hardening_20260609 followup. Without these resets, prior test state pollutes the next session via: - mma_tier_usage: stale per-tier model/input/output values cause the mma_state_update handler to merge with old data, leaving the new push's active_tier effectively lost in the GUI render path. - rag_engine / rag_config: the chroma collection from a prior test doesn't get rebuilt, causing RAG queries to return nothing. """ import time import pytest from src import api_hook_client @pytest.mark.clean_baseline def test_reset_session_clears_mma_tier_usage(live_gui) -> None: """reset_session() must restore mma_tier_usage to the default (all empty).""" client = api_hook_client.ApiHookClient() assert client.wait_for_server(timeout=15) # Pollute the mma_tier_usage via a mma_state_update push client.push_event('mma_state_update', { 'status': 'running', 'active_tier': 'Tier 3 (Worker)', 'tier_usage': {'Tier 1': {'model': 'polluted'}}, 'tickets': [] }) # Poll until the polluted entry is visible. Without this, the reset # can fire BEFORE the push_event task is processed (async via io_pool # + GUI render loop), and the test would falsely pass even if the # reset didn't actually clear anything. for _ in range(40): state = client.get_gui_state() mma = state.get('mma_state', {}) tier1 = mma.get('tier_usage', {}).get('Tier 1', {}) if tier1.get('model') == 'polluted': break time.sleep(0.25) # Trigger the reset client.reset_session() # Poll until the polluted entry is gone for _ in range(40): state = client.get_gui_state() mma = state.get('mma_state', {}) tier1 = mma.get('tier_usage', {}).get('Tier 1', {}) if tier1.get('model') != 'polluted': break time.sleep(0.25) # Verify the polluted entry is gone state = client.get_gui_state() mma = state.get('mma_state', {}) tier1 = mma.get('tier_usage', {}).get('Tier 1', {}) assert tier1.get('model') != 'polluted', ( f"reset_session() did not clear mma_tier_usage: {tier1!r}" ) @pytest.mark.clean_baseline def test_reset_session_clears_mma_status(live_gui) -> None: """reset_session() must set mma_status back to 'idle'.""" client = api_hook_client.ApiHookClient() assert client.wait_for_server(timeout=15) client.push_event('mma_state_update', { 'status': 'running', 'active_tier': 'Tier 1 (Orchestrator)', 'tier_usage': {}, 'tickets': [] }) # Poll for the polluted status to be visible BEFORE the reset for _ in range(40): state = client.get_gui_state() if state.get('mma_status') == 'running': break time.sleep(0.25) client.reset_session() # Poll for the reset to have taken effect. Without this, the # mma_state_update task can fire AFTER the reset, setting status # back to 'running' (race condition surfaced in batched live_gui). for _ in range(40): state = client.get_gui_state() if state.get('mma_status') == 'idle': break time.sleep(0.25) assert state.get('mma_status') == 'idle', ( f"mma_status not reset: {state.get('mma_status')!r}" ) @pytest.mark.clean_baseline def test_reset_session_clears_active_tier(live_gui) -> None: """reset_session() must set active_tier back to None.""" client = api_hook_client.ApiHookClient() assert client.wait_for_server(timeout=15) client.push_event('mma_state_update', { 'status': 'running', 'active_tier': 'Tier 2 (Tech Lead)', 'tier_usage': {}, 'tickets': [] }) # Poll for the polluted active_tier to be visible BEFORE the reset for _ in range(40): state = client.get_gui_state() if state.get('active_tier') == 'Tier 2 (Tech Lead)': break time.sleep(0.25) client.reset_session() # Poll for the reset to have taken effect for _ in range(40): state = client.get_gui_state() if state.get('active_tier') is None: break time.sleep(0.25) assert state.get('active_tier') is None, ( f"active_tier not reset: {state.get('active_tier')!r}" )