From f2324b15d37a6d3587ea02463f76a6c6b48f9dd3 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Mon, 5 Oct 2026 23:18:57 +0400 Subject: [PATCH] =?UTF-8?q?test(mapping):=20=D0=B0=D0=B2=D1=82=D0=BE=D0=B7?= =?UTF-8?q?=D0=B0=D0=BC=D0=B5=D0=BD=D0=B0=20=D0=BD=D0=B5=D1=81=D0=BA=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D0=BA=D0=B8=D1=85=20=D1=84=D0=B0=D0=B9=D0=BB=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=BE=D0=B4=D0=BD=D0=B8=D0=BC=20=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D1=8B=D0=BC=20=E2=80=94=20N=20=D1=81=D0=BE=D0=B1=D1=8B=D1=82?= =?UTF-8?q?=D0=B8=D0=B9,=20version=5Fno=20=3D=20max=20+=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ревью T8.1: новый файл с ОСВ 60/62/66 заменяет три прежних файла с version_no 1, 3, 2 по возрастанию id (максимум в середине порядка блокировки) → три FILE_REPLACE, version_no = 4. Мутации `locked[0]` / `locked[-1]` вместо max тест ловит. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2 --- api/tests/test_result_mapping.py | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/api/tests/test_result_mapping.py b/api/tests/test_result_mapping.py index 0f76fa8..8905423 100644 --- a/api/tests/test_result_mapping.py +++ b/api/tests/test_result_mapping.py @@ -980,3 +980,43 @@ async def test_one_event_per_replaced_file(committed_sessionmaker: async_session assert "OSV 60 2025 Q1 (CONFIRMED)" in event.description assert "OSV 62 2025 Q1 (NEEDS_CHECK)" in event.description assert "confirmed=true" in event.description + + +async def test_new_file_supersedes_several_files(committed_sessionmaker: async_sessionmaker[AsyncSession]): + """Один новый файл (ОСВ 60, 62, 66) заменяет три разных прежних файла: событие `FILE_REPLACE` на каждый заменённый + файл, у нового `version_no` = МАКСИМУМ заменённых + 1. Версии раздаются по возрастанию `id` как 1, 3, 2: максимум — + в середине порядка блокировки, поэтому ни первый, ни последний заблокированный файл его не дают.""" + maker, storage = committed_sessionmaker, FakeStorage() + entity_id = await _entity(maker) + old_ids = [ + await _old_file(maker, entity_id, [(account, 2025, PeriodQuarter.Q1, DocumentStatus.RECOGNIZED)]) + for account in ("60", "62", "66") + ] + ordered = sorted(old_ids) + versions = dict(zip(ordered, (1, 3, 2), strict=True)) + async with maker() as session, session.begin(): + for file_id, version_no in versions.items(): + file = await session.get(StoredFile, file_id) + assert file is not None + file.version_no = version_no + result = _minimal() + for number, account in enumerate(("62", "66"), start=2): + sheet = copy.deepcopy(_osv(result)) | {"locator": f"sheet:Лист_{number}"} + sheet["header"]["account_code_raw"] = account + result["documents"].append(sheet) + async with maker() as session: + entity = await session.get(LegalEntity, entity_id) + new_id, _, _ = await _prepare(maker, storage, result, entity=entity) + + assert await process_once(maker, storage, _settings()) is True + + expected_version = 4 # max(1, 3, 2) + 1 + for old_id in old_ids: + old = await _file(maker, old_id) + assert (old.status, old.superseded_by_file_id) == (FileStatus.SUPERSEDED, new_id) + assert (await _file(maker, new_id)).version_no == expected_version + events = await _replace_events(maker) + assert sorted(event.object_id for event in events) == ordered # type: ignore[type-var] + for event in events: + assert event.user_id is None + assert event.after_value == {"file_id": str(new_id), "version_no": expected_version}