test(site-finder): cover is_residential_zone raw_props/subcategory branch (#1353 follow-up) #1705

Merged
lekss361 merged 1 commit from test/residential-zone-rawprops-1681 into main 2026-06-17 18:34:31 +00:00

View file

@ -242,3 +242,120 @@ def test_nspd_subcategory_path_backward_compat():
verdict = compute_gate_verdict(nspd_zoning, overlaps, [], {"available": True})
assert verdict["can_build_mkd"] is False
assert any(b["code"] == "ZOUIT_OVERLAP_SUB17" for b in verdict["blockers"])
# ── is_residential_zone: raw_props / subcategory branch (#1353) ───────────────
def test_rawprops_subcategory_2_is_residential():
"""raw_props subcategory=2 (Жилые зоны МКД) → True (#1353)."""
assert is_residential_zone(None, None, {"subcategory": 2}) is True
def test_rawprops_subcategory_3_is_residential():
"""raw_props subcategory=3 (Смешанное использование, МКД допустим по ПЗЗ ЕКБ) → True (#1353)."""
assert is_residential_zone(None, None, {"subcategory": 3}) is True
def test_rawprops_subcategory_1_not_residential():
"""raw_props subcategory=1 (ИЖС/малоэтажка) → False: МКД не разрешён (#1353)."""
assert is_residential_zone(None, None, {"subcategory": 1}) is False
def test_rawprops_subcategory_4_not_residential():
"""raw_props subcategory=4 (Общественно-деловые) → False."""
assert is_residential_zone(None, None, {"subcategory": 4}) is False
def test_rawprops_subcategory_as_string_int_coercion():
"""raw_props subcategory='2' (строка из JSON) → int() coercion → True (#1353)."""
assert is_residential_zone(None, None, {"subcategory": "2"}) is True
def test_rawprops_subcategory_absent_fallback_to_zone_code():
"""raw_props без subcategory, но zone_code ^Ж → True (tier-1 wins)."""
assert is_residential_zone("Ж-2", None, {"other_field": "value"}) is True
def test_rawprops_absent_zone_code_zh_still_residential():
"""raw_props=None, zone_code ^Ж → tier-1 detects residential (no crash)."""
assert is_residential_zone("Жс", None, None) is True
def test_rawprops_absent_no_zone_code_no_name_not_residential():
"""raw_props=None, zone_code=None, zone_name=None → False, no crash (#1353)."""
assert is_residential_zone(None, None, None) is False
def test_rawprops_empty_dict_no_crash():
"""raw_props={} (пустой) → subcategory=None → переходит на tier-3 fallback, без исключения."""
assert is_residential_zone(None, None, {}) is False
def test_rawprops_subcategory_invalid_value_no_crash():
"""raw_props subcategory='N/A' (нечисловое) → ValueError поглощается → False."""
assert is_residential_zone(None, None, {"subcategory": "N/A"}) is False
# ── compute_gate_verdict: nspd_zoning with raw_props (#1353) ─────────────────
def test_verdict_nspd_zoning_rawprops_sub2_can_build():
"""nspd_zoning с raw_props subcategory=2 (без zone_code Ж) → жилая → Можно."""
nspd_zoning = {
"zone_code": "66:41-7.14", # кадастровый номер NSPD quarter-dump (не ^Ж)
"zone_name": None,
"raw_props": {"subcategory": 2},
}
verdict = compute_gate_verdict(nspd_zoning, [], [{"name": "ТП-5"}], {"available": True})
assert verdict["can_build_mkd"] is True
assert verdict["verdict_label"] == "Можно"
assert verdict["blockers"] == []
def test_verdict_nspd_zoning_rawprops_sub3_can_build():
"""nspd_zoning с raw_props subcategory=3 (смешанное, без zone_code Ж) → Можно."""
nspd_zoning = {
"zone_code": "66:41-7.21",
"zone_name": None,
"raw_props": {"subcategory": 3},
}
verdict = compute_gate_verdict(nspd_zoning, [], [{"name": "ТП-1"}], {"available": True})
assert verdict["can_build_mkd"] is True
assert verdict["verdict_label"] == "Можно"
assert verdict["blockers"] == []
def test_verdict_nspd_zoning_rawprops_sub1_blocks():
"""nspd_zoning с raw_props subcategory=1 (ИЖС) → PZZ_NOT_RESIDENTIAL → Нельзя (#1353)."""
nspd_zoning = {
"zone_code": "66:41-7.05",
"zone_name": "Малоэтажная застройка",
"raw_props": {"subcategory": 1},
}
verdict = compute_gate_verdict(nspd_zoning, [], [], {"available": True})
assert verdict["can_build_mkd"] is False
assert verdict["verdict_label"] == "Нельзя"
assert any(b["code"] == "PZZ_NOT_RESIDENTIAL" for b in verdict["blockers"])
def test_verdict_nspd_zoning_no_rawprops_zh_zone_code_can_build():
"""raw_props отсутствует, zone_code=^Ж → tier-1 → Можно (регрессия: no crash)."""
nspd_zoning = {"zone_code": "Ж-1", "zone_name": None}
verdict = compute_gate_verdict(nspd_zoning, [], [{"name": "КНС-1"}], {"available": True})
assert verdict["can_build_mkd"] is True
assert verdict["verdict_label"] == "Можно"
assert verdict["blockers"] == []
def test_verdict_nspd_zoning_no_rawprops_no_zone_blocks():
"""raw_props=None, zone_code=кадастровый (не ^Ж), zone_name=None → PZZ_NOT_RESIDENTIAL."""
nspd_zoning = {
"zone_code": "66:41-7.99",
"zone_name": None,
"raw_props": None,
}
verdict = compute_gate_verdict(nspd_zoning, [], [], {"available": True})
assert verdict["can_build_mkd"] is False
assert verdict["verdict_label"] == "Нельзя"
assert any(b["code"] == "PZZ_NOT_RESIDENTIAL" for b in verdict["blockers"])