165 lines
6.2 KiB
Python
165 lines
6.2 KiB
Python
"""Reapplicant history — badge vs kept attempts.
|
|
|
|
No database: the helpers decide whether a prior row counts as Reapplied
|
|
and how a system-dropped attempt is labelled.
|
|
"""
|
|
from job.candidate.serializers import (
|
|
is_assigned_application,
|
|
is_kept_application,
|
|
rejection_reason,
|
|
serialize_application_history,
|
|
serialize_application_history_item,
|
|
)
|
|
from job.candidate.views import _is_current_application, _is_earlier_application
|
|
|
|
|
|
def test_unassigned_inbox_is_not_a_reapplication():
|
|
row = {
|
|
"source": "inbox",
|
|
"job_post_id": None,
|
|
"job_title": None,
|
|
"status": "CLOSED",
|
|
"attachment": True,
|
|
"match_status": "matched",
|
|
}
|
|
assert is_assigned_application(row) is False
|
|
assert is_kept_application(row) is True
|
|
assert rejection_reason(row) is None
|
|
|
|
|
|
def test_assigned_inbox_is_a_reapplication():
|
|
row = {"source": "inbox", "job_post_id": "job-1", "job_title": "Engineer"}
|
|
assert is_assigned_application(row) is True
|
|
assert rejection_reason(row) is None
|
|
|
|
|
|
def test_form_role_name_counts_without_job_post():
|
|
row = {"source": "form", "job_post_id": None, "job_title": "Data Analyst"}
|
|
assert is_assigned_application(row) is True
|
|
|
|
|
|
def test_unreadable_cv_is_wrong_format():
|
|
row = {
|
|
"source": "inbox",
|
|
"job_post_id": None,
|
|
"attachment": True,
|
|
"match_status": "no_text",
|
|
"status": "CLOSED",
|
|
}
|
|
assert rejection_reason(row) == "wrong_format"
|
|
item = serialize_application_history_item(row)
|
|
assert item["status"] == "WRONG_FORMAT"
|
|
assert item["rejection_reason"] == "wrong_format"
|
|
assert is_kept_application(item) is True
|
|
|
|
|
|
def test_unreadable_cv_plus_later_mail_is_a_reapplication():
|
|
"""Attached CVs count even when the first PDF had no extractable text."""
|
|
later = {
|
|
"source": "inbox",
|
|
"message_id": "new",
|
|
"job_post_id": None,
|
|
"status": "CLOSED",
|
|
"attachment": True,
|
|
"match_status": "matched",
|
|
}
|
|
earlier = {
|
|
"source": "inbox",
|
|
"message_id": "old",
|
|
"job_post_id": None,
|
|
"status": "CLOSED",
|
|
"attachment": True,
|
|
"match_status": "no_text",
|
|
}
|
|
history = serialize_application_history("a@x.com", applications=[later, earlier])
|
|
assert history["is_reapplicant"] is True
|
|
assert [item["rejection_reason"] for item in history["applications"]] == [None, "wrong_format"]
|
|
|
|
|
|
def test_body_only_mail_is_wrong_format():
|
|
row = {"source": "inbox", "job_post_id": None, "attachment": False, "status": "CLOSED"}
|
|
assert rejection_reason(row) == "wrong_format"
|
|
|
|
|
|
def test_filtered_classifier_row_is_wrong_format():
|
|
row = {"source": "filtered", "job_post_id": None, "status": "WRONG_FORMAT"}
|
|
assert rejection_reason(row) == "wrong_format"
|
|
history = serialize_application_history("a@x.com", applications=[row])
|
|
assert history["is_reapplicant"] is False
|
|
assert history["applications"] == []
|
|
|
|
|
|
def test_history_reapplicant_counts_two_unassigned_mails():
|
|
unassigned = {"source": "inbox", "job_post_id": None, "status": "CLOSED", "attachment": True, "match_status": "matched"}
|
|
only_mail = serialize_application_history("a@x.com", applications=[unassigned])
|
|
assert only_mail["is_reapplicant"] is False
|
|
both = serialize_application_history("a@x.com", applications=[unassigned, dict(unassigned)])
|
|
assert both["is_reapplicant"] is True
|
|
assert len(both["applications"]) == 2
|
|
|
|
|
|
def test_wrong_format_plus_one_mail_is_not_a_reapplication():
|
|
mail = {"source": "inbox", "job_post_id": None, "status": "CLOSED", "attachment": True, "match_status": "matched"}
|
|
dropped = {"source": "filtered", "job_post_id": None, "status": "WRONG_FORMAT"}
|
|
history = serialize_application_history("a@x.com", applications=[mail, dropped])
|
|
assert history["is_reapplicant"] is False
|
|
assert len(history["applications"]) == 1
|
|
assert history["applications"][0]["source"] == "inbox"
|
|
|
|
|
|
def test_clicked_inbox_row_is_not_a_previous_application():
|
|
"""List payloads use `source` for the To address, not 'inbox'."""
|
|
pk = "11111111-1111-1111-1111-111111111111"
|
|
item = {"source": "inbox", "message_id": pk, "inbox_id": 9, "job_post_id": None}
|
|
payload = {
|
|
"id": pk,
|
|
"email": "a@x.com",
|
|
"source": "careers-rozee@example.com",
|
|
"position": "Backend Engineer",
|
|
}
|
|
assert _is_current_application(item, payload) is True
|
|
assert _is_current_application(item, {"id": "other", "source": "careers-rozee@example.com"}) is False
|
|
|
|
|
|
def test_form_row_matches_without_a_sheet_field():
|
|
fid = "22222222-2222-2222-2222-222222222222"
|
|
item = {"source": "form", "form_data_id": fid, "job_title": "Analyst"}
|
|
assert _is_current_application(item, {"id": fid, "candidate_email": "a@x.com"}) is True
|
|
|
|
|
|
def test_filtered_row_matches_graph_id_on_detail_payload():
|
|
item = {"source": "filtered", "message_id": "AAMkGraph", "upstream_id": "AAMkGraph"}
|
|
payload = {"id": "11111111-1111-1111-1111-111111111111", "message_id": "AAMkGraph"}
|
|
assert _is_current_application(item, payload) is True
|
|
|
|
|
|
def test_later_mail_is_not_a_previous_application():
|
|
"""12:52 is later than 12:51 — it is not a previous attempt of the 12:51 row."""
|
|
later = {"source": "inbox", "message_id": "b", "applied_at": "2026-09-07T07:52:00+00:00"}
|
|
payload = {"id": "a", "received": "2026-09-07T07:51:00+00:00"}
|
|
assert _is_current_application(later, payload) is False
|
|
assert _is_earlier_application(later, payload) is False
|
|
|
|
|
|
def test_earlier_mail_is_a_previous_application():
|
|
earlier = {"source": "inbox", "message_id": "a", "applied_at": "2026-09-07T07:51:00+00:00"}
|
|
payload = {"id": "b", "received": "2026-09-07T07:52:00+00:00"}
|
|
assert _is_earlier_application(earlier, payload) is True
|
|
|
|
|
|
def test_undated_row_is_not_guessed_earlier():
|
|
item = {"source": "inbox", "message_id": "b", "applied_at": None}
|
|
payload = {"id": "a", "received": "2026-09-07T07:51:00+00:00"}
|
|
assert _is_earlier_application(item, payload) is False
|
|
|
|
|
|
def test_history_item_keeps_user_id():
|
|
item = serialize_application_history_item({
|
|
"source": "manual",
|
|
"user_id": "user-1",
|
|
"manual_upload_candidate_id": "m-1",
|
|
"job_post_id": "job-1",
|
|
"job_title": "Analyst",
|
|
})
|
|
assert item["user_id"] == "user-1"
|