Spaces:
Sleeping
Sleeping
File size: 3,586 Bytes
2bcd6c2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | """
Regression for the v2 ATS pipeline (jd_analyzer + evidence_matcher +
ats_scoring_v2 + ats_report). Deterministic only (no LLM) — proves the floor.
Asserts:
1. Structured JD analysis returns categorized requirements.
2. Evidence matcher marks out-of-domain skills (SIEM/SOAR/threat intel) as
GAPS against the real PM resume (no faking), and PM-core as supported/transferable.
3. Weighted JD match + ATS readability produce sane numbers.
4. The full report assembles with all explanation fields.
5. Missing-keyword feedback loop decides correctly.
"""
import os, sys, io, shutil
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.path.insert(0, os.path.abspath("."))
from src.resume_parser_v2 import parse_resume_pdf
from src.jd_analyzer import analyze_jd
from src.evidence_matcher import classify_all, gap_terms
from src.ats_scoring_v2 import score_ats_readability, score_jd_match
from src.ats_report import build_ats_report, reconcile_missing_keywords
src_pdf = r"C:\Users\Nxtwave\Desktop\resume\Saiteja_Tirunagari_Resume A 26 - Copy.pdf"
dst = "data/resume/resume.pdf"
os.makedirs("data/resume", exist_ok=True)
if not os.path.exists(dst) and os.path.exists(src_pdf):
shutil.copyfile(src_pdf, dst)
base = parse_resume_pdf(dst).to_flat_text()
ok = True
def check(name, cond, extra=""):
global ok
ok = ok and cond
print(f" [{'PASS' if cond else 'FAIL'}] {name} {extra}")
# 1. Structured analysis
jd_sec = open("tests/fixtures/jds/sumo_logic_pm.txt", encoding="utf-8").read()
req = analyze_jd(jd_sec)
print("1. Structured JD analysis (Sumo Logic / security PM):")
check("has target title", bool(req.target_role_titles), req.target_role_titles)
check("has hard skills", len(req.required_hard_skills + req.preferred_hard_skills) > 0)
check("has domain terms", len(req.domain_terms) > 0, [d.term for d in req.domain_terms])
# 2. Evidence honesty: security domain must be gaps; PM-core not faked
verdicts = classify_all(req.all_requirements(), base)
gaps = {v.keyword.lower() for v in gap_terms(verdicts)}
print("2. Evidence honesty:")
check("SIEM is a gap (not faked)", "siem" in gaps)
check("threat intelligence is a gap", "threat intelligence" in gaps)
supported = {v.keyword.lower() for v in verdicts if v.evidence_status in ("supported", "transferable")}
check("product management supported/transferable", "product management" in supported)
# 3 + 4. Full report on an IN-DOMAIN PM JD (use base as stand-in final text)
jd_pm = open("tests/fixtures/jds/generic_pm_3_7yrs.txt", encoding="utf-8").read()
rep = build_ats_report(base, base, jd_pm)
print("3+4. Report (generic PM JD):", rep["estimated_scores"])
check("ats_readability is int 0-100", 0 <= rep["estimated_scores"]["ats_readability"] <= 100)
check("jd_match is int 0-100", 0 <= rep["estimated_scores"]["jd_match"] <= 100)
check("has combined_range", "-" in rep["estimated_scores"]["combined_range"])
for f in ("strong_matches", "weak_matches", "unsupported_missing_keywords",
"formatting_checks", "recommendations", "jd_match_breakdown"):
check(f"report has {f}", f in rep)
# 5. Feedback loop
rows = reconcile_missing_keywords(["Kubernetes", "stakeholder management", "blockchain"],
jd_pm, base)
print("5. Feedback loop:")
dmap = {r["keyword"]: r["decision"] for r in rows}
check("Kubernetes -> ignore (not in JD)", dmap.get("Kubernetes") == "ignore")
check("blockchain -> ignore (not in JD)", dmap.get("blockchain") == "ignore")
print("\n" + ("✓ ALL PASS" if ok else "✗ SOME FAILED"))
sys.exit(0 if ok else 1)
|