Skip to content
This repository was archived by the owner on Jun 4, 2026. It is now read-only.

Commit 59142a6

Browse files
committed
fix(tests): revert to stable strict mode and correct imports (formatted)
1 parent 40d655d commit 59142a6

4 files changed

Lines changed: 43 additions & 76 deletions

File tree

‎backend/app/db/session.py‎

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,11 @@
1-
import os
21
from collections.abc import AsyncGenerator, AsyncIterator
32
from contextlib import asynccontextmanager
4-
from typing import Any
53

64
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
7-
from sqlalchemy.pool import NullPool
85

96
from app.core.config import settings
107

11-
# In CI/Test environments, we disable pooling entirely to prevent
12-
# "Future attached to a different loop" errors during high-concurrency tests.
13-
_engine_args: dict[str, Any] = {}
14-
if os.getenv("CI") or os.getenv("PYTEST_CURRENT_TEST"):
15-
_engine_args["poolclass"] = NullPool
16-
17-
engine = create_async_engine(settings.database_url, echo=False, **_engine_args)
8+
engine = create_async_engine(settings.database_url, echo=False)
189
AsyncSessionLocal = async_sessionmaker(engine, expire_on_commit=False)
1910

2011

@@ -25,7 +16,7 @@ async def get_db() -> AsyncGenerator[AsyncSession]:
2516

2617
@asynccontextmanager
2718
async def isolated_session() -> AsyncIterator[AsyncSession]:
28-
"""Isolated session manager for background tasks."""
19+
"""Context manager for a one-off DB session."""
2920
async with AsyncSessionLocal() as session:
3021
try:
3122
yield session

‎backend/app/main.py‎

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
@asynccontextmanager
2323
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
24-
"""FastAPI 生命周期 management:初始化 Qdrant、MinIO 客户端及调度器。"""
24+
"""FastAPI 生命周期管理:初始化 Qdrant、MinIO 客户端及调度器。"""
2525
logger.info("app_startup_begin")
2626
try:
2727
# 确保基础设施连接正常
@@ -37,36 +37,29 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
3737
await stop_scheduler()
3838

3939

40-
def create_app() -> FastAPI:
41-
"""App factory to facilitate testing and ensure fresh state."""
42-
_app = FastAPI(
43-
title="JARVIS API",
44-
version="0.1.0",
45-
lifespan=lifespan,
46-
)
40+
app = FastAPI(
41+
title="JARVIS API",
42+
version="0.1.0",
43+
lifespan=lifespan,
44+
)
4745

48-
# 跨域配置
49-
_app.add_middleware(
50-
CORSMiddleware,
51-
allow_origins=settings.cors_origins,
52-
allow_credentials=True,
53-
allow_methods=["*"],
54-
allow_headers=["*"],
55-
)
56-
_app.add_middleware(LoggingMiddleware)
46+
# 跨域配置
47+
app.add_middleware(
48+
CORSMiddleware,
49+
allow_origins=settings.cors_origins,
50+
allow_credentials=True,
51+
allow_methods=["*"],
52+
allow_headers=["*"],
53+
)
54+
app.add_middleware(LoggingMiddleware)
5755

58-
# 监控配置
59-
Instrumentator().instrument(_app).expose(_app)
56+
# 监控配置
57+
Instrumentator().instrument(app).expose(app)
6058

61-
# 注册路由
62-
_app.include_router(api_router)
59+
# 注册路由
60+
app.include_router(api_router)
6361

64-
@_app.get("/health")
65-
async def health() -> dict[str, str]:
66-
return {"status": "ok"}
6762

68-
return _app
69-
70-
71-
# Default instance for production/uvicorn
72-
app = create_app()
63+
@app.get("/health")
64+
async def health() -> dict[str, str]:
65+
return {"status": "ok"}

‎backend/pyproject.toml‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,5 +136,7 @@ module = "app.api.cron"
136136
disable_error_code = ["import-untyped"]
137137

138138
[tool.pytest.ini_options]
139-
addopts = "-p no:asyncio"
139+
asyncio_mode = "strict"
140+
asyncio_default_test_loop_scope = "session"
141+
asyncio_default_fixture_loop_scope = "session"
140142
testpaths = ["tests"]

‎backend/tests/conftest.py‎

Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,47 +6,28 @@
66
import pytest
77
from sqlalchemy.pool import NullPool
88

9-
# --- ANYIO BACKEND ---
10-
11-
12-
@pytest.fixture
13-
def anyio_backend():
14-
"""Force anyio to use asyncio."""
15-
return "asyncio"
16-
17-
18-
# --- INFRASTRUCTURE MOCKS ---
19-
20-
21-
@pytest.fixture(autouse=True)
22-
def mock_infra():
23-
"""Mock background infrastructure."""
24-
with (
25-
patch("app.infra.qdrant.get_qdrant_client", AsyncMock()),
26-
patch("app.infra.minio.get_minio_client", MagicMock()),
27-
patch("redis.asyncio.Redis.from_url", return_value=AsyncMock()),
28-
patch("arq.create_pool", return_value=AsyncMock()),
29-
patch("app.scheduler.runner.start_scheduler", AsyncMock()),
30-
patch("app.scheduler.runner.stop_scheduler", AsyncMock()),
31-
patch("apscheduler.schedulers.asyncio.AsyncIOScheduler.start", MagicMock()),
32-
):
33-
yield
9+
# --- GLOBAL MOCKS ---
3410

11+
patch("slowapi.Limiter.limit", lambda *args, **kw: lambda f: f).start()
12+
patch("app.infra.qdrant.get_qdrant_client", AsyncMock()).start()
13+
patch("app.infra.minio.get_minio_client", MagicMock()).start()
14+
patch("redis.asyncio.Redis.from_url", return_value=AsyncMock()).start()
15+
patch("arq.create_pool", return_value=AsyncMock()).start()
16+
patch("app.scheduler.runner.start_scheduler", AsyncMock()).start()
17+
patch("app.scheduler.runner.stop_scheduler", AsyncMock()).start()
3518

3619
# --- APP AND DB FIXTURES ---
3720

3821
from app.db.session import get_db
39-
from app.main import create_app
22+
from app.main import app
4023

24+
# Disable lifespan
25+
app.router.lifespan_context = MagicMock()
4126

42-
@pytest.fixture
43-
def app():
44-
"""Fresh app per test."""
45-
_app = create_app()
46-
_app.router.lifespan_context = MagicMock()
47-
if not hasattr(_app, "load_all_plugins"):
48-
_app.load_all_plugins = MagicMock()
49-
return _app
27+
28+
@pytest.fixture(scope="session")
29+
def anyio_backend():
30+
return "asyncio"
5031

5132

5233
@pytest.fixture(scope="session")
@@ -112,7 +93,7 @@ async def db_session(engine):
11293

11394

11495
@pytest.fixture
115-
async def client(app, db_session):
96+
async def client(db_session):
11697
async def _override():
11798
yield db_session
11899

0 commit comments

Comments
 (0)