"""Test bug fix: ValueEstimator should NOT apply blind age deductions when listing condition is 'Updated/Remodeled' or description mentions new items. Test fixture: 2352 SCENIC VIEW Court Jacksonville FL (Zillow 44455413_zpid). Year built: 1997, Condition: "Updated/Remodeled", Features: "BRAND NEW ROOF", "NEW AC", "Fresh paint", description: "Fully updated throughout and move in ready". EXPECTED before fix: total_deductions ≈ $28,000 (AC + roof + plumbing? No, plumbing no aplica porque 1997 > 1995. Solo AC + roof = $16K). EXPECTED after fix: total_deductions = $0 (condition + keywords ambos disparan suprimir). """ from __future__ import annotations import sys sys.path.insert(0, "D:/Proyectos Software/AR-House") def test_scenic_view_data(): """Real user fixture — 2352 SCENIC VIEW Court.""" from data_fetchers.property_value import calculate_age_deductions # Real Zillow data as user reported fixture = { "year_built": 1997, "listing_description": ( "Fully updated throughout and move in ready. Fresh paint, " "BRAND NEW ROOF, garage epoxy floor, NEW AC, exterior paint." ), "condition_status": "Updated/Remodeled", "features_special": [ "Fresh paint", "BRAND NEW ROOF", "Garage epoxy", "NEW AC", "Exterior paint", ], } # CASE A: global skip via condition_status='Updated/Remodeled' result_a = calculate_age_deductions(**fixture) print("=" * 70) print("CASE A — Full fixture (condition + features + description):") print(f" total: ${result_a['total']:,}") print(f" _skipped_global: {result_a.get('_skipped_global')}") print(f" _skip_reason: {result_a.get('_skip_reason')}") assert result_a["total"] == 0, f"Expected 0 deductions, got ${result_a['total']:,}" assert result_a["_skipped_global"] is True, "Expected _skipped_global=True" print(" PASS: total=$0, _skipped_global=True") # CASE B: only condition_status, no description/features → still skip result_b = calculate_age_deductions( year_built=1997, condition_status="Updated/Remodeled", ) print() print("CASE B — Only condition_status='Updated/Remodeled':") print(f" total: ${result_b['total']:,}") assert result_b["total"] == 0, f"Expected 0, got ${result_b['total']:,}" print(" PASS: total=$0 (condition tag alone suffices)") # CASE C: only description, no condition_status → still skip via 'fully updated' result_c = calculate_age_deductions( year_built=1997, listing_description="Fully updated throughout and move in ready.", ) print() print("CASE C — Only description 'fully updated, move in ready':") print(f" total: ${result_c['total']:,}") print(f" _skip_reason: {result_c.get('_skip_reason')}") assert result_c["total"] == 0, f"Expected 0, got ${result_c['total']:,}" print(" PASS: global keyword detected, total=$0") # CASE D: only features array (NEW AC, BRAND NEW ROOF) → per-item suppression # No 'Updated/Remodeled' tag, no global 'fully updated' keyword. # AC suppressed (year<2010), Roof suppressed (year<2005 N/A since 1997). result_d = calculate_age_deductions( year_built=1997, features_special=["BRAND NEW ROOF", "NEW AC"], ) print() print("CASE D — Only features_special tags (no condition, no description):") print(f" total: ${result_d['total']:,}") print(f" ac: ${result_d['ac']:,} (year<2010, expecting suppression)") print(f" roof: ${result_d['roof']:,} (year=1997 > 2005, no deduction triggered anyway)") print(f" _suppressed_items: {result_d.get('_suppressed_items')}") print(f" _reasons: {result_d.get('_reasons')}") assert result_d["ac"] == 0, f"AC should be suppressed (NEW AC in features), got ${result_d['ac']:,}" assert "ac" in result_d["_suppressed_items"], "ac should be in suppressed_items" print(" PASS: AC deduction suppressed by features tag 'NEW AC'") # CASE E: baseline — old property NO renovation evidence → all deductions apply result_e = calculate_age_deductions(year_built=1985) print() print("CASE E — Old property (1985), no renovation evidence (baseline):") print(f" total: ${result_e['total']:,}") print(f" ac: ${result_e['ac']:,}, roof: ${result_e['roof']:,}, plumbing: ${result_e['plumbing']:,}, panel: ${result_e['panel']:,}") assert result_e["ac"] > 0, "AC should apply (year<2010)" assert result_e["roof"] > 0, "Roof should apply (year<2005)" assert result_e["plumbing"] > 0, "Plumbing polybutylene should apply (1985 in 1978-1995)" assert result_e["panel"] > 0, "Panel should apply (year<1990)" print(f" PASS: all 4 deductions apply correctly = ${result_e['total']:,}") # CASE F: same old property BUT description says repiped + new panel result_f = calculate_age_deductions( year_built=1985, listing_description="Re-piped 2022 with PEX. New 200 amp panel. Original AC and roof.", ) print() print("CASE F — Old property (1985) with partial renovation evidence:") print(f" total: ${result_f['total']:,}") print(f" ac: ${result_f['ac']:,} (should apply — Original AC)") print(f" roof: ${result_f['roof']:,} (should apply — Original roof)") print(f" plumbing: ${result_f['plumbing']:,} (should be 0 — repiped)") print(f" panel: ${result_f['panel']:,} (should be 0 — new panel)") print(f" _suppressed_items: {result_f.get('_suppressed_items')}") assert result_f["plumbing"] == 0, "Plumbing should be suppressed by 'Re-piped'" assert result_f["panel"] == 0, "Panel should be suppressed by 'New 200 amp panel'" assert result_f["ac"] > 0, "AC should still apply (Original AC mentioned)" assert result_f["roof"] > 0, "Roof should still apply" print(" PASS: partial suppression works correctly") print() print("=" * 70) print("ALL TESTS PASSED. Bug fix verified.") print("=" * 70) def test_zillow_detail_parser(): """Test the markdown parser extracts condition/features/status correctly.""" from scrapers.zillow import _parse_property_detail_md # Simulated Zillow markdown for 2352 SCENIC VIEW fake_md = """ # 2352 Scenic View Ct, Jacksonville, FL 32218 **$265,000** Status: Active under contract ## What's special - Fresh paint - BRAND NEW ROOF - Garage epoxy - NEW AC - Exterior paint ## Description Fully updated throughout and move in ready. This 1997 home features fresh paint inside and out, brand new roof, new AC, garage epoxy floor. ## Facts & Features Year built: 1997 Condition: Updated/Remodeled Type: Single Family ## Home value Zestimate: $261,800 Tax assessed value: $222,653 """ parsed = _parse_property_detail_md(fake_md) print() print("=" * 70) print("ZILLOW DETAIL PARSER TEST:") print("=" * 70) for k, v in parsed.items(): if isinstance(v, list): print(f" {k}: {v[:5]}") elif isinstance(v, str) and len(v) > 80: print(f" {k}: {v[:100]}...") else: print(f" {k}: {v}") assert parsed["condition_status"] in ("Updated/Remodeled", "Updated/Remodeled".lower().capitalize()), \ f"Expected condition_status='Updated/Remodeled', got {parsed['condition_status']!r}" assert parsed["year_built"] == 1997, f"Expected year_built=1997, got {parsed['year_built']}" assert "active under contract" in (parsed["home_status"] or "").lower(), \ f"Expected active under contract, got {parsed['home_status']!r}" assert parsed["active_under_contract"] is True, "Expected active_under_contract=True" assert len(parsed["features_special"]) >= 4, \ f"Expected ≥4 features, got {len(parsed['features_special'])}: {parsed['features_special']}" assert parsed["zestimate"] == 261800, f"Expected zestimate=261800, got {parsed['zestimate']}" assert parsed["tax_assessed_value"] == 222653, \ f"Expected tax_assessed=222653, got {parsed['tax_assessed_value']}" assert len(parsed["renovation_keywords_found"]) > 0, "Expected renovation keywords detected" print() print("PASS: parser extracts all expected fields correctly.") if __name__ == "__main__": test_scenic_view_data() test_zillow_detail_parser()