feat: add official Kempen operating scope
GeoIntel CI / docs-smoke (push) Canceled after 0s
GeoIntel CI / contract-smoke (push) Canceled after 0s

This commit is contained in:
Codex
2026-07-14 18:32:32 +02:00
parent e9c75b2fd6
commit bb7310e015
17 changed files with 1020 additions and 9 deletions
@@ -18,7 +18,7 @@ def test_map_first_explorer_is_the_default_product_flow() -> None:
assert "Wat bevindt zich in dit gebied?" in workspace
assert "Kies een datathema" in workspace
assert "Teken rechthoek" in workspace
assert "Volledige gemeente" in workspace
assert "Volledig werkgebied" in workspace
assert "Alle beschikbare thema" in workspace
assert "Bron nog niet ingeladen" in workspace
assert "useMapThemeSelectionInsights" in workspace
@@ -0,0 +1,134 @@
from __future__ import annotations
import importlib.util
from pathlib import Path
import sys
from shapely.geometry import Polygon, shape
ROOT = Path(__file__).resolve().parents[2]
SCRIPTS = ROOT / "scripts"
def load_module(name: str, filename: str):
scripts_path = str(SCRIPTS)
if scripts_path not in sys.path:
sys.path.insert(0, scripts_path)
spec = importlib.util.spec_from_file_location(name, SCRIPTS / filename)
assert spec is not None
assert spec.loader is not None
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
def test_kempen_scope_matches_official_28_municipality_policy_region() -> None:
scopes = load_module("geographic_scopes_test", "geographic_scopes.py")
scope = scopes.KEMPEN_TRANSPORT_REGION_SCOPE
assert scope.key == "kempen-transport-region"
assert scope.project_name == "Kempen Regional Workbench"
assert scope.scope_type == "transport_region"
assert len(scope.members) == 28
assert len(set(scope.nis_codes)) == 28
assert ("Mol", "13025") in {(member.name, member.nis_code) for member in scope.members}
assert ("Nijlen", "12026") in {(member.name, member.nis_code) for member in scope.members}
assert "vervoerregio-kempen" in scope.authority_url
assert "geen claim" in scope.limitation_message
def test_scope_union_preserves_member_identity_and_policy_limitation() -> None:
scopes = load_module("geographic_scopes_union_test", "geographic_scopes.py")
provisioner = load_module("provision_geographic_scope_test", "provision_geographic_scope.py")
scope = scopes.GeographicScope(
key="test-region",
display_name="Testregio",
project_name="Test Regional Workbench",
project_region="Test",
area_name="Testregio - operationele grens",
authority_name="Test authority",
authority_url="https://example.test/scope",
scope_type="policy_region",
limitation_message="Operationele testgrens; geen landschappelijke claim.",
members=(scopes.ScopeMember("Alpha", "10001"), scopes.ScopeMember("Beta", "10002")),
)
source_features = [
{
"type": "Feature",
"id": "alpha",
"geometry": Polygon([(4.0, 51.0), (4.1, 51.0), (4.1, 51.1), (4.0, 51.1)]).__geo_interface__,
"properties": {"NAAM": "Alpha", "NISCODE": "10001"},
},
{
"type": "Feature",
"id": "beta",
"geometry": Polygon([(4.1, 51.0), (4.2, 51.0), (4.2, 51.1), (4.1, 51.1)]).__geo_interface__,
"properties": {"NAAM": "Beta", "NISCODE": "10002"},
},
]
boundary, members, summary = provisioner.build_scope_payloads(
scope,
source_features,
source_url="https://example.test/vrbg",
generated_at="2026-07-14T00:00:00+00:00",
)
assert len(boundary["features"]) == 1
assert len(members["features"]) == 2
assert shape(boundary["features"][0]["geometry"]).is_valid
assert boundary["features"][0]["properties"]["member_nis_codes"] == ["10001", "10002"]
assert boundary["features"][0]["properties"]["scope_limitation"] == scope.limitation_message
assert [feature["properties"]["municipality"] for feature in members["features"]] == ["Alpha", "Beta"]
assert summary["member_count"] == 2
assert summary["area_km2"] > 0
def test_scope_api_pagination_respects_canonical_limit() -> None:
provisioner = load_module("provision_geographic_scope_paging_test", "provision_geographic_scope.py")
class Response:
ok = True
status_code = 200
text = ""
def __init__(self, payload):
self.payload = payload
def json(self):
return {"data": self.payload}
class Session:
def __init__(self) -> None:
self.offsets = []
def get(self, url, *, params, timeout):
del url, timeout
self.offsets.append(params["offset"])
offset = params["offset"]
page = [{"id": index} for index in range(offset, min(offset + 200, 401))]
return Response({"items": page, "total": 401})
session = Session()
items = provisioner.list_paginated_items(session, "http://backend/api/v1/projects", timeout=30)
assert len(items) == 401
assert session.offsets == [0, 200, 400]
def test_kempen_scope_operator_is_packaged_and_exposed_in_map_flow() -> None:
dockerfile = (ROOT / "deploy/unraid/Dockerfile.all-in-one").read_text(encoding="utf-8")
readiness = (ROOT / "scripts/run_readiness_check.sh").read_text(encoding="utf-8")
workspace = (ROOT / "frontend/src/components/map/MapWorkspace.tsx").read_text(encoding="utf-8")
app = (ROOT / "frontend/src/App.tsx").read_text(encoding="utf-8")
assert "COPY scripts/geographic_scopes.py" in dockerfile
assert "COPY scripts/provision_geographic_scope.py" in dockerfile
assert "py_compile scripts/provision_geographic_scope.py" in readiness
assert "Immutable scope dataset" in (ROOT / "scripts/provision_geographic_scope.py").read_text(encoding="utf-8")
assert "Kempen (28 gemeenten)" in workspace
assert 'aria-label="Regio"' in workspace
assert "projects={projects}" in app
assert "onSelectProject={selectProject}" in app