Updated the tests for the project

This commit is contained in:
JKuijperM 2026-07-24 09:32:11 +02:00
parent 06e61ed1bf
commit ed17d27457
10 changed files with 627 additions and 84 deletions

View File

@ -0,0 +1,31 @@
import pytest
from decimal import Decimal
from expenses.models import Account, Category
@pytest.fixture
def user(db, django_user_model):
return django_user_model.objects.create_user(
username="tester", password="testpass123"
)
@pytest.fixture
def auth_client(client, user):
client.login(username=user.username, password="testpass123")
return client
@pytest.fixture
def account(user):
return Account.objects.create(
owner=user,
name="Cuenta principal",
initial_balance=Decimal("0"),
active=True,
)
@pytest.fixture
def category(user):
return Category.objects.create(owner=user, name="General")

View File

@ -0,0 +1,86 @@
import pytest
from datetime import date
from decimal import Decimal
from expenses.models import Account, Expense, Income, Category
pytestmark = pytest.mark.django_db
def test_current_balance_reflects_income_and_expenses(user, category):
acc = Account.objects.create(owner=user, name="Cuenta", initial_balance=Decimal("100"))
Income.objects.create(owner=user, account=acc, name="Nomina", amount=Decimal("50"), date=date.today())
Expense.objects.create(owner=user, account=acc, category=category, amount=Decimal("30"), date=date.today())
assert acc.current_balance() == Decimal("120")
def test_monthly_balance_returns_12_rows_in_order(user):
acc = Account.objects.create(owner=user, name="Cuenta", initial_balance=Decimal("100"))
data = acc.monthly_balance(2020)
assert len(data) == 12
assert [row["month"] for row in data] == list(range(1, 13))
assert all(row["balance"] == 100.0 for row in data)
def test_monthly_balance_is_cumulative_across_months(user, category):
acc = Account.objects.create(owner=user, name="Cuenta", initial_balance=Decimal("0"))
year = date.today().year - 1 # avoid the current-month live-balance patch
Expense.objects.create(owner=user, account=acc, category=category, amount=Decimal("40"), date=date(year, 3, 1))
Income.objects.create(owner=user, account=acc, name="Extra", amount=Decimal("100"), date=date(year, 6, 1))
data = acc.monthly_balance(year)
assert data[2]["balance"] == -40.0 # month 3
assert data[5]["balance"] == 60.0 # month 6, cumulative
assert data[11]["balance"] == 60.0 # carries forward to December
def test_monthly_balance_respects_year_param_and_excludes_other_years(user, category):
acc = Account.objects.create(owner=user, name="Cuenta", initial_balance=Decimal("0"))
Expense.objects.create(owner=user, account=acc, category=category, amount=Decimal("50"), date=date(2023, 6, 15))
Income.objects.create(owner=user, account=acc, name="Extra", amount=Decimal("30"), date=date(2024, 3, 10))
data_2023 = acc.monthly_balance(2023)
assert data_2023[5]["balance"] == -50.0 # month 6
assert data_2023[11]["balance"] == -50.0
data_2024 = acc.monthly_balance(2024)
assert data_2024[1]["balance"] == -50.0 # month 2: prior year's expense folded into start
assert data_2024[2]["balance"] == -20.0 # month 3: 2024 income applied
def test_monthly_balance_current_month_patched_with_live_balance(user, category):
today = date.today()
acc = Account.objects.create(owner=user, name="Cuenta", initial_balance=Decimal("100"))
Expense.objects.create(owner=user, account=acc, category=category, amount=Decimal("30"), date=today)
data = acc.monthly_balance(today.year)
assert data[today.month - 1]["balance"] == float(acc.current_balance())
def test_monthly_net_returns_income_minus_expense_per_month(user, category):
acc = Account.objects.create(owner=user, name="Cuenta", initial_balance=Decimal("0"))
Income.objects.create(owner=user, account=acc, name="Nomina", amount=Decimal("100"), date=date(2024, 4, 5))
Expense.objects.create(owner=user, account=acc, category=category, amount=Decimal("40"), date=date(2024, 4, 20))
data = acc.monthly_net(2024)
assert len(data) == 12
assert data[3]["net"] == 60.0 # month 4
assert data[0]["net"] == 0.0
def test_balance_until_only_counts_entries_on_or_before_date(user, category):
acc = Account.objects.create(owner=user, name="Cuenta", initial_balance=Decimal("0"))
Expense.objects.create(owner=user, account=acc, category=category, amount=Decimal("20"), date=date(2024, 1, 10))
Expense.objects.create(owner=user, account=acc, category=category, amount=Decimal("5"), date=date(2024, 1, 15))
Income.objects.create(owner=user, account=acc, name="Later", amount=Decimal("50"), date=date(2024, 1, 20))
assert acc.balance_until(date(2024, 1, 15)) == Decimal("-25")

