Skip to content

Commit d35a7cc

Browse files
committed
add tests; update uv.lock
1 parent dca1f3c commit d35a7cc

2 files changed

Lines changed: 249 additions & 22 deletions

File tree

Lines changed: 247 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,259 @@
11
"""Serializer tests."""
22

3+
import json
34
import pytest
5+
import pandas as pd
6+
import geopandas as gpd
7+
from shapely.geometry import Point, Polygon
48
from keplergl.serializers import serialize_dataset
59

610

7-
def test_serialize_dataframe(sample_df):
8-
result = serialize_dataset(sample_df, "test")
9-
assert result["id"] == "test"
10-
assert result["format"] == "arrow"
11-
assert "data" in result
11+
class TestDataFrameSerialization:
12+
"""Tests for DataFrame serialization (from DataFrame.ipynb)."""
1213

14+
def test_serialize_dataframe(self, sample_df):
15+
result = serialize_dataset(sample_df, "test")
16+
assert result["id"] == "test"
17+
assert result["format"] == "df"
18+
assert "data" in result
1319

14-
def test_serialize_geodataframe(sample_gdf):
15-
result = serialize_dataset(sample_gdf, "test")
16-
assert result["id"] == "test"
17-
assert result["format"] == "geoarrow"
18-
assert "data" in result
20+
def test_serialize_dataframe_with_cities(self):
21+
"""Test DataFrame with city data (from DataFrame.ipynb)."""
22+
df = pd.DataFrame({
23+
'City': ['Buenos Aires', 'Brasilia', 'Santiago', 'Bogota', 'Caracas'],
24+
'Country': ['Argentina', 'Brazil', 'Chile', 'Colombia', 'Venezuela'],
25+
'Latitude': [-34.58, -15.78, -33.45, 4.60, 10.48],
26+
'Longitude': [-58.66, -47.91, -70.66, -74.08, -66.86],
27+
'Time': ['2019-09-01 08:00', '2019-09-01 09:00', '2019-09-01 10:00',
28+
'2019-09-01 11:00', '2019-09-01 12:00'],
29+
})
30+
result = serialize_dataset(df, "data_1")
31+
assert result["id"] == "data_1"
32+
assert result["format"] == "df"
33+
assert result["data"]["columns"] == ['City', 'Country', 'Latitude', 'Longitude', 'Time']
34+
assert len(result["data"]["data"]) == 5
1935

36+
def test_serialize_dataframe_with_hex_data(self):
37+
"""Test DataFrame with H3 hex IDs and mixed types (from Load kepler.gl.ipynb)."""
38+
df = pd.DataFrame({
39+
'hex_id': ['89283082c2fffff', '8928308288fffff', '89283082c07ffff'],
40+
'value': [64, 73, 65],
41+
'is_true': [True, True, True],
42+
'float_value': [64.1, 73.1, 65.1],
43+
'empty': ['', '', ''],
44+
'time': ['11/1/17 11:00', '11/1/17 11:00', '11/1/17 11:00'],
45+
})
46+
result = serialize_dataset(df, "data_1")
47+
assert result["id"] == "data_1"
48+
assert result["format"] == "df"
49+
assert 'hex_id' in result["data"]["columns"]
50+
assert 'value' in result["data"]["columns"]
51+
assert 'is_true' in result["data"]["columns"]
2052

21-
def test_serialize_csv():
22-
csv_data = "lat,lng\n37.7749,-122.4194"
23-
result = serialize_dataset(csv_data, "test")
24-
assert result["format"] == "csv"
25-
assert result["data"] == csv_data
53+
def test_serialize_dataframe_with_nan_filled(self):
54+
"""Test DataFrame with NaN values filled with empty string."""
55+
df = pd.DataFrame({
56+
'col1': [1, 2, 3],
57+
'col2': ['a', None, 'c'],
58+
})
59+
df = df.fillna('')
60+
result = serialize_dataset(df, "test")
61+
assert result["format"] == "df"
62+
assert result["data"]["data"][1][1] == ''
2663

2764

