From 0175759f95887f097ed08e51e6e34c1cdd2eacd8 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Wed, 28 Oct 2026 21:05:55 +0400 Subject: [PATCH] =?UTF-8?q?test(api):=20=D1=84=D0=B8=D0=BA=D1=81=D1=82?= =?UTF-8?q?=D1=83=D1=80=D0=B0=20db=5Fsession=20=D0=B8=20=D1=81=D0=BD=D0=B8?= =?UTF-8?q?=D0=BC=D0=BE=D0=BA=20=D1=81=D1=85=D0=B5=D0=BC=D1=8B=20OpenAPI?= =?UTF-8?q?=20=D1=82=D1=8F=D0=B6=D1=91=D0=BB=D1=8B=D1=85=20GET?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- api/tests/integration/conftest.py | 9 ++- api/tests/integration/test_heavy_get_json.py | 66 ++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 api/tests/integration/test_heavy_get_json.py diff --git a/api/tests/integration/conftest.py b/api/tests/integration/conftest.py index 81cb26d..6c067f5 100644 --- a/api/tests/integration/conftest.py +++ b/api/tests/integration/conftest.py @@ -13,8 +13,15 @@ from src.main import app @pytest.fixture -def client(): +def db_session(): + """Сессия БД теста: её же использует client, всё откатывается в конце теста.""" db_ref = SessionLocal() + yield db_ref + + +@pytest.fixture +def client(db_session): + db_ref = db_session loop_ref = None async def _get_db(): diff --git a/api/tests/integration/test_heavy_get_json.py b/api/tests/integration/test_heavy_get_json.py new file mode 100644 index 0000000..6b16a28 --- /dev/null +++ b/api/tests/integration/test_heavy_get_json.py @@ -0,0 +1,66 @@ +"""AURORA-1848: тяжёлые GET отдают готовый JSON из Postgres. + +Эталон — старый путь (методы репозиториев и сервисов, которыми пользуется выгрузка Excel) +в той же сессии теста. Спека: openspec/changes/aurora-1848-sheet-json/specs/sheet-read-api/spec.md +""" +from types import SimpleNamespace + +import pytest + +from src.main import app + +ADMIN = SimpleNamespace(id=1, role_id=1) # fixture.sql: admin +ISP = SimpleNamespace(id=2, role_id=2) # fixture.sql: isp1, EXECUTOR_DFIP +YEAR = 2026 + +HEAVY_GET_PATHS = [ + "/api/v1/form/{form_id}/sheet/{sheet}", + "/api/v1/svod/1/{sheet}/{year}", + "/api/v1/svod/2/{sheet}/{year}", + "/api/v1/projects/{project_id}/report/{year}/{report_type}", + "/api/v1/rf-rollup/{branch_id}/{year}", + "/api/v1/projects/{project_id}/summary", + "/api/v1/projects/plan-summary", + "/api/v1/projects/fact-summary", + "/api/v1/projects/detail-summary", +] +OPENAPI_REF = "#/components/schemas/BaseListResponse_SheetResponse_" + + +def _payload(rows) -> list[dict]: + """Строки старого пути в том виде, в каком их отдавал BaseListResponse[SheetResponse].""" + return [{"row_type": r[0], "depth": r[1], "sort_order": r[2], "data": r[3]} for r in rows] + + +def old_rows(client, coro_factory) -> list[dict]: + """Вызвать старый метод (coro_factory() -> awaitable list[tuple]) в цикле приложения.""" + async def _go(): + return _payload(await coro_factory()) + + return client.portal.call(_go) + + +def expected_sheet(rows: list[dict]) -> list[dict]: + """Ожидаемый ответ листа и сводов по эталону старого пути. Фаза B (Task 13) меняет тело на strip_nulls.""" + return rows + + +def get_ok(client, url, headers, params=None) -> list[dict]: + r = client.get(url, headers=headers, params=params) + assert r.status_code == 200, r.text + assert r.headers["content-type"] == "application/json" + assert float(r.headers["x-db-time-ms"]) >= 0 + body = r.json() + assert list(body) == ["success", "message", "result", "count"] + assert body["success"] is True and body["message"] is None + assert body["count"] == len(body["result"]) + for row in body["result"]: + assert row.keys() == {"row_type", "depth", "sort_order", "data"} + return body["result"] + + +def test_openapi_heavy_get_schema_unchanged(): + spec = app.openapi() + for path in HEAVY_GET_PATHS: + schema = spec["paths"][path]["get"]["responses"]["200"]["content"]["application/json"]["schema"] + assert schema == {"$ref": OPENAPI_REF}, path