fix(tests): isolate db state and stabilize voice runtime suite
This commit is contained in:
@@ -40,6 +40,8 @@ init_sql_schema()
|
||||
|
||||
|
||||
LOGGER = logging.getLogger("uvicorn.error")
|
||||
_VOICE_START_THREADS_LOCK = threading.Lock()
|
||||
_VOICE_START_THREADS: set[threading.Thread] = set()
|
||||
|
||||
|
||||
def _bool_env(name: str, default: bool) -> bool:
|
||||
@@ -220,6 +222,38 @@ def _orchestrator_request(method: str, path: str, *, payload: dict[str, Any] | N
|
||||
return _request(method, f"{_ai_orchestrator_service_url()}{path}", payload=payload, timeout=timeout)
|
||||
|
||||
|
||||
def _register_voice_start_thread(thread: threading.Thread) -> None:
|
||||
with _VOICE_START_THREADS_LOCK:
|
||||
_VOICE_START_THREADS.add(thread)
|
||||
|
||||
|
||||
def _unregister_voice_start_thread(thread: threading.Thread) -> None:
|
||||
with _VOICE_START_THREADS_LOCK:
|
||||
_VOICE_START_THREADS.discard(thread)
|
||||
|
||||
|
||||
def _run_voice_start_thread(session_id: str, start_payload: VoiceAIStartIn) -> None:
|
||||
current = threading.current_thread()
|
||||
try:
|
||||
_complete_voice_ai_session_start(session_id, start_payload)
|
||||
finally:
|
||||
_unregister_voice_start_thread(current)
|
||||
|
||||
|
||||
def _wait_for_background_voice_start_threads(timeout_seconds: float = 5.0) -> None:
|
||||
deadline = time.monotonic() + max(timeout_seconds, 0.0)
|
||||
while True:
|
||||
with _VOICE_START_THREADS_LOCK:
|
||||
threads = [thread for thread in _VOICE_START_THREADS if thread.is_alive()]
|
||||
if not threads:
|
||||
return
|
||||
remaining = max(deadline - time.monotonic(), 0.0)
|
||||
if remaining <= 0:
|
||||
return
|
||||
for thread in threads:
|
||||
thread.join(timeout=min(0.25, remaining))
|
||||
|
||||
|
||||
def _bridge_request(method: str, path: str, *, payload: dict[str, Any] | None = None, timeout: float = 10.0) -> dict[str, Any]:
|
||||
return _request(method, f"{_asterisk_bridge_service_url()}{path}", payload=payload, timeout=timeout)
|
||||
|
||||
@@ -1381,12 +1415,14 @@ def create_voice_ai_session(
|
||||
metadata=payload_metadata,
|
||||
)
|
||||
if should_start_async:
|
||||
threading.Thread(
|
||||
target=_complete_voice_ai_session_start,
|
||||
voice_start_thread = threading.Thread(
|
||||
target=_run_voice_start_thread,
|
||||
args=(voice_session.session_id, start_payload),
|
||||
name=f"voice-start-{voice_session.session_id}",
|
||||
daemon=True,
|
||||
).start()
|
||||
)
|
||||
_register_voice_start_thread(voice_start_thread)
|
||||
voice_start_thread.start()
|
||||
return VoiceAISessionOut(
|
||||
voice_session_id=voice_session.session_id,
|
||||
ai_session_id=voice_session.ai_session_id,
|
||||
|
||||
@@ -599,8 +599,19 @@ def init_sql_schema() -> None:
|
||||
if schema_management_mode() == "migrations":
|
||||
validate_schema_migrations_applied()
|
||||
elif not _BASE_SCHEMA_INITIALIZED:
|
||||
Base.metadata.create_all(bind=engine)
|
||||
_BASE_SCHEMA_INITIALIZED = True
|
||||
for attempt in range(5):
|
||||
try:
|
||||
Base.metadata.create_all(bind=engine)
|
||||
_BASE_SCHEMA_INITIALIZED = True
|
||||
break
|
||||
except OperationalError as exc:
|
||||
message = str(exc).lower()
|
||||
if "already exists" in message:
|
||||
_BASE_SCHEMA_INITIALIZED = True
|
||||
break
|
||||
if "database is locked" not in message or attempt >= 4:
|
||||
raise
|
||||
time.sleep(0.25 * (attempt + 1))
|
||||
for attempt in range(5):
|
||||
try:
|
||||
_apply_runtime_schema_compatibility()
|
||||
|
||||
Reference in New Issue
Block a user