243 lines
8.6 KiB
Python
243 lines
8.6 KiB
Python
from __future__ import annotations
|
|
|
|
from decimal import Decimal
|
|
|
|
from app.extensions import db
|
|
from app.models import AllocationSuggestion, Category, CommunityAccount, Month, MonthlyEntryValue
|
|
|
|
|
|
def test_health_route(client):
|
|
response = client.get("/health")
|
|
assert response.status_code == 200
|
|
assert response.json["status"] == "ok"
|
|
|
|
|
|
def test_first_run_redirects_to_setup(empty_client):
|
|
response = empty_client.get("/", follow_redirects=False)
|
|
|
|
assert response.status_code == 302
|
|
assert "/auth/setup" in response.headers["Location"]
|
|
|
|
|
|
def test_current_month_is_available_after_login(logged_in_client):
|
|
response = logged_in_client.get("/")
|
|
assert response.status_code == 200
|
|
assert b"2026-04" in response.data or b"2026-05" in response.data
|
|
assert b"Dauerauftr\xc3\xa4ge pr\xc3\xbcfen" in response.data
|
|
assert b"Extern mitzuteilen" in response.data
|
|
|
|
|
|
def test_analytics_route_uses_new_cost_focused_sections(logged_in_client):
|
|
response = logged_in_client.get("/analytics")
|
|
content = response.get_data(as_text=True)
|
|
assert response.status_code == 200
|
|
assert "Kategorien im Monat" in content
|
|
assert "Kosten nach Zuordnung" in content
|
|
assert "Budgets im Monatsverlauf" in content
|
|
assert "Größte Einträge im Monat" in content
|
|
assert "Sparkonten" in content
|
|
assert "category-chart-back" in content
|
|
assert "entry-drilldown-chart" not in content
|
|
assert '"Pers\\u00f6nliche Auszahlung"' in content
|
|
assert "Person A" in content
|
|
assert "Person B" in content
|
|
|
|
|
|
def test_planning_detail_refreshes_stale_suggestions_after_distribution_sync(logged_in_client):
|
|
month = Month.query.filter_by(label="2026-04").first()
|
|
sparen_value = next(
|
|
item for item in month.entry_values if item.entry.name == "Sparziel"
|
|
)
|
|
sparen_allocation = next(
|
|
item for item in month.allocations if item.target_account.slug == "sparen"
|
|
)
|
|
sparen_value.planned_amount = Decimal("0.00")
|
|
sparen_allocation.amount = Decimal("1539.20")
|
|
for suggestion in month.suggestions:
|
|
suggestion.suggested_amount = Decimal("0.00")
|
|
db.session.commit()
|
|
|
|
response = logged_in_client.get("/planning/2026-04")
|
|
|
|
db.session.refresh(sparen_value)
|
|
refreshed_suggestions = AllocationSuggestion.query.filter_by(month_id=month.id).all()
|
|
|
|
assert response.status_code == 200
|
|
assert sparen_value.planned_amount == Decimal("1539.20")
|
|
assert sum((item.suggested_amount for item in refreshed_suggestions), Decimal("0.00")) > Decimal("0.00")
|
|
|
|
|
|
def test_community_entry_can_be_updated_via_annual_amount(app, logged_in_client):
|
|
month = Month.query.filter_by(label="2026-04").first()
|
|
value = next(
|
|
item for item in month.entry_values if item.entry.name == "Miete"
|
|
)
|
|
|
|
response = logged_in_client.post(
|
|
f"/planning/{month.label}/entry",
|
|
data={
|
|
"value_id": value.id,
|
|
"return_dialog": f"category-dialog-{value.entry.category_id}",
|
|
"entry_name": value.entry.name,
|
|
"category_id": value.entry.category_id,
|
|
"planned_amount": "",
|
|
"annual_amount": "1800.00",
|
|
"benefit_scope": value.entry.benefit_scope,
|
|
"note": value.note or "",
|
|
},
|
|
)
|
|
|
|
updated_value = db.session.get(MonthlyEntryValue, value.id)
|
|
|
|
assert response.status_code == 302
|
|
assert updated_value.planned_amount == Decimal("150.00")
|
|
|
|
|
|
def test_distribution_dialog_shows_direct_budget_form(logged_in_client):
|
|
response = logged_in_client.get("/planning/2026-04")
|
|
|
|
assert response.status_code == 200
|
|
assert b"Budget direkt anpassen" in response.data
|
|
assert b"Sparkonto" in response.data
|
|
|
|
|
|
def test_planning_shows_budgets_and_community_accounts(logged_in_client):
|
|
response = logged_in_client.get("/planning/2026-04")
|
|
|
|
assert response.status_code == 200
|
|
assert b"Budgets" in response.data
|
|
assert b"Gemeinschaftskonten" in response.data
|
|
assert b"Auszahlung Person A" in response.data
|
|
assert b"Auszahlung Person B" in response.data
|
|
|
|
|
|
def test_planning_delete_actions_render_app_confirmation_dialogs(logged_in_client):
|
|
response = logged_in_client.get("/planning/2026-04")
|
|
|
|
assert response.status_code == 200
|
|
assert b"confirm-delete-income-" in response.data
|
|
assert b"confirm-delete-category-" in response.data
|
|
assert b"confirm-delete-community-account-" in response.data
|
|
assert b"confirm-delete-entry-" in response.data
|
|
assert b"data-confirm-submit" not in response.data
|
|
|
|
|
|
def test_community_account_can_assign_budget_categories(logged_in_client):
|
|
community_account = CommunityAccount.query.filter_by(slug="hauptkonto").first()
|
|
category = Category.query.filter_by(slug="wohnen").first()
|
|
|
|
response = logged_in_client.post(
|
|
f"/planning/2026-04/community-accounts/{community_account.id}",
|
|
data={
|
|
"name": community_account.name,
|
|
"description": community_account.description or "",
|
|
"category_ids": [str(category.id)],
|
|
},
|
|
)
|
|
|
|
db.session.refresh(category)
|
|
|
|
assert response.status_code == 302
|
|
assert category.community_account_id == community_account.id
|
|
|
|
|
|
def test_deleted_category_can_be_created_again(logged_in_client):
|
|
create_response = logged_in_client.post(
|
|
"/planning/2026-04/categories",
|
|
data={"name": "Testbudget", "area": "budget"},
|
|
)
|
|
assert create_response.status_code == 302
|
|
|
|
category = Category.query.filter_by(slug="testbudget").first()
|
|
original_id = category.id
|
|
|
|
delete_response = logged_in_client.post(f"/planning/2026-04/categories/{category.id}/delete")
|
|
assert delete_response.status_code == 302
|
|
|
|
recreate_response = logged_in_client.post(
|
|
"/planning/2026-04/categories",
|
|
data={"name": "Testbudget", "area": "budget"},
|
|
)
|
|
|
|
restored = Category.query.filter_by(slug="testbudget").first()
|
|
|
|
assert recreate_response.status_code == 302
|
|
assert restored.id == original_id
|
|
assert restored.is_active is True
|
|
|
|
|
|
def test_deleted_community_account_can_be_created_again(logged_in_client):
|
|
create_response = logged_in_client.post(
|
|
"/planning/2026-04/community-accounts",
|
|
data={"name": "Testkonto", "description": ""},
|
|
)
|
|
assert create_response.status_code == 302
|
|
|
|
community_account = CommunityAccount.query.filter_by(slug="testkonto").first()
|
|
original_id = community_account.id
|
|
|
|
delete_response = logged_in_client.post(
|
|
f"/planning/2026-04/community-accounts/{community_account.id}/delete"
|
|
)
|
|
assert delete_response.status_code == 302
|
|
|
|
recreate_response = logged_in_client.post(
|
|
"/planning/2026-04/community-accounts",
|
|
data={"name": "Testkonto", "description": "Wieder da"},
|
|
)
|
|
|
|
restored = CommunityAccount.query.filter_by(slug="testkonto").first()
|
|
|
|
assert recreate_response.status_code == 302
|
|
assert restored.id == original_id
|
|
assert restored.is_active is True
|
|
assert restored.description == "Wieder da"
|
|
|
|
|
|
def test_community_account_rejects_budget_assigned_to_other_account(logged_in_client):
|
|
primary_account = CommunityAccount.query.filter_by(slug="hauptkonto").first()
|
|
secondary_response = logged_in_client.post(
|
|
"/planning/2026-04/community-accounts",
|
|
data={"name": "Fixkostenkonto", "description": ""},
|
|
follow_redirects=True,
|
|
)
|
|
assert secondary_response.status_code == 200
|
|
secondary_account = CommunityAccount.query.filter_by(slug="fixkostenkonto").first()
|
|
category = Category.query.filter_by(slug="wohnen").first()
|
|
category.community_account_id = primary_account.id
|
|
db.session.commit()
|
|
|
|
response = logged_in_client.post(
|
|
f"/planning/2026-04/community-accounts/{secondary_account.id}",
|
|
data={
|
|
"name": secondary_account.name,
|
|
"description": secondary_account.description or "",
|
|
"category_ids": [str(category.id)],
|
|
},
|
|
follow_redirects=True,
|
|
)
|
|
|
|
db.session.refresh(category)
|
|
|
|
assert response.status_code == 200
|
|
assert category.community_account_id == primary_account.id
|
|
assert b"bereits anderen Konten zugewiesen" in response.data
|
|
|
|
|
|
def test_community_account_can_be_deleted_and_unassigns_budgets(logged_in_client):
|
|
community_account = CommunityAccount.query.filter_by(slug="hauptkonto").first()
|
|
category = Category.query.filter_by(slug="wohnen").first()
|
|
category.community_account_id = community_account.id
|
|
db.session.commit()
|
|
|
|
response = logged_in_client.post(
|
|
f"/planning/2026-04/community-accounts/{community_account.id}/delete"
|
|
)
|
|
|
|
db.session.refresh(category)
|
|
db.session.refresh(community_account)
|
|
|
|
assert response.status_code == 302
|
|
assert community_account.is_active is False
|
|
assert category.community_account_id is None
|