test(sim): Add stress test for concurrent MMA tracks
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import pytest
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "src")))
|
||||
|
||||
from src import api_hook_client
|
||||
|
||||
def _poll_mma_workers(client: api_hook_client.ApiHookClient, timeout: int, condition, label: str) -> tuple[bool, dict]:
|
||||
"""Poll get_mma_workers() until condition(workers) is True or timeout."""
|
||||
workers = {}
|
||||
for i in range(timeout):
|
||||
res = client.get_mma_workers() or {}
|
||||
workers = res.get('workers', {})
|
||||
print(f"[SIM][{label}] t={i}s active_workers={list(workers.keys())}")
|
||||
if condition(workers):
|
||||
return True, workers
|
||||
time.sleep(1)
|
||||
return False, workers
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.timeout(600)
|
||||
def test_mma_concurrent_tracks_stress(live_gui) -> None:
|
||||
"""
|
||||
Stress test: Start two tracks concurrently and verify they both progress
|
||||
without crashing the GUI or losing state.
|
||||
"""
|
||||
client = api_hook_client.ApiHookClient()
|
||||
assert client.wait_for_server(timeout=15), "Hook server did not start"
|
||||
|
||||
# 1. Setup mock provider
|
||||
client.set_value('current_provider', 'gemini_cli')
|
||||
client.set_value('gcli_path', f'"{sys.executable}" "{os.path.abspath("tests/mock_gemini_cli.py")}"')
|
||||
client.click('btn_project_save')
|
||||
time.sleep(1.0)
|
||||
|
||||
# 2. Generate two tracks via Epic
|
||||
client.set_value('mma_epic_input', 'STRESS TEST: TRACK A AND TRACK B')
|
||||
client.click('btn_mma_plan_epic')
|
||||
|
||||
# Wait for tracks to be generated and accepted
|
||||
# Note: Epic planning usually generates multiple tracks.
|
||||
start = time.time()
|
||||
while time.time() - start < 60:
|
||||
status = client.get_mma_status()
|
||||
if status.get('proposed_tracks'):
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
client.click('btn_mma_accept_tracks')
|
||||
time.sleep(2.0)
|
||||
|
||||
# 3. Get track IDs
|
||||
status = client.get_mma_status()
|
||||
tracks = status.get('tracks', [])
|
||||
assert len(tracks) >= 2, f"Need at least 2 tracks for stress test, found {len(tracks)}"
|
||||
track_id_a = tracks[0]['id']
|
||||
track_id_b = tracks[1]['id']
|
||||
|
||||
# 4. Start both tracks
|
||||
print(f"[SIM] Starting Track A: {track_id_a}")
|
||||
client.click('btn_mma_load_track', user_data=track_id_a)
|
||||
time.sleep(0.5)
|
||||
client.click('btn_mma_start_track', user_data=track_id_a)
|
||||
|
||||
print(f"[SIM] Starting Track B: {track_id_b}")
|
||||
client.click('btn_mma_load_track', user_data=track_id_b)
|
||||
time.sleep(0.5)
|
||||
client.click('btn_mma_start_track', user_data=track_id_b)
|
||||
|
||||
# 5. Verify workers from both tracks appear
|
||||
# Workers are named "Tier 3 (Worker): <ticket_id>"
|
||||
ok, workers = _poll_mma_workers(client, timeout=30, label="wait-workers",
|
||||
condition=lambda w: any(track_id_a in str(k) for k in w) and any(track_id_b in str(k) for k in w))
|
||||
|
||||
# Note: ticket_id might not contain track_id directly, but the mock epic generator
|
||||
# usually includes some identifier. If not, we just check for multiple Tier 3 workers.
|
||||
if not ok:
|
||||
print("[SIM] WARNING: Could not explicitly find both tracks in worker names, checking for multiple workers instead.")
|
||||
ok, workers = _poll_mma_workers(client, timeout=30, label="wait-multi-workers",
|
||||
condition=lambda w: sum(1 for k in w if "Tier 3" in k) >= 2)
|
||||
|
||||
assert ok, f"Did not see concurrent workers starting. Active: {list(workers.keys())}"
|
||||
|
||||
# 6. Wait for completion
|
||||
print("[SIM] Waiting for all workers to finish...")
|
||||
ok, workers = _poll_mma_workers(client, timeout=120, label="wait-completion",
|
||||
condition=lambda w: all("COMPLETED" in str(v) or "FAILED" in str(v) for v in w.values()) if w else False)
|
||||
|
||||
# Final check: GUI should still be responsive
|
||||
res = client.get_status()
|
||||
assert res.get('status') == 'ok', "GUI crashed during concurrent execution"
|
||||
|
||||
print("[SIM] MMA Concurrent Tracks stress test PASSED.")
|
||||
Reference in New Issue
Block a user