Replaces NotImplementedError stub with full LLM-backed implementation. Builds a prompt from the last 3 resume positions plus already-selected skills/domains/keywords, calls LLMRouter, and returns de-duped suggestions in all three categories.
148 lines
6.4 KiB
Python
148 lines
6.4 KiB
Python
"""Tests for scripts/suggest_helpers.py."""
|
|
import json
|
|
import pytest
|
|
from pathlib import Path
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
RESUME_PATH = Path(__file__).parent.parent / "config" / "plain_text_resume.yaml"
|
|
|
|
|
|
# ── _parse_json ───────────────────────────────────────────────────────────────
|
|
|
|
def test_parse_json_extracts_valid_object():
|
|
from scripts.suggest_helpers import _parse_json
|
|
raw = 'Here is the result: {"a": [1, 2], "b": "hello"} done.'
|
|
assert _parse_json(raw) == {"a": [1, 2], "b": "hello"}
|
|
|
|
|
|
def test_parse_json_returns_empty_on_invalid():
|
|
from scripts.suggest_helpers import _parse_json
|
|
assert _parse_json("no json here") == {}
|
|
assert _parse_json('{"broken": ') == {}
|
|
|
|
|
|
# ── suggest_search_terms ──────────────────────────────────────────────────────
|
|
|
|
BLOCKLIST = {
|
|
"companies": ["Meta", "Amazon"],
|
|
"industries": ["gambling"],
|
|
"locations": [],
|
|
}
|
|
USER_PROFILE = {
|
|
"career_summary": "Customer success leader with 10 years in B2B SaaS.",
|
|
"mission_preferences": {
|
|
"animal_welfare": "I volunteer at my local shelter.",
|
|
"education": "",
|
|
},
|
|
"nda_companies": ["Acme Corp"],
|
|
}
|
|
|
|
|
|
def _mock_llm(response_dict: dict):
|
|
"""Return a patcher that makes LLMRouter().complete() return a JSON string."""
|
|
mock_router = MagicMock()
|
|
mock_router.complete.return_value = json.dumps(response_dict)
|
|
return patch("scripts.suggest_helpers.LLMRouter", return_value=mock_router)
|
|
|
|
|
|
def test_suggest_search_terms_returns_titles_and_excludes():
|
|
from scripts.suggest_helpers import suggest_search_terms
|
|
payload = {"suggested_titles": ["VP Customer Success"], "suggested_excludes": ["cold calling"]}
|
|
with _mock_llm(payload):
|
|
result = suggest_search_terms(["Customer Success Manager"], RESUME_PATH, BLOCKLIST, USER_PROFILE)
|
|
assert result["suggested_titles"] == ["VP Customer Success"]
|
|
assert result["suggested_excludes"] == ["cold calling"]
|
|
|
|
|
|
def test_suggest_search_terms_prompt_contains_blocklist_companies():
|
|
from scripts.suggest_helpers import suggest_search_terms
|
|
with _mock_llm({"suggested_titles": [], "suggested_excludes": []}) as mock_cls:
|
|
suggest_search_terms(["CSM"], RESUME_PATH, BLOCKLIST, USER_PROFILE)
|
|
prompt_sent = mock_cls.return_value.complete.call_args[0][0]
|
|
assert "Meta" in prompt_sent
|
|
assert "Amazon" in prompt_sent
|
|
|
|
|
|
def test_suggest_search_terms_prompt_contains_mission():
|
|
from scripts.suggest_helpers import suggest_search_terms
|
|
with _mock_llm({"suggested_titles": [], "suggested_excludes": []}) as mock_cls:
|
|
suggest_search_terms(["CSM"], RESUME_PATH, BLOCKLIST, USER_PROFILE)
|
|
prompt_sent = mock_cls.return_value.complete.call_args[0][0]
|
|
assert "animal_welfare" in prompt_sent or "animal welfare" in prompt_sent.lower()
|
|
|
|
|
|
def test_suggest_search_terms_prompt_contains_career_summary():
|
|
from scripts.suggest_helpers import suggest_search_terms
|
|
with _mock_llm({"suggested_titles": [], "suggested_excludes": []}) as mock_cls:
|
|
suggest_search_terms(["CSM"], RESUME_PATH, BLOCKLIST, USER_PROFILE)
|
|
prompt_sent = mock_cls.return_value.complete.call_args[0][0]
|
|
assert "Customer success leader" in prompt_sent
|
|
|
|
|
|
def test_suggest_search_terms_returns_empty_on_bad_json():
|
|
from scripts.suggest_helpers import suggest_search_terms
|
|
mock_router = MagicMock()
|
|
mock_router.complete.return_value = "sorry, I cannot help with that"
|
|
with patch("scripts.suggest_helpers.LLMRouter", return_value=mock_router):
|
|
result = suggest_search_terms(["CSM"], RESUME_PATH, BLOCKLIST, USER_PROFILE)
|
|
assert result == {"suggested_titles": [], "suggested_excludes": []}
|
|
|
|
|
|
def test_suggest_search_terms_raises_on_llm_exhausted():
|
|
from scripts.suggest_helpers import suggest_search_terms
|
|
mock_router = MagicMock()
|
|
mock_router.complete.side_effect = RuntimeError("All LLM backends exhausted")
|
|
with patch("scripts.suggest_helpers.LLMRouter", return_value=mock_router):
|
|
with pytest.raises(RuntimeError, match="All LLM backends exhausted"):
|
|
suggest_search_terms(["CSM"], RESUME_PATH, BLOCKLIST, USER_PROFILE)
|
|
|
|
|
|
# ── suggest_resume_keywords ───────────────────────────────────────────────────
|
|
|
|
CURRENT_KW = {
|
|
"skills": ["Customer Success", "SQL"],
|
|
"domains": ["B2B SaaS"],
|
|
"keywords": ["NPS"],
|
|
}
|
|
|
|
|
|
def test_suggest_resume_keywords_returns_all_three_categories():
|
|
from scripts.suggest_helpers import suggest_resume_keywords
|
|
payload = {
|
|
"skills": ["Project Management"],
|
|
"domains": ["EdTech"],
|
|
"keywords": ["churn prevention"],
|
|
}
|
|
with _mock_llm(payload):
|
|
result = suggest_resume_keywords(RESUME_PATH, CURRENT_KW)
|
|
assert "skills" in result
|
|
assert "domains" in result
|
|
assert "keywords" in result
|
|
|
|
|
|
def test_suggest_resume_keywords_excludes_already_selected():
|
|
from scripts.suggest_helpers import suggest_resume_keywords
|
|
with _mock_llm({"skills": [], "domains": [], "keywords": []}) as mock_cls:
|
|
suggest_resume_keywords(RESUME_PATH, CURRENT_KW)
|
|
prompt_sent = mock_cls.return_value.complete.call_args[0][0]
|
|
# Already-selected tags should appear in the prompt so LLM knows to skip them
|
|
assert "Customer Success" in prompt_sent
|
|
assert "NPS" in prompt_sent
|
|
|
|
|
|
def test_suggest_resume_keywords_returns_empty_on_bad_json():
|
|
from scripts.suggest_helpers import suggest_resume_keywords
|
|
mock_router = MagicMock()
|
|
mock_router.complete.return_value = "I cannot assist."
|
|
with patch("scripts.suggest_helpers.LLMRouter", return_value=mock_router):
|
|
result = suggest_resume_keywords(RESUME_PATH, CURRENT_KW)
|
|
assert result == {"skills": [], "domains": [], "keywords": []}
|
|
|
|
|
|
def test_suggest_resume_keywords_raises_on_llm_exhausted():
|
|
from scripts.suggest_helpers import suggest_resume_keywords
|
|
mock_router = MagicMock()
|
|
mock_router.complete.side_effect = RuntimeError("All LLM backends exhausted")
|
|
with patch("scripts.suggest_helpers.LLMRouter", return_value=mock_router):
|
|
with pytest.raises(RuntimeError, match="All LLM backends exhausted"):
|
|
suggest_resume_keywords(RESUME_PATH, CURRENT_KW)
|