Skip to content

Commit 29fa5d7

Browse files
first unit tests STACAPIJobDatabase
1 parent 46ab758 commit 29fa5d7

File tree

2 files changed

+268
-8
lines changed

2 files changed

+268
-8
lines changed

openeo/extra/stac_job_db.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import concurrent
2+
import datetime
23
import logging
3-
from datetime import datetime
44
from typing import Iterable, List, Union
55

66
import geopandas as gpd
@@ -132,11 +132,11 @@ def item_from(self, series: pd.Series) -> pystac.Item:
132132

133133
dt = series_dict.get("datetime", None)
134134
if dt and item_dict["properties"].get("datetime", None) is None:
135-
dt_str = pystac.utils.datetime_to_str(dt) if isinstance(dt, datetime) else dt
135+
dt_str = pystac.utils.datetime_to_str(dt) if isinstance(dt, datetime.datetime) else dt
136136
item_dict["properties"]["datetime"] = dt_str
137137

138138
else:
139-
item_dict["properties"]["datetime"] = pystac.utils.datetime_to_str(datetime.now())
139+
item_dict["properties"]["datetime"] = pystac.utils.datetime_to_str(datetime.datetime.now())
140140

141141
if self.has_geometry:
142142
item_dict["geometry"] = series[self.geometry_column]

tests/extra/test_stac_jobdb.py

Lines changed: 265 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,271 @@
1+
import datetime
2+
from unittest.mock import MagicMock, patch
3+
4+
import geopandas as gpd
5+
import pandas as pd
6+
import pandas.testing as pdt
7+
import pystac
8+
import pystac_client
9+
import pytest
10+
from requests.auth import AuthBase
11+
from shapely.geometry import Point
12+
13+
from openeo.extra.job_management import MultiBackendJobManager
114
from openeo.extra.stac_job_db import STACAPIJobDatabase
215

316

