from __future__ import annotations def _seed_budget_via_api(client): client.post( "/sections/income/entries", data={"name": "Paycheck", "amount": "2500.00"}, ) client.post( "/sections/fixed_bill/entries", data={"name": "Rent", "amount": "1200.00"}, ) client.post( "/sections/debt_minimum/entries", data={"name": "Card A", "amount": "40.00"}, ) def test_missing_month_renders_create_flow(client): response = client.get("/month/2026-04") assert response.status_code == 200 assert "No snapshot yet" in response.text assert 'hx-post="/month/2026-04/create"' in response.text def test_create_month_redirects_via_htmx(client): _seed_budget_via_api(client) response = client.post("/month/2026-04/create") assert response.status_code == 204 assert response.headers.get("hx-redirect") == "/month/2026-04" def test_created_month_renders_snapshot(client): _seed_budget_via_api(client) client.post("/month/2026-04/create") response = client.get("/month/2026-04") assert response.status_code == 200 assert "Paycheck" in response.text assert "Rent" in response.text assert "Card A" in response.text # totals: applied / planned assert "$2500.00" in response.text assert "$1200.00" in response.text def test_applied_update_returns_section_partial(client): _seed_budget_via_api(client) client.post("/month/2026-04/create") # fetch month page, identify rent month_entry id = 2 (after income id 1) response = client.post( "/month/2026-04/entries/2", data={"applied": "1200.00"} ) assert response.status_code == 200 # rent is in fixed_bill section; total_applied should now include 1200 assert 'id="section-fixed_bill"' in response.text # two $1200.00 occurrences: total_applied and planned (same amount) assert response.text.count("$1200.00") >= 2 def test_name_edit_flips_to_modified(client): _seed_budget_via_api(client) client.post("/month/2026-04/create") response = client.post( "/month/2026-04/entries/2", data={"name": "Rent (April)"} ) assert response.status_code == 200 assert "state-edited" in response.text assert 'class="tag tag-edited">modified' in response.text def test_add_new_entry_within_month_is_marked_new(client): _seed_budget_via_api(client) client.post("/month/2026-04/create") response = client.post( "/month/2026-04/sections/other/entries", data={"name": "Gift", "planned": "50.00"}, ) assert response.status_code == 200 assert "state-new_in_month" in response.text assert "new this month" in response.text def test_delete_month_entry(client): _seed_budget_via_api(client) client.post("/month/2026-04/create") response = client.delete("/month/2026-04/entries/2") assert response.status_code == 200 assert "Rent" not in response.text def test_delete_month_debt_minimum_updates_target(client): _seed_budget_via_api(client) client.post("/month/2026-04/create") # Card A is entry id 3 in the month snapshot response = client.delete("/month/2026-04/entries/3") assert response.status_code == 200 assert "section-debt_target" in response.text assert "No target selected" in response.text def test_set_month_target(client): _seed_budget_via_api(client) client.post( "/sections/debt_minimum/entries", data={"name": "Card B", "amount": "60.00"}, ) client.post("/month/2026-04/create") # Card B is month_entry id 4 response = client.post( "/month/2026-04/target", data={"month_entry_id": "4"} ) assert response.status_code == 200 assert "Card B" in response.text def test_month_target_isolated_between_months(client): _seed_budget_via_api(client) client.post( "/sections/debt_minimum/entries", data={"name": "Card B", "amount": "60.00"}, ) client.post("/month/2026-04/create") client.post("/month/2026-05/create") # Change April target to Card B (month_entry id 4 within April) client.post("/month/2026-04/target", data={"month_entry_id": "4"}) may_page = client.get("/month/2026-05") # May still points at Card A (copied from budget) assert "Card A" in may_page.text def test_malformed_year_month_returns_404(client): response = client.get("/month/2026-13") assert response.status_code == 404 def test_budget_page_shows_month_nav(client): response = client.get("/") assert response.status_code == 200 assert "This month" in response.text