# ============================================================================= # QWHPI — Quebec Weekly Housing Price Index # Author : Simon-Pierre Boucher # Contact : contact@spboucher.ai # File : engine/tests/test_smoke_pipeline.py # Purpose : CI smoke test — synthetic market through stage 1 + stage 2; # the recovered index must track the simulated price path. # ============================================================================= """End-to-end smoke test on a fully synthetic market (no data files).""" from __future__ import annotations import datetime as dt import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) import numpy as np import polars as pl from qwhpi.hierarchy import cell_deviation, stage1_residualize, week_grid RNG = np.random.default_rng(3) def simulate(n_weeks: int = 60, per_week: int = 150) -> tuple[pl.DataFrame, np.ndarray]: """Synthetic province with a known weekly log price path.""" path = np.cumsum(RNG.normal(0.005, 0.003, n_weeks)) # clear boom rows = [] start = dt.date(2021, 1, 4) for w in range(n_weeks): week = start + dt.timedelta(weeks=w) for i in range(per_week): fa = float(np.exp(RNG.normal(4.7, 0.35))) age_yrs = int(RNG.integers(0, 90)) ptype = ["unifamilial", "condo", "plex"][int(RNG.integers(0, 3))] base = 12.0 + 0.6 * (np.log(fa) - 4.7) - 0.002 * age_yrs \ + {"unifamilial": 0.0, "condo": -0.15, "plex": 0.2}[ptype] lp = base + path[w] + RNG.normal(0, 0.25) rows.append({ "id": f"{w}-{i}", "log_amount": lp, "week_str": week.isoformat(), "propertyType": ptype, "log_fa": np.log(fa), "fa_missing": False, "age_bin": f"bin{age_yrs // 20}", "building_type": "single-story", "loc_fine": f"Z{int(RNG.integers(0, 6))}", "region_code": "03" if RNG.random() < 0.5 else "06", "geo_code": "23027", "municipality": "Québec", }) return pl.DataFrame(rows), path def test_pipeline_recovers_simulated_path(): df, truth = simulate() s1 = stage1_residualize(df) # province path per type ~ truth (up to a constant) uni = s1.paths.filter(pl.col("property_type") == "unifamilial").sort("week") est = uni["delta"].to_numpy() est = est - est.mean() tr = truth[-len(est):] - truth[-len(est):].mean() corr = np.corrcoef(est, tr)[0, 1] assert corr > 0.9, f"stage-1 path corr {corr:.3f}" # stage 2: a region with no true deviation should stay near zero grid = week_grid(s1.residuals) cell = s1.residuals.filter( (pl.col("region_code") == "03") & (pl.col("propertyType") == "unifamilial")).rename({"resid": "dev"}) dev = cell_deviation(cell.select("week_str", "dev"), grid, float(cell["dev"].var())) assert np.abs(dev.fit.smoothed).max() < 0.05