View File

@ -0,0 +1,101 @@
import pytest
from datetime import date
from decimal import Decimal
from django.contrib.messages import get_messages
from django.urls import reverse
from django.utils.text import slugify
from expenses.models import Category, Expense
from expenses.forms import CategoryForm
pytestmark = pytest.mark.django_db
def test_slug_auto_generated_from_name_on_save(user):
cat = Category.objects.create(name="Ropa y Calzado", owner=user)
assert cat.slug == slugify("Ropa y Calzado")
def test_slug_not_overwritten_if_explicitly_set(user):
cat = Category(name="Comida", slug="mi-slug-personalizado", owner=user)
cat.save()
assert cat.slug == "mi-slug-personalizado"
def test_descendant_ids_includes_grandchildren(user):
parent = Category.objects.create(name="A", owner=user)
child = Category.objects.create(name="B", owner=user, parent=parent)
grandchild = Category.objects.create(name="C", owner=user, parent=child)
ids = set(parent.descendant_ids())
assert ids == {parent.pk, child.pk, grandchild.pk}
def test_descendant_ids_can_exclude_self(user):
parent = Category.objects.create(name="A", owner=user)
child = Category.objects.create(name="B", owner=user, parent=parent)
ids = parent.descendant_ids(include_self=False)
assert parent.pk not in ids
assert child.pk in ids
def test_categoryform_parent_persists_on_save(auth_client, user):
other = Category.objects.create(name="Otra", owner=user)
auth_client.post(reverse('category_list'), {'name': 'Nueva', 'parent': str(other.pk)})
created = Category.objects.get(name='Nueva', owner=user)
assert created.parent_id == other.pk
def test_categoryform_excludes_self_and_descendants_from_parent_queryset_when_editing(user):
parent = Category.objects.create(name="P", owner=user)
child = Category.objects.create(name="C", owner=user, parent=parent)
form = CategoryForm(instance=parent, user=user)
queryset_ids = set(form.fields['parent'].queryset.values_list('pk', flat=True))
assert parent.pk not in queryset_ids
assert child.pk not in queryset_ids
def test_categoryform_clean_parent_rejects_self_as_parent(user):
cat = Category.objects.create(name="X", owner=user)
form = CategoryForm(data={'name': 'X', 'parent': str(cat.pk)}, instance=cat, user=user)
assert not form.is_valid()
assert 'parent' in form.errors
def test_categoryform_rejects_descendant_as_parent(user):
parent = Category.objects.create(name="P", owner=user)
child = Category.objects.create(name="C", owner=user, parent=parent)
form = CategoryForm(data={'name': 'P', 'parent': str(child.pk)}, instance=parent, user=user)
assert not form.is_valid()
def test_category_delete_with_expenses_is_protected(auth_client, user, account, category):
Expense.objects.create(owner=user, account=account, category=category, amount=Decimal("10"), date=date.today())
response = auth_client.post(reverse('category_delete', args=[category.pk]))
assert response.status_code == 302
assert Category.objects.filter(pk=category.pk).exists()
messages = [str(m) for m in get_messages(response.wsgi_request)]
assert any("gastos asociados" in m for m in messages)
def test_category_delete_without_expenses_succeeds(auth_client, user):
cat = Category.objects.create(name="Vacia", owner=user)
response = auth_client.post(reverse('category_delete', args=[cat.pk]))
assert response.status_code == 302
assert not Category.objects.filter(pk=cat.pk).exists()