28-
def test_serialize_geojson():
29-
geojson = {"type": "FeatureCollection", "features": []}
30-
result = serialize_dataset(geojson, "test")
31-
assert result["format"] == "geojson"
32-
assert result["data"] == geojson
65+
class TestGeoDataFrameSerialization:
66+
"""Tests for GeoDataFrame serialization (from GeoDataFrame.ipynb)."""
67+
68+
def test_serialize_geodataframe(self, sample_gdf):
69+
result = serialize_dataset(sample_gdf, "test")
70+
assert result["id"] == "test"
71+
assert result["format"] == "geoarrow"
72+
assert "data" in result
73+
74+
def test_serialize_geodataframe_with_timestamp(self):
75+
"""Test GeoDataFrame with pd.Timestamp column.
76+
77+
Regression test for issue where GeoDataFrame containing Timestamp
78+
columns would fail during Arrow serialization.
79+
"""
80+
df = pd.DataFrame({
81+
'City': ['Buenos Aires'],
82+
'Country': ['Argentina'],
83+
'Latitude': [-34.58],
84+
'Longitude': [-58.66],
85+
'Timestamp': pd.Timestamp(2002, 3, 3),
86+
})
87+
gdf = gpd.GeoDataFrame(
88+
df,
89+
geometry=gpd.points_from_xy(df.Longitude, df.Latitude),
90+
)
91+
result = serialize_dataset(gdf, "cities")
92+
assert result["id"] == "cities"
93+
assert result["format"] == "geoarrow"
94+
assert "data" in result
95+
96+
def test_serialize_geodataframe_points_from_xy(self):
97+
"""Test GeoDataFrame created with points_from_xy (from GeoDataFrame.ipynb)."""
98+
df = pd.DataFrame({
99+
'City': ['Buenos Aires', 'Brasilia', 'Santiago', 'Bogota', 'Caracas'],
100+
'Country': ['Argentina', 'Brazil', 'Chile', 'Colombia', 'Venezuela'],
101+
'Latitude': [-34.58, -15.78, -33.45, 4.60, 10.48],
102+
'Longitude': [-58.66, -47.91, -70.66, -74.08, -66.86],
103+
})
104+
gdf = gpd.GeoDataFrame(
105+
df,
106+
geometry=gpd.points_from_xy(df.Longitude, df.Latitude),
107+
)
108+
result = serialize_dataset(gdf, "cities")
109+
assert result["id"] == "cities"
110+
assert result["format"] == "geoarrow"
111+
assert "data" in result
112+
113+
def test_serialize_geodataframe_with_polygons(self):
114+
"""Test GeoDataFrame with Polygon geometries (like zipcode boundaries)."""
115+
gdf = gpd.GeoDataFrame({
116+
'ZIP_CODE': ['94107', '94105'],
117+
'geometry': [
118+
Polygon([(-122.40, 37.78), (-122.39, 37.78),
119+
(-122.39, 37.77), (-122.40, 37.77)]),
120+
Polygon([(-122.39, 37.79), (-122.38, 37.79),
121+
(-122.38, 37.78), (-122.39, 37.78)]),
122+
],
123+
})
124+
result = serialize_dataset(gdf, "zipcode")
125+
assert result["id"] == "zipcode"
126+
assert result["format"] == "geoarrow"
127+
assert "data" in result
128+
129+
def test_serialize_geodataframe_with_crs(self):
130+
"""Test GeoDataFrame with explicit CRS."""
131+
gdf = gpd.GeoDataFrame(
132+
{"name": ["SF", "LA"]},
133+
geometry=[Point(-122.4194, 37.7749), Point(-118.2437, 34.0522)],
134+
crs="EPSG:4326",
135+
)
136+
result = serialize_dataset(gdf, "test")
137+
assert result["format"] == "geoarrow"
138+
assert "data" in result
139+
140+
141+
class TestGeoJSONSerialization:
142+
"""Tests for GeoJSON serialization (from GeoJSON.ipynb)."""
143+
144+
def test_serialize_geojson_dict(self):
145+
"""Test GeoJSON as dict."""
146+
geojson = {"type": "FeatureCollection", "features": []}
147+
result = serialize_dataset(geojson, "test")
148+
assert result["format"] == "geojson"
149+
assert result["data"] == geojson
150+
151+
def test_serialize_geojson_feature_collection(self):
152+
"""Test GeoJSON FeatureCollection with features."""
153+
geojson = {
154+
"type": "FeatureCollection",
155+
"features": [
156+
{
157+
"type": "Feature",
158+
"geometry": {"type": "Point", "coordinates": [-122.4, 37.8]},
159+
"properties": {"name": "San Francisco"},
160+
},
161+
{
162+
"type": "Feature",
163+
"geometry": {"type": "Point", "coordinates": [-118.2, 34.0]},
164+
"properties": {"name": "Los Angeles"},
165+
},
166+
],
167+
}
168+
result = serialize_dataset(geojson, "geojson")
169+
assert result["id"] == "geojson"
170+
assert result["format"] == "geojson"
171+
assert result["data"]["type"] == "FeatureCollection"
172+
assert len(result["data"]["features"]) == 2
173+
174+
def test_serialize_geojson_string(self):
175+
"""Test GeoJSON as string (from GeoJSON.ipynb - reading from file)."""
176+
geojson_str = json.dumps({
177+
"type": "FeatureCollection",
178+
"features": [
179+
{
180+
"type": "Feature",
181+
"geometry": {"type": "Point", "coordinates": [-122.4, 37.8]},
182+
"properties": {"name": "Test"},
183+
},
184+
],
185+
})
186+
result = serialize_dataset(geojson_str, "geojson")
187+
assert result["id"] == "geojson"
188+
assert result["format"] == "geojson"
189+
assert result["data"]["type"] == "FeatureCollection"
190+
191+
def test_serialize_geojson_polygon(self):
192+
"""Test GeoJSON with Polygon geometry."""
193+
geojson = {
194+
"type": "Feature",
195+
"geometry": {
196+
"type": "Polygon",
197+
"coordinates": [[
198+
[-122.4, 37.8], [-122.3, 37.8],
199+
[-122.3, 37.7], [-122.4, 37.7], [-122.4, 37.8],
200+
]],
201+
},
202+
"properties": {"name": "Test Area"},
203+
}
204+
result = serialize_dataset(geojson, "polygon")
205+
assert result["format"] == "geojson"
206+
207+
208+
class TestCSVSerialization:
209+
"""Tests for CSV string serialization."""
210+
211+
def test_serialize_csv(self):
212+
csv_data = "lat,lng\n37.7749,-122.4194"
213+
result = serialize_dataset(csv_data, "test")
214+
assert result["format"] == "csv"
215+
assert result["data"] == csv_data
216+
217+
def test_serialize_csv_multirow(self):
218+
"""Test CSV with multiple rows."""
219+
csv_data = "City,Latitude,Longitude\nSF,37.77,-122.42\nLA,34.05,-118.24"
220+
result = serialize_dataset(csv_data, "cities")
221+
assert result["id"] == "cities"
222+
assert result["format"] == "csv"
223+
assert result["data"] == csv_data
224+
225+
226+
class TestEdgeCases:
227+
"""Tests for edge cases and error handling."""
228+
229+
def test_serialize_unsupported_type(self):
230+
"""Test that unsupported types raise ValueError."""
231+
with pytest.raises(ValueError, match="Unsupported data type"):
232+
serialize_dataset([1, 2, 3], "test")
233+
234+
def test_serialize_empty_dataframe(self):
235+
"""Test serializing empty DataFrame."""
236+
df = pd.DataFrame({'col1': [], 'col2': []})
237+
result = serialize_dataset(df, "empty")
238+
assert result["format"] == "df"
239+
assert result["data"]["columns"] == ['col1', 'col2']
240+
assert result["data"]["data"] == []
241+
242+
def test_serialize_single_row_dataframe(self):
243+
"""Test serializing single-row DataFrame."""
244+
df = pd.DataFrame({'lat': [37.77], 'lng': [-122.42]})
245+
result = serialize_dataset(df, "single")
246+
assert result["format"] == "df"
247+
assert len(result["data"]["data"]) == 1
248+
249+
def test_serialize_dataframe_with_various_dtypes(self):
250+
"""Test DataFrame with various data types."""
251+
df = pd.DataFrame({
252+
'int_col': [1, 2, 3],
253+
'float_col': [1.1, 2.2, 3.3],
254+
'str_col': ['a', 'b', 'c'],
255+
'bool_col': [True, False, True],
256+
})
257+
result = serialize_dataset(df, "mixed")
258+
assert result["format"] == "df"
259+
assert len(result["data"]["columns"]) == 4

bindings/python/uv.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)