from __future__ import annotations from decimal import Decimal import pytest from quartermaster import service from quartermaster.models import Section def test_add_and_total(db): service.add_entry(db, Section.income, "Paycheck", Decimal("2500.00")) service.add_entry(db, Section.income, "Side gig", Decimal("250.50")) entries = service.list_entries(db, Section.income) assert [e.name for e in entries] == ["Paycheck", "Side gig"] assert service.section_total(entries) == Decimal("2750.50") def test_delete_entry(db): entry = service.add_entry(db, Section.other, "One-off", Decimal("10.00")) service.delete_entry(db, entry.id) assert service.list_entries(db, Section.other) == [] def test_set_debt_target(db): dm = service.add_entry(db, Section.debt_minimum, "Card A", Decimal("50.00")) target = service.set_debt_target(db, dm.id) assert target.debt_minimum_id == dm.id assert target.entry is not None and target.entry.name == "Card A" def test_debt_target_rejects_non_debt_minimum(db): income = service.add_entry(db, Section.income, "Paycheck", Decimal("1.00")) with pytest.raises(ValueError): service.set_debt_target(db, income.id) def test_debt_target_cleared_on_delete(db): dm = service.add_entry(db, Section.debt_minimum, "Card B", Decimal("75.00")) service.set_debt_target(db, dm.id) service.delete_entry(db, dm.id) target = service.get_debt_target(db) assert target.debt_minimum_id is None def test_update_entry_name_only(db): entry = service.add_entry(db, Section.subscription, "Twitch", Decimal("10.99")) updated = service.update_entry(db, entry.id, name="Twitch Prime") assert updated is not None assert updated.name == "Twitch Prime" assert updated.amount == Decimal("10.99") def test_update_entry_amount_only(db): entry = service.add_entry(db, Section.subscription, "Twitch", Decimal("10.99")) updated = service.update_entry(db, entry.id, amount=Decimal("11.99")) assert updated is not None assert updated.name == "Twitch" assert updated.amount == Decimal("11.99") def test_update_entry_notes_set_and_clear(db): entry = service.add_entry(db, Section.other, "Parking", Decimal("25.00")) updated = service.update_entry(db, entry.id, notes="work") assert updated is not None assert updated.notes == "work" updated = service.update_entry(db, entry.id, notes="") assert updated is not None assert updated.notes is None service.update_entry(db, entry.id, notes="work again") updated = service.update_entry(db, entry.id, notes=None) assert updated is not None assert updated.notes is None def test_update_entry_all_three_atomic(db): entry = service.add_entry(db, Section.food, "Groceries", Decimal("400.00")) updated = service.update_entry( db, entry.id, name="Groceries (Costco)", amount=Decimal("450.00"), notes="weekly run", ) assert updated is not None assert updated.name == "Groceries (Costco)" assert updated.amount == Decimal("450.00") assert updated.notes == "weekly run" def test_update_entry_notes_untouched_when_sentinel(db): entry = service.add_entry( db, Section.other, "Gift", Decimal("25.00"), notes="birthday" ) updated = service.update_entry(db, entry.id, amount=Decimal("30.00")) assert updated is not None assert updated.notes == "birthday" def test_update_entry_missing_returns_none(db): assert service.update_entry(db, 9999, name="Whatever") is None def test_update_entry_does_not_mutate_existing_month_snapshot(db): from quartermaster import month_service entry = service.add_entry( db, Section.subscription, "Twitch", Decimal("10.99") ) month = month_service.create_month(db, "2026-04") me = next(e for e in month.entries if e.source_entry_id == entry.id) assert me.planned == Decimal("10.99") assert me.origin_planned == Decimal("10.99") assert me.name == "Twitch" assert me.origin_name == "Twitch" service.update_entry( db, entry.id, name="Twitch Prime", amount=Decimal("11.99") ) db.refresh(me) assert me.planned == Decimal("10.99") assert me.origin_planned == Decimal("10.99") assert me.name == "Twitch" assert me.origin_name == "Twitch"