"""Словари на реальных ОСВ из ../fixtures: формулировки шапки, блоков сумм, группировок и периодов покрыты. Проверяются сами формулировки файлов (текст ячеек шапки), а не позиции: детекторы Task 4–5 строятся поверх этих же словарей. Фикстуры — регрессия формулировок, не оракул формата. """ from pathlib import Path from typing import cast import pytest from src.contract import FileType from src.parser.grid import Sheet from src.parser.readers import read_book from src.parser.rules import Pattern, Rules, first_match, load_rules HEAD_ROWS = 30 # шапка и заголовок таблицы — в начале листа @pytest.fixture(scope="module") def rules() -> Rules: return load_rules() def _head_texts(sheet: Sheet) -> list[str]: last = min(sheet.n_rows, HEAD_ROWS) return [cell.text for row in range(1, last + 1) for _, cell in sheet.row_cells(row) if cell.kind == "text"] def _after_colon(text: str) -> str: return text.split(":", 1)[1] if ":" in text else text @pytest.mark.fixtures def test_real_osv_phrases_are_covered_by_dictionaries(osv_file: Path, rules: Rules) -> None: sheets, _ = read_book(osv_file.read_bytes(), cast(FileType, osv_file.suffix.lstrip(".").upper())) texts = [text for sheet in sheets for text in _head_texts(sheet)] headers = rules.headers def any_cell(patterns: list[Pattern]) -> bool: return any(pattern.search(text) for text in texts for pattern in patterns) assert any_cell(headers.sheet_markers), "нет заголовка ведомости" for group in headers.groups: assert any_cell(headers.groups[group]), f"нет блока сумм {group}" assert any_cell(headers.debit) assert any_cell(headers.credit) assert any_cell(headers.header_markers["org_params"]) assert any_cell(headers.header_markers["data_type"]) title = next(text for text in texts if any(p.search(text) for p in headers.sheet_markers)) account = next(m for p in headers.header_markers["account"] if (m := p.search(title))) assert account.string[account.end() :].strip()[:1].isdigit() period = next(m for p in headers.header_markers["period"] if (m := p.search(title))) period_text = period.string[period.end() :] assert rules.periods.match(period_text) is not None, title assert rules.periods.year.search(period_text) is not None, title data_type_line = next(t for t in texts if any(p.search(t) for p in headers.header_markers["data_type"])) # T3.9: реальные файлы не спецификация — не фиксируем «BU» и «RUB», проверяем, что словарь значение опознал assert first_match(rules.data_types.header, _after_colon(data_type_line)) is not None currency_line = next(t for t in texts if any(p.search(t) for p in headers.header_markers["currency"])) assert rules.data_types.currency_code(_after_colon(currency_line)) is not None