"""E2E integration — the full curate machinery (deterministic, offline) + an opt-in LIVE stack run. The offline test mocks the gateway so the WHOLE two-stage curate path runs in CI: Opus-score JSON → rubric.aggregate → calibration → floor filter → best-first sort → Haiku prose merge. It asserts the contract that matters: an aligned job reaches the 90s, a weak job is DROPPED at the floor, and every surviving match decomposes to real evidence. The live test (RUN_LIVE_E2E=1) runs the real new stack + Opus/Haiku — documents the manual verification (sweep → ~133 jobs incl. TimesJobs full-signal → scores reach 96). """ from __future__ import annotations import json import os import pytest from app.engine import curate as C from app.config import get_settings # ── deterministic offline e2e (mocks the gateway) ──────────────────────────── class _Msg: def __init__(self, content): self.content = content class _Choice: def __init__(self, content): self.message = _Msg(content); self.finish_reason = "stop" class _Resp: def __init__(self, content): self.choices = [_Choice(content)] self.usage = type("U", (), {"prompt_tokens": 50, "completion_tokens": 50})() class _Completions: def __init__(self, score_model, score_json, cards_json): self._sm, self._s, self._c = score_model, score_json, cards_json def create(self, *, model, **kw): return _Resp(self._s if model == self._sm else self._c) class _Chat: def __init__(self, *a): self.completions = _Completions(*a) class _FakeClient: def __init__(self, score_model, score_json, cards_json): self.chat = _Chat(score_model, score_json, cards_json) def test_curate_e2e_scores_calibrate_floor_and_decompose(monkeypatch): s = get_settings() sifted = [ {"id": "j1", "title": "Senior Sales Manager", "organization": "Acme", "details": {"description": "lead B2B sales"}}, {"id": "j2", "title": "Sales Manager", "organization": "Beta", "details": {"description": "manage accounts"}}, {"id": "j3", "title": "Junior Analyst", "organization": "Gamma", "details": {"description": "data entry"}}, ] # Opus rates dimensions; j1 = strongly aligned (→ 90s), j2 = good, j3 = weak (raw < floor → dropped). score_json = json.dumps({"kept": [ {"id": "j1", "fit": "fit", "dimensions": {"role": {"score": 100, "note": "same function"}, "skills": {"score": 92, "note": "B2B + CRM"}, "seniority": {"score": 95, "note": "right level"}, "location": {"score": 100, "note": "target city"}, "industry": {"score": 90, "note": "fintech"}, "experience": {"score": 90, "note": "9 yrs"}}}, {"id": "j2", "fit": "fit", "dimensions": {"role": {"score": 90}, "skills": {"score": 75}, "seniority": {"score": 80}, "location": {"score": 100}, "experience": {"score": 75}}}, {"id": "j3", "fit": "stretch", "dimensions": {"role": {"score": 20}, "skills": {"score": 25}, "seniority": {"score": 30}, "location": {"score": 100}, "experience": {"score": 20}}}, ]}) cards_json = json.dumps({"cards": [ {"id": "j1", "archetype": "The Perfect Fit", "one_line": "Strong on role and seniority.", "coach_note": "Lead with revenue numbers.", "growth": {"text": "x", "from": 90, "to": 96}}, {"id": "j2", "archetype": "The Solid Step", "one_line": "Good role match, lighter on skills."}, ]}) monkeypatch.setattr(C, "curate_enabled", lambda: True) monkeypatch.setattr(C, "gateway_client", lambda: _FakeClient(s.CURATE_MODEL, score_json, cards_json)) out = C.curate({"title": "Sales Manager"}, {"skills": ["B2B Sales"]}, sifted) assert out is not None ids = [o["id"] for o in out] assert "j3" not in ids, "weak job (raw < floor) must be DROPPED — floor honesty" assert ids == ["j1", "j2"], "best-first by the COMPUTED score" j1 = out[0] assert j1["matchScore"] >= 90, f"aligned job should reach the 90s, got {j1['matchScore']}" assert all(o["matchScore"] >= s.MATCH_FLOOR for o in out), "no sub-floor match surfaces" # decomposes to real evidence bd = {d["name"]: d["score"] for d in j1["match"]["breakdown"]} assert len(bd) >= 5 and j1["match"]["archetype"] == "The Perfect Fit" assert j1["match"]["coach_note"] == "Lead with revenue numbers." def test_curate_floor_only_returns_empty_not_unfiltered_fallback(monkeypatch): # Opus succeeds but keeps nothing ≥ floor → honest empty deck, NEVER the unfiltered white-box s = get_settings() sifted = [{"id": "j1", "title": "X", "organization": "Y", "details": {}}] score_json = json.dumps({"kept": [{"id": "j1", "fit": "stretch", "dimensions": {"role": {"score": 10}, "skills": {"score": 10}, "seniority": {"score": 10}, "location": {"score": 10}, "experience": {"score": 10}}}]}) monkeypatch.setattr(C, "curate_enabled", lambda: True) monkeypatch.setattr(C, "gateway_client", lambda: _FakeClient(s.CURATE_MODEL, score_json, "{}")) out = C.curate({"title": "X"}, None, sifted) assert out == [], "all-below-floor → empty, not a fallback that could leak sub-floor matches" # ── opt-in LIVE e2e (real stack + Opus) ────────────────────────────────────── @pytest.mark.skipif(os.environ.get("RUN_LIVE_E2E") != "1", reason="live stack run — set RUN_LIVE_E2E=1") def test_live_stack_reaches_90s_with_full_signal(): import asyncio from app.engine import search, sift as _sift prefs = {"title": "Sales Manager", "location": ["Mumbai · India"], "role": ["Sales"], "industry": ["Fintech"], "experience": ["Senior"], "user_context": {"id": "live", "skills": ["B2B Sales", "Account Management", "CRM"], "current_role": "Senior Sales Manager", "years_experience": 9}} ctx = prefs["user_context"] sweep = asyncio.run(search.run_sweep({**prefs, "_fresh": True}, fresh=True)) assert len(sweep["sources"]) >= 4, "most of the stack should return" assert len(sweep["opportunities"]) >= 80, "deck should be deep" top, _ = _sift.sift(prefs, ctx, [dict(j) for j in sweep["opportunities"]]) cur = C.curate(prefs, ctx, top) or [] scores = [o["matchScore"] for o in cur] assert scores and max(scores) >= 90, f"aligned profile should reach 90s, got max {max(scores) if scores else None}" assert min(scores) >= get_settings().MATCH_FLOOR, "floor honored"