88 lines
2.5 KiB
Python
88 lines
2.5 KiB
Python
"""Unit-тесты services/metrics.py — чистая логика, без БД."""
|
|
from decimal import Decimal
|
|
|
|
import pytest
|
|
|
|
from services.metrics import (
|
|
check_arithmetic,
|
|
normalize_date,
|
|
_to_decimal,
|
|
ClassifyMetrics,
|
|
)
|
|
|
|
|
|
class TestNormalizeDate:
|
|
def test_dd_mm_yyyy(self):
|
|
assert normalize_date("01.01.2025") == "2025-01-01"
|
|
|
|
def test_already_iso(self):
|
|
assert normalize_date("2025-01-01") == "2025-01-01"
|
|
|
|
def test_empty(self):
|
|
assert normalize_date("") is None
|
|
assert normalize_date(None) is None
|
|
|
|
def test_unrecognized_passthrough(self):
|
|
assert normalize_date("01 января 2025") == "01 января 2025"
|
|
|
|
|
|
class TestToDecimal:
|
|
def test_int(self):
|
|
assert _to_decimal("50000") == Decimal("50000")
|
|
|
|
def test_russian_number(self):
|
|
assert _to_decimal("50 000,00") == Decimal("50000.00")
|
|
|
|
def test_nbsp(self):
|
|
assert _to_decimal("1\u00a0000,50") == Decimal("1000.50")
|
|
|
|
def test_none(self):
|
|
assert _to_decimal(None) is None
|
|
assert _to_decimal("") is None
|
|
|
|
def test_garbage(self):
|
|
assert _to_decimal("не число") is None
|
|
|
|
|
|
class TestCheckArithmetic:
|
|
def test_ok(self):
|
|
ops = [{"action": "ADD", "new_row": {"name": "x", "price": 100, "qty": 2, "sum": 200}}]
|
|
assert check_arithmetic(ops) == []
|
|
|
|
def test_mismatch(self):
|
|
ops = [{"action": "ADD", "new_row": {"name": "x", "price": 100, "qty": 2, "sum": 250}}]
|
|
m = check_arithmetic(ops)
|
|
assert len(m) == 1
|
|
assert m[0]["expected_sum"] == 200.0
|
|
assert m[0]["actual_sum"] == 250.0
|
|
assert m[0]["diff"] == 50.0
|
|
|
|
def test_update_values(self):
|
|
ops = [{"action": "UPDATE", "new_values": {"price": 10, "qty": 3, "sum": 30}}]
|
|
assert check_arithmetic(ops) == []
|
|
|
|
def test_skip_incomplete(self):
|
|
# нет qty → пропуск
|
|
ops = [{"action": "ADD", "new_row": {"name": "x", "price": 100, "sum": 200}}]
|
|
assert check_arithmetic(ops) == []
|
|
|
|
def test_skip_non_add_update(self):
|
|
ops = [{"action": "DELETE", "target_hash": "h"}]
|
|
assert check_arithmetic(ops) == []
|
|
|
|
|
|
class TestClassifyMetrics:
|
|
def test_fix_rate(self):
|
|
m = ClassifyMetrics()
|
|
m.record(False)
|
|
m.record(True)
|
|
m.record(False)
|
|
assert m.total == 3
|
|
assert m.fixes == 1
|
|
assert m.fix_rate == pytest.approx(1 / 3)
|
|
|
|
def test_empty(self):
|
|
m = ClassifyMetrics()
|
|
assert m.fix_rate == 0.0
|
|
assert m.summary()["total_classifications"] == 0
|