DFiP_Budget_planing/api/tests/integration/test_db_functions_integration.py
2026-05-20 11:18:05 +03:00

333 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import uuid
import pytest
def _pick_form_and_sheet(client, admin_tokens, auth_headers) -> tuple[int, str, str | None]:
response = client.get("/api/v1/form/", headers=auth_headers(admin_tokens))
assert response.status_code == 200
forms = response.json().get("result") or []
for form in forms:
form_id = form.get("id")
form_type = form.get("form_type_code")
if not form_id or not form_type:
continue
sheets_response = client.get(
f"/api/v1/form/{form_id}/sheets",
headers=auth_headers(admin_tokens),
)
if sheets_response.status_code != 200:
continue
sheets = (sheets_response.json().get("result") or {}).get("sheets") or []
if "AHR" not in sheets:
continue
direction = "Support" if form_type == "FORM_1" else None
return int(form_id), "AHR", direction
pytest.skip("В тестовой БД не найдена форма с листом AHR")
def _pick_form_with_sheet(
client, admin_tokens, auth_headers, target_sheet: str, form_type_code: str | None = None
) -> tuple[int, str]:
response = client.get("/api/v1/form/", headers=auth_headers(admin_tokens))
assert response.status_code == 200
forms = response.json().get("result") or []
for form in forms:
form_id = form.get("id")
if not form_id:
continue
if form_type_code and form.get("form_type_code") != form_type_code:
continue
sheets_response = client.get(
f"/api/v1/form/{form_id}/sheets",
headers=auth_headers(admin_tokens),
)
if sheets_response.status_code != 200:
continue
sheets = (sheets_response.json().get("result") or {}).get("sheets") or []
if target_sheet in sheets:
return int(form_id), target_sheet
pytest.skip(f"Не найдена форма с листом {target_sheet}")
def _pick_input_line_id(
client, admin_tokens, auth_headers, form_id: int, sheet: str, direction: str | None
) -> int:
url = f"/api/v1/form/{form_id}/sheet/{sheet}"
params = []
if direction:
params.append(f"direction={direction}")
if params:
url = f"{url}?{'&'.join(params)}"
response = client.get(url, headers=auth_headers(admin_tokens))
assert response.status_code == 200
rows = response.json().get("result") or []
for row in rows:
data = row.get("data") or {}
if row.get("row_type") == "INPUT" and data.get("line_id"):
return int(data["line_id"])
pytest.skip("Не найден INPUT line_id для проверки upd_form_cell")
def test_backend_calls_v_form_view_via_sheet_endpoint(client, admin_tokens, auth_headers):
form_id, sheet, direction = _pick_form_and_sheet(client, admin_tokens, auth_headers)
url = f"/api/v1/form/{form_id}/sheet/{sheet}"
if direction:
url = f"{url}?direction={direction}"
response = client.get(url, headers=auth_headers(admin_tokens))
assert response.status_code == 200
payload = response.json()
assert isinstance(payload.get("result"), list)
if payload["result"]:
first_row = payload["result"][0]
assert "row_type" in first_row
assert "data" in first_row
def test_backend_calls_upd_form_cell_and_maps_sql_validation_error(
client, admin_tokens, auth_headers
):
form_id, sheet, direction = _pick_form_and_sheet(client, admin_tokens, auth_headers)
line_id = _pick_input_line_id(
client, admin_tokens, auth_headers, form_id, sheet, direction
)
url = f"/api/v1/form/{form_id}/sheet/{sheet}/cell"
if direction:
url = f"{url}?direction={direction}"
response = client.patch(
url,
json={"line_id": line_id, "column": "bad.scope", "value": 1},
headers=auth_headers(admin_tokens),
)
# SQL-функция отдает prefixed validation error.
assert response.status_code in (400, 422)
payload = response.json()
assert isinstance(payload, dict)
assert "message" in payload
assert isinstance(payload["message"], str)
def test_backend_calls_form3_add_project_and_add_line_functions(
client, admin_tokens, auth_headers
):
create = client.post(
"/api/v1/projects",
json={"name": f"ITEST_DB_FUNC_{uuid.uuid4().hex[:6]}", "year": 2026, "branch_id": 1},
headers=auth_headers(admin_tokens),
)
assert create.status_code == 200
project_payload = create.json()
assert "project_id" in project_payload
assert "limit_report_id" in project_payload
assert "current_expenses_report_id" in project_payload
project_id = int(project_payload["project_id"])
add_line = client.post(
f"/api/v1/projects/{project_id}/report/2026/LIMIT/line",
json={"expense_item_id": 1},
headers=auth_headers(admin_tokens),
)
assert add_line.status_code == 200
rows = add_line.json()
assert isinstance(rows, list)
def test_forms_validation_invalid_sections_returns_400(client, admin_tokens, auth_headers):
form_id, sheet = _pick_form_with_sheet(client, admin_tokens, auth_headers, "AHR")
response = client.get(
f"/api/v1/form/{form_id}/sheet/{sheet}?sections=bad_section",
headers=auth_headers(admin_tokens),
)
assert response.status_code == 400
payload = response.json()
assert isinstance(payload, dict)
assert "message" in payload
def test_forms_validation_empty_sections_csv_is_ignored(client, admin_tokens, auth_headers):
form_id, sheet, direction = _pick_form_and_sheet(client, admin_tokens, auth_headers)
url = f"/api/v1/form/{form_id}/sheet/{sheet}?sections= , , "
if direction:
url += f"&direction={direction}"
response = client.get(url, headers=auth_headers(admin_tokens))
assert response.status_code == 200
payload = response.json()
assert isinstance(payload.get("result"), list)
def test_forms_validation_direction_required_for_form1_ahr(
client, admin_tokens, auth_headers
):
form_id, sheet = _pick_form_with_sheet(
client, admin_tokens, auth_headers, "AHR", form_type_code="FORM_1"
)
response = client.get(
f"/api/v1/form/{form_id}/sheet/{sheet}",
headers=auth_headers(admin_tokens),
)
assert response.status_code == 400
payload = response.json()
assert isinstance(payload, dict)
assert "message" in payload
assert "direction" in payload["message"].lower()
def test_forms_validation_direction_case_sensitive(client, admin_tokens, auth_headers):
form_id, sheet = _pick_form_with_sheet(
client, admin_tokens, auth_headers, "AHR", form_type_code="FORM_1"
)
response = client.get(
f"/api/v1/form/{form_id}/sheet/{sheet}?direction=support",
headers=auth_headers(admin_tokens),
)
assert response.status_code == 422
def test_forms_validation_direction_forbidden_on_non_form1(
client, admin_tokens, auth_headers
):
form_id, sheet = _pick_form_with_sheet(
client, admin_tokens, auth_headers, "AHR", form_type_code="FORM_2"
)
response = client.get(
f"/api/v1/form/{form_id}/sheet/{sheet}?direction=Support",
headers=auth_headers(admin_tokens),
)
assert response.status_code == 400
payload = response.json()
assert isinstance(payload, dict)
assert "message" in payload
assert "direction" in payload["message"].lower()
def test_form3_validation_invalid_sections_returns_400(client, admin_tokens, auth_headers):
create = client.post(
"/api/v1/projects",
json={"name": f"ITEST_F3_SECT_{uuid.uuid4().hex[:6]}", "year": 2026, "branch_id": 1},
headers=auth_headers(admin_tokens),
)
assert create.status_code == 200
project_id = int(create.json()["project_id"])
response = client.get(
f"/api/v1/projects/{project_id}/report/2026/LIMIT?sections=q1,q5",
headers=auth_headers(admin_tokens),
)
assert response.status_code == 400
payload = response.json()
assert isinstance(payload, dict)
assert "message" in payload
def test_form3_validation_empty_sections_csv_is_ignored(client, admin_tokens, auth_headers):
create = client.post(
"/api/v1/projects",
json={"name": f"ITEST_F3_EMPTY_{uuid.uuid4().hex[:6]}", "year": 2026, "branch_id": 1},
headers=auth_headers(admin_tokens),
)
assert create.status_code == 200
project_id = int(create.json()["project_id"])
response = client.get(
f"/api/v1/projects/{project_id}/report/2026/LIMIT?sections= , , ",
headers=auth_headers(admin_tokens),
)
assert response.status_code == 200
assert isinstance(response.json().get("result"), list)
def test_form3_validation_duplicate_sections_is_allowed(client, admin_tokens, auth_headers):
create = client.post(
"/api/v1/projects",
json={"name": f"ITEST_F3_DUP_{uuid.uuid4().hex[:6]}", "year": 2026, "branch_id": 1},
headers=auth_headers(admin_tokens),
)
assert create.status_code == 200
project_id = int(create.json()["project_id"])
response = client.get(
f"/api/v1/projects/{project_id}/report/2026/LIMIT?sections=q1,q1",
headers=auth_headers(admin_tokens),
)
assert response.status_code == 200
assert isinstance(response.json().get("result"), list)
def test_form3_add_project_invalid_name_returns_422(client, admin_tokens, auth_headers):
# Пробел запрещён regex-правилом AddProjectBody.
response = client.post(
"/api/v1/projects",
json={"name": "INVALID NAME", "year": 2026, "branch_id": 1},
headers=auth_headers(admin_tokens),
)
assert response.status_code == 422
def test_health_and_ready_endpoints(client):
healthz = client.get("/healthz")
assert healthz.status_code == 200
health_payload = healthz.json()
assert health_payload.get("status") == "ok"
readyz = client.get("/readyz")
assert readyz.status_code in (200, 503)
ready_payload = readyz.json()
assert "status" in ready_payload
assert "db_status" in ready_payload
assert "mv_expense_item_tree" in ready_payload
# Тест временно отключён: внутри создаётся пользователь.
# def test_admin_refresh_tree_requires_admin_role(client, admin_tokens, auth_headers):
# admin_resp = client.post(
# "/api/v1/admin/refresh-tree",
# headers=auth_headers(admin_tokens),
# )
# assert admin_resp.status_code in (200, 503)
#
# uname = f"itest_non_admin_{uuid.uuid4().hex[:6]}"
# create_user = client.put(
# "/api/v1/users/",
# json={
# "email": f"{uname}@example.com",
# "username": uname,
# "password": "pass123",
# "full_name": "Integration User",
# "role_id": 2,
# },
# headers=auth_headers(admin_tokens),
# )
# if create_user.status_code != 200:
# pytest.skip("Не удалось создать non-admin пользователя в текущей БД")
#
# login_resp = client.post("/api/v1/auth/login", json={"username": uname, "password": "pass123"})
# if login_resp.status_code != 200:
# pytest.skip("Не удалось залогинить non-admin пользователя в текущей БД")
# user_tokens = login_resp.json()
#
# non_admin_resp = client.post(
# "/api/v1/admin/refresh-tree",
# headers=auth_headers(user_tokens),
# )
# assert non_admin_resp.status_code == 403
#
# # Что бы не было переполнения бд
# deleted = client.delete(
# f"/api/v1/users/{create_user.json()['result']['id']}",
# headers=auth_headers(admin_tokens),
# )
# assert deleted.status_code == 200
def test_admin_refresh_tree_requires_auth(client):
response = client.post("/api/v1/admin/refresh-tree")
assert response.status_code in (401, 403)