gendesign/backend/tests/services/generative/test_teap_financial.py
Light1YT 8242e51b61
Some checks failed
CI / frontend-tests (push) Has been skipped
CI / changes (push) Successful in 10s
CI / changes (pull_request) Successful in 7s
CI / frontend-tests (pull_request) Has been skipped
CI / backend-tests (push) Successful in 7m39s
CI / backend-tests (pull_request) Successful in 7m39s
Deploy / deploy (push) Has been cancelled
Deploy / build-frontend (push) Has been cancelled
Deploy / build-backend (push) Has been cancelled
Deploy / build-worker (push) Has been cancelled
Deploy / changes (push) Successful in 6s
feat(generative): exporters (dxf/pdf) + generative test suite (#54 #55 #56)
Дополняет движок: exporters/{dxf,pdf}.py (ezdxf + WeasyPrint lazy) + 5 тест-модулей
(geometry/placement/teap_financial/exporters/api). Не вошли в предыдущий commit
(untracked-директории).
2026-06-13 21:33:20 +05:00

114 lines
4.2 KiB
Python

"""Stage 1c tests — ТЭП and financial model arithmetic.
Pure-arithmetic unit tests against known footprint geometry: GFA, residential area,
apartment count, FAR, parking, revenue/cost/margin and the IRR-proxy clamp.
"""
from __future__ import annotations
from shapely.geometry import box
from app.schemas.concept import TEAP
from app.services.generative import financial, teap
# Один прямоугольник 20 x 10 = 200 кв.м пятна.
_FOOTPRINT = box(0, 0, 20, 10)
def test_teap_basic_arithmetic() -> None:
result = teap.compute_teap(
footprints=[_FOOTPRINT],
floors=10,
site_area_sqm=1000.0,
housing_class="comfort",
)
assert result.built_area_sqm == 200.0
# GFA = пятно * этажность.
assert result.total_floor_area_sqm == 2000.0
# FAR = GFA / участок.
assert result.density == 2.0
# Жилая = GFA * efficiency(comfort=0.78).
assert result.residential_area_sqm == 1560.0
# Квартир = floor(жилая / avg(comfort=55)).
assert result.apartments_count == int(1560.0 // 55.0)
# Парковка = ceil(квартир * 1.0).
assert result.parking_spaces == result.apartments_count
def test_teap_class_changes_efficiency_and_lot() -> None:
econ = teap.compute_teap(
footprints=[_FOOTPRINT], floors=10, site_area_sqm=1000.0, housing_class="econom"
)
biz = teap.compute_teap(
footprints=[_FOOTPRINT], floors=10, site_area_sqm=1000.0, housing_class="business"
)
# Эконом эффективнее по площади -> больше жилой при той же GFA.
assert econ.residential_area_sqm > biz.residential_area_sqm
# Бизнес — крупнее лот -> меньше квартир.
assert biz.apartments_count < econ.apartments_count
# Бизнес — выше норма парковки на квартиру.
assert biz.parking_spaces / max(1, biz.apartments_count) >= 1.4
def test_teap_zero_site_area_no_division_error() -> None:
result = teap.compute_teap(
footprints=[_FOOTPRINT], floors=5, site_area_sqm=0.0, housing_class="comfort"
)
assert result.density == 0.0
def test_teap_empty_placement_is_zeroed() -> None:
result = teap.compute_teap(
footprints=[], floors=9, site_area_sqm=1000.0, housing_class="comfort"
)
assert result.built_area_sqm == 0.0
assert result.total_floor_area_sqm == 0.0
assert result.apartments_count == 0
assert result.parking_spaces == 0
def _teap(residential: float, gfa: float) -> TEAP:
return TEAP(
built_area_sqm=100.0,
total_floor_area_sqm=gfa,
residential_area_sqm=residential,
apartments_count=10,
density=1.0,
parking_spaces=10,
)
def test_financial_revenue_cost_margin() -> None:
t = _teap(residential=1000.0, gfa=1300.0)
model = financial.compute_financial(
teap=t, housing_class="comfort", land_cost_rub=50_000_000.0
)
# revenue = 1000 * 145_000.
assert model.revenue_rub == 1000.0 * 145_000.0
# cost = 1300 * 88_000 + land.
assert model.cost_rub == 1300.0 * 88_000.0 + 50_000_000.0
assert model.gross_margin_rub == model.revenue_rub - model.cost_rub
def test_financial_land_cost_optional() -> None:
t = _teap(residential=1000.0, gfa=1300.0)
no_land = financial.compute_financial(teap=t, housing_class="comfort", land_cost_rub=None)
with_land = financial.compute_financial(
teap=t, housing_class="comfort", land_cost_rub=10_000_000.0
)
# Земля увеличивает затраты ровно на свою стоимость.
assert with_land.cost_rub - no_land.cost_rub == 10_000_000.0
def test_financial_irr_proxy_clamped() -> None:
# Огромная маржа -> irr-proxy зажат в [-1, 1].
t = _teap(residential=100_000.0, gfa=1.0)
model = financial.compute_financial(teap=t, housing_class="business", land_cost_rub=None)
assert -1.0 <= model.irr <= 1.0
def test_financial_zero_cost_no_division_error() -> None:
t = _teap(residential=0.0, gfa=0.0)
model = financial.compute_financial(teap=t, housing_class="comfort", land_cost_rub=None)
assert model.irr == 0.0
assert model.cost_rub == 0.0