Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from __future__ import annotations

import os
import shutil
import tempfile
import unittest


class GeemapTestCase(unittest.TestCase):

@classmethod
def setUpClass(cls) -> None:
cls.test_data_dir = os.path.join(os.path.dirname(__file__), "data")
cls.temp_dir = tempfile.mkdtemp()

@classmethod
def tearDownClass(cls) -> None:
if os.path.exists(cls.temp_dir):
shutil.rmtree(cls.temp_dir)

def get_fixture(self, filename: str) -> str:
return os.path.join(self.test_data_dir, filename)

def get_temp_path(self, filename: str) -> str:
return os.path.join(self.temp_dir, filename)
23 changes: 23 additions & 0 deletions tests/helpers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from __future__ import annotations

from tests.helpers.assertions import (
assert_file_created,
assert_valid_geojson,
assert_valid_hex_color,
)
from tests.helpers.factories import (
create_ee_feature_collection,
create_ee_geometry,
create_ee_image,
create_sample_dataframe,
)

__all__ = [
"assert_file_created",
"assert_valid_geojson",
"assert_valid_hex_color",
"create_ee_feature_collection",
"create_ee_geometry",
"create_ee_image",
"create_sample_dataframe",
]
77 changes: 77 additions & 0 deletions tests/helpers/assertions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from __future__ import annotations

import os
import re
import unittest


def assert_valid_geojson(test_case: unittest.TestCase, geojson: dict) -> None:
valid_types = [
"Feature",
"FeatureCollection",
"Point",
"LineString",
"Polygon",
"MultiPoint",
"MultiLineString",
"MultiPolygon",
"GeometryCollection",
]
test_case.assertIn("type", geojson)
test_case.assertIn(geojson["type"], valid_types)


def assert_file_created(test_case: unittest.TestCase, filepath: str) -> None:
test_case.assertTrue(
os.path.exists(filepath),
f"File not created: {filepath}",
)


def assert_file_not_empty(test_case: unittest.TestCase, filepath: str) -> None:
assert_file_created(test_case, filepath)
test_case.assertGreater(
os.path.getsize(filepath),
0,
f"File is empty: {filepath}",
)


def assert_valid_hex_color(test_case: unittest.TestCase, color: str) -> None:
pattern = r"^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"
test_case.assertIsNotNone(
re.match(pattern, color),
f"Invalid hex color: {color}",
)


def assert_valid_rgb_color(
test_case: unittest.TestCase, color: tuple[int, int, int]
) -> None:
test_case.assertEqual(len(color), 3, "RGB color must have 3 components")
for i, component in enumerate(color):
test_case.assertGreaterEqual(component, 0, f"RGB component {i} must be >= 0")
test_case.assertLessEqual(component, 255, f"RGB component {i} must be <= 255")


def assert_valid_bbox(
test_case: unittest.TestCase,
bbox: tuple[float, float, float, float],
) -> None:
test_case.assertEqual(len(bbox), 4, "Bounding box must have 4 components")
west, south, east, north = bbox
test_case.assertLessEqual(west, east, "West must be <= East")
test_case.assertLessEqual(south, north, "South must be <= North")
test_case.assertGreaterEqual(west, -180, "West must be >= -180")
test_case.assertLessEqual(east, 180, "East must be <= 180")
test_case.assertGreaterEqual(south, -90, "South must be >= -90")
test_case.assertLessEqual(north, 90, "North must be <= 90")


def assert_dict_contains_keys(
test_case: unittest.TestCase,
d: dict,
keys: list[str],
) -> None:
for key in keys:
test_case.assertIn(key, d, f"Missing key: {key}")
111 changes: 111 additions & 0 deletions tests/helpers/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from __future__ import annotations

import pandas as pd

from tests.mocks import mock_ee


def create_ee_image(
bands: list[str] | None = None,
crs: str = "EPSG:4326",
) -> mock_ee.Image:
img = mock_ee.Image()
img._bands = bands or ["B1", "B2", "B3"]
img._crs = crs
return img