View File

@ -2,7 +2,7 @@ import pytest
from datetime import date from datetime import date
from decimal import Decimal from decimal import Decimal
from django.urls import reverse from django.urls import reverse
from expenses.models import Account, Expense, Category from expenses.models import Expense, Category
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
@ -10,96 +10,73 @@ def test_dashboard_requires_login(client):
url = reverse('dashboard') url = reverse('dashboard')
response = client.get(url) response = client.get(url)
assert response.status_code == 302 assert response.status_code == 302
def test_dashboard_logged_user_can_access(client, django_user_model): def test_dashboard_logged_user_can_access(auth_client):
user = django_user_model.objects.create_user(
username='test',
password='1234'
)
client.login(username='test', password='1234')
url = reverse('dashboard') url = reverse('dashboard')
response = client.get(url) response = auth_client.get(url)
assert response.status_code == 200 assert response.status_code == 200
def test_dashboard_groups_by_category(client, django_user_model): def test_dashboard_groups_by_category(auth_client, user, account):
user = django_user_model.objects.create_user(
username='test',
password='1234'
)
client.login(username='test', password='1234')
food = Category.objects.create(name='Food', owner=user) food = Category.objects.create(name='Food', owner=user)
rent = Category.objects.create(name='Rent', owner=user) rent = Category.objects.create(name='Rent', owner=user)
general_account = Account.objects.create(name='General', owner=user, initial_balance=300, active=True)
Expense.objects.create( Expense.objects.create(
owner=user, owner=user,
category=food, category=food,
amount=Decimal('10'), amount=Decimal('10'),
date=date(2024, 1, 1), date=date(2024, 1, 1),
account=general_account, account=account,
) )
Expense.objects.create( Expense.objects.create(
owner=user, owner=user,
category=food, category=food,
amount=Decimal('5'), amount=Decimal('5'),
date=date(2024, 1, 2), date=date(2024, 1, 2),
account=general_account, account=account,
) )
Expense.objects.create( Expense.objects.create(
owner=user, owner=user,
category=rent, category=rent,
amount=Decimal('20'), amount=Decimal('20'),
date=date(2024, 1, 3), date=date(2024, 1, 3),
account=general_account, account=account,
) )
response = client.get( response = auth_client.get(
reverse('dashboard'), reverse('dashboard'),
{'year': 2024} {'year': 2024}
) )
data = list(response.context['by_category']) data = list(response.context['by_category'])
assert {'category__name': 'Food', 'total': Decimal('15')} in data assert {'category__name': 'Food', 'total': Decimal('15')} in data
assert {'category__name': 'Rent', 'total': Decimal('20')} in data assert {'category__name': 'Rent', 'total': Decimal('20')} in data
def test_dashboard_filters_by_year(client, django_user_model): def test_dashboard_filters_by_year(auth_client, user, account, category):
user = django_user_model.objects.create_user(
username='test',
password='1234'
)
client.login(username='test', password='1234')
cat = Category.objects.create(name='General', owner=user)
general_account = Account.objects.create(name='General', owner=user, initial_balance=300, active=True)
Expense.objects.create( Expense.objects.create(
owner=user, owner=user,
category=cat, category=category,
amount=10, amount=10,
date=date(2023, 5, 1), date=date(2023, 5, 1),
account=general_account, account=account,
) )
Expense.objects.create( Expense.objects.create(
owner=user, owner=user,
category=cat, category=category,
amount=20, amount=20,
date=date(2024, 5, 1), date=date(2024, 5, 1),
account=general_account, account=account,
) )
response = client.get( response = auth_client.get(
reverse('dashboard'), reverse('dashboard'),
{'year': 2024}, {'year': 2024},
) )
by_month = list(response.context['by_month']) chart_data = response.context['chart_data']
totals = [row['total'] for row in by_month] assert len(chart_data) == 12
assert Decimal('20') in totals assert chart_data[4] == 20.0 # May = month 5 -> index 4
assert Decimal('10') not in totals assert sum(chart_data) == 20.0 # only the 2024 expense contributes

View File

