import numpy as np from fastapi.testclient import TestClient from vlm.webconsole.demo1.agent_server import create_agent_app class FakeFrame: is_mock = True is_connected = False def get_latest_frame(self): return np.zeros((48, 64, 3), dtype=np.uint8) class FakeMotion: def run(self, cmd, cancel=None): yield {"type": "step", "id": "motion", "status": "running", "title": "Moving"} yield {"type": "nav", "distance_remaining": 0.3} yield {"type": "step", "id": "motion", "status": "done"} yield {"type": "answer", "text": "Done.", "state": "UNKNOWN"} def estop(self): pass class FakeAction: def run(self, act, cancel=None): yield {"type": "step", "id": "action", "status": "running", "title": act["vi"]} yield {"type": "step", "id": "action", "status": "done"} yield {"type": "answer", "text": "ok", "state": "UNKNOWN"} class FakeNavLoop: def run(self, command, cancel=None): yield {"type": "step", "id": "vla", "status": "running", "title": "Bước 1"} yield {"type": "answer", "text": "đã tới", "state": "YES"} def _drain(ws): events = [] while True: m = ws.receive_json() events.append(m) if m["type"] in ("answer", "error"): return events def test_root_html(): r = TestClient(create_agent_app(FakeFrame())).get("/") assert r.status_code == 200 and " VLA loop (VLM).""" client = TestClient(create_agent_app(FakeFrame(), motion=FakeMotion(), action=FakeAction(), navloop=FakeNavLoop())) with client.websocket_connect("/ws") as ws: ws.send_json({"command": "đi tới cái bình nước rồi rẽ phải, gặp ghế thì dừng"}) events = _drain(ws) assert any(e.get("id") == "vla" for e in events if e["type"] == "step") assert events[-1]["type"] == "answer" and events[-1]["state"] == "YES" def test_natural_command_without_navloop_errors(): client = TestClient(create_agent_app(FakeFrame(), motion=FakeMotion())) with client.websocket_connect("/ws") as ws: ws.send_json({"command": "đi tới cái ghế"}) m = ws.receive_json() assert m["type"] == "error" and "VLM nav" in m["message"] class FakeAnn: label = "bottle" def target_box(self): return (1000, 100, 1100, 300) def frame_height(self): return 720 def frame_width(self): return 1280 def test_debug_endpoint_metrics(): client = TestClient(create_agent_app(FakeFrame(), motion=FakeMotion(), annotator=FakeAnn(), navloop=FakeNavLoop())) d = client.get("/debug").json() assert d["target_detected"] is True assert d["label"] == "bottle" assert d["yolo_gap_px"] == 420 # 720 - 300 assert d["center_offset_deg"] is not None and d["center_offset_deg"] > 0 def test_debug_endpoint_no_target(): d = TestClient(create_agent_app(FakeFrame())).get("/debug").json() assert d["target_detected"] is False assert d["yolo_gap_px"] is None def test_estop_returns_halt_message(): client = TestClient(create_agent_app(FakeFrame(), motion=FakeMotion())) with client.websocket_connect("/ws") as ws: ws.send_json({"action": "estop"}) m = ws.receive_json() assert m["type"] == "error" and "EMERGENCY" in m["message"]