77 lines
2.7 KiB
Python
77 lines
2.7 KiB
Python
"""Tests for ledgerly.services.reports (R11)."""
|
|
|
|
from datetime import date
|
|
|
|
import pytest
|
|
|
|
from ledgerly.models import Month
|
|
from ledgerly.money import Money
|
|
from ledgerly.repository import SqliteRepository
|
|
from ledgerly.services import BudgetService, LedgerService, ReportingService
|
|
|
|
|
|
@pytest.fixture()
|
|
def services():
|
|
repo = SqliteRepository(":memory:")
|
|
yield LedgerService(repo), BudgetService(repo), ReportingService(repo)
|
|
repo.close()
|
|
|
|
|
|
def test_monthly_summary_empty(services):
|
|
_ledger, _budgets, reports = services
|
|
summary = reports.monthly_summary(Month(2026, 10))
|
|
assert summary.income == Money(0)
|
|
assert summary.expenses == Money(0)
|
|
assert summary.net == Money(0)
|
|
|
|
|
|
def test_monthly_summary_with_data(services):
|
|
ledger, _budgets, reports = services
|
|
ledger.add_transaction(Money(300000), "Salary", date(2026, 10, 1))
|
|
ledger.add_transaction(Money(50000), "Groceries", date(2026, 10, 5))
|
|
ledger.add_transaction(Money(1), "Rent", date(2026, 9, 5)) # out of month
|
|
summary = reports.monthly_summary(Month(2026, 10))
|
|
assert summary.income == Money(300000)
|
|
assert summary.expenses == Money(50000)
|
|
assert summary.net == Money(250000)
|
|
|
|
|
|
def test_category_totals_ordering(services):
|
|
ledger, _budgets, reports = services
|
|
ledger.add_transaction(Money(300000), "Salary", date(2026, 10, 1))
|
|
ledger.add_transaction(Money(50000), "Groceries", date(2026, 10, 2))
|
|
ledger.add_transaction(Money(90000), "Rent", date(2026, 10, 3))
|
|
totals = reports.category_totals(Month(2026, 10))
|
|
assert [(t.category, t.total) for t in totals] == [
|
|
("Salary", Money(300000)),
|
|
("Rent", Money(90000)),
|
|
("Groceries", Money(50000)),
|
|
]
|
|
|
|
|
|
def test_budget_status_budgeted_and_unbudgeted(services):
|
|
ledger, budgets, reports = services
|
|
budgets.set_budget("Rent", Money(150000))
|
|
ledger.add_transaction(Money(160000), "Rent", date(2026, 10, 1)) # over
|
|
ledger.add_transaction(Money(40000), "Groceries", date(2026, 10, 2)) # unbudgeted
|
|
lines = reports.budget_status(Month(2026, 10))
|
|
by_name = {line.category: line for line in lines}
|
|
assert by_name["Rent"].is_over
|
|
assert by_name["Rent"].remaining == Money(-10000)
|
|
assert by_name["Groceries"].budget == Money(0)
|
|
assert by_name["Groceries"].is_over
|
|
|
|
|
|
def test_budget_status_within_budget(services):
|
|
ledger, budgets, reports = services
|
|
budgets.set_budget("Rent", Money(150000))
|
|
ledger.add_transaction(Money(100000), "Rent", date(2026, 10, 1))
|
|
line = reports.budget_status(Month(2026, 10))[0]
|
|
assert not line.is_over
|
|
assert line.remaining == Money(50000)
|
|
|
|
|
|
def test_budget_status_empty(services):
|
|
_ledger, _budgets, reports = services
|
|
assert reports.budget_status(Month(2026, 10)) == []
|