@ -0,0 +1,61 @@
import pytest
from datetime import date
from decimal import Decimal
from django.urls import reverse
from expenses.models import Goal, Tag, Income
pytestmark = pytest.mark.django_db
def test_goal_delete_get_does_not_delete(auth_client, user):
goal = Goal.objects.create(owner=user, name="Meta", target_amount=Decimal("100"))
response = auth_client.get(reverse('goal_delete', args=[goal.pk]))
assert response.status_code == 200
assert Goal.objects.filter(pk=goal.pk).exists()
def test_goal_delete_post_deletes(auth_client, user):
goal = Goal.objects.create(owner=user, name="Meta", target_amount=Decimal("100"))
response = auth_client.post(reverse('goal_delete', args=[goal.pk]))
assert response.status_code == 302
assert not Goal.objects.filter(pk=goal.pk).exists()
def test_tag_delete_get_does_not_delete(auth_client, user):
tag = Tag.objects.create(name="Food", owner=user)
response = auth_client.get(reverse('tag_delete', args=[tag.pk]))
assert response.status_code == 200
assert Tag.objects.filter(pk=tag.pk).exists()
def test_tag_delete_post_deletes(auth_client, user):
tag = Tag.objects.create(name="Food", owner=user)
response = auth_client.post(reverse('tag_delete', args=[tag.pk]))
assert response.status_code == 302
assert not Tag.objects.filter(pk=tag.pk).exists()
def test_income_delete_get_does_not_delete(auth_client, user, account):
income = Income.objects.create(owner=user, account=account, name="Nomina", amount=Decimal("500"), date=date.today())
response = auth_client.get(reverse('income_delete', args=[income.pk]))
assert response.status_code == 200
assert Income.objects.filter(pk=income.pk).exists()
def test_income_delete_post_deletes(auth_client, user, account):
income = Income.objects.create(owner=user, account=account, name="Nomina", amount=Decimal("500"), date=date.today())
response = auth_client.post(reverse('income_delete', args=[income.pk]))
assert response.status_code == 302
assert not Income.objects.filter(pk=income.pk).exists()

View File

@ -0,0 +1,42 @@
import pytest
from datetime import date
from decimal import Decimal
from django.urls import reverse
from expenses.models import Expense, Tag
pytestmark = pytest.mark.django_db
def test_tag_filter_with_non_numeric_value_returns_200(auth_client, user, account, category):
Expense.objects.create(owner=user, account=account, category=category, amount=Decimal("10"), date=date.today())
response = auth_client.get(reverse('expense_list'), {'tag': 'abc'})
assert response.status_code == 200
assert len(response.context['page_obj']) == 1
def test_tag_filter_with_valid_tag_returns_only_matching_expenses(auth_client, user, account, category):
tag = Tag.objects.create(name="Food", owner=user)
tagged = Expense.objects.create(owner=user, account=account, category=category, amount=Decimal("10"), date=date.today())
tagged.tags.add(tag)
Expense.objects.create(owner=user, account=account, category=category, amount=Decimal("20"), date=date.today())
response = auth_client.get(reverse('expense_list'), {'tag': str(tag.pk)})
results = list(response.context['page_obj'])
assert results == [tagged]
def test_tag_filter_multiple_tags_are_ORed(auth_client, user, account, category):
tag1 = Tag.objects.create(name="Food", owner=user)
tag2 = Tag.objects.create(name="Transport", owner=user)
expense1 = Expense.objects.create(owner=user, account=account, category=category, amount=Decimal("10"), date=date.today())
expense1.tags.add(tag1)
expense2 = Expense.objects.create(owner=user, account=account, category=category, amount=Decimal("20"), date=date.today())
expense2.tags.add(tag2)
response = auth_client.get(reverse('expense_list'), {'tag': [str(tag1.pk), str(tag2.pk)]})
results = set(response.context['page_obj'])
assert results == {expense1, expense2}

View File

