mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-04 21:43:16 +02:00
Fix ruff violations in ~15 source files and ~60+ test files to minimize per-file-ignores in pyproject.toml. Remaining ignores are justified with comments explaining why each suppression is necessary. Source fixes: FBT003 (keyword args), S310 (URL validation), SLF001 (private access), T201 (print→logging), C901 (complexity), E501 (line length), E402 (import order). Test fixes: SIM117 (combined with), FBT (boolean args), PERF203 (try in loop), S310/S607 (URLs/executables), E402/E501 (imports/lines), S108 (tmp paths), PLR0913 (too many args), ARG (unused args), ANN (type annotations), RUF059 (unused unpacked vars), PT019 (fixture naming). Remaining per-file-ignores (with justifications): - Tests: ARG, D, PLC0415, PLR2004, S101, SLF001 - music_gen sources: PLC0415 (heavy ML lazy imports) - moviepy_showcase: PLC0415 (circular dependency) - generate_images: PLR0913 (matplotlib helpers need many params) - praca_magisterska_video: E501, E402 (long paths, mpl.use)
488 lines
16 KiB
Python
488 lines
16 KiB
Python
"""Tests for python_pkg.geo_data._common module."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import Any
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
from shapely.geometry import (
|
|
GeometryCollection,
|
|
LineString,
|
|
MultiPolygon,
|
|
Point,
|
|
Polygon,
|
|
)
|
|
|
|
from python_pkg.geo_data._common import (
|
|
_build_osiedla_geometry,
|
|
_download_github_geojson,
|
|
_ensure_cache_dir,
|
|
_extract_line_from_way,
|
|
_extract_osiedla_rings,
|
|
_extract_polygon_from_element,
|
|
_extract_polygonal_geometry,
|
|
_overpass_query,
|
|
_try_single_request,
|
|
)
|
|
|
|
|
|
class TestEnsureCacheDir:
|
|
"""Tests for _ensure_cache_dir."""
|
|
|
|
def test_creates_directory(self) -> None:
|
|
with patch.object(Path, "mkdir") as mock_mkdir:
|
|
_ensure_cache_dir()
|
|
mock_mkdir.assert_called_once_with(parents=True, exist_ok=True)
|
|
|
|
|
|
class TestExtractPolygonalGeometry:
|
|
"""Tests for _extract_polygonal_geometry."""
|
|
|
|
def test_polygon_returned_directly(self) -> None:
|
|
poly = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
|
|
result = _extract_polygonal_geometry(poly)
|
|
assert result is poly
|
|
|
|
def test_multipolygon_returned_directly(self) -> None:
|
|
mp = MultiPolygon(
|
|
[
|
|
Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]),
|
|
Polygon([(2, 2), (3, 2), (3, 3), (2, 3)]),
|
|
]
|
|
)
|
|
result = _extract_polygonal_geometry(mp)
|
|
assert result is mp
|
|
|
|
def test_geometry_collection_single_polygon(self) -> None:
|
|
poly = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
|
|
gc = GeometryCollection([poly, LineString([(0, 0), (1, 1)])])
|
|
result = _extract_polygonal_geometry(gc)
|
|
assert result is not None
|
|
assert result.equals(poly)
|
|
|
|
def test_geometry_collection_multiple_polygons(self) -> None:
|
|
p1 = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
|
|
p2 = Polygon([(2, 2), (3, 2), (3, 3), (2, 3)])
|
|
gc = GeometryCollection([p1, p2, LineString([(0, 0), (1, 1)])])
|
|
result = _extract_polygonal_geometry(gc)
|
|
assert isinstance(result, MultiPolygon)
|
|
|
|
def test_geometry_collection_with_multipolygon(self) -> None:
|
|
p1 = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
|
|
mp = MultiPolygon(
|
|
[
|
|
Polygon([(2, 2), (3, 2), (3, 3), (2, 3)]),
|
|
Polygon([(4, 4), (5, 4), (5, 5), (4, 5)]),
|
|
]
|
|
)
|
|
gc = GeometryCollection([p1, mp])
|
|
result = _extract_polygonal_geometry(gc)
|
|
assert isinstance(result, MultiPolygon)
|
|
|
|
def test_geometry_collection_no_polygons(self) -> None:
|
|
gc = GeometryCollection([LineString([(0, 0), (1, 1)])])
|
|
result = _extract_polygonal_geometry(gc)
|
|
assert result is None
|
|
|
|
def test_unsupported_geometry_type(self) -> None:
|
|
point = Point(0, 0)
|
|
result = _extract_polygonal_geometry(point)
|
|
assert result is None
|
|
|
|
|
|
class TestTrySingleRequest:
|
|
"""Tests for _try_single_request."""
|
|
|
|
@patch("python_pkg.geo_data._common.requests.post")
|
|
@patch("python_pkg.geo_data._common.sys.stdout")
|
|
def test_successful_request(
|
|
self, mock_stdout: MagicMock, mock_post: MagicMock
|
|
) -> None:
|
|
mock_response = MagicMock()
|
|
mock_response.json.return_value = {"elements": []}
|
|
mock_post.return_value = mock_response
|
|
|
|
result, error = _try_single_request("http://example.com", "query")
|
|
assert result == {"elements": []}
|
|
assert error is None
|
|
|
|
@patch("python_pkg.geo_data._common.requests.post")
|
|
@patch("python_pkg.geo_data._common.sys.stdout")
|
|
def test_request_exception(
|
|
self, mock_stdout: MagicMock, mock_post: MagicMock
|
|
) -> None:
|
|
import requests
|
|
|
|
mock_post.side_effect = requests.RequestException("fail")
|
|
result, error = _try_single_request("http://example.com", "query")
|
|
assert result is None
|
|
assert isinstance(error, requests.RequestException)
|
|
|
|
@patch("python_pkg.geo_data._common.requests.post")
|
|
@patch("python_pkg.geo_data._common.sys.stdout")
|
|
def test_invalid_response_format(
|
|
self, mock_stdout: MagicMock, mock_post: MagicMock
|
|
) -> None:
|
|
mock_response = MagicMock()
|
|
mock_response.json.return_value = {"no_elements": True}
|
|
mock_post.return_value = mock_response
|
|
|
|
result, error = _try_single_request("http://example.com", "query")
|
|
assert result is None
|
|
assert isinstance(error, ValueError)
|
|
|
|
@patch("python_pkg.geo_data._common.requests.post")
|
|
@patch("python_pkg.geo_data._common.sys.stdout")
|
|
def test_non_dict_response(
|
|
self, mock_stdout: MagicMock, mock_post: MagicMock
|
|
) -> None:
|
|
mock_response = MagicMock()
|
|
mock_response.json.return_value = [1, 2, 3]
|
|
mock_post.return_value = mock_response
|
|
|
|
result, error = _try_single_request("http://example.com", "query")
|
|
assert result is None
|
|
assert isinstance(error, ValueError)
|
|
|
|
@patch("python_pkg.geo_data._common.requests.post")
|
|
@patch("python_pkg.geo_data._common.sys.stdout")
|
|
def test_value_error_on_json_parse(
|
|
self, mock_stdout: MagicMock, mock_post: MagicMock
|
|
) -> None:
|
|
mock_response = MagicMock()
|
|
mock_response.json.side_effect = ValueError("bad json")
|
|
mock_post.return_value = mock_response
|
|
|
|
result, error = _try_single_request("http://example.com", "query")
|
|
assert result is None
|
|
assert isinstance(error, ValueError)
|
|
|
|
@patch("python_pkg.geo_data._common.requests.post")
|
|
@patch("python_pkg.geo_data._common.sys.stdout")
|
|
def test_timeout_error(self, mock_stdout: MagicMock, mock_post: MagicMock) -> None:
|
|
import requests
|
|
|
|
mock_post.side_effect = requests.Timeout("timeout")
|
|
result, error = _try_single_request("http://example.com", "query")
|
|
assert result is None
|
|
assert isinstance(error, requests.Timeout)
|
|
|
|
|
|
class TestOverpassQuery:
|
|
"""Tests for _overpass_query."""
|
|
|
|
@patch("python_pkg.geo_data._common._try_single_request")
|
|
def test_success_on_first_try(self, mock_req: MagicMock) -> None:
|
|
mock_req.return_value = ({"elements": []}, None)
|
|
result = _overpass_query("query")
|
|
assert result == {"elements": []}
|
|
|
|
@patch("python_pkg.geo_data._common.time.sleep")
|
|
@patch("python_pkg.geo_data._common._try_single_request")
|
|
@patch("python_pkg.geo_data._common.sys.stdout")
|
|
def test_retries_then_succeeds(
|
|
self, mock_stdout: MagicMock, mock_req: MagicMock, mock_sleep: MagicMock
|
|
) -> None:
|
|
mock_req.side_effect = [
|
|
(None, ValueError("fail1")),
|
|
({"elements": []}, None),
|
|
]
|
|
result = _overpass_query("query")
|
|
assert result == {"elements": []}
|
|
|
|
@patch("python_pkg.geo_data._common.time.sleep")
|
|
@patch("python_pkg.geo_data._common._try_single_request")
|
|
@patch("python_pkg.geo_data._common.sys.stdout")
|
|
def test_all_endpoints_fail(
|
|
self, mock_stdout: MagicMock, mock_req: MagicMock, mock_sleep: MagicMock
|
|
) -> None:
|
|
mock_req.return_value = (None, ValueError("fail"))
|
|
with pytest.raises(RuntimeError, match="All Overpass API endpoints failed"):
|
|
_overpass_query("query")
|
|
|
|
|
|
class TestDownloadGithubGeojson:
|
|
"""Tests for _download_github_geojson."""
|
|
|
|
@patch("python_pkg.geo_data._common.gpd.read_file")
|
|
def test_cached_file_exists(self, mock_read: MagicMock) -> None:
|
|
mock_gdf = MagicMock()
|
|
mock_read.return_value = mock_gdf
|
|
cache_path = MagicMock()
|
|
cache_path.exists.return_value = True
|
|
|
|
result = _download_github_geojson("http://example.com/data.geojson", cache_path)
|
|
assert result is mock_gdf
|
|
mock_read.assert_called_once_with(cache_path)
|
|
|
|
@patch("python_pkg.geo_data._common.gpd.GeoDataFrame.from_features")
|
|
@patch("python_pkg.geo_data._common._ensure_cache_dir")
|
|
@patch("python_pkg.geo_data._common.requests.get")
|
|
@patch("python_pkg.geo_data._common.sys.stdout")
|
|
def test_downloads_and_caches(
|
|
self,
|
|
mock_stdout: MagicMock,
|
|
mock_get: MagicMock,
|
|
mock_ensure: MagicMock,
|
|
mock_from_features: MagicMock,
|
|
) -> None:
|
|
features_data: dict[str, Any] = {
|
|
"features": [
|
|
{
|
|
"type": "Feature",
|
|
"properties": {"name": "test"},
|
|
"geometry": {"type": "Point", "coordinates": [0, 0]},
|
|
}
|
|
]
|
|
}
|
|
mock_response = MagicMock()
|
|
mock_response.json.return_value = features_data
|
|
mock_get.return_value = mock_response
|
|
|
|
mock_gdf = MagicMock()
|
|
mock_from_features.return_value = mock_gdf
|
|
|
|
cache_path = MagicMock()
|
|
cache_path.exists.return_value = False
|
|
|
|
result = _download_github_geojson(
|
|
"https://example.com/data.geojson", cache_path
|
|
)
|
|
assert result is mock_gdf
|
|
|
|
def test_unsupported_url_scheme(self) -> None:
|
|
cache_path = MagicMock()
|
|
cache_path.exists.return_value = False
|
|
with pytest.raises(ValueError, match="Unsupported URL scheme"):
|
|
_download_github_geojson("ftp://example.com/data", cache_path)
|
|
|
|
|
|
class TestExtractOsiedlaRings:
|
|
"""Tests for _extract_osiedla_rings."""
|
|
|
|
def test_outer_and_inner_rings(self) -> None:
|
|
element: dict[str, Any] = {
|
|
"members": [
|
|
{
|
|
"role": "outer",
|
|
"geometry": [
|
|
{"lon": 0, "lat": 0},
|
|
{"lon": 1, "lat": 0},
|
|
{"lon": 1, "lat": 1},
|
|
{"lon": 0, "lat": 1},
|
|
],
|
|
},
|
|
{
|
|
"role": "inner",
|
|
"geometry": [
|
|
{"lon": 0.2, "lat": 0.2},
|
|
{"lon": 0.4, "lat": 0.2},
|
|
{"lon": 0.4, "lat": 0.4},
|
|
{"lon": 0.2, "lat": 0.4},
|
|
],
|
|
},
|
|
]
|
|
}
|
|
outer, inner = _extract_osiedla_rings(element, 4)
|
|
assert len(outer) == 1
|
|
assert len(inner) == 1
|
|
|
|
def test_ring_too_short(self) -> None:
|
|
element: dict[str, Any] = {
|
|
"members": [
|
|
{
|
|
"role": "outer",
|
|
"geometry": [{"lon": 0, "lat": 0}, {"lon": 1, "lat": 0}],
|
|
}
|
|
]
|
|
}
|
|
outer, inner = _extract_osiedla_rings(element, 4)
|
|
assert len(outer) == 0
|
|
assert len(inner) == 0
|
|
|
|
def test_no_geometry_in_member(self) -> None:
|
|
element: dict[str, Any] = {"members": [{"role": "outer"}]}
|
|
outer, inner = _extract_osiedla_rings(element, 4)
|
|
assert len(outer) == 0
|
|
assert len(inner) == 0
|
|
|
|
def test_already_closed_ring(self) -> None:
|
|
element: dict[str, Any] = {
|
|
"members": [
|
|
{
|
|
"role": "outer",
|
|
"geometry": [
|
|
{"lon": 0, "lat": 0},
|
|
{"lon": 1, "lat": 0},
|
|
{"lon": 1, "lat": 1},
|
|
{"lon": 0, "lat": 0},
|
|
],
|
|
}
|
|
]
|
|
}
|
|
outer, _ = _extract_osiedla_rings(element, 4)
|
|
assert len(outer) == 1
|
|
# Already closed, so no extra point
|
|
assert outer[0][0] == outer[0][-1]
|
|
|
|
def test_no_members(self) -> None:
|
|
element: dict[str, Any] = {}
|
|
outer, inner = _extract_osiedla_rings(element, 4)
|
|
assert len(outer) == 0
|
|
assert len(inner) == 0
|
|
|
|
def test_unknown_role_ignored(self) -> None:
|
|
element: dict[str, Any] = {
|
|
"members": [
|
|
{
|
|
"role": "label",
|
|
"geometry": [
|
|
{"lon": 0, "lat": 0},
|
|
{"lon": 1, "lat": 0},
|
|
{"lon": 1, "lat": 1},
|
|
{"lon": 0, "lat": 1},
|
|
],
|
|
}
|
|
]
|
|
}
|
|
outer, inner = _extract_osiedla_rings(element, 4)
|
|
assert len(outer) == 0
|
|
assert len(inner) == 0
|
|
|
|
|
|
class TestBuildOsiedlaGeometry:
|
|
"""Tests for _build_osiedla_geometry."""
|
|
|
|
def test_single_outer_ring(self) -> None:
|
|
outer = [[(0, 0), (1, 0), (1, 1), (0, 0)]]
|
|
inner: list[list[tuple[float, float]]] = []
|
|
result = _build_osiedla_geometry(outer, inner)
|
|
assert result["type"] == "Polygon"
|
|
|
|
def test_single_outer_with_inner(self) -> None:
|
|
outer = [[(0, 0), (1, 0), (1, 1), (0, 0)]]
|
|
inner = [[(0.2, 0.2), (0.4, 0.2), (0.4, 0.4), (0.2, 0.2)]]
|
|
result = _build_osiedla_geometry(outer, inner)
|
|
assert result["type"] == "Polygon"
|
|
assert len(result["coordinates"]) == 2
|
|
|
|
def test_multiple_outer_rings(self) -> None:
|
|
outer = [
|
|
[(0, 0), (1, 0), (1, 1), (0, 0)],
|
|
[(2, 2), (3, 2), (3, 3), (2, 2)],
|
|
]
|
|
inner: list[list[tuple[float, float]]] = []
|
|
result = _build_osiedla_geometry(outer, inner)
|
|
assert result["type"] == "MultiPolygon"
|
|
|
|
|
|
class TestExtractPolygonFromElement:
|
|
"""Tests for _extract_polygon_from_element."""
|
|
|
|
def test_relation_with_rings(self) -> None:
|
|
element: dict[str, Any] = {
|
|
"type": "relation",
|
|
"members": [
|
|
{
|
|
"role": "outer",
|
|
"geometry": [
|
|
{"lon": 0, "lat": 0},
|
|
{"lon": 1, "lat": 0},
|
|
{"lon": 1, "lat": 1},
|
|
{"lon": 0, "lat": 1},
|
|
],
|
|
}
|
|
],
|
|
}
|
|
result = _extract_polygon_from_element(element)
|
|
assert result is not None
|
|
assert result["type"] == "Polygon"
|
|
|
|
def test_relation_without_outer_rings(self) -> None:
|
|
element: dict[str, Any] = {
|
|
"type": "relation",
|
|
"members": [{"role": "inner", "geometry": [{"lon": 0, "lat": 0}]}],
|
|
}
|
|
result = _extract_polygon_from_element(element)
|
|
assert result is None
|
|
|
|
def test_way_with_enough_coords(self) -> None:
|
|
element: dict[str, Any] = {
|
|
"type": "way",
|
|
"geometry": [
|
|
{"lon": 0, "lat": 0},
|
|
{"lon": 1, "lat": 0},
|
|
{"lon": 1, "lat": 1},
|
|
{"lon": 0, "lat": 1},
|
|
],
|
|
}
|
|
result = _extract_polygon_from_element(element)
|
|
assert result is not None
|
|
assert result["type"] == "Polygon"
|
|
# Should close the ring
|
|
assert result["coordinates"][0][0] == result["coordinates"][0][-1]
|
|
|
|
def test_way_already_closed(self) -> None:
|
|
element: dict[str, Any] = {
|
|
"type": "way",
|
|
"geometry": [
|
|
{"lon": 0, "lat": 0},
|
|
{"lon": 1, "lat": 0},
|
|
{"lon": 1, "lat": 1},
|
|
{"lon": 0, "lat": 0},
|
|
],
|
|
}
|
|
result = _extract_polygon_from_element(element)
|
|
assert result is not None
|
|
|
|
def test_way_too_few_coords(self) -> None:
|
|
element: dict[str, Any] = {
|
|
"type": "way",
|
|
"geometry": [{"lon": 0, "lat": 0}, {"lon": 1, "lat": 0}],
|
|
}
|
|
result = _extract_polygon_from_element(element)
|
|
assert result is None
|
|
|
|
def test_way_no_geometry(self) -> None:
|
|
element: dict[str, Any] = {"type": "way"}
|
|
result = _extract_polygon_from_element(element)
|
|
assert result is None
|
|
|
|
def test_unknown_type(self) -> None:
|
|
element: dict[str, Any] = {"type": "node"}
|
|
result = _extract_polygon_from_element(element)
|
|
assert result is None
|
|
|
|
|
|
class TestExtractLineFromWay:
|
|
"""Tests for _extract_line_from_way."""
|
|
|
|
def test_valid_way(self) -> None:
|
|
element: dict[str, Any] = {
|
|
"type": "way",
|
|
"geometry": [{"lon": 0, "lat": 0}, {"lon": 1, "lat": 1}],
|
|
}
|
|
result = _extract_line_from_way(element)
|
|
assert result is not None
|
|
assert result["type"] == "LineString"
|
|
|
|
def test_too_few_coords(self) -> None:
|
|
element: dict[str, Any] = {
|
|
"type": "way",
|
|
"geometry": [{"lon": 0, "lat": 0}],
|
|
}
|
|
result = _extract_line_from_way(element)
|
|
assert result is None
|
|
|
|
def test_not_a_way(self) -> None:
|
|
element: dict[str, Any] = {"type": "node"}
|
|
result = _extract_line_from_way(element)
|
|
assert result is None
|
|
|
|
def test_way_no_geometry(self) -> None:
|
|
element: dict[str, Any] = {"type": "way"}
|
|
result = _extract_line_from_way(element)
|
|
assert result is None
|