from types import SimpleNamespace from fastapi.testclient import TestClient from app.main import create_app from app.models.domain import ToolResult from tests.conftest import signed_body, whatsapp_payload def test_whatsapp_verification_success(test_app): with TestClient(test_app) as client: response = client.get( "/webhooks/whatsapp", params={ "hub.mode": "subscribe", "hub.verify_token": "verify-token", "hub.challenge": "challenge-code", }, ) assert response.status_code == 200 assert response.text == "challenge-code" def test_whatsapp_verification_rejects_wrong_token(test_app): with TestClient(test_app) as client: response = client.get( "/webhooks/whatsapp", params={ "hub.mode": "subscribe", "hub.verify_token": "wrong", "hub.challenge": "challenge-code", }, ) assert response.status_code == 401 def test_post_webhook_validates_signature_and_accepts_text(test_app): payload = whatsapp_payload(message_id="wamid.100", text="I need Aden to Mukalla") body, signature = signed_body(payload, "app-secret") with TestClient(test_app) as client: response = client.post( "/webhooks/whatsapp", content=body, headers={ "content-type": "application/json", "x-hub-signature-256": signature, }, ) conversation = test_app.state.test_container.conversation assert response.status_code == 200 assert response.json() == {"status": "accepted", "messages": 1} assert len(conversation.calls) == 1 assert conversation.calls[0].text == "I need Aden to Mukalla" def test_post_webhook_debug_returns_reply_and_status(test_app): payload = whatsapp_payload(message_id="wamid.200", text="Debug this message") body, signature = signed_body(payload, "app-secret") with TestClient(test_app) as client: response = client.post( "/webhooks/whatsapp/debug", content=body, headers={ "content-type": "application/json", "x-hub-signature-256": signature, }, ) conversation = test_app.state.test_container.conversation assert response.status_code == 200 assert response.json() == { "status": "accepted", "messages": 1, "replies": ["ok"], } assert len(conversation.calls) == 1 def test_post_webhook_rejects_bad_signature(test_app): payload = whatsapp_payload() body, _ = signed_body(payload, "app-secret") with TestClient(test_app) as client: response = client.post( "/webhooks/whatsapp", content=body, headers={ "content-type": "application/json", "x-hub-signature-256": "sha256=bad", }, ) assert response.status_code == 401 def test_admin_routes_require_key(test_app): with TestClient(test_app) as client: rejected = client.post("/admin/seed-info") accepted = client.post("/admin/sync-trips", headers={"X-Admin-Api-Key": "admin-secret"}) assert rejected.status_code == 401 assert accepted.status_code == 200 assert accepted.json() == {"indexed_trips": 3} def test_admin_driver_debug_returns_llm_and_tool_results(settings): class DummyPrimaryProvider: async def chat(self, messages, tools, tool_choice, temperature): return SimpleNamespace( content="Driver debug reply", tool_calls=[ SimpleNamespace( id="call-1", name="add_trip_by_driver", arguments='{"departure":"A","destination":"B","departure_date":"2026-06-20","departure_time":"morning","available_seats":2,"total_seats":4,"price":30}', ) ], ) class DummyConversation: def _tool_registry(self, customer, *, remoteJid, user_mode="driver", current_message=None): class DummyRegistry: async def execute(self, name, arguments): return ToolResult(ok=True, data={"name": name, "arguments": arguments}) return DummyRegistry() container = SimpleNamespace( settings=settings, conversation=DummyConversation(), ai=SimpleNamespace(primary=DummyPrimaryProvider()), ) app = create_app(settings=settings, container=container) with TestClient(app) as client: response = client.post( "/admin/driver-debug", json={"message": "debug driver", "client_number": "967700000002"}, headers={"X-Admin-Api-Key": "admin-secret"}, ) assert response.status_code == 200 payload = response.json() assert payload["llm_response"] == "Driver debug reply" assert payload["tool_calls"][0]["name"] == "add_trip_by_driver" assert payload["tool_results"][0]["result"]["ok"] is True