Initial deploy setup
deploy / deploy (push) Canceled after 0s

This commit is contained in:
Jens
2026-07-21 14:00:00 +02:00
commit b8091e59bd
285 changed files with 27854 additions and 0 deletions
+194
View File
@@ -0,0 +1,194 @@
from __future__ import annotations
from decimal import Decimal
import pytest
from apps.jobs.models import AiAnalysisCache
from apps.jobs.services.ai import AiAnalysis
from apps.jobs.models import Employer, JobPosting, ScoreRun
from apps.jobs.services import scoring
from apps.jobs.services.scoring import calculate_score
@pytest.fixture
def matching_job(db):
employer = Employer.objects.create(
name="Example Public IT",
normalized_name="example public it",
domain="jobs.example.org",
is_direct_employer=True,
)
return JobPosting.objects.create(
employer=employer,
original_title="Infrastructure Engineer",
normalized_title="infrastructure engineer",
canonical_url="https://jobs.example.org/jobs/1",
canonical_key="a" * 64,
content_hash="b" * 64,
description_text=(
"Beheer Microsoft 365 en VMware. Hybride werk en beperkte tweedelijnssupport."
),
raw_location="Hasselt, Limburg",
region="Limburg",
municipality="Hasselt",
postal_code="3500",
latitude=Decimal("50.930700"),
longitude=Decimal("5.332500"),
workplace_type=JobPosting.Workplace.HYBRID,
employment_types=["full_time", "permanent"],
skills_required=["Microsoft 365", "VMware"],
analysis_features={"support_ratio": 0.1, "public_sector_signal": 0.8},
direct_employer=True,
recruiter=False,
extraction_confidence=Decimal("0.95"),
status=JobPosting.Status.ACTIVE,
)
def test_matching_job_gets_recommendation(matching_job, profile):
result = calculate_score(matching_job, profile)
assert result.score >= profile.recommendation_threshold
assert result.recommendation in {
ScoreRun.Recommendation.STRONG,
ScoreRun.Recommendation.POSSIBLE,
}
assert not result.hard_exclusions
def test_hard_title_exclusion_wins(matching_job, profile):
matching_job.original_title = "IT Sales Infrastructure Engineer"
matching_job.save(update_fields=["original_title"])
result = calculate_score(matching_job, profile)
assert result.recommendation == ScoreRun.Recommendation.HIDDEN
assert any("sales" in reason.lower() for reason in result.hard_exclusions)
def test_distance_boundary_inclusief(matching_job, profile, monkeypatch):
profile.max_distance_km = 50
profile.save(update_fields=["max_distance_km"])
monkeypatch.setattr(scoring, "haversine_km", lambda *_args, **_kwargs: 50.0)
matching_job.refresh_from_db()
matching_job.save(update_fields=["latitude", "longitude"])
result = calculate_score(matching_job, profile)
assert result.evidence["distance_km"] == 50.0
assert result.recommendation != ScoreRun.Recommendation.HIDDEN
assert not any("boven maximum" in issue for issue in result.hard_exclusions)
def test_remote_job_heeft_geen_afstandsexclusie(matching_job, profile):
matching_job.workplace_type = JobPosting.Workplace.REMOTE
matching_job.postal_code = None
matching_job.municipality = None
matching_job.raw_location = None
matching_job.save(update_fields=["workplace_type", "postal_code", "municipality", "raw_location"])
matching_job.latitude = None
matching_job.longitude = None
result = calculate_score(matching_job, profile)
assert result.recommendation != ScoreRun.Recommendation.HIDDEN
assert all("boven maximum" not in issue for issue in result.hard_exclusions)
def test_onbekende_locatie_leidt_niet_tot_automatische_uitsluiting(matching_job, profile):
matching_job.postal_code = None
matching_job.municipality = None
matching_job.raw_location = "onbekend"
matching_job.latitude = None
matching_job.longitude = None
matching_job.save(update_fields=["postal_code", "municipality", "raw_location", "latitude", "longitude"])
result = calculate_score(matching_job, profile)
assert result.evidence["distance_km"] is None
assert result.recommendation != ScoreRun.Recommendation.HIDDEN
def test_score_is_tijdzoneonafhankelijk(profile, user, matching_job):
utc_profile = profile
utc_profile.timezone = "UTC"
utc_profile.save(update_fields=["timezone"])
result_utc = calculate_score(matching_job, utc_profile)
ny_profile = profile.__class__.objects.create(
user=user,
name="UTC vergelijken",
is_active=False,
home_municipality=utc_profile.home_municipality,
home_latitude=utc_profile.home_latitude,
home_longitude=utc_profile.home_longitude,
max_distance_km=utc_profile.max_distance_km,
desired_titles=utc_profile.desired_titles,
excluded_titles=utc_profile.excluded_titles,
desired_skills=utc_profile.desired_skills,
excluded_skills=utc_profile.excluded_skills,
allowed_employment_types=utc_profile.allowed_employment_types,
preferred_workplace=utc_profile.preferred_workplace,
preferred_regions=utc_profile.preferred_regions,
excluded_regions=utc_profile.excluded_regions,
recommendation_threshold=utc_profile.recommendation_threshold,
top_match_threshold=utc_profile.top_match_threshold,
digest_time=utc_profile.digest_time,
quiet_hours_start=utc_profile.quiet_hours_start,
quiet_hours_end=utc_profile.quiet_hours_end,
learning_enabled=utc_profile.learning_enabled,
weights=utc_profile.weights,
timezone="America/New_York",
)
result_ny = calculate_score(matching_job, ny_profile)
assert result_ny.score == result_utc.score
def test_ai_score_influence_is_opt_in_and_bounded(matching_job, profile, monkeypatch):
profile.ai_scoring_enabled = True
profile.weights = {**profile.weights, "ai": 999}
profile.save(update_fields=["ai_scoring_enabled", "weights"])
monkeypatch.setattr(
scoring,
"analyze_job_text",
lambda *args, **kwargs: AiAnalysis(
features={
"support_ratio": 0.1,
"consultancy_ratio": 0.0,
"travel_ratio": 0.0,
"seniority": "senior",
"evidence": ["microsoft 365"],
},
summary_nl="Sterke technische rol.",
warnings=[],
model="local-model",
status=AiAnalysisCache.Status.OK,
error_category="",
cached=False,
),
)
result = calculate_score(matching_job, profile)
assert result.recommendation in {ScoreRun.Recommendation.STRONG, ScoreRun.Recommendation.POSSIBLE}
assert result.model_version == "local-model"
assert result.evidence["ai"]["status"] == AiAnalysisCache.Status.OK
assert result.evidence["ai"]["weight_applied"] == 20.0
assert "ai" in result.evidence
def test_ai_failure_keeps_deterministic_scoring(profile, matching_job, monkeypatch):
profile.ai_scoring_enabled = True
profile.weights = {**profile.weights, "ai": 10}
profile.save(update_fields=["ai_scoring_enabled", "weights"])
monkeypatch.setattr(
scoring,
"analyze_job_text",
lambda *args, **kwargs: AiAnalysis(
features={},
summary_nl="",
warnings=["service-timeout"],
model="local-model",
status=AiAnalysisCache.Status.TIMEOUT,
error_category="timeout",
cached=False,
),
)
result = calculate_score(matching_job, profile)
assert result.evidence["ai"]["status"] == AiAnalysisCache.Status.TIMEOUT
assert "AI-analyse" in "".join(result.concerns)
assert "components" in result.__dict__