@@ -0,0 +1,176 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
|
||||
import pytest
|
||||
|
||||
from apps.jobs.models import Employer, JobPosting
|
||||
from apps.jobs.services.employer_resolution import resolve_direct_employer_match
|
||||
from apps.jobs.services.normalization import CanonicalJobDraft
|
||||
from apps.sources.models import Source
|
||||
|
||||
|
||||
def _draft_for_test(job: JobPosting, *, **overrides) -> CanonicalJobDraft:
|
||||
values = {
|
||||
"source_url": "https://jobs.smartrecruiters.com/example/security-engineer",
|
||||
"canonical_url": "https://jobs.smartrecruiters.com/example/security-engineer",
|
||||
"external_id": "sr-security-123",
|
||||
"title": job.original_title,
|
||||
"normalized_title": job.normalized_title,
|
||||
"job_family": job.job_family,
|
||||
"employer_name": job.employer_name,
|
||||
"employer_domain": "jobs.example.org",
|
||||
"location_text": job.raw_location,
|
||||
"region": job.region,
|
||||
"municipality": job.municipality,
|
||||
"postal_code": job.postal_code,
|
||||
"country": job.country,
|
||||
"workplace_type": job.workplace_type,
|
||||
"employment_types": job.employment_types,
|
||||
"language": "nl",
|
||||
"description_html": "<p>Test</p>",
|
||||
"description_text": "test beschrijving",
|
||||
"date_posted": datetime(2026, 1, 10, tzinfo=UTC),
|
||||
"valid_through": datetime(2026, 6, 1, tzinfo=UTC),
|
||||
"compensation": {},
|
||||
"skills_required": [],
|
||||
"skills_preferred": [],
|
||||
"content_hash": job.content_hash,
|
||||
"canonical_key": "x" * 64,
|
||||
"evidence": [],
|
||||
"raw": {},
|
||||
}
|
||||
values.update(overrides)
|
||||
return CanonicalJobDraft(**values)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_resolver_merges_recruiter_alias_to_direct_source():
|
||||
employer = Employer.objects.create(
|
||||
name="Example IT",
|
||||
normalized_name="example it",
|
||||
domain="jobs.example.org",
|
||||
)
|
||||
JobPosting.objects.create(
|
||||
employer=employer,
|
||||
original_title="Security Engineer",
|
||||
normalized_title="security engineer",
|
||||
canonical_url="https://jobs.example.org/vacatures/security-engineer",
|
||||
canonical_key="a" * 64,
|
||||
job_family="security",
|
||||
content_hash="a" * 64,
|
||||
description_text="Security engineer voor beveiliging.",
|
||||
raw_location="Antwerpen",
|
||||
region="Antwerpen",
|
||||
municipality="Antwerpen",
|
||||
workplace_type=JobPosting.Workplace.HYBRID,
|
||||
employment_types=["full_time"],
|
||||
direct_employer=True,
|
||||
)
|
||||
source = Source.objects.create(
|
||||
name="SmartRecruiters",
|
||||
source_type=Source.Type.ATS,
|
||||
base_url="https://jobs.smartrecruiters.com/example",
|
||||
domain="jobs.smartrecruiters.com",
|
||||
status=Source.Status.ACTIVE,
|
||||
policy=Source.Policy.ALLOW,
|
||||
)
|
||||
# Use an existing direct employer job as the resolver target.
|
||||
target_job = JobPosting.objects.first()
|
||||
decision = resolve_direct_employer_match(
|
||||
_draft_for_test(target_job),
|
||||
source=source,
|
||||
)
|
||||
assert decision.job == target_job
|
||||
assert decision.reason == "resolved_direct_match"
|
||||
assert decision.canonical_url == "https://jobs.example.org/vacatures/security-engineer"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_resolver_flags_conflicting_location_for_review():
|
||||
employer = Employer.objects.create(
|
||||
name="Example IT",
|
||||
normalized_name="example it",
|
||||
domain="jobs.example.org",
|
||||
)
|
||||
JobPosting.objects.create(
|
||||
employer=employer,
|
||||
original_title="Security Engineer",
|
||||
normalized_title="security engineer",
|
||||
canonical_url="https://jobs.example.org/vacatures/security-engineer",
|
||||
canonical_key="b" * 64,
|
||||
job_family="security",
|
||||
content_hash="b" * 64,
|
||||
description_text="Security engineer voor beveiliging.",
|
||||
raw_location="Antwerpen",
|
||||
region="Antwerpen",
|
||||
municipality="Antwerpen",
|
||||
workplace_type=JobPosting.Workplace.HYBRID,
|
||||
employment_types=["full_time"],
|
||||
direct_employer=True,
|
||||
)
|
||||
source = Source.objects.create(
|
||||
name="SmartRecruiters",
|
||||
source_type=Source.Type.ATS,
|
||||
base_url="https://jobs.smartrecruiters.com/example",
|
||||
domain="jobs.smartrecruiters.com",
|
||||
status=Source.Status.ACTIVE,
|
||||
policy=Source.Policy.ALLOW,
|
||||
)
|
||||
decision = resolve_direct_employer_match(
|
||||
_draft_for_test(
|
||||
JobPosting.objects.first(),
|
||||
location_text="Gent",
|
||||
municipality="Gent",
|
||||
region="Oost-Vlaanderen",
|
||||
),
|
||||
source=source,
|
||||
)
|
||||
assert decision.job is None
|
||||
assert decision.reason == "review_direct_conflict"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_resolver_keeps_candidate_unmerged_when_similarity_is_conservative():
|
||||
employer = Employer.objects.create(
|
||||
name="Example IT",
|
||||
normalized_name="example it",
|
||||
domain="jobs.example.org",
|
||||
)
|
||||
JobPosting.objects.create(
|
||||
employer=employer,
|
||||
original_title="Security Engineer",
|
||||
normalized_title="security engineer",
|
||||
canonical_url="https://jobs.example.org/vacatures/security-engineer",
|
||||
canonical_key="c" * 64,
|
||||
job_family="security",
|
||||
content_hash="c" * 64,
|
||||
description_text="Security engineer voor beveiliging.",
|
||||
raw_location="Antwerpen",
|
||||
region="Antwerpen",
|
||||
municipality="Antwerpen",
|
||||
workplace_type=JobPosting.Workplace.HYBRID,
|
||||
employment_types=["full_time"],
|
||||
direct_employer=True,
|
||||
)
|
||||
source = Source.objects.create(
|
||||
name="StaffingBoard",
|
||||
source_type=Source.Type.ATS,
|
||||
base_url="https://jobs.staffingboard.com/example",
|
||||
domain="jobs.staffingboard.com",
|
||||
status=Source.Status.ACTIVE,
|
||||
policy=Source.Policy.ALLOW,
|
||||
)
|
||||
decision = resolve_direct_employer_match(
|
||||
_draft_for_test(
|
||||
JobPosting.objects.first(),
|
||||
title="Data Analyst",
|
||||
normalized_title="data analyst",
|
||||
location_text="Brussel",
|
||||
employer_name="Another Employer",
|
||||
employer_domain="jobs.staffingboard.com",
|
||||
),
|
||||
source=source,
|
||||
)
|
||||
assert decision.job is None
|
||||
assert decision.reason == "no_direct_resolution"
|
||||
Reference in New Issue
Block a user