Spaces:
Sleeping
Sleeping
| """ | |
| Deterministic regression for External ATS Feedback Repair Mode. | |
| Uses StubProvider (no API keys). Pastes fake external-checker feedback that mixes: | |
| - LOW/plausible PM terms (should be woven in), | |
| - a BLOCKED credential (CISSP β must NEVER appear), | |
| - a HIGH-risk specialized term (SIEM β must NOT be auto-added; surfaced for | |
| confirmation). | |
| Asserts the honesty guarantees hold: blocked/high terms stay OUT of the resume, | |
| scores come from the re-parsed export, and the result carries the right buckets. | |
| """ | |
| import os, sys, io, shutil | |
| sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8") | |
| sys.path.insert(0, os.path.abspath(".")) | |
| # Isolate the vault so prior confirmations don't change results. | |
| import src.candidate_vault as _cv | |
| _cv._VAULT_PATH = "data/_test_vault_feedback.json" | |
| if os.path.exists(_cv._VAULT_PATH): | |
| os.remove(_cv._VAULT_PATH) | |
| import shutil as _sh | |
| 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): | |
| _sh.copyfile(src_pdf, dst) | |
| from src.resume_parser_v2 import parse_resume_pdf | |
| from src.providers import StubProvider | |
| from src.jobalytics_repair import parse_external_feedback, repair_with_external_feedback | |
| from src.resume_customizer import _read_docx_text | |
| ok = True | |
| def check(name, cond, detail=""): | |
| global ok; ok = ok and cond | |
| print(f" [{'PASS' if cond else 'FAIL'}] {name} {detail}") | |
| # 1. Feedback parser | |
| fb = ("Resume Match 73%. Missing keywords: roadmap, stakeholder management, " | |
| "A/B testing, CISSP, SIEM. Matched keywords: agile, jira") | |
| parsed = parse_external_feedback(fb) | |
| print("1. parse_external_feedback:") | |
| check("external_score parsed = 73", parsed["external_score"] == 73, str(parsed["external_score"])) | |
| check("missing keywords parsed", "roadmap" in [m.lower() for m in parsed["missing_keywords"]] | |
| and "cissp" in [m.lower() for m in parsed["missing_keywords"]]) | |
| check("no trailing punctuation", all(not m.endswith(".") for m in parsed["missing_keywords"])) | |
| # 2. Repair honesty | |
| base = parse_resume_pdf(dst) | |
| jd = open("tests/fixtures/jds/generic_pm_3_7yrs.txt", encoding="utf-8").read() | |
| job = {"title": "Product Manager", "company": "TestCo", "description": jd, "_raw_assessment": {}} | |
| res = repair_with_external_feedback(job, feedback_text=fb, provider=StubProvider(), | |
| base_resume=base, output_dir="data/output/resumes/_fb_test") | |
| print("2. repair_with_external_feedback:", res.get("status"), res.get("scores")) | |
| check("no error", "error" not in res, res.get("error", "")) | |
| check("CISSP is blocked", "CISSP" in res.get("blocked_terms", [])) | |
| check("SIEM is unresolved-high", "SIEM" in res.get("unresolved_high_risk_terms", [])) | |
| check("external_score carried = 73", res.get("external_score") == 73) | |
| check("scores present (internal/independent)", | |
| isinstance(res.get("scores", {}).get("internal_jd_match"), int) | |
| and isinstance(res.get("scores", {}).get("independent_jd_match"), int)) | |
| txt = _read_docx_text(res["resume_path"]).lower() | |
| check("CISSP NOT in resume (no fabricated cert)", "cissp" not in txt) | |
| check("SIEM NOT auto-added (high-risk, unconfirmed)", "siem" not in txt) | |
| # 3. No-keywords guard | |
| res2 = repair_with_external_feedback(job, feedback_text="great resume, no notes", | |
| provider=StubProvider(), base_resume=base, | |
| output_dir="data/output/resumes/_fb_test") | |
| check("empty feedback -> no_missing_keywords error", res2.get("error") == "no_missing_keywords") | |
| print("\n" + ("β ALL FEEDBACK-REPAIR CHECKS PASS" if ok else "β SOME FAILED")) | |
| shutil.rmtree("data/output/resumes/_fb_test", ignore_errors=True) | |
| if os.path.exists(_cv._VAULT_PATH): | |
| os.remove(_cv._VAULT_PATH) | |
| sys.exit(0 if ok else 1) | |