import importlib import os import time import pytest from fastapi.testclient import TestClient from sqlalchemy import select from services.shared.db import get_session from services.shared.sql_models import Interaction, VoiceAISessionRow, VoiceEventRow, WhatsAppMessageRow, WhatsAppThreadRow pytestmark = pytest.mark.skipif( not os.getenv("DATABASE_URL", "").startswith("postgresql"), reason="PostgreSQL readiness smoke requires PostgreSQL", ) def _admin_headers() -> dict[str, str]: return {"X-User": "admin", "X-Role": "admin"} def test_postgres_readiness_smoke(monkeypatch): interaction_module = importlib.import_module("services.interaction_service.app") whatsapp_module = importlib.import_module("services.whatsapp_adapter_service.app") voice_module = importlib.import_module("services.voice_adapter_service.app") runtime_module = importlib.import_module("services.ai_voice_runtime_service.app") monkeypatch.setattr( runtime_module, "_orchestrator_request", lambda method, path, *, payload=None, timeout=10.0: { "session_id": "ais_pg_smoke", "language": "ru", "greeting_text": "Здравствуйте, это AI-оператор.", "disclosure_required": True, }, ) interaction_client = TestClient(interaction_module.app) whatsapp_client = TestClient(whatsapp_module.app) voice_client = TestClient(voice_module.app) runtime_client = TestClient(runtime_module.app) assert interaction_client.get("/health").status_code == 200 assert whatsapp_client.get("/health").status_code == 200 assert voice_client.get("/health").status_code == 200 assert runtime_client.get("/health").status_code == 200 created_interaction = interaction_client.post( "/interactions", json={ "channel": "voice", "subject": "Postgres smoke interaction", "customer_id": None, "queue_id": "q_pg_smoke", "priority": 2, }, headers=_admin_headers(), ) assert created_interaction.status_code == 200 interaction_id = created_interaction.json()["interaction_id"] fetched_interaction = interaction_client.get(f"/interactions/{interaction_id}") assert fetched_interaction.status_code == 200 assert fetched_interaction.json()["interaction_id"] == interaction_id created_whatsapp = whatsapp_client.post( "/integrations/whatsapp/webhook", json={ "chat_id": "pg_smoke_chat", "text": "Привет из PostgreSQL smoke", "external_message_id": "pg_smoke_ext_1", "whatsapp_user_id": "wa_pg_smoke_user", "phone_number": "+77015550077", "display_name": "PG Smoke", "payload": {"provider": "manual"}, }, ) assert created_whatsapp.status_code == 200 thread_id = created_whatsapp.json()["thread_id"] listed_threads = whatsapp_client.get("/integrations/whatsapp/threads", headers=_admin_headers()) assert listed_threads.status_code == 200 assert any(item["thread_id"] == thread_id for item in listed_threads.json()) created_voice_event = voice_client.post( "/integrations/voice/events", headers=_admin_headers(), json={ "event_type": "call.started", "call_id": "pg_smoke_call", "interaction_id": interaction_id, "source_event_id": "pg_smoke_call_started", "payload": {"source": "postgres-smoke"}, }, ) assert created_voice_event.status_code == 200 listed_voice_events = voice_client.get("/integrations/voice/events", headers=_admin_headers()) assert listed_voice_events.status_code == 200 assert any(item["source_event_id"] == "pg_smoke_call_started" for item in listed_voice_events.json()) created_voice_session = runtime_client.post( "/internal/voice-ai/sessions", headers=_admin_headers(), json={ "call_id": "pg_smoke_voice_call", "linked_id": "pg_smoke_linked", "interaction_id": interaction_id, "queue_id": "q_pg_smoke", "caller_number": "+77010000077", "caller_name": "PG Smoke Caller", "agent_profile": "voice_support", "language_hint": "ru", "handoff_queue_id": "q_pg_smoke", "metadata": {"queue_code": "pg_smoke"}, }, ) assert created_voice_session.status_code == 200 voice_session_id = created_voice_session.json()["voice_session_id"] deadline = time.time() + 2.0 while time.time() < deadline: session = get_session() try: voice_session = session.execute( select(VoiceAISessionRow).where(VoiceAISessionRow.session_id == voice_session_id) ).scalar_one_or_none() if voice_session and voice_session.ai_session_id == "ais_pg_smoke": break finally: session.close() time.sleep(0.05) else: raise AssertionError("voice AI session did not finish async startup") session = get_session() try: stored_interaction = session.execute( select(Interaction).where(Interaction.interaction_id == interaction_id) ).scalar_one_or_none() stored_thread = session.execute( select(WhatsAppThreadRow).where(WhatsAppThreadRow.thread_id == thread_id) ).scalar_one_or_none() stored_message = session.execute( select(WhatsAppMessageRow).where(WhatsAppMessageRow.thread_id == thread_id) ).scalar_one_or_none() stored_event = session.execute( select(VoiceEventRow).where(VoiceEventRow.source_event_id == "pg_smoke_call_started") ).scalar_one_or_none() stored_voice_session = session.execute( select(VoiceAISessionRow).where(VoiceAISessionRow.session_id == voice_session_id) ).scalar_one_or_none() assert stored_interaction is not None assert stored_thread is not None assert stored_message is not None assert stored_event is not None assert stored_voice_session is not None assert stored_voice_session.ai_session_id == "ais_pg_smoke" finally: session.close()