"""Реестр парсеров отчётов: какой файл достаётся какому парсеру (plan §2). Проверка выглядит тривиальной, но ловит ровно ту ошибку, которую больше не поймает никто: универсальный CSV узнаёт файл по набору колонок, то есть по определению шире любого брокерского формата, и достаточно поставить его в списке первым, чтобы отчёт Сбера тихо уехал в чужой парсер. Порядок в `PARSERS` — это поведение, а не оформление. """ from __future__ import annotations from pathlib import Path import pytest from fintracker.sources.reports import registry FIXTURES = Path(__file__).resolve().parents[1] / "fixtures" / "reports" CASES = [ (FIXTURES / "sber" / "S930W42_11022026_17092026.html", "report_sber"), (FIXTURES / "sber" / "S930W42_01082026_31082026.html", "report_sber"), (FIXTURES / "vtb" / "reportd0235f1e-55ac-fdc8-5c7b-279e4fe26e2b.xlsx", "report_vtb"), (FIXTURES / "vtb" / "report8ef8347a-2deb-e880-959a-d9a8ca12f735.xlsx", "report_vtb"), (next((FIXTURES / "snowball").glob("*.csv")), "csv"), ] @pytest.mark.parametrize("path,expected", CASES, ids=lambda v: getattr(v, "name", v)) def test_pick_routes_each_fixture_to_its_parser(path: Path, expected: str) -> None: chosen = registry.pick(path.read_bytes(), path.name) assert chosen is not None, f"{path.name}: ни один парсер не узнал файл" assert chosen.name == expected def test_every_registered_parser_is_reachable_by_name() -> None: assert set(registry.names()) == {"report_sber", "report_vtb", "csv"} for name in registry.names(): assert registry.get(name).name == name def test_unknown_format_is_a_none_not_an_exception() -> None: """`pick` возвращает None, и роутер превращает это в 415 — не в 500.""" assert registry.pick(b"%PDF-1.4\n%\xe2\xe3\xcf\xd3\n", "statement.pdf") is None assert registry.pick(b"", "empty.txt") is None def test_parser_metadata_is_filled_in() -> None: """`parser_name` и `parser_version` уходят в `raw_report_file` — пустых быть не должно.""" for parser in registry.PARSERS: assert parser.name and parser.broker and parser.version assert parser.formats def test_csv_is_last_so_it_cannot_shadow_a_broker_format() -> None: names = registry.names() assert names.index("csv") == len(names) - 1