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.
173 lines
5.6 KiB
Python
173 lines
5.6 KiB
Python
"""Tests for the role gate (title classification) and candidate logs."""
|
|
from datetime import date, timedelta
|
|
|
|
from gimme_job.proactive import roles
|
|
from gimme_job.proactive.plan import ProactivePlan
|
|
from gimme_job.proactive.roles import (
|
|
NON_CLINICAL,
|
|
OTHER_SPECIALTY,
|
|
SUPPORT,
|
|
TARGET,
|
|
UNKNOWN,
|
|
classify_role,
|
|
effective_role_terms,
|
|
is_role_excluded,
|
|
normalize_title,
|
|
prune_candidate_logs,
|
|
read_role_candidates,
|
|
record_role_candidate,
|
|
)
|
|
|
|
# ── normalize ────────────────────────────────────────────────────────────────
|
|
|
|
|
|
def test_normalize_title_strips_prefix_company_location_ids():
|
|
assert (
|
|
normalize_title(
|
|
"Job Application for Dental Assistant at Anthem Pediatric Dentistry "
|
|
"at Specialty Dental Brands"
|
|
)
|
|
== "Dental Assistant"
|
|
)
|
|
assert normalize_title("Orthodontist - Phoenix, AZ") == "Orthodontist"
|
|
assert normalize_title("Dentist | Requisition #12345") == "Dentist"
|
|
assert normalize_title(" Orthodontist ") == "Orthodontist"
|
|
|
|
|
|
# ── classify ─────────────────────────────────────────────────────────────────
|
|
|
|
|
|
def test_target_titles():
|
|
for title in (
|
|
"Orthodontist",
|
|
"Orthodontist Job in Phoenix, AZ at Smile Doctors",
|
|
"Staff Dentist",
|
|
"Dentist II (Orthodontics)",
|
|
"Staff Dental Officer",
|
|
"Specialty Dentist",
|
|
"Dental Specialist",
|
|
):
|
|
role, _ = classify_role(title)
|
|
assert role == TARGET, title
|
|
|
|
|
|
def test_support_titles():
|
|
for title in (
|
|
"Dental Assistant at Anthem Pediatric Dentistry",
|
|
"Orthodontic Assistant",
|
|
"Registered Dental Hygienist",
|
|
"Orthodontic Technician",
|
|
"Treatment Coordinator",
|
|
"Front Desk Receptionist",
|
|
"Orthodontic Clinician I",
|
|
):
|
|
role, _ = classify_role(title)
|
|
assert role == SUPPORT, title
|
|
|
|
|
|
def test_non_clinical_titles():
|
|
for title in (
|
|
"Account Payable Specialist at Specialty Dental Brands",
|
|
"Staff Accountant",
|
|
"Payroll Clerk",
|
|
"Director of Operations",
|
|
"Regional Manager",
|
|
"Marketing Manager",
|
|
"HR Generalist",
|
|
):
|
|
role, _ = classify_role(title)
|
|
assert role == NON_CLINICAL, title
|
|
|
|
|
|
def test_other_specialty_titles():
|
|
"""Explicit non-ortho specialties are filtered even though they say "dentist"."""
|
|
for title in (
|
|
"Job Application for Pediatric Dentist - Associate at Specialty Dental Brands",
|
|
"Pediatric Dentist",
|
|
"Pediatric Dentistry Associate",
|
|
"Endodontist",
|
|
"Periodontist",
|
|
"Prosthodontist",
|
|
"Oral Surgeon",
|
|
"General Dentist",
|
|
"Public Health Dentist",
|
|
):
|
|
role, _ = classify_role(title)
|
|
assert role == OTHER_SPECIALTY, title
|
|
|
|
|
|
def test_orthodontist_variants_stay_target():
|
|
for title in (
|
|
"Pediatric Orthodontist",
|
|
"Orthodontist",
|
|
"Orthodontic Specialist",
|
|
"Staff Dentist",
|
|
"Dentist II (Orthodontics)",
|
|
):
|
|
role, _ = classify_role(title)
|
|
assert role == TARGET, title
|
|
|
|
|
|
def test_unknown_titles():
|
|
for title in ("Clinical Lead", "Dental Program Specialist", ""):
|
|
role, _ = classify_role(title)
|
|
assert role == UNKNOWN, title
|
|
|
|
|
|
def test_target_beats_support_on_compound_titles():
|
|
role, _ = classify_role("Orthodontist and Clinical Assistant")
|
|
assert role == TARGET
|
|
|
|
|
|
def test_is_role_excluded():
|
|
assert is_role_excluded("Dental Assistant")[0] == SUPPORT
|
|
assert is_role_excluded("Payroll Clerk")[0] == NON_CLINICAL
|
|
assert is_role_excluded("Pediatric Dentist")[0] == OTHER_SPECIALTY
|
|
assert is_role_excluded("Orthodontist") is None
|
|
assert is_role_excluded("Clinical Lead") is None
|
|
|
|
|
|
def test_effective_role_terms_merges_plan_additions():
|
|
plan = ProactivePlan()
|
|
plan.verification.role_terms = {"support": ["surgical tech"]}
|
|
terms = effective_role_terms(plan)
|
|
assert "surgical tech" in terms["support"]
|
|
assert "assistant" in terms["support"]
|
|
assert classify_role("Surgical Tech", plan)[0] == SUPPORT
|
|
|
|
|
|
# ── candidate log ────────────────────────────────────────────────────────────
|
|
|
|
|
|
def test_record_read_and_prune_candidate_logs():
|
|
today = date(2026, 9, 12)
|
|
record_role_candidate(
|
|
"Dental Assistant", "https://x/j/1", "greenhouse", SUPPORT, "matched", run_date=today
|
|
)
|
|
record_role_candidate(
|
|
"Orthodontic Clinician", "https://x/j/2", "workday", UNKNOWN, "no keyword", run_date=today
|
|
)
|
|
record_role_candidate(
|
|
"Orthodontist", "https://x/j/3", "lever", TARGET, "target", run_date=today
|
|
)
|
|
|
|
entries = read_role_candidates(today)
|
|
assert len(entries) == 2 # target is not logged
|
|
assert entries[0]["title"] == "Dental Assistant"
|
|
assert entries[0]["role"] == SUPPORT
|
|
|
|
old = today - timedelta(days=31)
|
|
record_role_candidate("Old", "https://x/old", "lever", UNKNOWN, "old", run_date=old)
|
|
removed = prune_candidate_logs(30, now=today)
|
|
assert removed == 1
|
|
assert {e["title"] for e in read_role_candidates(today)} == {
|
|
"Dental Assistant",
|
|
"Orthodontic Clinician",
|
|
}
|
|
assert read_role_candidates(old) == []
|
|
|
|
|
|
def test_candidate_log_path_shape():
|
|
path = roles.candidates_log_path(date(2026, 1, 2))
|
|
assert path.name == "2026-01-02.roles.jsonl"
|