test(concept): turn greedy backward-compat into a real golden pin (#1965 Stage 3a)
All checks were successful
CI / changes (pull_request) Successful in 7s
CI / frontend-tests (pull_request) Successful in 1m3s
CI / openapi-codegen-check (pull_request) Successful in 1m51s
CI / backend-tests (pull_request) Successful in 14m7s

The old test_no_program_reproduces_greedy_output_unchanged only compared
building_program=None (default) vs explicit None — both through the new
_Placer code — so it proved the two None branches agree but did NOT pin
the greedy geometry; it would still pass if the _Placer extraction had
drifted the output. test_placement.py only checks invariants, never
concrete counts/TEAP, so there was no anti-regression guard that the
greedy path is byte-identical after the refactor.

Replace it with two tests:
- test_greedy_output_matches_golden_pin: hard-coded literals per strategy
  on the fixed _BIG_PARCEL — (features, built_area_sqm, total_floor_area_sqm,
  apartments_count) — frozen from the current (== pre-refactor) output, so
  any future deterministic drift in greedy placement FAILS.
- test_explicit_none_program_equals_default_greedy: keeps the None-branch
  equivalence check (default vs explicit None go one greedy path).
This commit is contained in:
Light1YT 2026-06-28 02:23:55 +05:00
parent 94cf1f6217
commit e8b05d821e

View file

@ -51,31 +51,58 @@ def _payload(coords: list[list[float]], **overrides: object) -> ConceptInput:
return ConceptInput(**base) # type: ignore[arg-type] return ConceptInput(**base) # type: ignore[arg-type]
# ── (a) backward-compat: building_program=None reproduces greedy output ───────────── # ── (a) backward-compat: greedy output is byte-identical after the _Placer refactor ──
# GOLDEN PIN — детерминированный жадный выход на фиксированном участке _BIG_PARCEL
# (comfort / 9 эт. / mid_rise). Захвачен ПОСЛЕ извлечения _Placer и сверен ревьювером
# как идентичный до-рефактора. Любой будущий ДРЕЙФ жадной геометрии (число секций /
# пятно / GFA / квартиры) уронит этот тест — это и есть anti-regression guard, которого
# не давали инвариантные тесты в test_placement.py. Формат на стратегию:
# (features, built_area_sqm, total_floor_area_sqm, apartments_count).
_GREEDY_GOLDEN: dict[str, tuple[int, float, float, int]] = {
"max_area": (83, 35856.0, 358560.0, 4830),
"max_insolation": (70, 15120.0, 120960.0, 1629),
"balanced": (88, 27720.0, 249480.0, 3361),
}
def test_no_program_reproduces_greedy_output_unchanged() -> None: def test_greedy_output_matches_golden_pin() -> None:
# Тот же участок: с building_program=None раскладка ДОЛЖНА совпасть с жадной 1b # Реальный golden-pin: жадный путь (building_program не задан) должен выдать РОВНО
# (три стратегии, те же ТЭП/число секций) — program-ветка не трогает greedy-путь. # замороженные литералы. Так фиксируем, что _Placer-рефактор не сдвинул раскладку.
payload = _payload(_BIG_PARCEL)
parcel = geometry.parse_parcel(payload)
variants = {v.strategy: v for v in placement.place_all_strategies(parcel, payload)}
assert set(variants) == set(_GREEDY_GOLDEN)
for name, (features, built, gfa, apts) in _GREEDY_GOLDEN.items():
v = variants[name]
assert len(v.buildings_geojson["features"]) == features, name
assert round(v.teap.built_area_sqm, 1) == built, name
assert round(v.teap.total_floor_area_sqm, 1) == gfa, name
assert v.teap.apartments_count == apts, name
# Greedy-режим не выставляет partial-fit сигнал (остаётся None).
assert v.placed_count is None
assert v.requested_count is None
def test_explicit_none_program_equals_default_greedy() -> None:
# building_program=None (явно) и дефолт (поле опущено) идут одним greedy-путём →
# должны совпасть покомпонентно. Дополняет golden-pin: ветка None не разветвляется.
payload_default = _payload(_BIG_PARCEL) payload_default = _payload(_BIG_PARCEL)
payload_none = _payload(_BIG_PARCEL, building_program=None) payload_none = _payload(_BIG_PARCEL, building_program=None)
variants_default = placement.place_all_strategies(
parcel = geometry.parse_parcel(payload_default) geometry.parse_parcel(payload_default), payload_default
greedy = placement.place_all_strategies(parcel, payload_default) )
none_branch = placement.place_all_strategies(geometry.parse_parcel(payload_none), payload_none) variants_none = placement.place_all_strategies(
geometry.parse_parcel(payload_none), payload_none
assert {v.strategy for v in greedy} == {"max_area", "max_insolation", "balanced"} )
assert {v.strategy for v in none_branch} == {"max_area", "max_insolation", "balanced"} by_default = {v.strategy: v for v in variants_default}
by_strategy_g = {v.strategy: v for v in greedy} by_none = {v.strategy: v for v in variants_none}
by_strategy_n = {v.strategy: v for v in none_branch} assert set(by_default) == set(by_none)
for name, gv in by_strategy_g.items(): for name, dv in by_default.items():
nv = by_strategy_n[name] nv = by_none[name]
assert gv.teap == nv.teap assert dv.teap == nv.teap
assert gv.financial == nv.financial assert dv.financial == nv.financial
assert len(gv.buildings_geojson["features"]) == len(nv.buildings_geojson["features"]) assert len(dv.buildings_geojson["features"]) == len(nv.buildings_geojson["features"])
# Greedy-режим не выставляет partial-fit сигнал (остаётся None).
assert gv.placed_count is None
assert gv.requested_count is None
# ── (b) 2-item program places EXACTLY the requested sections; TEAP reflects them ───── # ── (b) 2-item program places EXACTLY the requested sections; TEAP reflects them ─────