feat(api): implement phase 4 headless refinement and verification
This commit is contained in:
117
tests/test_headless_simulation.py
Normal file
117
tests/test_headless_simulation.py
Normal file
@@ -0,0 +1,117 @@
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
from src.api_hook_client import ApiHookClient
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mma_track_lifecycle_simulation():
|
||||
"""
|
||||
This test simulates the sequence of API calls an external orchestrator
|
||||
would make to manage an MMA track lifecycle via the Hook API.
|
||||
It verifies that ApiHookClient correctly routes requests to the
|
||||
corresponding endpoints in src/api_hooks.py.
|
||||
"""
|
||||
|
||||
client = ApiHookClient("http://localhost:8999")
|
||||
|
||||
with patch('requests.get') as mock_get, patch('requests.post') as mock_post:
|
||||
# --- PHASE 1: Initialization & Discovery ---
|
||||
|
||||
# Mock successful status check
|
||||
mock_get.return_value.status_code = 200
|
||||
mock_get.return_value.json.return_value = {"status": "ok"}
|
||||
assert client.get_status()["status"] == "ok"
|
||||
|
||||
# Mock project state retrieval
|
||||
mock_get.return_value.json.return_value = {"project": {"name": "test_project"}}
|
||||
project = client.get_project()
|
||||
assert project["project"]["name"] == "test_project"
|
||||
|
||||
# --- PHASE 2: Track Planning & Initialization ---
|
||||
|
||||
# Inject some files into context for the AI to work with
|
||||
mock_post.return_value.status_code = 200
|
||||
mock_post.return_value.json.return_value = {"status": "queued"}
|
||||
inject_data = {"files": ["src/app_controller.py", "tests/test_basic.py"]}
|
||||
res = client.inject_context(inject_data)
|
||||
assert res["status"] == "queued"
|
||||
mock_post.assert_called_with("http://localhost:8999/api/context/inject", json=inject_data, headers={}, timeout=5.0)
|
||||
|
||||
# --- PHASE 3: Worker Spawn & Execution ---
|
||||
|
||||
# Spawn a worker to start a ticket
|
||||
spawn_data = {
|
||||
"track_id": "track_20260311",
|
||||
"ticket_id": "TKT-001",
|
||||
"role": "tier3-worker",
|
||||
"prompt": "Implement the new logging feature"
|
||||
}
|
||||
res = client.spawn_mma_worker(spawn_data)
|
||||
assert res["status"] == "queued"
|
||||
mock_post.assert_called_with("http://localhost:8999/api/mma/workers/spawn", json=spawn_data, headers={}, timeout=5.0)
|
||||
|
||||
# --- PHASE 4: DAG Mutation & Dependency Management ---
|
||||
|
||||
# Add a second ticket that depends on the first one
|
||||
dag_mutation = {
|
||||
"action": "add_ticket",
|
||||
"ticket": {
|
||||
"id": "TKT-002",
|
||||
"deps": ["TKT-001"],
|
||||
"role": "tier4-qa",
|
||||
"prompt": "Verify the logging feature"
|
||||
}
|
||||
}
|
||||
res = client.mutate_mma_dag(dag_mutation)
|
||||
assert res["status"] == "queued"
|
||||
mock_post.assert_called_with("http://localhost:8999/api/mma/dag/mutate", json=dag_mutation, headers={}, timeout=5.0)
|
||||
|
||||
# --- PHASE 5: Monitoring & Status Polling ---
|
||||
|
||||
# Poll for MMA status
|
||||
mock_get.return_value.json.return_value = {
|
||||
"mma_status": "running",
|
||||
"active_tickets": ["TKT-001"],
|
||||
"active_tier": "Tier 3",
|
||||
"tracks": [{"id": "track_20260311", "status": "active"}]
|
||||
}
|
||||
mma_status = client.get_mma_status()
|
||||
assert mma_status["mma_status"] == "running"
|
||||
assert "TKT-001" in mma_status["active_tickets"]
|
||||
|
||||
# Check worker stream status
|
||||
mock_get.return_value.json.return_value = {
|
||||
"workers": {
|
||||
"TKT-001": {"status": "running", "output": "Starting work..."}
|
||||
}
|
||||
}
|
||||
workers = client.get_mma_workers()
|
||||
assert workers["workers"]["TKT-001"]["status"] == "running"
|
||||
|
||||
# --- PHASE 6: Human-in-the-Loop Interaction ---
|
||||
|
||||
# Mock a tool approval request
|
||||
# In a real scenario, this would block until a POST to /api/ask/respond occurs
|
||||
mock_post.return_value.json.return_value = {"status": "ok", "response": True}
|
||||
approved = client.request_confirmation("run_powershell", {"script": "ls -Recurse"})
|
||||
assert approved is True
|
||||
|
||||
# --- PHASE 7: Completion & Cleanup ---
|
||||
|
||||
# Mock completion status
|
||||
mock_get.return_value.json.return_value = {
|
||||
"mma_status": "idle",
|
||||
"active_tickets": [],
|
||||
"tracks": [{"id": "track_20260311", "status": "completed"}]
|
||||
}
|
||||
final_status = client.get_mma_status()
|
||||
assert final_status["mma_status"] == "idle"
|
||||
assert len(final_status["active_tickets"]) == 0
|
||||
|
||||
# Reset session to clean up
|
||||
client.reset_session()
|
||||
# Verify reset click was pushed
|
||||
mock_post.assert_called_with("http://localhost:8999/api/gui", json={"action": "click", "item": "btn_reset", "user_data": None}, headers={}, timeout=5.0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import asyncio
|
||||
asyncio.run(test_mma_track_lifecycle_simulation())
|
||||
Reference in New Issue
Block a user