testsAndMisc/python_pkg/geo_data/tests/test_warsaw.py

425 lines
16 KiB
Python
Raw Normal View History

"""Tests for python_pkg.geo_data._warsaw module."""
from __future__ import annotations
from typing import Any
from unittest.mock import MagicMock, patch
import geopandas as gpd
from shapely.geometry import LineString, Polygon
from python_pkg.geo_data._warsaw import (
_merge_bridge_segments,
get_vistula_river,
get_warsaw_boundary,
get_warsaw_bridges,
get_warsaw_districts,
)
class TestGetWarsawBoundary:
"""Tests for get_warsaw_boundary."""
@patch("python_pkg.geo_data._warsaw.gpd.read_file")
@patch("python_pkg.geo_data._warsaw.CACHE_DIR")
def test_cached(self, mock_cache_dir: MagicMock, mock_read: MagicMock) -> None:
mock_path = MagicMock()
mock_cache_dir.__truediv__ = MagicMock(return_value=mock_path)
mock_path.exists.return_value = True
mock_gdf = MagicMock(spec=gpd.GeoDataFrame)
mock_read.return_value = mock_gdf
result = get_warsaw_boundary()
assert result is mock_gdf
@patch("python_pkg.geo_data._warsaw.gpd.GeoDataFrame.to_file")
@patch("python_pkg.geo_data._warsaw._ensure_cache_dir")
@patch("python_pkg.geo_data._warsaw.gpd.read_file")
@patch("python_pkg.geo_data._warsaw._PKG_DIR")
@patch("python_pkg.geo_data._warsaw.CACHE_DIR")
def test_from_districts_file_with_warszawa(
self,
mock_cache_dir: MagicMock,
mock_pkg_dir: MagicMock,
mock_read: MagicMock,
mock_ensure: MagicMock,
mock_to_file: MagicMock,
) -> None:
mock_cache_path = MagicMock()
mock_cache_dir.__truediv__ = MagicMock(return_value=mock_cache_path)
mock_cache_path.exists.return_value = False
mock_districts_path = MagicMock()
mock_pkg_dir.__truediv__ = MagicMock(return_value=MagicMock())
mock_pkg_dir.__truediv__.return_value.__truediv__ = MagicMock(
return_value=MagicMock()
)
mock_pkg_dir.__truediv__.return_value.__truediv__.return_value.__truediv__ = (
MagicMock(return_value=mock_districts_path)
)
mock_districts_path.exists.return_value = True
mock_warsaw_gdf = gpd.GeoDataFrame(
{"name": ["Warszawa", "Mokotów"]},
geometry=[
Polygon([(20, 52), (21, 52), (21, 53), (20, 53)]),
Polygon([(20.5, 52.5), (20.6, 52.5), (20.6, 52.6), (20.5, 52.6)]),
],
crs="EPSG:4326",
)
mock_read.return_value = mock_warsaw_gdf
result = get_warsaw_boundary()
assert len(result) == 1
@patch("python_pkg.geo_data._warsaw.gpd.GeoDataFrame.to_file")
@patch("python_pkg.geo_data._warsaw._ensure_cache_dir")
@patch("python_pkg.geo_data._warsaw.gpd.read_file")
@patch("python_pkg.geo_data._warsaw._PKG_DIR")
@patch("python_pkg.geo_data._warsaw.CACHE_DIR")
def test_from_districts_file_no_warszawa_entry(
self,
mock_cache_dir: MagicMock,
mock_pkg_dir: MagicMock,
mock_read: MagicMock,
mock_ensure: MagicMock,
mock_to_file: MagicMock,
) -> None:
mock_cache_path = MagicMock()
mock_cache_dir.__truediv__ = MagicMock(return_value=mock_cache_path)
mock_cache_path.exists.return_value = False
mock_districts_path = MagicMock()
mock_pkg_dir.__truediv__ = MagicMock(return_value=MagicMock())
mock_pkg_dir.__truediv__.return_value.__truediv__ = MagicMock(
return_value=MagicMock()
)
mock_pkg_dir.__truediv__.return_value.__truediv__.return_value.__truediv__ = (
MagicMock(return_value=mock_districts_path)
)
mock_districts_path.exists.return_value = True
# No "Warszawa" entry
mock_warsaw_gdf = gpd.GeoDataFrame(
{"name": ["Mokotów", "Śródmieście"]},
geometry=[
Polygon([(20, 52), (21, 52), (21, 53), (20, 53)]),
Polygon([(20.5, 52.5), (20.6, 52.5), (20.6, 52.6), (20.5, 52.6)]),
],
crs="EPSG:4326",
)
mock_read.return_value = mock_warsaw_gdf
result = get_warsaw_boundary()
assert len(result) == 1
def test_fallback_overpass(self) -> None:
with (
patch("python_pkg.geo_data._warsaw.sys.stdout"),
patch("python_pkg.geo_data._warsaw.CACHE_DIR") as mock_cache_dir,
patch("python_pkg.geo_data._warsaw._PKG_DIR") as mock_pkg_dir,
patch("python_pkg.geo_data._warsaw._overpass_query") as mock_query,
patch("python_pkg.geo_data._warsaw._ensure_cache_dir"),
patch(
"python_pkg.geo_data._warsaw.gpd.GeoDataFrame.from_features"
) as mock_from_features,
):
mock_cache_path = MagicMock()
mock_cache_dir.__truediv__ = MagicMock(return_value=mock_cache_path)
mock_cache_path.exists.return_value = False
mock_districts_path = MagicMock()
mock_pkg_dir.__truediv__ = MagicMock(return_value=MagicMock())
mock_pkg_dir.__truediv__.return_value.__truediv__ = MagicMock(
return_value=MagicMock()
)
nested = mock_pkg_dir.__truediv__.return_value.__truediv__
nested.return_value.__truediv__ = MagicMock(
return_value=mock_districts_path
)
mock_districts_path.exists.return_value = False
mock_query.return_value = {
"elements": [
{
"type": "relation",
"members": [
{
"role": "outer",
"geometry": [
{"lon": 20, "lat": 52},
{"lon": 21, "lat": 52},
{"lon": 21, "lat": 53},
],
},
# non-outer member
{
"role": "inner",
"geometry": [
{"lon": 20.5, "lat": 52.5},
],
},
],
},
# Not a relation
{"type": "way"},
# Relation with no outer geometry (empty coords)
{
"type": "relation",
"members": [
{"role": "inner", "geometry": [{"lon": 20, "lat": 52}]},
],
},
]
}
mock_gdf = MagicMock(spec=gpd.GeoDataFrame)
mock_from_features.return_value = mock_gdf
result = get_warsaw_boundary()
assert result is mock_gdf
class TestGetWarsawDistricts:
"""Tests for get_warsaw_districts."""
@patch("python_pkg.geo_data._warsaw.gpd.read_file")
@patch("python_pkg.geo_data._warsaw._PKG_DIR")
def test_districts_file_exists(
self, mock_pkg_dir: MagicMock, mock_read: MagicMock
) -> None:
mock_districts_path = MagicMock()
mock_pkg_dir.__truediv__ = MagicMock(return_value=MagicMock())
mock_pkg_dir.__truediv__.return_value.__truediv__ = MagicMock(
return_value=MagicMock()
)
mock_pkg_dir.__truediv__.return_value.__truediv__.return_value.__truediv__ = (
MagicMock(return_value=mock_districts_path)
)
mock_districts_path.exists.return_value = True
mock_gdf = gpd.GeoDataFrame(
{"name": ["Warszawa", "Mokotów", "Śródmieście"]},
geometry=[
Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]),
Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]),
Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]),
],
crs="EPSG:4326",
)
mock_read.return_value = mock_gdf
result = get_warsaw_districts()
assert "Warszawa" not in result["name"].values
@patch("python_pkg.geo_data._warsaw._PKG_DIR")
def test_districts_file_not_found(self, mock_pkg_dir: MagicMock) -> None:
mock_districts_path = MagicMock()
mock_pkg_dir.__truediv__ = MagicMock(return_value=MagicMock())
mock_pkg_dir.__truediv__.return_value.__truediv__ = MagicMock(
return_value=MagicMock()
)
mock_pkg_dir.__truediv__.return_value.__truediv__.return_value.__truediv__ = (
MagicMock(return_value=mock_districts_path)
)
mock_districts_path.exists.return_value = False
import pytest
with pytest.raises(FileNotFoundError, match="Warsaw districts GeoJSON"):
get_warsaw_districts()
class TestGetVistulaRiver:
"""Tests for get_vistula_river."""
@patch("python_pkg.geo_data._warsaw.gpd.read_file")
@patch("python_pkg.geo_data._warsaw.CACHE_DIR")
def test_cached(self, mock_cache_dir: MagicMock, mock_read: MagicMock) -> None:
mock_path = MagicMock()
mock_cache_dir.__truediv__ = MagicMock(return_value=mock_path)
mock_path.exists.return_value = True
mock_gdf = MagicMock(spec=gpd.GeoDataFrame)
mock_read.return_value = mock_gdf
result = get_vistula_river()
assert result is mock_gdf
@patch("python_pkg.geo_data._warsaw.gpd.GeoDataFrame.from_features")
@patch("python_pkg.geo_data._warsaw._ensure_cache_dir")
@patch("python_pkg.geo_data._warsaw._overpass_query")
@patch("python_pkg.geo_data._warsaw.CACHE_DIR")
@patch("python_pkg.geo_data._warsaw.sys.stdout")
def test_downloads(
self,
mock_stdout: MagicMock,
mock_cache_dir: MagicMock,
mock_query: MagicMock,
mock_ensure: MagicMock,
mock_from_features: MagicMock,
) -> None:
mock_path = MagicMock()
mock_cache_dir.__truediv__ = MagicMock(return_value=mock_path)
mock_path.exists.return_value = False
mock_query.return_value = {
"elements": [
{
"type": "way",
"geometry": [
{"lon": 20.0, "lat": 52.0},
{"lon": 21.0, "lat": 52.5},
],
},
# Too few coords
{
"type": "way",
"geometry": [{"lon": 20.0, "lat": 52.0}],
},
# Not a way
{"type": "node"},
# Way without geometry
{"type": "way"},
]
}
mock_gdf = MagicMock(spec=gpd.GeoDataFrame)
mock_from_features.return_value = mock_gdf
result = get_vistula_river()
assert result is mock_gdf
class TestGetWarsawBridges:
"""Tests for get_warsaw_bridges."""
@patch("python_pkg.geo_data._warsaw.gpd.read_file")
@patch("python_pkg.geo_data._warsaw.CACHE_DIR")
def test_cached(self, mock_cache_dir: MagicMock, mock_read: MagicMock) -> None:
mock_path = MagicMock()
mock_cache_dir.__truediv__ = MagicMock(return_value=mock_path)
mock_path.exists.return_value = True
mock_gdf = MagicMock(spec=gpd.GeoDataFrame)
mock_read.return_value = mock_gdf
result = get_warsaw_bridges()
assert result is mock_gdf
def test_downloads(self) -> None:
with (
patch("python_pkg.geo_data._warsaw.sys.stdout"),
patch("python_pkg.geo_data._warsaw.CACHE_DIR") as mock_cache_dir,
patch("python_pkg.geo_data._warsaw.get_vistula_river") as mock_vistula,
patch("python_pkg.geo_data._warsaw._overpass_query") as mock_query,
patch("python_pkg.geo_data._warsaw._ensure_cache_dir"),
patch(
"python_pkg.geo_data._warsaw.gpd.GeoDataFrame.from_features"
) as mock_from_features,
):
mock_path = MagicMock()
mock_cache_dir.__truediv__ = MagicMock(return_value=mock_path)
mock_path.exists.return_value = False
# Create a real Vistula geometry for intersection tests
vistula_gdf = gpd.GeoDataFrame(
{"name": ["Wisła"]},
geometry=[LineString([(20.0, 52.2), (21.0, 52.2)])],
crs="EPSG:4326",
)
mock_vistula.return_value = vistula_gdf
mock_query.return_value = {
"elements": [
# Bridge that intersects vistula buffer
{
"type": "way",
"id": 1,
"tags": {"name": "Most Łazienkowski"},
"geometry": [
{"lon": 20.5, "lat": 52.19},
{"lon": 20.5, "lat": 52.21},
],
},
# Bridge far from vistula
{
"type": "way",
"id": 2,
"tags": {"name": "Most Daleki"},
"geometry": [
{"lon": 20.5, "lat": 55.0},
{"lon": 20.5, "lat": 55.1},
],
},
# Not a way
{"type": "node", "tags": {"name": "Most X"}},
# Way without geometry
{"type": "way", "tags": {"name": "Most Y"}},
# No name
{
"type": "way",
"id": 3,
"tags": {},
"geometry": [
{"lon": 20.5, "lat": 52.19},
{"lon": 20.5, "lat": 52.21},
],
},
# Duplicate
{
"type": "way",
"id": 4,
"tags": {"name": "Most Łazienkowski"},
"geometry": [
{"lon": 20.5, "lat": 52.19},
{"lon": 20.5, "lat": 52.21},
],
},
# Too few coords
{
"type": "way",
"id": 5,
"tags": {"name": "Most Short"},
"geometry": [{"lon": 20.5, "lat": 52.19}],
},
]
}
mock_gdf = MagicMock(spec=gpd.GeoDataFrame)
mock_from_features.return_value = mock_gdf
result = get_warsaw_bridges()
assert result is mock_gdf
class TestMergeBridgeSegments:
"""Tests for _merge_bridge_segments."""
def test_single_segment(self) -> None:
features: list[dict[str, Any]] = [
{
"properties": {"name": "Most A"},
"geometry": {"coordinates": [(20, 52), (21, 52)]},
}
]
result = _merge_bridge_segments(features)
assert len(result) == 1
assert result[0]["geometry"]["type"] == "LineString"
def test_multiple_segments_same_name(self) -> None:
features: list[dict[str, Any]] = [
{
"properties": {"name": "Most A"},
"geometry": {"coordinates": [(20, 52), (21, 52)]},
},
{
"properties": {"name": "Most A"},
"geometry": {"coordinates": [(21, 52), (22, 52)]},
},
]
result = _merge_bridge_segments(features)
assert len(result) == 1
assert result[0]["geometry"]["type"] == "MultiLineString"