arm-tabular-parser/api/tests/unit/test_fixtures_helpers.py
Andrey Epifancev af8ec918c5 feat(osv): шапка ОСВ без меток и «Счет: Код / Наименование» (ОСВ 63 Новоросметалл), «Итого» ↔ Σ уровня 0 по нетто
- header: период отдельной ячейкой (periods.yaml: standalone), тип данных отдельной ячейкой («Бухгалтерский»,
  data_types.yaml: standalone_header), организация — хвост названия ведомости; код счёта — из первой строки-счёта
  таблицы (с предупреждением), если «по счету» в шапке нет.
- classify/detect: текст строки может стоять правее колонки текста (колонка задаёт уровень), подписи «Код»/
  «Наименование» (osv_headers.yaml: column_captions) — не группировки, строка сумм без подписи в конце таблицы —
  «Итого» (с предупреждением).
- checks: сумма уровня 0 сверяется с «Итого» по правилу R-21 (свёрнутое сальдо «Итого» — по нетто), как у родителя.
- tests: osv_dkb_files, покрытие словарей включает osv-dkb/**; README.

Refs #11

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
2026-10-06 13:04:31 +04:00

118 lines
4.4 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.

"""Хелперы доступа к ../fixtures: каталог из ARM_KA_FIXTURES, список файлов ОСВ без синтетики, причина skip."""
from pathlib import Path
import pytest
from fixtures_helpers import (
FIXTURES_ENV,
fixtures_skip_reason,
osv_dkb_files,
osv_fixture_files,
resolve_fixtures_dir,
synthetic_files,
synthetic_skip_reason,
)
def test_env_overrides_fixtures_dir(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv(FIXTURES_ENV, str(tmp_path))
assert resolve_fixtures_dir() == tmp_path
def test_default_fixtures_dir_is_sibling_of_repository(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.delenv(FIXTURES_ENV, raising=False)
assert resolve_fixtures_dir() == Path(__file__).resolve().parents[4] / "fixtures"
def test_no_osv_directory_gives_no_files(tmp_path: Path) -> None:
assert osv_fixture_files(tmp_path) == []
assert synthetic_files(tmp_path) == []
def test_synthetic_excluded_and_foreign_files_ignored(tmp_path: Path) -> None:
osv = tmp_path / "osv"
(osv / "rb").mkdir(parents=True)
(osv / "synthetic").mkdir()
(osv / "rb" / "b.xlsx").write_bytes(b"")
(osv / "a.xls").write_bytes(b"")
(osv / "c.xlsb").write_bytes(b"")
(osv / ".DS_Store").write_bytes(b"")
(osv / "notes.txt").write_bytes(b"")
(osv / "synthetic" / "s.xlsx").write_bytes(b"")
(osv / "synthetic" / ".gitkeep").write_bytes(b"")
(osv / "synthetic" / "README.md").write_bytes(b"")
assert osv_fixture_files(tmp_path) == [osv / "a.xls", osv / "c.xlsb", osv / "rb" / "b.xlsx"]
assert synthetic_files(tmp_path) == [osv / "synthetic" / "s.xlsx"]
def test_osv_dkb_files_skip_analysis_reports_and_hidden_files(tmp_path: Path) -> None:
dkb = tmp_path / "osv-dkb"
(dkb / "nm" / "2025-Q3").mkdir(parents=True)
(dkb / "analysis-reports").mkdir()
(dkb / "nm" / "2025-Q3" / "a.xlsx").write_bytes(b"")
(dkb / "nm" / "2025-Q3" / "~$a.xlsx").write_bytes(b"")
(dkb / "nm" / "README.md").write_bytes(b"")
(dkb / "analysis-reports" / "report.xls").write_bytes(b"")
assert osv_dkb_files(tmp_path) == [dkb / "nm" / "2025-Q3" / "a.xlsx"]
assert osv_dkb_files(tmp_path / "нет") == []
def test_hidden_and_excel_lock_files_are_ignored(tmp_path: Path) -> None:
"""T1.1: скрытые файлы и каталоги и lock-файлы Excel (`~$…`, пока книга открыта) — не фикстуры."""
osv = tmp_path / "osv"
(osv / "rb" / ".cache").mkdir(parents=True)
(osv / "synthetic").mkdir()
for path in (
osv / "rb" / "b.xlsx",
osv / "rb" / "~$b.xlsx",
osv / "rb" / ".b.xlsx",
osv / "rb" / ".cache" / "c.xlsx",
osv / "synthetic" / "s.xlsx",
osv / "synthetic" / "~$s.xlsx",
osv / "synthetic" / ".s.xls",
):
path.write_bytes(b"")
assert osv_fixture_files(tmp_path) == [osv / "rb" / "b.xlsx"]
assert synthetic_files(tmp_path) == [osv / "synthetic" / "s.xlsx"]
def test_skip_reason_mentions_env_and_path(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
missing = tmp_path / "нет-такого"
monkeypatch.setenv(FIXTURES_ENV, str(missing))
reason = fixtures_skip_reason()
assert reason is not None
assert FIXTURES_ENV in reason
assert str(missing) in reason
def test_skip_reason_none_when_available(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
(tmp_path / "osv").mkdir()
monkeypatch.setenv(FIXTURES_ENV, str(tmp_path))
assert fixtures_skip_reason() is None
def test_synthetic_skip_reason_when_fixtures_exist_but_synthetic_is_missing_or_empty(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
(tmp_path / "osv").mkdir()
monkeypatch.setenv(FIXTURES_ENV, str(tmp_path))
reason = synthetic_skip_reason()
assert reason is not None
assert "osv" in reason
assert "synthetic" in reason
(tmp_path / "osv" / "synthetic").mkdir()
(tmp_path / "osv" / "synthetic" / "README.md").write_bytes(b"")
assert synthetic_skip_reason() == reason
(tmp_path / "osv" / "synthetic" / "s01.xlsx").write_bytes(b"")
assert synthetic_skip_reason() is None
def test_synthetic_skip_reason_is_none_without_fixtures(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv(FIXTURES_ENV, str(tmp_path / "нет"))
assert synthetic_skip_reason() is None # причину тогда даёт fixtures_skip_reason