Andrey Epifancev bc143ef140 test: golden-снимки, линтер координат, покрытие словарей; make golden-update
- golden_helpers, scripts/golden_update.py (--check), test_golden на 66 ОСВ и синтетике; make golden-update/golden-check/test-fixtures
- тест покрытия словарей (R-22): счётчик срабатываний Pattern; из osv_headers.yaml удалены неиспользуемые service_columns kpp/code/quantity/currency
- синтетика s14–s16 в генераторе и таблице ожиданий; skip вместо падения при отсутствии osv/synthetic (T11.1); комментарий R-21 в s07 (T11.2)
- линтер: предел адреса 10 вместо 4 (T3.1), обращение cell(3, 4) (T3.8); реальные файлы не фиксируют BU/RUB (T3.9)
- README: golden-процесс; отмечены Task 12 и пункты 4.2, 4.3

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-05 15:28:41 +04:00

73 lines
3.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Генератор синтетики: детерминированность, состав набора и читаемость xlsb-писателя (не требует каталога fixtures)."""
import re
import time
from decimal import Decimal
from pathlib import Path
from src.parser.readers import read_book
from tests.synthetic.generate import FILE_NAMES, generate_all
from tests.synthetic.xlsb_writer import write_xlsb
# Сколько ждать между двумя генерациями: у записей zip разрешение метки времени — 2 с
ZIP_TIME_RESOLUTION_SECONDS = 2.1
LONG_TEXT_LENGTH = 400
BIG_ROW = 70000
def test_generation_is_reproducible_byte_for_byte(tmp_path: Path) -> None:
"""Повторный запуск (позже, чем разрешение меток времени) даёт те же байты для каждого файла набора."""
first = generate_all(tmp_path / "first")
time.sleep(ZIP_TIME_RESOLUTION_SECONDS)
second = generate_all(tmp_path / "second")
assert [path.name for path in first] == [path.name for path in second]
for left, right in zip(first, second, strict=True):
assert left.read_bytes() == right.read_bytes(), left.name
def test_generated_set_is_s01_to_s16_with_one_xlsb(tmp_path: Path) -> None:
names = [path.name for path in generate_all(tmp_path)]
assert names == list(FILE_NAMES)
assert [re.match(r"s(\d\d)_", name) is not None for name in names] == [True] * len(names)
assert [name[1:3] for name in names] == [f"{number:02d}" for number in range(1, len(names) + 1)]
assert [name for name in names if name.endswith(".xlsb")] == ["s13_no_outline_indent.xlsb"]
assert sorted(path.name for path in tmp_path.iterdir()) == sorted(names)
def test_xlsb_writer_round_trips_through_the_reader() -> None:
"""Тексты (с ведущими пробелами и кириллицей), числа и несколько листов читаются `src.parser.readers`."""
long_text = "щ" * LONG_TEXT_LENGTH # запись длиннее 127 байт: длина кодируется двумя байтами
data = write_xlsb(
{
"Лист_1": {
(1, 1): "Привет",
(2, 3): Decimal("1234567.89"),
(3, 1): " отступ",
(4, 2): long_text,
(BIG_ROW, 4): 7,
},
"Второй": {(1, 1): "Привет"},
}
)
sheets, warnings = read_book(data, "XLSB")
assert [sheet.name for sheet in sheets] == ["Лист_1", "Второй"]
first = sheets[0]
assert first.text_at(1, 1) == "Привет"
assert first.cell(2, 3).value == Decimal("1234567.89")
assert (first.text_at(3, 1), first.cell(3, 1).indent) == ("отступ", 4)
assert first.text_at(4, 2) == long_text
assert first.cell(BIG_ROW, 4).value == Decimal(7)
assert first.n_rows == BIG_ROW
assert sheets[1].text_at(1, 1) == "Привет"
assert warnings == ["xlsb: outline level недоступен"]
def test_xlsb_writer_is_deterministic() -> None:
cells = {"Лист_1": {(1, 1): "а", (2, 2): Decimal("1.50")}}
assert write_xlsb(cells) == write_xlsb(cells)