Category overhaul (#20)
Tests / test (push) Successful in 16s
Tests / lint (push) Successful in 16s

Reviewed-on: #20
Co-authored-by: Mathias <mathias2784@web.de>
This commit was merged in pull request #20.
This commit is contained in:
2026-10-27 18:08:10 +00:00
committed by mathias2784
parent 4efef98c9c
commit 6babc560c7
36 changed files with 3021 additions and 176 deletions
+112
View File
@@ -0,0 +1,112 @@
from datetime import date
from decimal import Decimal
from sqlalchemy import inspect
from sqlalchemy.orm import Session
from src.models.category import CategoryBudgetDb, CategoryDb
from tests.factories import CategoryFactory
def test_category_budget_schema(db_session: Session):
inspector = inspect(db_session.get_bind())
columns = {c["name"]: c for c in inspector.get_columns("category_budget")}
assert set(columns) == {
"id",
"category_id",
"amount_eur",
"valid_from",
"valid_to",
}
assert columns["id"]["nullable"] is False
assert columns["category_id"]["nullable"] is False
assert columns["amount_eur"]["nullable"] is False
assert columns["valid_from"]["nullable"] is True
assert columns["valid_to"]["nullable"] is True
assert str(columns["amount_eur"]["type"]) == "NUMERIC(12, 2)"
foreign_keys = inspector.get_foreign_keys("category_budget")
assert len(foreign_keys) == 1
assert foreign_keys[0]["constrained_columns"] == ["category_id"]
assert foreign_keys[0]["referred_table"] == "category"
def test_category_budget_defaults_null_dates(db_session: Session):
category = CategoryDb(name=CategoryFactory.build().name)
db_session.add(category)
db_session.flush()
budget = CategoryBudgetDb(category_id=category.id, amount_eur=Decimal("150.00"))
db_session.add(budget)
db_session.commit()
db_session.refresh(budget)
assert budget.id is not None
assert budget.category_id == category.id
assert budget.amount_eur == Decimal("150.00")
# Null means unbounded in either direction.
assert budget.valid_from is None
assert budget.valid_to is None
def test_category_budget_date_window(db_session: Session):
category = CategoryDb(name=CategoryFactory.build().name)
db_session.add(category)
db_session.flush()
budget = CategoryBudgetDb(
category_id=category.id,
amount_eur=Decimal("200.50"),
valid_from=date(2026, 10, 1),
valid_to=date(2026, 10, 31),
)
db_session.add(budget)
db_session.commit()
db_session.refresh(budget)
assert budget.valid_from == date(2026, 10, 1)
assert budget.valid_to == date(2026, 10, 31)
def test_category_budget_relationship_both_ways(db_session: Session):
category = CategoryDb(name=CategoryFactory.build().name)
db_session.add(category)
db_session.flush()
budget = CategoryBudgetDb(category_id=category.id, amount_eur=Decimal("1.00"))
db_session.add(budget)
db_session.commit()
# Force re-read from the database so the relationship is loaded, not
# just resolved from the session identity map.
db_session.expire_all()
assert [b.id for b in category.budgets] == [budget.id]
assert budget.category.id == category.id
def test_category_has_multiple_budgets(db_session: Session):
category = CategoryDb(name=CategoryFactory.build().name)
db_session.add(category)
db_session.flush()
budgets = [
CategoryBudgetDb(
category_id=category.id,
amount_eur=Decimal("1200.00"),
valid_from=date(2026, 10, 1),
valid_to=date(2026, 10, 31),
),
CategoryBudgetDb(
category_id=category.id,
amount_eur=Decimal("1300.00"),
valid_from=date(2026, 11, 1),
valid_to=date(2026, 11, 30),
),
]
for budget in budgets:
db_session.add(budget)
db_session.commit()
db_session.expire_all()
assert [b.id for b in category.budgets] == [b.id for b in budgets]