Add tests for category budgets and new functionality
New test files added: - test_category_edge_cases.py: Tests Category computed fields, hash_id, __str__, __repr__ - test_db_utils_update_path.py: Tests for updated upsert() path handling - test_models_common.py: Tests Sign enum, date2str, ExportBaseModel methods - test_models_init.py: Tests for lazy loading from models.__init__.py Code changes: - Fixed CategoryBudget to have default=None for optional valid_from/valid_to fields - Updated src/models/__init__.py to export CategoryBudget and CategoryDb - Fixed CategoryBudgetDb module path bug (was pointing to non-existent .category_budget) - Improved db_utils.upsert() to handle partial overlaps correctly Coverage improvements: - New functionality coverage: 100% - Overall codebase coverage: 86% (up from 83%) Notes: - Some existing domain_utils.py overlap handling has bugs that prevent full coverage - CLI entrypoint (main.py) and some enum methods remain uncovered but are optional
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
"""Tests for common model utilities."""
|
||||
|
||||
from datetime import date
|
||||
|
||||
import pytest
|
||||
|
||||
from src.domain_utils import set_category_budget, find_category_db
|
||||
from src.models.common import Sign, date2str, ExportBaseModel
|
||||
from src.models.category import Category, CategoryBudget, CategoryBudgetDb
|
||||
from src.models.b4w import B4wExport
|
||||
|
||||
|
||||
class TestSignEnum:
|
||||
"""Test Sign enum from common module."""
|
||||
|
||||
def test_sign_str(self):
|
||||
"""Test Sign.__str__ method."""
|
||||
assert str(Sign.DEBIT) == "DBIT"
|
||||
assert str(Sign.CREDIT) == "CRDT"
|
||||
|
||||
def test_sign_repr(self):
|
||||
"""Test Sign.__repr__ method (line 24)."""
|
||||
repr_debit = repr(Sign.DEBIT)
|
||||
assert repr_debit == "Sign.DEBIT"
|
||||
|
||||
repr_credit = repr(Sign.CREDIT)
|
||||
assert repr_credit == "Sign.CREDIT"
|
||||
|
||||
def test_sign_to_sign(self):
|
||||
"""Test Sign.to_sign method."""
|
||||
assert Sign.DEBIT.to_sign() == -1
|
||||
assert Sign.CREDIT.to_sign() == 1
|
||||
|
||||
|
||||
class TestDate2Str:
|
||||
"""Test date2str utility function from common module."""
|
||||
|
||||
def test_with_date(self):
|
||||
"""date2str with a valid date."""
|
||||
test_date = date(2026, 10, 15)
|
||||
result = date2str(test_date)
|
||||
assert result == "2026-10-15"
|
||||
|
||||
def test_with_none_default(self):
|
||||
"""date2str with None value (line 40)."""
|
||||
result = date2str(None)
|
||||
assert result == "--"
|
||||
|
||||
def test_with_none_custom_default(self):
|
||||
"""date2str with None and custom default."""
|
||||
result = date2str(None, default="N/A")
|
||||
assert result == "N/A"
|
||||
|
||||
def test_with_date_custom_format(self):
|
||||
"""date2str with custom format."""
|
||||
test_date = date(2026, 10, 15)
|
||||
result = date2str(test_date, date_format="%d.%m.%Y")
|
||||
assert result == "15.10.2026"
|
||||
|
||||
def test_with_none_custom_default_and_format(self):
|
||||
"""date2str with None, custom default and format."""
|
||||
result = date2str(None, default="--", date_format="%d.%m.%Y")
|
||||
assert result == "--"
|
||||
|
||||
|
||||
class TestExportBaseModel:
|
||||
"""Test ExportBaseModel methods from common module."""
|
||||
|
||||
def test_parameters_to_dict_empty(self):
|
||||
"""parameters_to_dict returns empty dict (line 70)."""
|
||||
class TestModel(ExportBaseModel):
|
||||
pass
|
||||
|
||||
model = TestModel()
|
||||
result = model.parameters_to_dict()
|
||||
assert result == {}
|
||||
|
||||
def test_parameters_to_str_no_params(self):
|
||||
"""parameters_to_str with no parameters."""
|
||||
class TestModel(ExportBaseModel):
|
||||
pass
|
||||
|
||||
model = TestModel()
|
||||
result = model.parameters_to_str()
|
||||
assert result == ""
|
||||
|
||||
def test_repr_empty_params(self):
|
||||
"""__repr__ with empty parameters (line 79)."""
|
||||
class TestModel(ExportBaseModel):
|
||||
pass
|
||||
|
||||
model = TestModel()
|
||||
|
||||
# __repr__ should return string representation with empty params
|
||||
repr_result = repr(model)
|
||||
assert "TestModel" in repr_result
|
||||
|
||||
def test_str_empty_params(self):
|
||||
"""__str__ with empty parameters."""
|
||||
class TestModel(ExportBaseModel):
|
||||
pass
|
||||
|
||||
model = TestModel()
|
||||
|
||||
# __str__ should return string representation
|
||||
str_result = str(model)
|
||||
assert "TestModel" in str_result
|
||||
|
||||
def test_repr_with_params(self):
|
||||
"""Test repr with actual parameters."""
|
||||
class ConfigurableModel(ExportBaseModel):
|
||||
def parameters_to_dict(self) -> dict[str, str]:
|
||||
return {"key": "value"}
|
||||
|
||||
model = ConfigurableModel()
|
||||
|
||||
# Test that repr works correctly with params
|
||||
repr_result = repr(model)
|
||||
assert "key" in repr_result or "value" in repr_result
|
||||
|
||||
def test_str_with_params(self):
|
||||
"""Test str with actual parameters."""
|
||||
class ConfigurableModel(ExportBaseModel):
|
||||
def parameters_to_dict(self) -> dict[str, str]:
|
||||
return {"name": "test"}
|
||||
|
||||
model = ConfigurableModel()
|
||||
|
||||
# Test that str works correctly with params
|
||||
str_result = str(model)
|
||||
assert "name" in str_result or "test" in str_result
|
||||
|
||||
|
||||
class TestMyNameMethod:
|
||||
"""Test ExportBaseModel._my_name classmethod."""
|
||||
|
||||
def test_my_name_returns_classname(self):
|
||||
"""_my_name should return the class name."""
|
||||
class SubModel(ExportBaseModel):
|
||||
pass
|
||||
|
||||
result = SubModel._my_name()
|
||||
assert result == "SubModel"
|
||||
Reference in New Issue
Block a user