def create_ee_feature_collection(
features: list | None = None,
) -> mock_ee.FeatureCollection:
fc = mock_ee.FeatureCollection(features or [])
return fc


def create_ee_geometry(
geom_type: str = "Point",
coords: list | None = None,
) -> mock_ee.Geometry:
coords = coords or [0, 0]
if geom_type == "Point":
return mock_ee.Geometry.Point(coords)
elif geom_type == "Polygon":
return mock_ee.Geometry.Polygon(coords)
elif geom_type == "LineString":
return mock_ee.Geometry.LineString(coords)
elif geom_type == "Rectangle":
return mock_ee.Geometry.Rectangle(coords)
else:
geom = mock_ee.Geometry(type=mock_ee.String(geom_type))
geom._coords = coords
return geom


def create_ee_feature(
geometry: mock_ee.Geometry | None = None,
properties: dict | None = None,
) -> mock_ee.Feature:
geom = geometry or create_ee_geometry("Point", [0, 0])
return mock_ee.Feature(geom, properties)


def create_ee_image_collection(
images: list[mock_ee.Image] | None = None,
count: int = 3,
) -> mock_ee.ImageCollection:
if images is None:
images = [create_ee_image() for _ in range(count)]
return mock_ee.ImageCollection(images)


def create_sample_dataframe(
rows: int = 5,
columns: list[str] | None = None,
) -> pd.DataFrame:
columns = columns or ["x", "y", "value"]
data = {col: list(range(rows)) for col in columns}
return pd.DataFrame(data)


def create_sample_geodataframe(
rows: int = 5,
crs: str = "EPSG:4326",
):
try:
import geopandas as gpd
from shapely.geometry import Point
except ImportError:
return None

data = {
"name": [f"feature_{i}" for i in range(rows)],
"value": list(range(rows)),
"geometry": [Point(i, i) for i in range(rows)],
}
return gpd.GeoDataFrame(data, crs=crs)


def create_sample_geojson(
num_features: int = 3,
geom_type: str = "Point",
) -> dict:
features = []
for i in range(num_features):
if geom_type == "Point":
coordinates = [i, i]
elif geom_type == "Polygon":
coordinates = [[[i, i], [i + 1, i], [i + 1, i + 1], [i, i + 1], [i, i]]]
elif geom_type == "LineString":
coordinates = [[i, i], [i + 1, i + 1]]
else:
coordinates = [i, i]

features.append(
{
"type": "Feature",
"properties": {"id": i, "name": f"feature_{i}"},
"geometry": {"type": geom_type, "coordinates": coordinates},
}
)

return {"type": "FeatureCollection", "features": features}
54 changes: 54 additions & 0 deletions tests/mocks/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from __future__ import annotations

from tests.mocks.mock_ee import (
Algorithms,
Dictionary,
Feature,
FeatureCollection,
Geometry,
Image,
ImageCollection,
List,
Reducer,
String,
)
from tests.mocks.mock_map import (
FakeEeTileLayer,
FakeGeoJSONLayer,
FakeMap,
FakeTileLayer,
)
from tests.mocks.mock_osmnx import (
MockGeoDataFrame,
mock_features_from_address,
mock_features_from_bbox,
mock_features_from_point,
)
from tests.mocks.mock_plotly import MockFigure, MockPlotlyExpress
from tests.mocks.mock_requests import MockResponse, RequestError, create_mock_response

__all__ = [
"Algorithms",
"create_mock_response",
"Dictionary",
"FakeEeTileLayer",
"FakeGeoJSONLayer",
"FakeMap",
"FakeTileLayer",
"Feature",
"FeatureCollection",
"Geometry",
"Image",
"ImageCollection",
"List",
"mock_features_from_address",
"mock_features_from_bbox",
"mock_features_from_point",
"MockFigure",
"MockGeoDataFrame",
"MockPlotlyExpress",
"MockResponse",
"Reducer",
"RequestError",
"String",
]
Loading