4-
def test_create_db():
5-
#TODO mock stac client
6-
db = STACAPIJobDatabase("biopar_jobs","https://stac.openeo.vito.be")
17+
@pytest.fixture
18+
def mock_auth():
19+
return MagicMock(spec=AuthBase)
20+
21+
22+
@pytest.fixture
23+
def mock_stac_api_job_database(mock_auth) -> STACAPIJobDatabase:
24+
return STACAPIJobDatabase(collection_id="test_id", stac_root_url="http://fake-stac-api", auth=mock_auth)
25+
26+
27+
@pytest.fixture
28+
def mock_pystac_client():
29+
mock_client = MagicMock(spec=pystac_client.Client)
30+
31+
mock_client.get_collections.return_value = [
32+
MagicMock(id="collection-1"),
33+
MagicMock(id="collection-2"),
34+
]
35+
36+
with patch("pystac_client.Client.open", return_value=mock_client):
37+
yield mock_client
38+
39+
40+
@pytest.fixture
41+
def job_db_exists(mock_pystac_client) -> STACAPIJobDatabase:
42+
return STACAPIJobDatabase(
43+
collection_id="collection-1",
44+
stac_root_url="http://fake-stac-api",
45+
auth=None,
46+
)
47+
48+
49+
@pytest.fixture
50+
def job_db_not_exists(mock_pystac_client) -> STACAPIJobDatabase:
51+
return STACAPIJobDatabase(
52+
collection_id="collection-3",
53+
stac_root_url="http://fake-stac-api",
54+
auth=None,
55+
has_geometry=False,
56+
)
57+
58+
59+
@pytest.fixture
60+
def dummy_dataframe() -> pd.DataFrame:
61+
return pd.DataFrame({"no": [1], "geometry": [2], "here": [3]})
62+
63+
64+
@pytest.fixture
65+
def normalized_dummy_dataframe() -> pd.DataFrame:
66+
return pd.DataFrame(
67+
{
68+
"no": [1],
69+
"geometry": [2],
70+
"here": [3],
71+
"id": None,
72+
"backend_name": None,
73+
"status": ["not_started"],
74+
"start_time": None,
75+
"running_start_time": None,
76+
"cpu": None,
77+
"memory": None,
78+
"duration": None,
79+
"costs": None,
80+
},
81+
)
82+
83+
84+
@pytest.fixture
85+
def another_dummy_dataframe() -> pd.DataFrame:
86+
return pd.DataFrame({"no": [4], "geometry": [5], "here": [6]})
87+
88+
89+
@pytest.fixture
90+
def normalized_merged_dummy_dataframe() -> pd.DataFrame:
91+
return pd.DataFrame(
92+
{
93+
"no": [1, 4],
94+
"geometry": [2, 5],
95+
"here": [3, 6],
96+
"id": None,
97+
"backend_name": None,
98+
"status": ["not_started", "not_started"],
99+
"start_time": None,
100+
"running_start_time": None,
101+
"cpu": None,
102+
"memory": None,
103+
"duration": None,
104+
"costs": None,
105+
}
106+
)
107+
108+
109+
@pytest.fixture
110+
def dummy_geodataframe() -> gpd.GeoDataFrame:
111+
return gpd.GeoDataFrame(
112+
{
113+
"there": [1],
114+
"is": [2],
115+
"geometry": [Point(1, 1)],
116+
},
117+
geometry="geometry",
118+
)
119+
120+
121+
@pytest.fixture
122+
def normalized_dummy_geodataframe() -> pd.DataFrame:
123+
return pd.DataFrame(
124+
{
125+
"there": [1],
126+
"is": [2],
127+
"geometry": [{"type": "Point", "coordinates": (1.0, 1.0)}],
128+
"id": None,
129+
"backend_name": None,
130+
"status": ["not_started"],
131+
"start_time": None,
132+
"running_start_time": None,
133+
"cpu": None,
134+
"memory": None,
135+
"duration": None,
136+
"costs": None,
137+
}
138+
)
139+
140+
141+
FAKE_NOW = datetime.datetime(2020, 5, 22)
142+
143+
144+
@pytest.fixture
145+
def dummy_stac_item() -> pystac.Item:
146+
properties = {
147+
"datetime": pystac.utils.datetime_to_str(FAKE_NOW),
148+
"some_property": "value",
149+
}
150+
151+
return pystac.Item(id="test", geometry=None, bbox=None, properties=properties, datetime=FAKE_NOW)
152+
153+
154+
@pytest.fixture
155+
def dummy_stac_item_geometry() -> pystac.Item:
156+
properties = {
157+
"datetime": pystac.utils.datetime_to_str(FAKE_NOW),
158+
"some_property": "value",
159+
"geometry": {"type": "Polygon", "coordinates": (((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0), (0.0, 0.0)),)},
160+
}
161+
162+
return pystac.Item(
163+
id="test",
164+
geometry={"type": "Polygon", "coordinates": (((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0), (0.0, 0.0)),)},
165+
bbox=(0.0, 0.0, 1.0, 1.0),
166+
properties=properties,
167+
datetime=FAKE_NOW,
168+
)
169+
170+
171+
@pytest.fixture
172+
def dummy_series() -> pd.Series:
173+
return pd.Series({"datetime": pystac.utils.datetime_to_str(FAKE_NOW), "some_property": "value"}, name="test")
174+
175+
176+
@pytest.fixture
177+
def dummy_series_geometry() -> pd.Series:
178+
return pd.Series(
179+
{
180+
"datetime": pystac.utils.datetime_to_str(FAKE_NOW),
181+
"some_property": "value",
182+
"geometry": {
183+
"type": "Polygon",
184+
"coordinates": (((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0), (0.0, 0.0)),),
185+
},
186+
},
187+
name="test",
188+
)
189+
190+
191+
@pytest.fixture
192+
def patch_datetime_now():
193+
with patch("datetime.datetime") as mock_datetime:
194+
mock_datetime.now.return_value = FAKE_NOW
195+
yield mock_datetime
196+
197+
198+
class TestSTACAPIJobDatabase:
199+
def test_exists(self, job_db_exists, job_db_not_exists):
200+
201+
assert job_db_exists.exists() == True
202+
assert job_db_not_exists.exists() == False
203+
204+
@patch("openeo.extra.stac_job_db.STACAPIJobDatabase.persist", return_value=None)
205+
def test_initialize_from_df_non_existing(
206+
self, mock_persist, job_db_not_exists, dummy_dataframe, normalized_dummy_dataframe
207+
):
208+
209+
job_db_not_exists.initialize_from_df(dummy_dataframe)
210+
211+
mock_persist.assert_called_once()
212+
pdt.assert_frame_equal(mock_persist.call_args[0][0], normalized_dummy_dataframe)
213+
assert job_db_not_exists.has_geometry == False
214+
215+
def test_initialize_from_df_existing_error(self, job_db_exists, dummy_dataframe):
216+
with pytest.raises(FileExistsError):
217+
job_db_exists.initialize_from_df(dummy_dataframe)
218+
219+
@patch("openeo.extra.stac_job_db.STACAPIJobDatabase.persist", return_value=None)
220+
@patch("openeo.extra.stac_job_db.STACAPIJobDatabase.get_by_status")
221+
def test_initialize_from_df_existing_append(
222+
self,
223+
mock_get_by_status,
224+
mock_persist,
225+
job_db_exists,
226+
normalized_dummy_dataframe,
227+
another_dummy_dataframe,
228+
normalized_merged_dummy_dataframe,
229+
):
230+
mock_get_by_status.return_value = normalized_dummy_dataframe
231+
job_db_exists.initialize_from_df(another_dummy_dataframe, on_exists="append")
232+
233+
mock_persist.assert_called_once()
234+
pdt.assert_frame_equal(mock_persist.call_args[0][0], normalized_merged_dummy_dataframe)
235+
assert job_db_exists.has_geometry == False
236+
237+
@patch("openeo.extra.stac_job_db.STACAPIJobDatabase.persist", return_value=None)
238+
def test_initialize_from_df_with_geometry(
239+
self, mock_persists, job_db_not_exists, dummy_geodataframe, normalized_dummy_geodataframe
240+
):
241+
job_db_not_exists.initialize_from_df(dummy_geodataframe)
242+
243+
mock_persists.assert_called_once()
244+
pdt.assert_frame_equal(mock_persists.call_args[0][0], normalized_dummy_geodataframe)
245+
assert job_db_not_exists.has_geometry == True
246+
assert job_db_not_exists.geometry_column == "geometry"
247+
248+
def test_series_from(self, job_db_exists, dummy_series, dummy_stac_item):
249+
job_db_exists.has_geometry = True
250+
pdt.assert_series_equal(job_db_exists.series_from(dummy_stac_item), dummy_series)
251+
252+
def test_item_from(self, patch_datetime_now, job_db_exists, dummy_series, dummy_stac_item):
253+
item = job_db_exists.item_from(dummy_series)
254+
assert item.to_dict() == dummy_stac_item.to_dict()
255+
256+
def test_item_from_geometry(
257+
self, patch_datetime_now, job_db_exists, dummy_series_geometry, dummy_stac_item_geometry
258+
):
259+
job_db_exists.has_geometry = True
260+
item = job_db_exists.item_from(dummy_series_geometry)
261+
assert item.to_dict() == dummy_stac_item_geometry.to_dict()
7262

8-
assert db.exists()
263+
def test_count_by_status(self):
264+
pass
9265

10-
jobs = db.get_by_status(["not_started"])
266+
def test_get_by_status(self):
267+
pass
11268

269+
def test_persist(self):
270+
pass
271+
# This should test upload items bulk

0 commit comments

Comments
 (0)