172 lines
5.9 KiB
Python
172 lines
5.9 KiB
Python
"""employment_agent parse_employment_response — linkedin_url is an agent key.
|
|
|
|
parse_employment_response returns a DICT. These tests used to unpack it
|
|
positionally, which silently read dict KEYS instead of values and asserted
|
|
against whatever the last key happened to be.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from employment_agent.decorators import parse_employment_response
|
|
from employment_agent.prompt import EDUCATION, NO_COMPANY, NO_LINKEDIN
|
|
|
|
|
|
def test_parses_linkedin_url_key_separately():
|
|
# The resume must actually contain the slug: _clean_linkedin keeps a URL
|
|
# only when the CV evidences it, so a resume that never mentions LinkedIn
|
|
# correctly yields None however confident the model was.
|
|
fields = parse_employment_response(
|
|
{
|
|
"current_employment": "Acme",
|
|
"education": "BS CS",
|
|
"current_title": "Engineer",
|
|
"linkedin_url": "https://www.linkedin.com/in/jane-doe",
|
|
},
|
|
"Acme BS CS Engineer https://www.linkedin.com/in/jane-doe",
|
|
)
|
|
assert fields["current_employment"] == "Acme"
|
|
assert fields["education"] == "BS CS"
|
|
assert fields["current_title"] == "Engineer"
|
|
assert fields["linkedin_url"] == "https://www.linkedin.com/in/jane-doe"
|
|
|
|
|
|
def test_url_absent_from_the_resume_is_not_trusted():
|
|
fields = parse_employment_response(
|
|
{
|
|
"current_employment": "Acme",
|
|
"education": "BS CS",
|
|
"current_title": "Engineer",
|
|
"linkedin_url": "https://www.linkedin.com/in/jane-doe",
|
|
},
|
|
"Acme BS CS Engineer",
|
|
)
|
|
assert fields["linkedin_url"] is None
|
|
|
|
|
|
def test_sentinel_and_non_linkedin_are_dropped():
|
|
sentinel = parse_employment_response(
|
|
{
|
|
"current_employment": NO_COMPANY,
|
|
"education": EDUCATION,
|
|
"current_title": "x",
|
|
"linkedin_url": NO_LINKEDIN,
|
|
},
|
|
"",
|
|
)
|
|
assert sentinel["linkedin_url"] is None
|
|
|
|
github = parse_employment_response(
|
|
{
|
|
"current_employment": NO_COMPANY,
|
|
"education": EDUCATION,
|
|
"current_title": "x",
|
|
"linkedin_url": "https://github.com/jane",
|
|
},
|
|
"",
|
|
)
|
|
assert github["linkedin_url"] is None
|
|
|
|
|
|
def test_adds_scheme_and_rejects_company_page():
|
|
bare = parse_employment_response(
|
|
{
|
|
"current_employment": NO_COMPANY,
|
|
"education": EDUCATION,
|
|
"current_title": "x",
|
|
"linkedin_url": "www.linkedin.com/in/jane-doe",
|
|
},
|
|
"",
|
|
)
|
|
assert bare["linkedin_url"] == "https://www.linkedin.com/in/jane-doe"
|
|
|
|
company_page = parse_employment_response(
|
|
{
|
|
"current_employment": NO_COMPANY,
|
|
"education": EDUCATION,
|
|
"current_title": "x",
|
|
"linkedin_url": "https://www.linkedin.com/company/acme",
|
|
},
|
|
"",
|
|
)
|
|
assert company_page["linkedin_url"] is None
|
|
|
|
|
|
def test_city_prompt_asks_for_a_proper_city_name():
|
|
from employment_agent.prompt import prompt
|
|
text = prompt()
|
|
assert "Karachi(Malir)" in text
|
|
assert 'JSON city must be "Karachi"' in text
|
|
assert "Return ONE proper city name only" in text
|
|
assert "city_list_prompt" not in text
|
|
assert "Pakistan:" in text
|
|
assert "United Kingdom:" in text
|
|
assert "Karachi" in text
|
|
assert "London" in text
|
|
|
|
|
|
def test_countries_dataset_is_global_and_includes_pakistan():
|
|
from global_cities import CITY_BY_KEY, Countries, countries_prompt_block
|
|
|
|
assert isinstance(Countries, dict)
|
|
assert "Pakistan" in Countries
|
|
assert "Karachi" in Countries["Pakistan"]
|
|
assert "United Kingdom" in Countries
|
|
assert "London" in Countries["United Kingdom"]
|
|
assert "United States" in Countries
|
|
assert "New York" in Countries["United States"]
|
|
assert CITY_BY_KEY["karachi"] == "Karachi"
|
|
assert CITY_BY_KEY["london"] == "London"
|
|
block = countries_prompt_block()
|
|
assert block.startswith("Afghanistan:")
|
|
assert "Pakistan: " in block
|
|
assert "Karachi" in block
|
|
|
|
|
|
def test_canonical_city_maps_messy_localities():
|
|
from employment_agent.decorators import canonical_city
|
|
from employment_agent.prompt import NO_CITY
|
|
|
|
assert canonical_city("Karachi(Malir)") == "Karachi"
|
|
assert canonical_city("Karachi (Malir)") == "Karachi"
|
|
assert canonical_city("Karachi Malir") == "Karachi"
|
|
assert canonical_city("DHA Karachi") == "Karachi"
|
|
assert canonical_city("Karachi DHA") == "Karachi"
|
|
assert canonical_city("Lahore Cantt") == "Lahore"
|
|
assert canonical_city("Gulberg, Lahore") == "Lahore"
|
|
assert canonical_city("F-10 Islamabad") == "Islamabad"
|
|
assert canonical_city("Wah Cantt") == "Wah"
|
|
assert canonical_city("Karachi") == "Karachi"
|
|
assert canonical_city("Karachi Malir Wah Cantt") == "Karachi"
|
|
assert canonical_city("London(Westminster)") == "London"
|
|
assert canonical_city("New York") == "New York"
|
|
assert canonical_city("Dubai Marina") == "Dubai"
|
|
assert canonical_city("") is None
|
|
assert canonical_city(" ") is None
|
|
assert canonical_city(NO_CITY) is None
|
|
assert canonical_city("none") is None
|
|
assert canonical_city("n/a") is None
|
|
|
|
|
|
def test_list_cities_is_distinct_without_openai(monkeypatch):
|
|
import asyncio
|
|
import inspect
|
|
|
|
from g_sheet.models import FormData
|
|
from inbox.models import Inbox_Messages
|
|
from inbox.views import Email
|
|
|
|
source = inspect.getsource(Email.list_cities)
|
|
assert "normalize_cities" not in source
|
|
assert "llm_call" not in source
|
|
|
|
async def inbox_cities(session):
|
|
return ["Karachi", "Lahore", "Karachi"]
|
|
|
|
async def form_cities(session):
|
|
return ["Lahore", "Islamabad", " ", None]
|
|
|
|
monkeypatch.setattr(Inbox_Messages, "distinct_cities", inbox_cities)
|
|
monkeypatch.setattr(FormData, "distinct_cities", form_cities)
|
|
cities = asyncio.run(Email(session=object()).list_cities())
|
|
assert cities == ["Islamabad", "Karachi", "Lahore"]
|