File size: 7,232 Bytes
0af6235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355c4d1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0af6235
355c4d1
0af6235
 
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#!/usr/bin/env python3
"""Regression for the live-test bugs (Phase 8 follow-up):

  BUG A  Tectonic was invoked with `--synctex 0` -> the `0` is consumed as the
         INPUT and the real .tex becomes an "unexpected argument", so the PDF
         never compiles. Fix: minimal `tectonic --outdir <dir> --keep-logs <tex>`.
  BUG B  content.js stripChrome() read textContent off a DETACHED clone, joining
         adjacent elements with NO whitespace -> Frankenstein tokens like
         "engineergreater hyderabad" / "managerzamp". Fix: insert separators.
  BUG C  A jobs LIST / search / recommendations page (title "Top job picks for
         you") produced junk keywords and a 0% resume. Fix: detect + fail loudly.

Deterministic, ASCII-only, no network, no LaTeX engine required.
"""
import os
import re
import sys

ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, ROOT)

_ok = True


def check(name, cond, detail=""):
    global _ok
    status = "PASS" if cond else "FAIL"
    if not cond:
        _ok = False
    print(f"  [{status}] {name}" + (f"  {detail}" if detail else ""))
    return cond


def read(rel):
    with open(os.path.join(ROOT, rel), encoding="utf-8") as f:
        return f.read()


print("=" * 70)
print("Phase 8 follow-up: extraction + LaTeX-compile bug fixes")
print("=" * 70)

# ── BUG A: Tectonic command line ─────────────────────────────────────────────
print("[A] Tectonic invocation is well-formed (no --synctex value trap)")
from src.latex_resume import _engine_commands

cmds = _engine_commands("tectonic", "/tmp/out/resume.tex", "/tmp/out")
cmd = cmds[0]
check("engine is tectonic", cmd[0] == "tectonic", " ".join(cmd))
check("no --synctex flag", "--synctex" not in cmd)
check("no bare numeric positional ('0')", "0" not in cmd)
check("input .tex is the final positional",
      cmd[-1] == "/tmp/out/resume.tex", cmd[-1])
check("--outdir points at the out dir",
      "--outdir" in cmd and cmd[cmd.index("--outdir") + 1] == "/tmp/out")

# ── BUG B: stripChrome inserts whitespace separators ─────────────────────────
print("[B] content.js stripChrome separates tokens (no concatenation)")
content = read("extension/content.js")
check("inserts a separator text node between elements",
      "createTextNode(' \\n')" in content)
check("reads textContent after inserting separators",
      "clone.textContent" in content)

# ── BUG C: non-job / listing-page detection ──────────────────────────────────
print("[C] content.js rejects jobs-list / recommendation pages")
check("NON_JOB_TITLES table present", "NON_JOB_TITLES" in content)
check("'top job picks for you' is blacklisted",
      "top job picks for you" in content.lower())
check("looksLikeListingJunk() exists", "looksLikeListingJunk" in content)
check("flags repeated 'easy apply' rows", "easy apply" in content.lower())
check("sets extraction_reason='not_a_job_posting'",
      "not_a_job_posting" in content)
check("raises the min-JD-length gate to 80",
      "length < 80" in content)

print("[C] popup.js surfaces the not-a-posting reason")
popup = read("extension/popup/popup.js")
check("popup handles not_a_job_posting", "not_a_job_posting" in popup)

# ── Realistic LaTeX injection coverage (no compile needed) ────────────────────
print("[D] Realistic PM LaTeX + JD -> honest keyword coverage")
from src.latex_resume import optimize_latex_resume

SAMPLE_LATEX = r"""
\documentclass{article}
\begin{document}
\section*{Summary}
Product Manager with experience shipping B2B SaaS products, partnering with
engineering and design, and running discovery with customers.
\section*{Experience}
\textbf{Senior Product Manager} \\ Acme SaaS (2021--2024)
\begin{itemize}
\item Owned the product roadmap for a B2B analytics platform.
\item Ran A/B tests and user research to prioritize the backlog.
\item Partnered with engineering on go-to-market for two launches.
\end{itemize}
\end{document}
"""

JD = (
    "We are hiring a Product Manager for our B2B SaaS platform. You will own the "
    "product roadmap, define product strategy, run user research and discovery, "
    "prioritize the backlog, partner with engineering and design, lead "
    "go-to-market, run A/B testing and experimentation, define KPIs and metrics, "
    "and work with stakeholders. Experience with agile, data analysis, SQL, "
    "machine learning and generative AI product features is a plus. Strong "
    "stakeholder management and roadmapping skills required."
)

report = optimize_latex_resume(
    SAMPLE_LATEX, JD, maximum_ats_mode=True, confirmed_terms=[],
    blocked_terms=[], compile_pdf=False, out_dir=None, job_title="pm",
)
pct = int(report.get("pct", 0) or 0)
injected = report.get("injected", [])
gated = report.get("gated", {}) or {}
blocked_injected = [t for t in injected
                    if "blocked" in str(gated.get(t, "")).lower()]
print(f"      coverage pct = {pct}%   injected = {len(injected)} terms")
check("coverage >= 85% on a well-matched PM JD (path can reach 90%+)",
      pct >= 85, f"{pct}%")
check("no blocked/fabrication term injected", not blocked_injected,
      str(blocked_injected))
for fab in ("cissp", "pmp", "cuda", "12+ years"):
    blob = (report.get("tex", "") or "").lower()
    check(f"no fabrication term in output: '{fab}'", fab not in blob)

# ── Guaranteed PDF fallback + multi-engine resilience ────────────────────────
print("[E] Guaranteed PDF fallback + multi-engine resilience")
import tempfile
import shutil as _sh
from src.latex_resume import render_text_to_pdf, latex_engines_available

_tmp = tempfile.mkdtemp()
_fb = os.path.join(_tmp, "fb.pdf")
_out = render_text_to_pdf(
    "Jane Doe\nProduct Manager\n\nEXPERIENCE\nOwned the product roadmap.", _fb)
check("render_text_to_pdf() produces a non-empty PDF (no LaTeX engine needed)",
      bool(_out) and os.path.exists(_fb) and os.path.getsize(_fb) > 0,
      f"{os.path.getsize(_fb) if os.path.exists(_fb) else 0} bytes")
_sh.rmtree(_tmp, ignore_errors=True)
check("latex_engines_available() returns a list (multi-engine fallback)",
      isinstance(latex_engines_available(), list))

api = read("api_server.py")
check("both LaTeX branches add a pdf_fallback flag", api.count("pdf_fallback") >= 4,
      f"count={api.count('pdf_fallback')}")
check("API fallback uses render_text_to_pdf", "render_text_to_pdf" in api)

# ── Panel <-> popup run sync ─────────────────────────────────────────────────
print("[F] Side-panel <-> popup run-state sync")
check("popup reflects a 'running' change from another surface",
      "entry.status === 'running'" in popup)
check("popup surfaces the pdf_fallback note", "pdf_fallback" in popup)

print("-" * 70)
print("PASS - extraction junk-guard + tectonic fix + PDF fallback + sync verified"
      if _ok else "FAIL - see above")
sys.exit(0 if _ok else 1)