@ -1,41 +1,29 @@
import pytest import pytest
from datetime import date from datetime import date
from decimal import Decimal
from django.urls import reverse from django.urls import reverse
from expenses.models import Expense, Category, Tag, Account from expenses.models import Expense, Tag
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
def test_expense_can_have_tags(client, django_user_model): def test_expense_can_have_tags(user, category, account):
user = django_user_model.objects.create_user(username='test', password='1234')
client.login(username='test', password='1234')
tag = Tag.objects.create(name='Food', owner=user) tag = Tag.objects.create(name='Food', owner=user)
category = Category.objects.create(name='General', owner=user)
general_account = Account.objects.create(name='General', owner=user, initial_balance=300, active=True)
expense = Expense.objects.create( expense = Expense.objects.create(
owner=user, owner=user,
category=category, category=category,
amount=10, amount=10,
date=date.today(), date=date.today(),
account=general_account, account=account,
) )
expense.tags.add(tag) expense.tags.add(tag)
assert expense.tags.count() == 1 assert expense.tags.count() == 1
assert tag in expense.tags.all() assert tag in expense.tags.all()
def test_user_can_create_tag(client, django_user_model): def test_user_can_create_tag(auth_client, user):
user = django_user_model.objects.create_user( response = auth_client.post(
username='test', password='1234'
)
client.login(username='test', password='1234')
response = client.post(
reverse('tag_create'), reverse('tag_create'),
{'name': 'Food'} {'name': 'Food'}
) )
assert Tag.objects.filter(owner=user, name='Food').exists() assert Tag.objects.filter(owner=user, name='Food').exists()

View File

@ -0,0 +1,69 @@
import pytest
from datetime import date
from decimal import Decimal
from django.urls import reverse
from expenses.models import Expense, FuelEntry
pytestmark = pytest.mark.django_db
def _make_fuel_entry(user, account, category, odometer, liters, amount=Decimal("50")):
expense = Expense.objects.create(
owner=user, account=account, category=category, amount=amount, date=date.today()
)
return FuelEntry.objects.create(expense=expense, odometer=odometer, liters=liters)
def test_price_per_liter_divides_amount_by_liters(user, account, category):
entry = _make_fuel_entry(user, account, category, odometer=1000, liters=Decimal("25"), amount=Decimal("50"))
assert entry.price_per_liter() == Decimal("2")
def test_price_per_liter_zero_when_no_liters(user, account, category):
entry = _make_fuel_entry(user, account, category, odometer=1000, liters=Decimal("0"))
assert entry.price_per_liter() == 0
def test_km_since_previous_none_on_first_fillup(user, account, category):
entry = _make_fuel_entry(user, account, category, odometer=1000, liters=Decimal("20"))
assert entry.km_since_previous() is None
def test_km_since_previous_computes_delta_from_prior_entry(user, account, category):
_make_fuel_entry(user, account, category, odometer=1000, liters=Decimal("20"))
second = _make_fuel_entry(user, account, category, odometer=1300, liters=Decimal("24"))
assert second.km_since_previous() == 300
def test_consumption_none_when_no_previous_entry(user, account, category):
entry = _make_fuel_entry(user, account, category, odometer=1000, liters=Decimal("20"))
assert entry.consumption() is None
def test_consumption_computes_liters_per_100km(user, account, category):
_make_fuel_entry(user, account, category, odometer=1000, liters=Decimal("20"))
second = _make_fuel_entry(user, account, category, odometer=1300, liters=Decimal("24"))
assert second.consumption() == Decimal("8")
def test_fuel_create_invalid_post_returns_200_not_500(auth_client):
response = auth_client.post(reverse('fuel_create'), {})
assert response.status_code == 200
assert FuelEntry.objects.count() == 0
def test_fuel_delete_post_cascades_to_fuelentry(auth_client, user, account, category):
entry = _make_fuel_entry(user, account, category, odometer=1000, liters=Decimal("20"))
expense = entry.expense
response = auth_client.post(reverse('fuel_delete', args=[expense.pk]))
assert not Expense.objects.filter(pk=expense.pk).exists()
assert not FuelEntry.objects.filter(pk=entry.pk).exists()

View File

