Files
Jens a4eced8be5
deploy / deploy (push) Canceled after 0s
Fix release blockers and deployment build
2026-07-21 21:22:29 +02:00

210 lines
6.6 KiB
Python

from __future__ import annotations
from decimal import Decimal
from pathlib import Path
import pytest
from django.core.management import CommandError
from apps.jobs.models import GeocodeLocationLookup
from apps.jobs.services.geocoding import (
CsvGeocodeProvider,
GeoPoint,
LocationMatch,
LocationMatchResult,
import_csv_geodata,
parse_belgian_location_query,
resolve_location,
validate_csv_geodata,
)
@pytest.mark.django_db
def test_parse_belgian_location_query():
postal, municipality = parse_belgian_location_query("Gent, 9000")
assert postal == "9000"
assert municipality == "gent"
postal, municipality = parse_belgian_location_query("1050 Ixelles")
assert postal == "1050"
assert municipality == "ixelles"
postal, municipality = parse_belgian_location_query("Brussel")
assert postal is None
assert municipality == "brussel"
@pytest.mark.django_db
def test_geocode_provider_resolves_postal_and_municipality_exactly():
GeocodeLocationLookup.objects.create(
source_name="fixture",
source_version="2026-01-01",
source_license_name="",
source_license_url="",
source_metadata={},
query_kind="postal",
query_value="9000",
postal_code="9000",
municipality="Gent",
region="Vlaams-Brabant",
latitude=Decimal("51.0500"),
longitude=Decimal("3.7300"),
confidence=Decimal("0.90"),
)
GeocodeLocationLookup.objects.create(
source_name="fixture",
source_version="2026-01-01",
source_license_name="",
source_license_url="",
source_metadata={},
query_kind="municipality",
query_value="gent",
postal_code="9000",
municipality="Gent",
region="Vlaams-Brabant",
latitude=Decimal("51.0500"),
longitude=Decimal("3.7300"),
confidence=Decimal("0.90"),
)
provider = CsvGeocodeProvider(source_name="fixture", source_version="2026-01-01")
result = resolve_location("9000 Gent", provider)
assert result.ambiguous is False
assert result.location is not None
assert result.location.postal_code == "9000"
assert result.location.municipality == "Gent"
assert result.location.point == GeoPoint(latitude=51.05, longitude=3.73)
@pytest.mark.django_db
def test_geocode_provider_marks_ambiguous_as_unknown():
GeocodeLocationLookup.objects.create(
source_name="fixture",
source_version="2026-01-01",
source_license_name="",
source_license_url="",
source_metadata={},
query_kind="municipality",
query_value="brussel",
postal_code="1000",
municipality="Brussel",
region="Brussels Hoofdstedelijk Gewest",
latitude=Decimal("50.8500"),
longitude=Decimal("4.3500"),
confidence=Decimal("0.85"),
)
GeocodeLocationLookup.objects.create(
source_name="fixture",
source_version="2026-01-01",
source_license_name="",
source_license_url="",
source_metadata={},
query_kind="municipality",
query_value="brussel",
postal_code="1000-200",
municipality="Brussel",
region="Brussels Hoofdstedelijk Gewest",
latitude=Decimal("50.8400"),
longitude=Decimal("4.3400"),
confidence=Decimal("0.85"),
)
provider = CsvGeocodeProvider(source_name="fixture", source_version="2026-01-01")
result = resolve_location("Brussel", provider)
assert result.ambiguous is True
assert result.location is None
@pytest.mark.django_db
def test_validate_csv_geodata_flags_duplicates_and_invalid_coordinates(tmp_path: Path):
source = tmp_path / "geodata.csv"
source.write_text(
"postal_code,municipality,region,latitude,longitude\n"
"9000,Gent,Vlaams-Brabant,51.05,3.73\n"
"9000,Gent,Vlaams-Brabant,51.05,3.74\n"
)
with pytest.raises(CommandError, match="dubbel record"):
validate_csv_geodata(source)
source.write_text(
"postal_code,municipality,region,latitude,longitude\n9000,Gent,Vlaams-Brabant,99,3.73\n"
)
with pytest.raises(CommandError, match="latitude buiten bereik"):
validate_csv_geodata(source)
@pytest.mark.django_db
def test_import_csv_geodata_is_atomic_without_replace(tmp_path: Path):
source = tmp_path / "geodata.csv"
source.write_text(
"postal_code,municipality,region,latitude,longitude\n"
"9000,Gent,Vlaams-Brabant,51.05,3.73\n"
"1040,Brussel,Brussels Hoofdstedelijk Gewest,50.85,4.35\n"
)
GeocodeLocationLookup.objects.create(
source_name="fixture",
source_version="manual",
source_license_name="",
source_license_url="",
source_metadata={},
query_kind="municipality",
query_value="gent",
postal_code="9000",
municipality="Gent",
region="Vlaams-Brabant",
latitude=Decimal("51.05"),
longitude=Decimal("3.73"),
confidence=Decimal("1.00"),
)
GeocodeLocationLookup.objects.create(
source_name="fixture",
source_version="manual",
source_license_name="",
source_license_url="",
source_metadata={},
query_kind="postal",
query_value="9000",
postal_code="9000",
municipality="Gent",
region="Vlaams-Brabant",
latitude=Decimal("51.05"),
longitude=Decimal("3.73"),
confidence=Decimal("1.00"),
)
with pytest.raises(CommandError, match="overschrijven zonder --replace"):
import_csv_geodata(source, source_name="fixture", source_version="manual")
assert GeocodeLocationLookup.objects.count() == 2
def test_resolve_location_keeps_ambiguous_unknown() -> None:
class StubProvider:
def resolve(self, query: str):
return [
LocationMatch(
postal_code="1000",
municipality="Brussel",
region="Brussels",
point=None,
confidence=0.85,
source="csv",
source_version="manual",
metadata={},
),
LocationMatch(
postal_code="1001",
municipality="Brussel",
region="Brussels",
point=None,
confidence=0.85,
source="csv",
source_version="manual",
metadata={},
),
]
result = resolve_location("Brussel", StubProvider())
assert result.ambiguous is True
assert isinstance(result, LocationMatchResult)
assert result.location is None