项目文件夹

文件
2026-07-13 12:34:57 +08:00

111 行
2.5 KiB
Python

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
"""
0.2.x F2lifespan 关闭阶段自动调用 client.close()
约定:app.clients 中具有可调用 close() 方法的实例,在 worker_stop_hooks
之后、shutdown_hooks 之前由框架兜底关闭;close 异常降为 warning 不阻断。
"""
import pytest
from fastapi.testclient import TestClient
from myboot.core.application import Application
@pytest.fixture
def make_app(tmp_path, monkeypatch):
"""构造禁用自动配置的最小 Application"""
import myboot.core.config as config_module
monkeypatch.setattr(config_module, "_find_project_root", lambda: str(tmp_path))
config_module.reload_config()
def _make():
return Application(name="auto-close-test", auto_configuration=False)
yield _make
config_module.reload_config()
class _ClosableClient:
def __init__(self):
self.closed = 0
def close(self):
self.closed += 1
class _NoCloseClient:
pass
class _FailingClient:
def close(self):
raise RuntimeError("boom")
def _run_lifespan(app: Application) -> None:
"""走一遍完整 lifespanTestClient 上下文进入/退出即 startup/shutdown"""
with TestClient(app.get_fastapi_app()):
pass
def test_client_with_close_is_auto_closed(make_app):
app = make_app()
client = _ClosableClient()
app.clients["closable"] = client
_run_lifespan(app)
assert client.closed == 1
def test_client_without_close_is_skipped(make_app):
app = make_app()
app.clients["no_close"] = _NoCloseClient()
_run_lifespan(app) # 不抛错即通过
def test_failing_close_does_not_block_others(make_app):
app = make_app()
good = _ClosableClient()
# dict 保序:失败的排在前面,验证不阻断后续
app.clients["bad"] = _FailingClient()
app.clients["good"] = good
_run_lifespan(app)
assert good.closed == 1
def test_close_runs_after_worker_stop_hooks_before_shutdown_hooks(make_app):
app = make_app()
order = []
class _OrderClient:
def close(self):
order.append("client_close")
app.clients["order"] = _OrderClient()
app.add_worker_stop_hook(lambda: order.append("worker_stop"))
app.add_shutdown_hook(lambda: order.append("shutdown"))
_run_lifespan(app)
assert order == ["worker_stop", "client_close", "shutdown"]
def test_async_close_supported(make_app):
app = make_app()
closed = []
class _AsyncClient:
async def close(self):
closed.append(True)
app.clients["async"] = _AsyncClient()
_run_lifespan(app)
assert closed == [True]