mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-04 11:43:10 +02:00
- Add comprehensive tests for all packages (3572 tests, 100% branch coverage) - Split oversized test files to stay under 500-line limit - Add per-file ruff ignores for test-appropriate suppressions - Fix _cache_decks.py to properly convert JSON lists to tuples - Add session-scoped conftest fixture for logging handler cleanup (Python 3.14) - Update ruff pre-commit hook to v0.15.2 - Add codespell ignore words for test data - Add generated output files to .gitignore
386 lines
13 KiB
Python
386 lines
13 KiB
Python
"""Tests for python_pkg.geo_data._poland_nature module."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import geopandas as gpd
|
|
import pytest
|
|
from shapely.geometry import Polygon
|
|
|
|
from python_pkg.geo_data._poland_nature import (
|
|
get_polish_mountain_peaks,
|
|
get_polish_mountain_ranges,
|
|
get_polish_national_parks,
|
|
)
|
|
|
|
|
|
def _make_relation_element(name: str, *, include_outer: bool = True) -> dict[str, Any]:
|
|
"""Create a mock OSM relation element."""
|
|
members = []
|
|
if include_outer:
|
|
members.append(
|
|
{
|
|
"role": "outer",
|
|
"geometry": [
|
|
{"lon": 0, "lat": 0},
|
|
{"lon": 1, "lat": 0},
|
|
{"lon": 1, "lat": 1},
|
|
{"lon": 0, "lat": 1},
|
|
],
|
|
}
|
|
)
|
|
return {"type": "relation", "tags": {"name": name}, "members": members}
|
|
|
|
|
|
class TestGetPolishMountainPeaks:
|
|
"""Tests for get_polish_mountain_peaks."""
|
|
|
|
@patch("python_pkg.geo_data._poland_nature.gpd.read_file")
|
|
@patch("python_pkg.geo_data._poland_nature.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 = gpd.GeoDataFrame(
|
|
{"name": ["Rysy", "Babia Góra"], "elevation": [2499.0, 1725.0]},
|
|
geometry=[
|
|
Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]),
|
|
Polygon([(2, 2), (3, 2), (3, 3), (2, 3)]),
|
|
],
|
|
crs="EPSG:4326",
|
|
)
|
|
mock_read.return_value = mock_gdf
|
|
|
|
result = get_polish_mountain_peaks()
|
|
assert result.iloc[0]["elevation"] == 2499.0
|
|
|
|
@patch("python_pkg.geo_data._poland_nature.gpd.GeoDataFrame.from_features")
|
|
@patch("python_pkg.geo_data._poland_nature._ensure_cache_dir")
|
|
@patch("python_pkg.geo_data._poland_nature._overpass_query")
|
|
@patch("python_pkg.geo_data._poland_nature.CACHE_DIR")
|
|
@patch("python_pkg.geo_data._poland_nature.sys.stdout")
|
|
def test_downloads_peaks(
|
|
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": "node",
|
|
"tags": {"name": "Rysy", "ele": "2499"},
|
|
"lon": 20.0,
|
|
"lat": 49.0,
|
|
},
|
|
# Below threshold
|
|
{
|
|
"type": "node",
|
|
"tags": {"name": "LowPeak", "ele": "100"},
|
|
"lon": 20.0,
|
|
"lat": 49.0,
|
|
},
|
|
# Missing ele
|
|
{
|
|
"type": "node",
|
|
"tags": {"name": "NoEle"},
|
|
"lon": 20.0,
|
|
"lat": 49.0,
|
|
},
|
|
# Duplicate name
|
|
{
|
|
"type": "node",
|
|
"tags": {"name": "Rysy", "ele": "2499"},
|
|
"lon": 20.0,
|
|
"lat": 49.0,
|
|
},
|
|
# Not a node
|
|
{
|
|
"type": "way",
|
|
"tags": {"name": "Way", "ele": "500"},
|
|
},
|
|
# No name
|
|
{
|
|
"type": "node",
|
|
"tags": {"ele": "500"},
|
|
"lon": 20.0,
|
|
"lat": 49.0,
|
|
},
|
|
# Comma in ele
|
|
{
|
|
"type": "node",
|
|
"tags": {"name": "Peak2", "ele": "500,5 m"},
|
|
"lon": 20.0,
|
|
"lat": 49.0,
|
|
},
|
|
# Invalid ele
|
|
{
|
|
"type": "node",
|
|
"tags": {"name": "BadEle", "ele": "abc"},
|
|
"lon": 20.0,
|
|
"lat": 49.0,
|
|
},
|
|
]
|
|
}
|
|
|
|
mock_gdf = gpd.GeoDataFrame(
|
|
{"name": ["Rysy", "Peak2"], "elevation": [2499.0, 500.5]},
|
|
geometry=[
|
|
Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]),
|
|
Polygon([(2, 2), (3, 2), (3, 3), (2, 3)]),
|
|
],
|
|
crs="EPSG:4326",
|
|
)
|
|
mock_from_features.return_value = mock_gdf
|
|
|
|
result = get_polish_mountain_peaks()
|
|
assert result.iloc[0]["elevation"] == 2499.0
|
|
|
|
@patch("python_pkg.geo_data._poland_nature._ensure_cache_dir")
|
|
@patch("python_pkg.geo_data._poland_nature._overpass_query")
|
|
@patch("python_pkg.geo_data._poland_nature.CACHE_DIR")
|
|
@patch("python_pkg.geo_data._poland_nature.sys.stdout")
|
|
def test_no_peaks_raises(
|
|
self,
|
|
mock_stdout: MagicMock,
|
|
mock_cache_dir: MagicMock,
|
|
mock_query: MagicMock,
|
|
mock_ensure: 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": []}
|
|
|
|
with pytest.raises(ValueError, match="No mountain peaks found"):
|
|
get_polish_mountain_peaks()
|
|
|
|
|
|
class TestGetPolishMountainRanges:
|
|
"""Tests for get_polish_mountain_ranges."""
|
|
|
|
@patch("python_pkg.geo_data._poland_nature.gpd.read_file")
|
|
@patch("python_pkg.geo_data._poland_nature.CACHE_DIR")
|
|
def test_cached_with_area(
|
|
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
|
|
|
|
poly = Polygon([(20, 50), (21, 50), (21, 51), (20, 51)])
|
|
mock_gdf = gpd.GeoDataFrame(
|
|
{"name": ["Tatry"], "area_km2": [100.0]},
|
|
geometry=[poly],
|
|
crs="EPSG:4326",
|
|
)
|
|
mock_read.return_value = mock_gdf
|
|
|
|
result = get_polish_mountain_ranges()
|
|
assert "area_km2" in result.columns
|
|
|
|
@patch("python_pkg.geo_data._poland_nature.gpd.read_file")
|
|
@patch("python_pkg.geo_data._poland_nature.CACHE_DIR")
|
|
def test_cached_without_area(
|
|
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
|
|
|
|
poly = Polygon([(20, 50), (21, 50), (21, 51), (20, 51)])
|
|
mock_gdf = gpd.GeoDataFrame(
|
|
{"name": ["Tatry"]},
|
|
geometry=[poly],
|
|
crs="EPSG:4326",
|
|
)
|
|
mock_read.return_value = mock_gdf
|
|
|
|
result = get_polish_mountain_ranges()
|
|
assert len(result) >= 0
|
|
|
|
@patch("python_pkg.geo_data._poland_nature.gpd.GeoDataFrame.from_features")
|
|
@patch("python_pkg.geo_data._poland_nature._ensure_cache_dir")
|
|
@patch("python_pkg.geo_data._poland_nature._overpass_query")
|
|
@patch("python_pkg.geo_data._poland_nature.CACHE_DIR")
|
|
@patch("python_pkg.geo_data._poland_nature.sys.stdout")
|
|
def test_downloads_ranges(
|
|
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": [
|
|
# Relation
|
|
_make_relation_element("Tatry"),
|
|
# Way with enough coords
|
|
{
|
|
"type": "way",
|
|
"tags": {"name": "Bieszczady"},
|
|
"geometry": [
|
|
{"lon": 0, "lat": 0},
|
|
{"lon": 1, "lat": 0},
|
|
{"lon": 1, "lat": 1},
|
|
{"lon": 0, "lat": 1},
|
|
],
|
|
},
|
|
# Way with auto-close
|
|
{
|
|
"type": "way",
|
|
"tags": {"name": "Karkonosze"},
|
|
"geometry": [
|
|
{"lon": 0, "lat": 0},
|
|
{"lon": 1, "lat": 0},
|
|
{"lon": 1, "lat": 1},
|
|
{"lon": 0, "lat": 0.5},
|
|
],
|
|
},
|
|
# Way already closed (first == last)
|
|
{
|
|
"type": "way",
|
|
"tags": {"name": "Sudety"},
|
|
"geometry": [
|
|
{"lon": 2, "lat": 2},
|
|
{"lon": 3, "lat": 2},
|
|
{"lon": 3, "lat": 3},
|
|
{"lon": 2, "lat": 2},
|
|
],
|
|
},
|
|
# Way too few coords
|
|
{
|
|
"type": "way",
|
|
"tags": {"name": "Short"},
|
|
"geometry": [{"lon": 0, "lat": 0}, {"lon": 1, "lat": 0}],
|
|
},
|
|
# Duplicate
|
|
_make_relation_element("Tatry"),
|
|
# No name
|
|
_make_relation_element(""),
|
|
# Unknown type
|
|
{"type": "node", "tags": {"name": "Ignored"}},
|
|
# Way without geometry
|
|
{"type": "way", "tags": {"name": "NoGeom"}},
|
|
# Relation without outer rings
|
|
_make_relation_element("NoOuter", include_outer=False),
|
|
]
|
|
}
|
|
|
|
poly = Polygon([(20, 50), (21, 50), (21, 51), (20, 51)])
|
|
mock_gdf = gpd.GeoDataFrame(
|
|
{"name": ["Tatry", "Bieszczady", "Karkonosze", "Sudety"]},
|
|
geometry=[poly, poly, poly, poly],
|
|
crs="EPSG:4326",
|
|
)
|
|
mock_from_features.return_value = mock_gdf
|
|
|
|
result = get_polish_mountain_ranges()
|
|
assert len(result) >= 0
|
|
|
|
|
|
class TestGetPolishNationalParks:
|
|
"""Tests for get_polish_national_parks."""
|
|
|
|
@patch("python_pkg.geo_data._poland_nature.gpd.read_file")
|
|
@patch("python_pkg.geo_data._poland_nature.CACHE_DIR")
|
|
def test_cached_with_area(
|
|
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 = gpd.GeoDataFrame(
|
|
{"name": ["Tatrzański Park Narodowy"], "area_km2": [200.0]},
|
|
geometry=[Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])],
|
|
crs="EPSG:4326",
|
|
)
|
|
mock_read.return_value = mock_gdf
|
|
|
|
result = get_polish_national_parks()
|
|
assert result.iloc[0]["area_km2"] == 200.0
|
|
|
|
@patch("python_pkg.geo_data._poland_nature.gpd.read_file")
|
|
@patch("python_pkg.geo_data._poland_nature.CACHE_DIR")
|
|
def test_cached_without_area(
|
|
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 = gpd.GeoDataFrame(
|
|
{"name": ["Tatrzański Park Narodowy"]},
|
|
geometry=[Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])],
|
|
crs="EPSG:4326",
|
|
)
|
|
mock_read.return_value = mock_gdf
|
|
|
|
result = get_polish_national_parks()
|
|
assert len(result) == 1
|
|
|
|
@patch("python_pkg.geo_data._poland_nature.gpd.GeoDataFrame.from_features")
|
|
@patch("python_pkg.geo_data._poland_nature._ensure_cache_dir")
|
|
@patch("python_pkg.geo_data._poland_nature._overpass_query")
|
|
@patch("python_pkg.geo_data._poland_nature.CACHE_DIR")
|
|
@patch("python_pkg.geo_data._poland_nature.sys.stdout")
|
|
def test_downloads_parks(
|
|
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": [
|
|
_make_relation_element("Tatrzański Park Narodowy"),
|
|
# Not a national park (missing "Narodowy")
|
|
_make_relation_element("Some Reserve"),
|
|
# Not a relation
|
|
{"type": "way", "tags": {"name": "Park Narodowy X"}},
|
|
# No name
|
|
{"type": "relation", "tags": {}, "members": []},
|
|
# Duplicate
|
|
_make_relation_element("Tatrzański Park Narodowy"),
|
|
# No outer rings
|
|
_make_relation_element("Empty Park Narodowy", include_outer=False),
|
|
# Case insensitive match
|
|
_make_relation_element("park narodowy Biebrzy"),
|
|
]
|
|
}
|
|
|
|
poly = Polygon([(20, 50), (21, 50), (21, 51), (20, 51)])
|
|
mock_gdf = gpd.GeoDataFrame(
|
|
{"name": ["Tatrzański Park Narodowy", "park narodowy Biebrzy"]},
|
|
geometry=[poly, poly],
|
|
crs="EPSG:4326",
|
|
)
|
|
mock_from_features.return_value = mock_gdf
|
|
|
|
result = get_polish_national_parks()
|
|
assert len(result) >= 0
|