Fix category budget overlaps and document the uv run test workflow.
This commit is contained in:
@@ -414,6 +414,168 @@ class TestSetCategoryBudgetDateEdgeCases:
|
||||
)
|
||||
assert len(budgets) == 2
|
||||
|
||||
def test_shared_endpoint_is_replaced(self, db_session: Session):
|
||||
"""Inclusive ranges that share a date must not overlap after replacement."""
|
||||
cat = CategoryFactory.build()
|
||||
cat_db = upsert_category(db_session, cat, do_commit=False)
|
||||
db_session.commit()
|
||||
|
||||
set_category_budget(
|
||||
db_session,
|
||||
CategoryBudget(
|
||||
category=cat,
|
||||
amount_eur=100.00,
|
||||
valid_from=date(2026, 10, 1),
|
||||
valid_to=date(2026, 10, 15),
|
||||
),
|
||||
)
|
||||
set_category_budget(
|
||||
db_session,
|
||||
CategoryBudget(
|
||||
category=cat,
|
||||
amount_eur=200.00,
|
||||
valid_from=date(2026, 10, 15),
|
||||
valid_to=date(2026, 10, 31),
|
||||
),
|
||||
)
|
||||
|
||||
budgets = (
|
||||
db_session.query(CategoryBudgetDb)
|
||||
.filter_by(category_id=cat_db.id)
|
||||
.order_by(CategoryBudgetDb.valid_from)
|
||||
.all()
|
||||
)
|
||||
assert [(b.amount_eur, b.valid_from, b.valid_to) for b in budgets] == [
|
||||
(100.00, date(2026, 10, 1), date(2026, 10, 14)),
|
||||
(200.00, date(2026, 10, 15), date(2026, 10, 31)),
|
||||
]
|
||||
|
||||
|
||||
class TestSetCategoryBudgetRegressionCases:
|
||||
def test_new_range_overlapping_multiple_budgets_is_added_once(
|
||||
self, db_session: Session
|
||||
):
|
||||
cat = CategoryFactory.build()
|
||||
cat_db = upsert_category(db_session, cat, do_commit=False)
|
||||
db_session.commit()
|
||||
|
||||
for start, end, amount in (
|
||||
(date(2026, 10, 1), date(2026, 10, 31), 100.00),
|
||||
(date(2026, 12, 1), date(2026, 12, 31), 200.00),
|
||||
):
|
||||
set_category_budget(
|
||||
db_session,
|
||||
CategoryBudget(
|
||||
category=cat,
|
||||
amount_eur=amount,
|
||||
valid_from=start,
|
||||
valid_to=end,
|
||||
),
|
||||
)
|
||||
|
||||
set_category_budget(
|
||||
db_session,
|
||||
CategoryBudget(
|
||||
category=cat,
|
||||
amount_eur=300.00,
|
||||
valid_from=date(2026, 10, 1),
|
||||
valid_to=date(2026, 12, 31),
|
||||
),
|
||||
)
|
||||
|
||||
budgets = (
|
||||
db_session.query(CategoryBudgetDb).filter_by(category_id=cat_db.id).all()
|
||||
)
|
||||
assert len(budgets) == 1
|
||||
assert budgets[0].amount_eur == 300.00
|
||||
assert budgets[0].valid_from == date(2026, 10, 1)
|
||||
assert budgets[0].valid_to == date(2026, 12, 31)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("existing_start", "existing_end", "new_start", "new_end", "expected"),
|
||||
[
|
||||
(
|
||||
date(2026, 10, 1),
|
||||
date(2026, 12, 31),
|
||||
None,
|
||||
date(2026, 11, 15),
|
||||
[
|
||||
(100.00, date(2026, 11, 16), date(2026, 12, 31)),
|
||||
(200.00, None, date(2026, 11, 15)),
|
||||
],
|
||||
),
|
||||
(
|
||||
date(2026, 10, 1),
|
||||
date(2026, 12, 31),
|
||||
date(2026, 11, 15),
|
||||
None,
|
||||
[
|
||||
(100.00, date(2026, 10, 1), date(2026, 11, 14)),
|
||||
(200.00, date(2026, 11, 15), None),
|
||||
],
|
||||
),
|
||||
(
|
||||
None,
|
||||
date(2026, 12, 31),
|
||||
date(2026, 10, 1),
|
||||
date(2026, 10, 31),
|
||||
[
|
||||
(100.00, None, date(2026, 9, 30)),
|
||||
(200.00, date(2026, 10, 1), date(2026, 10, 31)),
|
||||
(100.00, date(2026, 11, 1), date(2026, 12, 31)),
|
||||
],
|
||||
),
|
||||
(
|
||||
date(2026, 10, 1),
|
||||
None,
|
||||
date(2026, 11, 1),
|
||||
date(2026, 11, 30),
|
||||
[
|
||||
(100.00, date(2026, 10, 1), date(2026, 10, 31)),
|
||||
(200.00, date(2026, 11, 1), date(2026, 11, 30)),
|
||||
(100.00, date(2026, 12, 1), None),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_partially_unbounded_ranges_are_split_correctly(
|
||||
self,
|
||||
db_session: Session,
|
||||
existing_start: date | None,
|
||||
existing_end: date | None,
|
||||
new_start: date | None,
|
||||
new_end: date | None,
|
||||
expected: list[tuple[float, date | None, date | None]],
|
||||
):
|
||||
cat = CategoryFactory.build()
|
||||
cat_db = upsert_category(db_session, cat, do_commit=False)
|
||||
db_session.commit()
|
||||
|
||||
set_category_budget(
|
||||
db_session,
|
||||
CategoryBudget(
|
||||
category=cat,
|
||||
amount_eur=100.00,
|
||||
valid_from=existing_start,
|
||||
valid_to=existing_end,
|
||||
),
|
||||
)
|
||||
set_category_budget(
|
||||
db_session,
|
||||
CategoryBudget(
|
||||
category=cat,
|
||||
amount_eur=200.00,
|
||||
valid_from=new_start,
|
||||
valid_to=new_end,
|
||||
),
|
||||
)
|
||||
|
||||
budgets = (
|
||||
db_session.query(CategoryBudgetDb).filter_by(category_id=cat_db.id).all()
|
||||
)
|
||||
actual = {(float(b.amount_eur), b.valid_from, b.valid_to) for b in budgets}
|
||||
assert actual == set(expected)
|
||||
|
||||
|
||||
class TestFindCategoryDb:
|
||||
"""Test find_category_db function."""
|
||||
|
||||
Reference in New Issue
Block a user