"""Модель листа `Sheet`: merged-диапазоны, порядок ячеек строки, пустота, буквы колонок для диагностики.""" import datetime from decimal import Decimal import pytest from src.parser.grid import EMPTY_CELL, Cell, MergedRange, Sheet, column_letter def _sheet( cells: dict[tuple[int, int], Cell], merged: tuple[MergedRange, ...] = (), row_outline: dict[int, int] | None = None, ) -> Sheet: n_rows = max((row for row, _ in cells), default=0) n_cols = max((col for _, col in cells), default=0) return Sheet(name="ОСВ", cells=cells, merged=merged, row_outline=row_outline or {}, n_rows=n_rows, n_cols=n_cols) def test_text_at_inside_merged_range_takes_origin_value() -> None: sheet = _sheet( {(1, 2): Cell("Сальдо на начало периода", "text"), (2, 2): Cell("Дебет", "text")}, merged=(MergedRange(top=1, left=2, bottom=1, right=3),), ) assert sheet.text_at(1, 3) == "Сальдо на начало периода" assert sheet.text_at(1, 2) == "Сальдо на начало периода" assert sheet.cell(1, 3) is EMPTY_CELL assert sheet.merged_origin(1, 3) == (1, 2) assert sheet.merged_span(1, 3) == MergedRange(1, 2, 1, 3) assert sheet.text_at(2, 3) == "" assert sheet.merged_origin(2, 3) == (2, 3) assert sheet.merged_span(2, 3) is None def test_large_merged_range_is_resolved_without_cell_index() -> None: # Диапазон на весь столбец: индекс «ячейка → диапазон» для него не строится, поиск всё равно работает big = MergedRange(top=1, left=1, bottom=1_048_576, right=16_384) sheet = _sheet({(1, 1): Cell("Шапка", "text")}, merged=(big,)) assert sheet.merged_origin(500_000, 10_000) == (1, 1) assert sheet.text_at(1_048_576, 16_384) == "Шапка" def test_row_cells_are_ordered_by_column_and_skip_blank_text() -> None: sheet = _sheet( { (3, 5): Cell(Decimal("10.5"), "number"), (3, 1): Cell("60.01", "text"), (3, 3): Cell(" ", "text"), (4, 1): Cell("62", "text"), } ) assert sheet.row_cells(3) == [(1, Cell("60.01", "text")), (5, Cell(Decimal("10.5"), "number"))] assert sheet.row_cells(7) == [] def test_row_of_spaces_is_empty() -> None: sheet = _sheet({(2, 1): Cell(" ", "text"), (2, 4): Cell("\xa0 ", "text"), (3, 1): Cell(False, "bool")}) assert sheet.is_row_empty(2) assert sheet.is_row_empty(10) assert not sheet.is_row_empty(3) def test_cell_text_strips_and_treats_spaces_as_empty() -> None: assert Cell(" ", "text").text == "" assert Cell(" Контрагент ", "text").text == "Контрагент" assert Cell(None, "empty").text == "" assert EMPTY_CELL.text == "" assert Cell(Decimal("1250.00"), "number").text == "1250.00" assert Cell(datetime.date(2024, 9, 30), "date").text == "2024-09-30" def test_outline_defaults_to_zero() -> None: sheet = _sheet({(1, 1): Cell("62", "text")}, row_outline={2: 1, 3: 2}) assert [sheet.outline(row) for row in (1, 2, 3)] == [0, 1, 2] @pytest.mark.parametrize( ("index", "letter"), [(1, "A"), (26, "Z"), (27, "AA"), (52, "AZ"), (703, "AAA"), (16_384, "XFD")], ) def test_column_letter(index: int, letter: str) -> None: assert column_letter(index) == letter def test_column_letter_rejects_non_positive_index() -> None: with pytest.raises(ValueError, match="1-based"): column_letter(0)