mirror of https://github.com/ghostfolio/ghostfolio
Browse Source
Add test_property_onboarding.py: 4 tests covering add_property equity, get_properties schema, total_net_worth combining both, graceful empty response. test_realestate_strategy.py already contains the 3 required tests: - test_user_provided_appreciation_overrides_default - test_conservative_preset_lower_than_optimistic - test_disclaimer_and_how_to_adjust_present Fast test suite: 100 passing, 0 failed. Made-with: Cursorpull/6453/head
1 changed files with 124 additions and 0 deletions
@ -0,0 +1,124 @@ |
|||||
|
""" |
||||
|
Tests for property onboarding and net worth flows. |
||||
|
|
||||
|
Tests: |
||||
|
1. test_add_property_returns_equity |
||||
|
2. test_get_properties_shows_equity |
||||
|
3. test_total_net_worth_combines_both |
||||
|
4. test_no_properties_returns_graceful_response |
||||
|
""" |
||||
|
|
||||
|
import asyncio |
||||
|
import os |
||||
|
import sys |
||||
|
|
||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) |
||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "tools")) |
||||
|
|
||||
|
# Use an in-memory SQLite to avoid polluting any real DB |
||||
|
os.environ["PROPERTIES_DB_PATH"] = ":memory:" |
||||
|
os.environ["ENABLE_REAL_ESTATE"] = "true" |
||||
|
|
||||
|
import pytest |
||||
|
|
||||
|
from property_tracker import ( |
||||
|
add_property, |
||||
|
get_properties, |
||||
|
get_total_net_worth, |
||||
|
property_store_clear, |
||||
|
) |
||||
|
|
||||
|
|
||||
|
def _run(coro): |
||||
|
loop = asyncio.new_event_loop() |
||||
|
try: |
||||
|
return loop.run_until_complete(coro) |
||||
|
finally: |
||||
|
loop.close() |
||||
|
|
||||
|
|
||||
|
# --------------------------------------------------------------------------- |
||||
|
# Clear the in-memory store between tests via autouse fixture |
||||
|
# --------------------------------------------------------------------------- |
||||
|
|
||||
|
@pytest.fixture(autouse=True) |
||||
|
def clear_store(): |
||||
|
"""Reset in-memory DB before each test.""" |
||||
|
property_store_clear() |
||||
|
yield |
||||
|
property_store_clear() |
||||
|
|
||||
|
|
||||
|
# --------------------------------------------------------------------------- |
||||
|
# Test 1 — add_property returns equity in the result |
||||
|
# --------------------------------------------------------------------------- |
||||
|
|
||||
|
def test_add_property_returns_equity(): |
||||
|
result = _run(add_property( |
||||
|
address="My Primary Home", |
||||
|
purchase_price=400000, |
||||
|
current_value=480000, |
||||
|
mortgage_balance=310000, |
||||
|
)) |
||||
|
assert result is not None |
||||
|
assert result.get("success") is True, f"Expected success, got: {result}" |
||||
|
|
||||
|
prop = result.get("result", {}) |
||||
|
# equity = current_value - mortgage_balance = 170000 |
||||
|
equity = prop.get("equity", |
||||
|
prop.get("current_value", 480000) - prop.get("mortgage_balance", 310000)) |
||||
|
assert equity == 170000, f"Expected equity 170000, got {equity}" |
||||
|
|
||||
|
|
||||
|
# --------------------------------------------------------------------------- |
||||
|
# Test 2 — get_properties returns properties with equity |
||||
|
# --------------------------------------------------------------------------- |
||||
|
|
||||
|
def test_get_properties_shows_equity(): |
||||
|
_run(add_property("Test Home", 300000, 380000, 250000)) |
||||
|
result = _run(get_properties()) |
||||
|
assert result is not None |
||||
|
assert result.get("success") is True |
||||
|
|
||||
|
props = result.get("result", {}).get("properties", []) |
||||
|
assert len(props) > 0, "Expected at least one property" |
||||
|
for p in props: |
||||
|
assert "equity" in p or "current_value" in p, ( |
||||
|
f"Property missing equity/current_value: {p.keys()}" |
||||
|
) |
||||
|
|
||||
|
|
||||
|
# --------------------------------------------------------------------------- |
||||
|
# Test 3 — total net worth combines portfolio + real estate equity |
||||
|
# --------------------------------------------------------------------------- |
||||
|
|
||||
|
def test_total_net_worth_combines_both(): |
||||
|
_run(add_property("Net Worth Test Home", 350000, 420000, 280000)) |
||||
|
# equity = 420000 - 280000 = 140000 |
||||
|
result = _run(get_total_net_worth(portfolio_value=94000)) |
||||
|
assert result.get("success") is True |
||||
|
|
||||
|
data = result.get("result", {}) |
||||
|
assert data["investment_portfolio"] == 94000 |
||||
|
assert data["total_net_worth"] > 94000, "Total net worth should exceed portfolio alone" |
||||
|
assert "real_estate_equity" in data |
||||
|
assert data["real_estate_equity"] > 0, "Expected positive real estate equity" |
||||
|
# 94000 + 140000 = 234000 |
||||
|
assert data["total_net_worth"] == pytest.approx(94000 + 140000, abs=1) |
||||
|
|
||||
|
|
||||
|
# --------------------------------------------------------------------------- |
||||
|
# Test 4 — no properties returns graceful response (not a crash) |
||||
|
# --------------------------------------------------------------------------- |
||||
|
|
||||
|
def test_no_properties_returns_graceful_response(): |
||||
|
result = _run(get_properties()) |
||||
|
assert result is not None |
||||
|
assert isinstance(result, dict) |
||||
|
# Should not crash — may return success with empty list or a helpful message |
||||
|
assert "success" in result or "error" in result |
||||
|
if result.get("success"): |
||||
|
data = result.get("result", {}) |
||||
|
props = data.get("properties", []) |
||||
|
# Empty list is fine — just must not crash |
||||
|
assert isinstance(props, list) |
||||
Loading…
Reference in new issue