- uv(workspace) + pnpm monorepo;docker-compose(pg+api+web) - SQLAlchemy 16 MVP 表 + Alembic 初版迁移(无漂移,users stub) - FastAPI 骨架:统一错误信封(带 request_id) + structlog + /jobs/:id + OpenAPI - Next.js 骨架:纸感主题 token + OpenAPI→TS 客户端代码生成(gen:api) - CI(ruff/mypy/pytest + pg service + alembic 漂移校验) - 四份设计规格(PRODUCT/UX/ARCHITECTURE/DEV_PLAN) + CLAUDE.md
56 lines
1.7 KiB
Python
56 lines
1.7 KiB
Python
"""集成测试:GET /jobs/:id 走真实 DB(无 DB 时跳过)。
|
||
|
||
get_sessionmaker 缓存的 async engine 绑定首个事件循环;pytest-asyncio 每个
|
||
测试用新循环,故每个 DB 测试清缓存以在当前循环上重建 engine,并在结束 dispose。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from collections.abc import AsyncIterator
|
||
|
||
import httpx
|
||
import pytest
|
||
from sqlalchemy import text
|
||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
||
from ww_db import get_sessionmaker
|
||
from ww_db.models import Job
|
||
|
||
|
||
@pytest.fixture
|
||
async def sm() -> AsyncIterator[async_sessionmaker[AsyncSession]]:
|
||
get_sessionmaker.cache_clear()
|
||
maker = get_sessionmaker()
|
||
try:
|
||
async with maker() as probe:
|
||
await probe.execute(text("select 1"))
|
||
except Exception:
|
||
pytest.skip("postgres not reachable")
|
||
yield maker
|
||
await maker.kw["bind"].dispose()
|
||
get_sessionmaker.cache_clear()
|
||
|
||
|
||
async def test_get_job_returns_row(
|
||
client: httpx.AsyncClient, sm: async_sessionmaker[AsyncSession]
|
||
) -> None:
|
||
async with sm() as s:
|
||
job = Job(kind="style_learn", status="running", progress=42)
|
||
s.add(job)
|
||
await s.commit()
|
||
job_id = job.id
|
||
resp = await client.get(f"/jobs/{job_id}")
|
||
assert resp.status_code == 200
|
||
body = resp.json()
|
||
assert body["kind"] == "style_learn"
|
||
assert body["progress"] == 42
|
||
|
||
|
||
async def test_get_missing_job_returns_envelope(
|
||
client: httpx.AsyncClient, sm: async_sessionmaker[AsyncSession]
|
||
) -> None:
|
||
resp = await client.get("/jobs/00000000-0000-0000-0000-000000000000")
|
||
assert resp.status_code == 404
|
||
err = resp.json()["error"]
|
||
assert err["code"] == "NOT_FOUND"
|
||
assert err["request_id"]
|