Правки финального ревью фазы 1: M-1, M-2, M-5

- M-1: поиск по перекрытым полям contract_summary (ооо лютик, дс-2, дс-1) и неперекрытому (70), явные assert для строки 910004
- M-2: в лог db_time вместо значений поиска и фильтров пишутся признаки has_*/search_len; unit-тест на caplog
- M-5: total читается по имени столбца total_count

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-10-28 22:12:14 +04:00
parent 100dba4a90
commit 782a04381b
4 changed files with 101 additions and 9 deletions

View File

@ -188,10 +188,11 @@ async def get_summary2(
"sort_by": sort_by,
"sort_direction": sort_direction,
"view": view,
"search": search,
"smeta_type": smeta_type,
"smeta_direction": smeta_direction,
"org_name": org_name,
"has_search": bool(search),
"search_len": len(search or ""),
"has_smeta_type": bool(smeta_type),
"has_smeta_direction": bool(smeta_direction),
"has_org_name": bool(org_name),
"user": current_user.id,
}
logger.info(f"db_time: {extra}", extra=extra)
@ -244,10 +245,11 @@ async def get_summary2_facets(
"sheet": sheet,
"year": year,
"org_ids": org_ids,
"search": search,
"smeta_type": smeta_type,
"smeta_direction": smeta_direction,
"org_name": org_name,
"has_search": bool(search),
"search_len": len(search or ""),
"has_smeta_type": bool(smeta_type),
"has_smeta_direction": bool(smeta_direction),
"has_org_name": bool(org_name),
"user": current_user.id,
}
logger.info(f"db_time: {extra}", extra=extra)

View File

@ -136,7 +136,7 @@ class SummaryRepository:
},
)
).all()
total = int(result[0][4]) if result else 0
total = int(result[0].total_count) if result else 0
return [tuple(row[:4]) for row in result], total
async def get_svod2_facets(

View File

@ -18,6 +18,7 @@ SMETA_TYPE_VARIANT = SMETA_TYPE.lower() + " "
SEARCHES = [
"аРЕНДА", "ё", "Ё", "ромаш", "лютик", "ромашка плюс", "1234.5", "1234.50", " аренда ",
" ", "1", ",", "777.25", "<cd_id>", "я" * 5000,
"ооо лютик", "дс-2", "дс-1", "70",
]
FILTERS = {
@ -140,6 +141,35 @@ def test_search_matches_screen_oracle(client, svod2_data, headers, sheet, search
assert all(t == len(expected) for t in totals)
# Строка 910004 имеет contract_summary (ООО Лютик, ДС-2, 70), но НЕ имеет contract_detail:
# пустая секция contract_detail перекрывает counterparty/reference из contract_summary
# (семантика экрана), а неперекрытое поле total_amount остаётся доступным для поиска.
LINE_NO_DETAIL = 910004
@pytest.mark.parametrize("sheet", SHEETS)
@pytest.mark.parametrize("search", ["ооо лютик", "дс-2"])
def test_search_overlapped_contract_summary_field_not_found(
client, svod2_data, headers, sheet, search
):
full = legacy_full(client, sheet, headers)
expected = screen_rows(full, search)
assert LINE_NO_DETAIL not in _line_ids(expected)
rows, _ = _all_pages(client, sheet, headers, 3, view="compact", search=search)
assert LINE_NO_DETAIL not in _line_ids(rows)
@pytest.mark.parametrize("sheet", ["GO"])
def test_search_non_overlapped_contract_summary_field_found(
client, svod2_data, headers, sheet
):
full = legacy_full(client, sheet, headers)
expected = screen_rows(full, "70")
assert LINE_NO_DETAIL in _line_ids(expected)
rows, _ = _all_pages(client, sheet, headers, 3, view="compact", search="70")
assert LINE_NO_DETAIL in _line_ids(rows)
@pytest.mark.parametrize("sheet", SHEETS)
@pytest.mark.parametrize("name", list(FILTERS))
def test_filters_match_screen_oracle(client, svod2_data, headers, sheet, name):

View File

@ -0,0 +1,60 @@
"""Лог db_time не содержит значений поиска и фильтров (только признаки)."""
import logging
from types import SimpleNamespace
import pytest
from fastapi import Response
from src.api.v1 import summary as summary_api
SECRET = "СЕКРЕТНЫЙ-ПОИСК-123"
FILTERS = {"smeta_type": "СЕКРЕТ-ТИП", "smeta_direction": "СЕКРЕТ-НАПР", "org_name": "СЕКРЕТ-ОРГ"}
class _FakeService:
def __init__(self, db):
pass
async def get_svod2_page(self, **kwargs):
return [], 0
async def get_svod2_facets(self, **kwargs):
return {}
@pytest.fixture(autouse=True)
def _fake_service(monkeypatch):
monkeypatch.setattr(summary_api, "SummaryService", _FakeService)
monkeypatch.setattr(summary_api, "Svod2Facets", lambda **kw: kw, raising=False)
def _assert_no_values(caplog):
records = [r for r in caplog.records if getattr(r, "type", None) == "db_time"]
assert records
for r in records:
text = r.getMessage() + repr(r.__dict__)
for value in (SECRET, *FILTERS.values()):
assert value not in text
assert r.has_search is True
assert r.search_len == len(SECRET)
assert r.has_smeta_type and r.has_smeta_direction and r.has_org_name
@pytest.mark.asyncio
async def test_page_log_has_no_search_values(caplog):
caplog.set_level(logging.INFO)
await summary_api.get_summary2(
sheet="GO", year=2031, response=Response(), search=SECRET, db=None,
current_user=SimpleNamespace(id=1), view="compact", **FILTERS,
)
_assert_no_values(caplog)
@pytest.mark.asyncio
async def test_facets_log_has_no_search_values(caplog):
caplog.set_level(logging.INFO)
await summary_api.get_summary2_facets(
sheet="GO", year=2031, response=Response(), search=SECRET, db=None,
current_user=SimpleNamespace(id=1), **FILTERS,
)
_assert_no_values(caplog)