Files
finance/backend/tests/test_budget.py
T
k1nq d582f15681 feat: add tests for import polling, login page, money utilities, and query keys
- Implemented tests for import polling functionality, ensuring correct behavior for active and terminal import statuses.
- Created tests for the login page, validating user interactions and error handling.
- Added unit tests for money utility functions, covering parsing, formatting, and summation of monetary values.
- Developed tests for query keys, ensuring proper scoping and invalidation roots.

chore: setup testing environment and configuration

- Added setup file for testing with Vitest and Testing Library.
- Configured TypeScript and Vite for the frontend project.
- Created Docker and Nginx configurations for local development and production environments.
- Established a PostgreSQL initialization script for setting up application roles.

docs: add README and environment configuration for infra

- Included README for the infra directory, detailing local development setup and service descriptions.
- Created an example environment configuration file for local development with placeholders for sensitive information.
2026-09-14 09:26:33 +05:00

101 lines
3.5 KiB
Python

from datetime import date
import pytest
from sqlalchemy.orm import Session
from app.budgeting.models import PeriodClosure
from app.budgeting.service import (
close_period,
compute_period_state,
copy_previous_month,
create_category,
get_or_create_period,
reopen_period,
set_assignments,
)
from app.errors import AppError
from app.identity.models import User
from app.ledger.service import create_account, create_expense
from app.spaces.service import create_space
def seed(db: Session):
user = User(email="budget@example.test", password_hash="unused-in-domain-tests")
db.add(user)
db.flush()
return user, create_space(db, user=user, name="Budget")
def test_uncategorized_expense_blocks_close(db: Session) -> None:
user, space = seed(db)
account = create_account(
db,
space_id=space.id,
user=user,
name="Cash",
kind="cash",
opened_on=date(2026, 3, 1),
opening_amount_kopecks=10_000,
)
create_expense(
db,
space_id=space.id,
user=user,
account_id=account.id,
amount_kopecks=1_000,
occurred_on=date(2026, 3, 2),
)
period = get_or_create_period(db, space_id=space.id, year=2026, month=3)
with pytest.raises(AppError) as exc:
close_period(db, space_id=space.id, user=user, period=period)
assert exc.value.code == "BUDGET_CLOSE_BLOCKED"
def test_copy_previous_month_clamps_expected_day(db: Session) -> None:
from app.budgeting.service import add_expected_income
user, space = seed(db)
category = create_category(db, space_id=space.id, user=user, kind="monthly", name="Food")
january = get_or_create_period(db, space_id=space.id, year=2026, month=1)
february = get_or_create_period(db, space_id=space.id, year=2026, month=2)
set_assignments(
db,
space_id=space.id,
user=user,
period=january,
items=[{"category_id": category.id, "planned_kopecks": 30_000}],
)
add_expected_income(
db,
space_id=space.id,
user=user,
period=january,
expected_on=date(2026, 1, 31),
amount_kopecks=100_000,
source_name="Salary",
)
result = copy_previous_month(db, space_id=space.id, user=user, period=february)
assert result == {"assignments_copied": 1, "expected_incomes_copied": 1}
state = compute_period_state(db, space_id=space.id, period=february)
assert state["expected_incomes"][0]["expected_on"] == "2026-02-28"
def test_reopen_preserves_immutable_closure_history(db: Session) -> None:
user, space = seed(db)
period = get_or_create_period(db, space_id=space.id, year=2026, month=4)
close_period(db, space_id=space.id, user=user, period=period)
reopen_period(db, space_id=space.id, user=user, period=period)
close_period(db, space_id=space.id, user=user, period=period)
assert db.query(PeriodClosure).filter_by(period_id=period.id).count() == 2
def test_periods_must_close_in_calendar_order(db: Session) -> None:
user, space = seed(db)
august = get_or_create_period(db, space_id=space.id, year=2026, month=8)
september = get_or_create_period(db, space_id=space.id, year=2026, month=9)
with pytest.raises(AppError) as exc:
close_period(db, space_id=space.id, user=user, period=september)
assert exc.value.code == "BUDGET_CLOSE_OUT_OF_ORDER"
close_period(db, space_id=space.id, user=user, period=august)
close_period(db, space_id=space.id, user=user, period=september)