"""Phase 1 — factor primitives + end-to-end shortlist on CACHED data ($0).""" import glob import json import pytest from app.engine import normalize as N from app.engine import rank as R _NORM = {"blackfalcondata": N.naukri_feed_to_scoutjob, "shahidirfan": N.foundit_to_scoutjob, "harvestapi": N.linkedin_to_scoutjob} def _cached_jobs() -> list[dict]: jobs = [] for f in glob.glob(".apify_cache/*.json"): fn = _NORM.get(f.split("/")[-1].split("_")[0]) if not fn: continue for it in json.load(open(f)): sj = fn(it) if sj: jobs.append(sj) return jobs # ── factor primitives ── def test_location_gate_and_tiers(): p = R.profile_from({"title": "PM", "location": ["New Delhi · India"]}) assert R.f_location(p, {"location_city": "New Delhi", "location_country": "India"}) == 1.0 assert R.f_location(p, {"location_city": "Bengaluru", "location_country": "India"}) == 0.6 assert R.f_location(p, {"location_city": "London", "location_country": "UK"}) == 0.0 assert R.f_location(p, {"location_mode": "remote"}) == 1.0 p_open = R.profile_from({"title": "PM"}) assert R.f_location(p_open, {"location_city": "X", "location_country": "Y"}) is None # Anywhere → absent def test_experience_band(): p = R.profile_from({"title": "PM", "years": 5}) assert R.f_experience(p, {"seniority_level": "mid"}) == 1.0 # 5 in [3,8] assert R.f_experience(p, {"seniority_level": "lead"}) < 1.0 # under-qualified assert R.f_experience(p, {"seniority_level": None}) is None # absent def test_e2e_ranks_all_gate_passing_no_early_cut(): jobs = _cached_jobs() if len(jobs) < 30: pytest.skip("no cached sweep data — run a search first") prefs = {"title": "Product Manager", "role": ["Product"], "location": ["New Delhi · India"], "experience": ["Mid-level"], "stretch": "balanced"} res = R.rank(prefs, None, jobs) opp = res["opportunities"] # the cheap white-box does NOT cut to 25 — it ranks ALL gate-passing jobs (cut is Phase 3) assert len(opp) >= 1 assert res["ranked"] == len(opp) and res["scanned"] == len(jobs) scores = [j["matchScore"] for j in opp] assert scores == sorted(scores, reverse=True) # ranked best-first for j in opp: m = j["match"] assert m["fit"] in ("fit", "stretch") and isinstance(m["breakdown"], list) assert m["score"] == j["matchScore"] # determinism res2 = R.rank(prefs, None, _cached_jobs()) assert [j["id"] for j in res2["opportunities"]] == [j["id"] for j in opp] def test_select_is_separate_and_capped(): # the final cut (Phase 3) — caps at ~25, floor so never empty, by fused matchScore ranked = [{"id": str(i), "matchScore": 100 - i} for i in range(80)] sel = R.select(ranked, "balanced") assert 1 <= len(sel) <= R.CAP assert [j["matchScore"] for j in sel] == sorted((j["matchScore"] for j in sel), reverse=True) # floor: even if nothing clears the bar, return at least `floor` low = [{"id": str(i), "matchScore": 10} for i in range(40)] assert len(R.select(low, "safe")) == R.FLOOR