arm-core/api/tests/test_domain_statement_checks.py

110 lines
4.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.

"""Проверки §7 ядра (spec statement-checks; design D-4, D-5): перенос проверок tabular-parser."""
from decimal import Decimal
import pytest
from src.domain.statement_checks import (
ChecksReport,
FailedCheck,
cross_form_check,
load_check_rules,
run_checks,
)
D = Decimal
RULES = load_check_rules()
def _lines(*items: tuple[str, dict[int, str | None]]) -> list[tuple[str, dict[int, Decimal | None]]]:
return [(code, {i: (D(v) if v is not None else None) for i, v in values.items()}) for code, values in items]
def test_rules_loaded_from_copy() -> None:
assert RULES.tolerance == D("1")
assert RULES.rounding_tolerance_percent == D("0.001")
relations = [rule.relation for rule in RULES.relations_for("BALANCE")]
assert "1600 = 1100 + 1200" in relations and "1600 = 1700" in relations
income = {rule.relation: rule for rule in RULES.relations_for("INCOME")}
assert income["2100 = 2110 - 2120"].abs_terms == frozenset({"2120"})
assert income["2400 = 2300 - 2410 + 2420"].strict is False
def test_hard_violation() -> None:
report = run_checks(
"BALANCE", _lines(("1100", {0: "300"}), ("1200", {0: "600"}), ("1600", {0: "1000"})), [0], RULES
)
assert FailedCheck(0, "1600 = 1100 + 1200", D("1000"), D("900")) in report.failed
def test_relation_skipped_without_target() -> None:
report = run_checks("BALANCE", _lines(("1110", {0: "5"}), ("1150", {0: "7"})), [0], RULES)
assert report == ChecksReport(failed=(), soft_failed=())
def test_tolerance_one_unit() -> None:
report = run_checks("BALANCE", _lines(("1100", {0: "300"}), ("1200", {0: "600"}), ("1600", {0: "901"})), [0], RULES)
assert all(item.relation != "1600 = 1100 + 1200" for item in report.failed)
def test_abs_terms_minus_sign() -> None:
report = run_checks("INCOME", _lines(("2110", {0: "100"}), ("2120", {0: "-60"}), ("2100", {0: "40"})), [0], RULES)
assert all(item.relation != "2100 = 2110 - 2120" for item in report.failed)
def test_rounding_goes_soft() -> None:
# расхождение 5 при числах ~1 000 000: 5 ≤ 1 000 000 × 0,001 % = 10 → мягкое «в пределах округления»
report = run_checks(
"BALANCE", _lines(("1100", {0: "500000"}), ("1200", {0: "500000"}), ("1600", {0: "1000005"})), [0], RULES
)
assert all(item.relation != "1600 = 1100 + 1200" for item in report.failed)
assert any(item.relation == "1600 = 1100 + 1200" for item in report.soft_failed)
def test_soft_relation_not_hard() -> None:
report = run_checks("INCOME", _lines(("2300", {0: "100"}), ("2410", {0: "20"}), ("2400", {0: "50"})), [0], RULES)
assert all(item.relation != "2400 = 2300 - 2410 + 2420" for item in report.failed)
assert any(item.relation == "2400 = 2300 - 2410 + 2420" for item in report.soft_failed)
def test_sublines_soft() -> None:
report = run_checks("BALANCE", _lines(("1150", {0: "10"}), ("11501", {0: "6"}), ("11502", {0: "2"})), [0], RULES)
assert any(item.relation == "1150 = 11501 + 11502" for item in report.soft_failed)
assert report.failed == ()
def test_per_column_and_null_as_zero() -> None:
report = run_checks(
"BALANCE",
_lines(("1100", {0: "300", 1: None}), ("1200", {0: "600", 1: "5"}), ("1600", {0: "900", 1: "5"})),
[0, 1],
RULES,
)
assert report.failed == ()
@pytest.mark.parametrize(
("b1370", "i2400", "expected"),
[
((D("500"), D("300")), D("200"), None),
(
(D("500"), D("300")),
D("150"),
FailedCheck(-1, "1370 − 1370(начало года) = 2400", D("200"), D("150")),
),
((None, D("300")), D("200"), None),
((D("500"), D("300")), None, None),
],
)
def test_cross_form(
b1370: tuple[Decimal | None, Decimal | None], i2400: Decimal | None, expected: FailedCheck | None
) -> None:
assert cross_form_check(b1370, i2400, D("1")) == expected
def test_broken_rules_file(tmp_path) -> None:
bad = tmp_path / "statement_checks.yaml"
bad.write_text("tolerance: 1\nrelations:\n - {form: BALANCE, relation: '16A0 = 1100', strict: true}\n", "utf-8")
with pytest.raises(ValueError, match="statement_checks.yaml"):
load_check_rules(bad)