deer-flow/backend/tests/test_scheduler_shutdown_timeout.py
Nan Gao a684ae2fe3
fix(sandbox): leave SIGTERM/SIGINT teardown to the Gateway lifespan (#7560)
* fix(sandbox): leave SIGTERM/SIGINT teardown to the Gateway lifespan

AIO and E2B providers built on the event-loop thread installed SIGTERM/SIGINT
handlers that force-destroyed every sandbox synchronously, before uvicorn
started the run drain. The Gateway lifespan now holds sandbox shutdown
ownership from its first step, so providers skip those handlers and the
existing hook closes the provider after run drain and MCP teardown. SIGHUP
keeps the provider fallback. AIO acquire now refuses once shutdown began.

* docs(sandbox): document Gateway-owned sandbox shutdown and AIO admission fence

* test(gateway): inspect the lifespan body now that lifespan wraps it

* docs(changelog): reference #7560 for the sandbox SIGTERM drain-order fix

* ci: retrigger sandbox image smoke after a registry pull timeout
2026-10-26 20:38:32 +08:00

38 lines
1.2 KiB
Python

from __future__ import annotations
import asyncio
import inspect
import logging
from unittest.mock import AsyncMock, MagicMock
import pytest
from fastapi import FastAPI
from app.gateway import app as gateway_app
@pytest.mark.asyncio
async def test_scheduled_task_service_shutdown_is_bounded(monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture) -> None:
async def hang() -> None:
await asyncio.sleep(3600)
app = FastAPI()
service = MagicMock()
service.stop = AsyncMock(side_effect=hang)
app.state.scheduled_task_service = service
monkeypatch.setattr(gateway_app, "_SHUTDOWN_HOOK_TIMEOUT_SECONDS", 0.01)
started = asyncio.get_running_loop().time()
with caplog.at_level(logging.WARNING, logger="app.gateway.app"):
await gateway_app._shutdown_scheduled_task_service(app)
elapsed = asyncio.get_running_loop().time() - started
assert elapsed < 0.5
service.stop.assert_awaited_once()
assert "Scheduled task service shutdown exceeded" in caplog.text
def test_lifespan_uses_bounded_scheduled_task_shutdown() -> None:
source = inspect.getsource(gateway_app._gateway_lifespan)
assert "await _shutdown_scheduled_task_service(app)" in source