gazet / tests /test_export.py
Daniel Wiesmann
Fix ruff formatting across all source and test files
61f1033
Raw
History Blame Contribute Delete
6.6 kB
"""Tests for gazet.export — GeoJSON FeatureCollection serialization."""
import json
import numpy as np
import pandas as pd
import pytest
from gazet.export import (
_is_geojson_col,
_to_serializable,
save_geojson,
to_feature_collection,
)
class TestToSerializable:
def test_bytearray(self):
assert _to_serializable(bytearray(b"hello")) is None
def test_bytes(self):
assert _to_serializable(b"hello") is None
def test_numpy_array(self):
arr = np.array([1, 2, 3])
assert _to_serializable(arr) == [1, 2, 3]
def test_numpy_int(self):
val = np.int64(42)
assert _to_serializable(val) == 42
assert isinstance(_to_serializable(val), int)
def test_numpy_float(self):
val = np.float64(3.14)
assert _to_serializable(val) == 3.14
assert isinstance(_to_serializable(val), float)
def test_numpy_bool(self):
val = np.bool_(True)
assert _to_serializable(val) is True
def test_plain_python_passthrough(self):
assert _to_serializable("hello") == "hello"
assert _to_serializable(42) == 42
assert _to_serializable(None) is None
def test_dict_passthrough(self):
d = {"a": 1}
assert _to_serializable(d) == d
class TestIsGeojsonCol:
def test_all_geojson_strings(self):
s = pd.Series(['{"type": "Point"}', '{"type": "Line"}'])
assert _is_geojson_col(s) is True
def test_mixed_content(self):
s = pd.Series(['{"type": "Point"}', "not geojson"])
assert not _is_geojson_col(s)
def test_empty_series(self):
s = pd.Series([], dtype=str)
assert _is_geojson_col(s) is False
def test_all_null(self):
s = pd.Series([None, None])
assert _is_geojson_col(s) is False
def test_geojson_with_whitespace(self):
s = pd.Series([' {"type": "Point"}'])
assert _is_geojson_col(s) is True
class TestToFeatureCollection:
def test_empty_dataframe(self):
df = pd.DataFrame()
fc = to_feature_collection(df)
assert fc["type"] == "FeatureCollection"
assert fc["features"] == []
def test_no_geometry_column(self):
df = pd.DataFrame({"id": [1], "name": ["test"]})
fc = to_feature_collection(df)
assert fc["type"] == "FeatureCollection"
assert fc["features"] == []
def test_single_feature(self):
point = json.dumps({"type": "Point", "coordinates": [12.0, 34.0]})
df = pd.DataFrame({"id": ["x1"], "name": ["Paris"], "geometry": [point]})
fc = to_feature_collection(df)
assert fc["type"] == "FeatureCollection"
assert len(fc["features"]) == 1
feat = fc["features"][0]
assert feat["type"] == "Feature"
assert feat["geometry"]["type"] == "Point"
assert feat["geometry"]["coordinates"] == [12.0, 34.0]
assert feat["properties"]["id"] == "x1"
assert feat["properties"]["name"] == "Paris"
def test_null_geometry(self):
df = pd.DataFrame({"id": ["x1"], "geometry": [None]})
fc = to_feature_collection(df)
assert fc["features"][0]["geometry"] is None
def test_invalid_geojson_string(self):
df = pd.DataFrame({"id": ["x1"], "geometry": ["not valid json"]})
fc = to_feature_collection(df)
assert fc["features"][0]["geometry"] is None
def test_null_properties_excluded(self):
point = json.dumps({"type": "Point", "coordinates": [0.0, 0.0]})
df = pd.DataFrame({"id": ["x1"], "name": [None], "geometry": [point]})
fc = to_feature_collection(df)
assert "name" not in fc["features"][0]["properties"]
def test_numpy_values_serialized(self):
point = json.dumps({"type": "Point", "coordinates": [0.0, 0.0]})
df = pd.DataFrame(
{
"id": [np.int64(1)],
"score": [np.float64(3.14)],
"geometry": [point],
}
)
fc = to_feature_collection(df)
props = fc["features"][0]["properties"]
assert props["id"] == 1
assert isinstance(props["id"], int)
assert props["score"] == pytest.approx(3.14)
def test_bytearray_property_becomes_null(self):
point = json.dumps({"type": "Point", "coordinates": [0.0, 0.0]})
df = pd.DataFrame(
{
"id": ["x1"],
"data": [bytearray(b"raw")],
"geometry": [point],
}
)
fc = to_feature_collection(df)
# bytearray should be converted to None via _to_serializable
assert fc["features"][0]["properties"]["data"] is None
def test_multiple_features(self):
geojsons = [
json.dumps({"type": "Point", "coordinates": [1.0, 2.0]}),
json.dumps({"type": "Point", "coordinates": [3.0, 4.0]}),
]
df = pd.DataFrame({"id": ["a", "b"], "geometry": geojsons})
fc = to_feature_collection(df)
assert len(fc["features"]) == 2
assert fc["features"][0]["properties"]["id"] == "a"
assert fc["features"][1]["properties"]["id"] == "b"
class TestSaveGeojson:
def test_writes_file(self, tmp_path):
point = json.dumps({"type": "Point", "coordinates": [0.0, 0.0]})
df = pd.DataFrame({"id": ["x1"], "geometry": [point]})
out = save_geojson(df, "test_query", output_dir=tmp_path)
assert out.exists()
assert out.suffix == ".geojson"
data = json.loads(out.read_text())
assert data["type"] == "FeatureCollection"
assert len(data["features"]) == 1
def test_slug_generation(self, tmp_path):
point = json.dumps({"type": "Point", "coordinates": [0.0, 0.0]})
df = pd.DataFrame({"id": ["x1"], "geometry": [point]})
out = save_geojson(df, "What's the boundary of Paris?", output_dir=tmp_path)
# Slug should be lowercase with non-word chars replaced
assert "what_s_the_boundary_of_paris" in out.name
def test_creates_parent_dirs(self, tmp_path):
point = json.dumps({"type": "Point", "coordinates": [0.0, 0.0]})
df = pd.DataFrame({"id": ["x1"], "geometry": [point]})
nested = tmp_path / "a" / "b" / "c"
out = save_geojson(df, "test", output_dir=nested)
assert out.parent == nested
assert out.exists()
def test_empty_dataframe(self, tmp_path):
df = pd.DataFrame()
out = save_geojson(df, "empty", output_dir=tmp_path)
data = json.loads(out.read_text())
assert data["features"] == []