HR-ATS-Portal/backend/tests/test_g_sheet_plugins.py

245 lines
8.4 KiB
Python

"""Unit tests for FormData.from_sheet_row + sheet row helpers.
No DB, no network. Pins the Google Form Responses header keys and YoG rules.
"""
from __future__ import annotations
from datetime import datetime, timezone
from g_sheet import plugins
from g_sheet.enums import FORM_DATA_FIELDS
from g_sheet.models import FormData
FORM_RESPONSE_RECORD = {
"Timestamp": "7/2/2026 17:50:19",
"Email": "nusratazra@gmail.com",
"Full Name": "Nusrat Azra",
"Gender": "Female",
"Date of Birth": "7/24/1984",
"Marital Status": "Single",
"CGPA": "3.5",
"University": "Karachi University",
"If your university is not listed above, please specify its name.": "",
"Educational Degree": "Masters",
"Year of Graduation": "12/2/2007",
"Position Applied For": "Executive Secretary",
"LinkedIn Profile Link": (
"https://www.linkedin.com/in/nusrat-azra-executive-manager-to-c-suite-3bb86719/"
),
"Drop your updated resume": "https://drive.google.com/open?id=1l5HOY5R6KiL6270A_sV56FkdX6EIGfI4",
"How soon can you join us?": "1 - 2 weeks",
"Phone number (03XX-XXXXXXX)": "03312464228",
"Are you willing to relocate?": "Yes",
"Residing City": "Karachi",
"Residing Country": "Pakistan",
"Area of Interest": "",
"Recruiter": "",
"Director / POC / Category": "Operations",
"National Identification No. (42000-XXXXXXX-X)": "4250105627772",
"Where did you hear about the position you're applying for?": "Indeed",
"Current Salary": "110k",
"Expected Salary": "150k",
"HR Comment": "Good profile",
}
def test_from_sheet_row_maps_form_response_keys():
mapped = FormData.from_sheet_row(
"Form Responses - Candidate Database Sheet 2026",
9,
FORM_RESPONSE_RECORD,
)
assert mapped["name"] == "Nusrat Azra"
assert mapped["candidate_email"] == "nusratazra@gmail.com"
assert mapped["candidate_number"] == "03312464228"
assert mapped["gender"] == "Female"
assert mapped["date_of_birth"] == datetime(1984, 7, 24, tzinfo=timezone.utc)
assert mapped["cnic"] == "4250105627772"
assert mapped["cgpa"] == "3.5"
assert mapped["degree"] == "Masters"
assert mapped["university"] == "Karachi University"
assert mapped["position_applied_for"] == "Executive Secretary"
assert mapped["notice_period"] == "1 - 2 weeks"
assert mapped["source_of_application"] == "Indeed"
assert mapped["ho_availability"] == "Yes"
assert mapped["marital_status"] == "Single"
assert mapped["residing_city"] == "Karachi"
assert mapped["residing_country"] == "Pakistan"
assert mapped["director_poc_category"] == "Operations"
assert mapped["hr_comments"] == "Good profile"
assert mapped["current_salary_value"] == 110000
assert mapped["expected_salary_value"] == 150000
assert mapped["entry_year"] == "12/2/2007"
assert mapped["entry_date"] is not None
assert mapped["entry_time"] == "17:50"
assert mapped["name"] != "7/2/2026 17:50:19"
assert mapped["row_number"] == 9
assert mapped["sheet"] == "Form Responses - Candidate Database Sheet 2026"
assert mapped["raw_record"]["Full Name"] == "Nusrat Azra"
def test_year_of_graduation_prefers_second_when_present():
data = {
"Full Name": "Ada",
"Year of Graduation": "2010",
"Year of Graduation_1": "2015",
}
mapped = FormData.from_sheet_row("tab", 2, data)
assert mapped["entry_year"] == "2015"
def test_year_of_graduation_uses_first_when_second_blank():
data = {
"Full Name": "Ada",
"Year of Graduation": "2010",
"Year of Graduation_1": " ",
}
mapped = FormData.from_sheet_row("tab", 2, data)
assert mapped["entry_year"] == "2010"
def test_year_of_graduation_none_when_both_blank():
data = {
"Full Name": "Ada",
"Year of Graduation": "",
"Year of Graduation_1": "",
}
mapped = FormData.from_sheet_row("tab", 2, data)
assert mapped["entry_year"] is None
def test_year_of_graduation_single_column():
data = {"Full Name": "Ada", "Year of Graduation": "2012"}
mapped = FormData.from_sheet_row("tab", 2, data)
assert mapped["entry_year"] == "2012"
def test_duplicate_year_header_becomes_year_of_graduation_1():
headers = plugins.normalise_headers(
["Full Name", "Year of Graduation", "Year of Graduation"],
)
assert headers == ["Full Name", "Year of Graduation", "Year of Graduation_1"]
def test_parse_salary_and_score():
assert plugins.parse_salary("110k") == 110000
assert plugins.parse_salary("60k-70k") == 60000
assert plugins.parse_salary("Negotiable") is None
assert plugins.parse_score("7") == 7
assert plugins.parse_score("15") is None
def test_rows_to_indexed_records_keeps_true_sheet_row_across_blank():
rows = [
["Name", "Age"],
["Ada", "30"],
["", ""],
["Bob", "40"],
]
indexed = plugins.rows_to_indexed_records(rows)
assert indexed == [
(2, {"Name": "Ada", "Age": "30"}),
(4, {"Name": "Bob", "Age": "40"}),
]
assert plugins.rows_to_records(rows) == [
{"Name": "Ada", "Age": "30"},
{"Name": "Bob", "Age": "40"},
]
def test_form_data_fields_match_model():
assert set(FORM_DATA_FIELDS) == set(FormData.model_fields)
def test_drive_file_id_from_form_open_url():
url = "https://drive.google.com/open?id=1l5HOY5R6KiL6270A_sV56FkdX6EIGfI4"
assert plugins.drive_file_id(url) == "1l5HOY5R6KiL6270A_sV56FkdX6EIGfI4"
def test_drive_file_id_from_file_view_url():
url = "https://drive.google.com/file/d/abc123XYZ/view?usp=sharing"
assert plugins.drive_file_id(url) == "abc123XYZ"
def test_drive_file_id_from_docs_url():
url = "https://docs.google.com/document/d/docFileId99/edit"
assert plugins.drive_file_id(url) == "docFileId99"
def test_drive_file_id_rejects_folder_and_non_drive():
assert plugins.drive_file_id("https://drive.google.com/drive/folders/abc") is None
assert plugins.drive_file_id("https://example.com/cv.pdf") is None
assert plugins.drive_file_id("") is None
assert plugins.drive_file_id(None) is None
def test_cv_dest_path_stays_inside_dir(tmp_path):
dest = plugins._cv_dest_path(tmp_path, "1l5HOY5R6KiL6270A_sV56FkdX6EIGfI4", "Resume.PDF")
assert dest.parent == tmp_path.resolve()
assert dest.name.endswith(".pdf")
assert dest.name.startswith("1l5HOY5R6KiL6270A_sV56FkdX6EIGfI4")
def test_title_ilike_pattern_contains_and_escapes():
from job.job_post.models import JobPosts
assert JobPosts.title_ilike_pattern("") is None
assert JobPosts.title_ilike_pattern(" ") is None
assert JobPosts.title_ilike_pattern("Brand Manager") == "%Brand Manager%"
assert JobPosts.title_ilike_pattern("C++ _dev%") == r"%C++ \_dev\%%"
async def test_stamp_suggested_job_posts_sets_ids_or_empty(monkeypatch):
import uuid
job_a = uuid.uuid4()
job_b = uuid.uuid4()
async def fake_ids(session, titles):
return {"Executive Secretary": [job_a, job_b]}
monkeypatch.setattr(
"job.job_post.models.JobPosts.ids_for_titles_ilike",
fake_ids,
)
records = [
{"position_applied_for": "Executive Secretary"},
{"position_applied_for": "Unknown Role"},
{"position_applied_for": None},
{"position_applied_for": " "},
]
out = await FormData.stamp_suggested_job_posts(object(), records)
assert out[0]["suggested_job_post_ids"] == [str(job_a), str(job_b)]
assert out[0]["job_post_id"] is None
assert out[0]["assigned_job_post_id"] is None
assert out[1]["suggested_job_post_ids"] == []
assert out[1]["job_post_id"] is None
assert out[1]["assigned_job_post_id"] is None
assert out[2]["suggested_job_post_ids"] == []
assert out[3]["suggested_job_post_ids"] == []
def test_score_job_ids_assigned_wins_else_suggested():
import uuid
a = uuid.uuid4()
b = uuid.uuid4()
assigned = uuid.uuid4()
assert FormData.score_job_ids({
"suggested_job_post_ids": [str(a), str(b)],
"assigned_job_post_id": assigned,
}) == [assigned]
assert FormData.score_job_ids({
"suggested_job_post_ids": [str(a), str(b)],
"job_post_id": assigned,
}) == [assigned]
assert FormData.score_job_ids({
"suggested_job_post_ids": [str(a), str(b), str(a)],
"assigned_job_post_id": None,
"job_post_id": None,
}) == [a, b]
assert FormData.score_job_ids({"suggested_job_post_ids": None}) == []
assert FormData.score_job_ids(None) == []