gendesign/tradein-mvp/backend/tests/test_estimator_cohort.py
lekss361 dd774032d8 fix(tradein-estimator): extend cohort filter to Tier S + Tier H, fix late_soviet overlap
PR #519 added cohort hard-filter but only to Tier W's inline SQL. Tier S
(address ILIKE) and Tier H (year ±15) share _COMMON_WHERE which was not
extended — for target_year=1978 (brezhnev), Tier H allowed year_built ∈
[1963, 1993] and a 1965 khrushchev or 1992 late_soviet match could win
before cohort filter ever evaluated.

Fixes (reviewer feedback on PR #519, item #1):
* Append cohort clause to _COMMON_WHERE so Tier S and Tier H inherit it.
* Add cohort_year_min/max binds to base_params (auto-propagates to both).
* Tier W's inline copy stays — not converted to _COMMON_WHERE (relevance_score
  CASE expressions require inline form). Comment near _COMMON_WHERE documents
  the dual code path requirement.

Also (reviewer item #5):
* Fix late_soviet range 1985-1999 → 1990-1999. Previous overlap with brezhnev
  1970-1989 + first-match semantics made late_soviet effectively cover only
  1990-1999 anyway — rename clarifies actual coverage.

Add parametrized pytest test_target_cohort_range covering None, out-of-range,
boundary cases, and the audit's target (1978 → brezhnev).
2026-05-24 16:09:28 +03:00

43 lines
1.9 KiB
Python

"""Unit tests for cohort filter helpers in estimator.py.
PR 10 (2026-05-24) — covers _target_cohort_range edge cases:
None, out-of-range, exact boundaries, first-match semantics.
"""
import os
# Settings requires DATABASE_URL at init time. Set dummy DSN before any app import.
os.environ.setdefault("DATABASE_URL", "postgresql://test:test@localhost/test_db")
import pytest
from app.services.estimator import _target_cohort_range
@pytest.mark.parametrize(
("year", "expected"),
[
(None, None), # no year → no cohort
(1900, None), # out-of-range below → no cohort
(1954, None), # below khrushchev floor
(1955, (1955, 1969)), # khrushchev floor
(1960, (1955, 1969)), # khrushchev mid
(1969, (1955, 1969)), # khrushchev ceiling
(1970, (1970, 1989)), # brezhnev floor
(1978, (1970, 1989)), # brezhnev (target audit case)
(1988, (1970, 1989)), # brezhnev pre-overlap fix held
(1989, (1970, 1989)), # brezhnev ceiling
(1990, (1990, 1999)), # late_soviet floor (post-PR10 fix)
(1995, (1990, 1999)), # late_soviet mid
(1999, (1990, 1999)), # late_soviet ceiling
(2000, (2000, 2010)), # 2000s floor
(2010, (2000, 2010)), # 2000s ceiling
(2011, (2011, 2100)), # modern floor
(2024, (2011, 2100)), # modern current
(2100, (2011, 2100)), # modern ceiling
(2101, None), # above modern ceiling → no cohort
(2200, None), # far future → no cohort
],
)
def test_target_cohort_range(year, expected):
"""First-match cohort mapping; out-of-range and None → None."""
assert _target_cohort_range(year) == expected