@ -0,0 +1,196 @@
import pytest
from datetime import date
from decimal import Decimal
from expenses.models import Account, Category, Expense, Goal
from expenses.forms import GoalForm
pytestmark = pytest.mark.django_db
def _last_month_date():
today = date.today()
if today.month == 1:
return date(today.year - 1, 12, 15)
return date(today.year, today.month - 1, 15)
def test_payment_goal_sums_expenses_since_start_date(user, account, category):
start = date(2024, 6, 1)
Expense.objects.create(owner=user, account=account, category=category, amount=Decimal("10"), date=date(2024, 5, 1))
Expense.objects.create(owner=user, account=account, category=category, amount=Decimal("20"), date=date(2024, 6, 15))
goal = Goal.objects.create(
owner=user, name="Pago", kind=Goal.KIND_PAYMENT, category=category,
include_subcategories=False, start_date=start, target_amount=Decimal("100"),
)
assert goal.progress() == Decimal("20")
def test_payment_goal_include_subcategories_true_includes_child_expenses(user, account, category):
child = Category.objects.create(owner=user, name="Sub", parent=category)
Expense.objects.create(owner=user, account=account, category=child, amount=Decimal("15"), date=date.today())
goal = Goal.objects.create(
owner=user, name="Pago", kind=Goal.KIND_PAYMENT, category=category,
include_subcategories=True, target_amount=Decimal("100"),
)
assert goal.progress() == Decimal("15")
def test_payment_goal_include_subcategories_false_excludes_child_expenses(user, account, category):
child = Category.objects.create(owner=user, name="Sub", parent=category)
Expense.objects.create(owner=user, account=account, category=child, amount=Decimal("15"), date=date.today())
goal = Goal.objects.create(
owner=user, name="Pago", kind=Goal.KIND_PAYMENT, category=category,
include_subcategories=False, target_amount=Decimal("100"),
)
assert goal.progress() == Decimal("0")
def test_budget_goal_period_month_counts_only_current_month(user, account, category):
Expense.objects.create(owner=user, account=account, category=category, amount=Decimal("30"), date=date.today())
Expense.objects.create(owner=user, account=account, category=category, amount=Decimal("999"), date=_last_month_date())
goal = Goal.objects.create(
owner=user, name="Presupuesto", kind=Goal.KIND_BUDGET, category=category,
period=Goal.PERIOD_MONTH, target_amount=Decimal("100"),
)
assert goal.progress() == Decimal("30")
def test_budget_goal_is_exceeded_true_over_target(user, account, category):
Expense.objects.create(owner=user, account=account, category=category, amount=Decimal("150"), date=date.today())
goal = Goal.objects.create(
owner=user, name="Presupuesto", kind=Goal.KIND_BUDGET, category=category,
period=Goal.PERIOD_MONTH, target_amount=Decimal("100"),
)
assert goal.is_exceeded() is True
def test_budget_goal_is_exceeded_false_under_target(user, account, category):
Expense.objects.create(owner=user, account=account, category=category, amount=Decimal("50"), date=date.today())
goal = Goal.objects.create(
owner=user, name="Presupuesto", kind=Goal.KIND_BUDGET, category=category,
period=Goal.PERIOD_MONTH, target_amount=Decimal("100"),
)
assert goal.is_exceeded() is False
def test_is_exceeded_always_false_for_non_budget_kind(user, account, category):
Expense.objects.create(owner=user, account=account, category=category, amount=Decimal("50"), date=date.today())
goal = Goal.objects.create(
owner=user, name="Pago", kind=Goal.KIND_PAYMENT, category=category, target_amount=Decimal("10"),
)
assert goal.progress() > goal.target_amount
assert goal.is_exceeded() is False
def test_saving_goal_progress_equals_account_balance(user):
acc = Account.objects.create(owner=user, name="Ahorros", initial_balance=Decimal("500"))
goal = Goal.objects.create(
owner=user, name="Ahorro", kind=Goal.KIND_SAVING, account=acc, target_amount=Decimal("1000"),
)
assert goal.progress() == acc.current_balance()
def test_bar_width_caps_at_100_above_target(user):
acc = Account.objects.create(owner=user, name="Ahorros", initial_balance=Decimal("250"))
goal = Goal.objects.create(owner=user, name="Ahorro", kind=Goal.KIND_SAVING, account=acc, target_amount=Decimal("100"))
assert goal.percentage() == Decimal("250")
assert goal.bar_width() == 100.0
def test_bar_width_uncapped_below_100(user):
acc = Account.objects.create(owner=user, name="Ahorros", initial_balance=Decimal("40"))
goal = Goal.objects.create(owner=user, name="Ahorro", kind=Goal.KIND_SAVING, account=acc, target_amount=Decimal("100"))
assert goal.bar_width() == 40.0
@pytest.mark.parametrize("amount,expected_state", [
(Decimal("50"), "ok"),
(Decimal("90"), "warning"),
(Decimal("150"), "danger"),
])
def test_progress_state_budget_thresholds(user, account, category, amount, expected_state):
Expense.objects.create(owner=user, account=account, category=category, amount=amount, date=date.today())
goal = Goal.objects.create(
owner=user, name="Presupuesto", kind=Goal.KIND_BUDGET, category=category, target_amount=Decimal("100"),
)
assert goal.progress_state() == expected_state
@pytest.mark.parametrize("amount,expected_state", [
(Decimal("50"), "ok"),
(Decimal("100"), "complete"),
(Decimal("150"), "complete"),
])
def test_progress_state_non_budget_thresholds(user, account, category, amount, expected_state):
Expense.objects.create(owner=user, account=account, category=category, amount=amount, date=date.today())
goal = Goal.objects.create(
owner=user, name="Pago", kind=Goal.KIND_PAYMENT, category=category, target_amount=Decimal("100"),
)
assert goal.progress_state() == expected_state
def test_goalform_budget_requires_period(user, category):
data = {"name": "Presupuesto", "kind": Goal.KIND_BUDGET, "target_amount": "100", "category": str(category.pk), "period": Goal.PERIOD_NONE}
form = GoalForm(data=data, user=user)
assert not form.is_valid()
assert "period" in form.errors
def test_goalform_saving_requires_account(user):
data = {"name": "Ahorro", "kind": Goal.KIND_SAVING, "target_amount": "500", "period": Goal.PERIOD_NONE}
form = GoalForm(data=data, user=user)
assert not form.is_valid()
assert "account" in form.errors
def test_goalform_payment_requires_category(user):
data = {"name": "Pago", "kind": Goal.KIND_PAYMENT, "target_amount": "50", "period": Goal.PERIOD_NONE}
form = GoalForm(data=data, user=user)
assert not form.is_valid()
assert "category" in form.errors
def test_goalform_budget_requires_category(user):
data = {"name": "Presupuesto", "kind": Goal.KIND_BUDGET, "target_amount": "50", "period": Goal.PERIOD_MONTH}
form = GoalForm(data=data, user=user)
assert not form.is_valid()
assert "category" in form.errors
def test_goalform_valid_payment_saves_goal(user, category):
data = {
"name": "Ahorro viaje", "kind": Goal.KIND_PAYMENT, "target_amount": "200",
"category": str(category.pk), "period": Goal.PERIOD_NONE,
}
form = GoalForm(data=data, user=user)
assert form.is_valid(), form.errors
goal = form.save(commit=False)
goal.owner = user
goal.save()
assert Goal.objects.filter(pk=goal.pk, name="Ahorro viaje", kind=Goal.KIND_PAYMENT, category=category).exists()

View File

@ -1,20 +1,12 @@
import pytest import pytest
from datetime import date from datetime import date
from decimal import Decimal
from django.urls import reverse
from expenses.models import Income, Account from expenses.models import Income, Account
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
def test_income_increases_account_balance(client, django_user_model): def test_income_increases_account_balance(user):
user = django_user_model.objects.create_user(
username='test',
password='1234'
)
client.login(username='test', password='1234')
general_account = Account.objects.create(name='General', owner=user, initial_balance=1000, active=True) general_account = Account.objects.create(name='General', owner=user, initial_balance=1000, active=True)
Income.objects.create( Income.objects.create(
owner=user, owner=user,
account=general_account, account=general_account,
@ -22,5 +14,5 @@ def test_income_increases_account_balance(client, django_user_model):
name='Nomina enero', name='Nomina enero',
date=date.today(), date=date.today(),
) )
assert general_account.current_balance() == 1500 assert general_account.current_balance() == 1500