You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

299 lines
8.7 KiB
Python

"""Tests for ATS board crawling (mock HTTP — no network)."""
from gimme_job.proactive.boards import (
BoardConfig,
_parse_icims_jobs,
fetch_board,
fetch_greenhouse,
fetch_lever,
fetch_smartrecruiters,
fetch_usajobs,
fetch_workday,
)
class _FakeClient:
"""Stand-in for httpx.Client: returns a configured response for each call."""
def __init__(self, responses):
self._responses = list(responses)
self._calls = 0
self.bodies = []
def __enter__(self):
return self
def __exit__(self, *a):
return False
def _next(self):
self._calls += 1
return self._responses[self._calls - 1]
def get(self, url):
return self._next()
def post(self, url, json=None):
self.bodies.append(json or {})
return self._next()
def _patch(monkeypatch, client):
monkeypatch.setattr("httpx.Client", lambda *a, **k: client)
class _Resp:
def __init__(self, **kw):
self._kw = kw
def raise_for_status(self):
pass
def json(self):
return self._kw.get("json", {})
@property
def content(self):
return self._kw.get("content", b"")
def test_greenhouse_filters_dental(monkeypatch):
client = _FakeClient(
[
_Resp(
json={
"jobs": [
{
"title": "Orthodontist",
"absolute_url": "https://gh/j/1",
"content": "braces",
},
{
"title": "Office Manager",
"absolute_url": "https://gh/j/2",
"content": "admin",
},
]
}
)
]
)
_patch(monkeypatch, client)
res = fetch_greenhouse(BoardConfig(name="Test", org="testorg", source="greenhouse"))
assert len(res) == 1
assert res[0].title == "Orthodontist"
assert res[0].url == "https://gh/j/1"
assert res[0].source == "greenhouse"
def test_greenhouse_ignores_employer_name_in_match(monkeypatch):
payload = {
"jobs": [
{
"title": "Account Payable Specialist at Specialty Dental Brands",
"absolute_url": "https://gh/j/1",
"content": "finance role",
},
{
"title": "Dentist at Anthem Pediatric Dentistry",
"absolute_url": "https://gh/j/2",
"content": "braces support",
},
]
}
client = _FakeClient([_Resp(json=payload)])
_patch(monkeypatch, client)
res = fetch_greenhouse(
BoardConfig(name="Specialty Dental Brands", org="sdb", source="greenhouse")
)
assert len(res) == 1
assert res[0].title.startswith("Dentist")
def test_boards_role_gate_drops_support_roles(monkeypatch):
payload = {
"jobs": [
{
"title": "Dental Assistant",
"absolute_url": "https://gh/j/1",
"content": "assist the orthodontic team",
},
{
"title": "Orthodontist",
"absolute_url": "https://gh/j/2",
"content": "braces",
},
]
}
client = _FakeClient([_Resp(json=payload)])
_patch(monkeypatch, client)
res = fetch_greenhouse(BoardConfig(name="Acme", org="acme", source="greenhouse"))
assert [r.title for r in res] == ["Orthodontist"]
def test_boards_drop_non_ortho_specialties(monkeypatch):
payload = {
"jobs": [
{
"title": "Pediatric Dentist",
"absolute_url": "https://gh/j/1",
"content": "orthodontics referrals",
},
{
"title": "Orthodontist",
"absolute_url": "https://gh/j/2",
"content": "braces",
},
]
}
client = _FakeClient([_Resp(json=payload)])
_patch(monkeypatch, client)
res = fetch_greenhouse(BoardConfig(name="Acme", org="acme", source="greenhouse"))
assert [r.title for r in res] == ["Orthodontist"]
def test_boards_pass_employer_type(monkeypatch):
payload = {
"jobs": [
{
"title": "Orthodontist",
"absolute_url": "https://gh/j/1",
"content": "braces",
}
]
}
client = _FakeClient([_Resp(json=payload)])
_patch(monkeypatch, client)
res = fetch_greenhouse(
BoardConfig(
name="Navajo Nation",
org="navajo",
source="greenhouse",
employer_type="tribal",
)
)
assert res[0].employer_type == "tribal"
def test_lever_filters_dental(monkeypatch):
client = _FakeClient(
[
_Resp(
json=[
{
"text": "Dentist II (Orthodontics)",
"hostedUrl": "https://lever/p/1",
"categories": {"location": "Phoenix, AZ"},
"descriptionPlain": "full-time orthodontics",
},
{
"text": "Nurse",
"hostedUrl": "https://lever/p/2",
"categories": {},
"descriptionPlain": "nursing",
},
]
)
]
)
_patch(monkeypatch, client)
res = fetch_lever(BoardConfig(name="Test", org="testorg", source="lever"))
assert len(res) == 1
assert res[0].title == "Dentist II (Orthodontics)"
def test_smartrecruiters_builds_job_url(monkeypatch):
payload = {
"content": [
{
"name": "Dentist",
"id": "12345",
"location": {"city": "Phoenix"},
},
]
}
# fetcher queries the API once per keyword (orthodont/dentist/dental)
client = _FakeClient([_Resp(json=payload)] * 3)
_patch(monkeypatch, client)
res = fetch_smartrecruiters(
BoardConfig(name="NH", org="NATIVEHEALTH", source="smartrecruiters")
)
assert len(res) == 1
assert res[0].url == "https://jobs.smartrecruiters.com/NATIVEHEALTH/12345"
def test_workday_filters(monkeypatch):
payload = {
"jobPostings": [
{
"title": "Orthodontist",
"externalPath": "/en-US/org/job/R1",
"locationsText": "OH",
},
{
"title": "Receptionist",
"externalPath": "/en-US/org/job/R2",
"locationsText": "OH",
},
]
}
# fetcher queries the tenant once per keyword (orthodontist/dentist/dental)
client = _FakeClient([_Resp(json=payload)] * 3)
_patch(monkeypatch, client)
res = fetch_workday(
BoardConfig(
name="Test",
org="org",
tenant="tenant",
domain="wd5.myworkdayjobs.com",
source="workday",
)
)
assert len(res) == 1
assert "myworkdayjobs.com/en-US/org/job/R1" in res[0].url
# limit=50 is rejected by some tenants (HTTP 400) — always request 20
assert client.bodies and all(b["limit"] == 20 for b in client.bodies)
def test_icims_parses_search_html():
html = (
'<li class="iCIMS_JobCardItem"><div class="row">'
'<div class="col-xs-6 header left">'
'<span class="sr-only field-label">Job Locations</span>'
"<span >US-IN-New Castle</span></div>"
'<div class="col-xs-12 title">'
'<a href="https://careers-x.icims.com/jobs/5374/dentist/job?in_iframe=1" '
'class="iCIMS_Anchor" title="5374 - Dentist"><h3 >\nDentist</h3></a></div>'
'<div class="col-xs-12 description">Great braces &amp; aligner work</div>'
"</div></li>"
).encode()
parsed = _parse_icims_jobs(html)
assert parsed == [
(
"Dentist",
"https://careers-x.icims.com/jobs/5374/dentist/job",
"US-IN-New Castle Great braces & aligner work",
)
]
def test_icims_empty_for_garbage():
assert _parse_icims_jobs(b"not html at all") == []
def test_usajobs_skips_without_key(monkeypatch):
import os
import dotenv
# prevent reload of .env (which now has the key) and force a clean env
monkeypatch.setattr(dotenv, "load_dotenv", lambda *a, **k: None)
env = dict(os.environ)
env.pop("USAJOBS_API_KEY", None)
env.pop("USAJOBS_API_EMAIL", None)
monkeypatch.setattr(os, "environ", env)
res = fetch_usajobs(BoardConfig(name="USAJOBS", source="usajobs"))
assert res == []
def test_fetch_board_unknown_source():
assert fetch_board(BoardConfig(name="X", source="unknown")) == []