diff --git a/src/deepfacility/utils/spatial.py b/src/deepfacility/utils/spatial.py index 8f634e0..35e35c2 100644 --- a/src/deepfacility/utils/spatial.py +++ b/src/deepfacility/utils/spatial.py @@ -5,10 +5,11 @@ from pathlib import Path from pyproj import CRS from shapely import Geometry, Polygon +from sklearn.cluster import KMeans +from typing import Any from deepfacility.utils import util -from typing import Any -from sklearn.cluster import KMeans + # Frequently used spatial global variables geom_col = 'geometry' diff --git a/tests/test_distance.py b/tests/test_distance.py index 6f3740a..99863a9 100644 --- a/tests/test_distance.py +++ b/tests/test_distance.py @@ -16,7 +16,8 @@ def mock_config(): rel = 1e-9 - + +@pytest.mark.unit def test_convert_to_cartesian(): # Test case 1: lon = 0, lat = 0, elevation = 0 # This is the prime meridian, equator, and sea level @@ -47,6 +48,7 @@ def test_convert_to_cartesian(): assert round(z1) == 1366661 +@pytest.mark.unit def test_calculate_minkowski_from_cartesian(): # Test with sample data df_loc = pd.DataFrame( @@ -63,6 +65,7 @@ def test_calculate_minkowski_from_cartesian(): assert result['minkowski'][2] == pytest.approx(2.0408871750129656, rel=rel) +@pytest.mark.unit def test_find_nearest_facility(): # generate two data frame with id and x, y columns df_loc = pd.DataFrame({'id': ['a', 'b', 'c'], 'x': [0, 1, 2], 'y': [0, 1, 2]}) @@ -83,6 +86,7 @@ def test_find_nearest_facility(): assert df_loc['distance'].tolist() == pytest.approx(expected_distances, rel=4) +@pytest.mark.unit def test_calculate_distance_df(): # Create sample data df = pd.DataFrame({'id': ['a', 'b', 'c'], 'lon': [0, 1, 2], 'lat': [0, 1, 2]}) @@ -110,6 +114,7 @@ def cal_dist(a, b, p): return [np.power(np.sum(np.abs(a - b) ** p), 1 / p), 0, 0 assert all(abs(result['distance_minkowski'].values - expected_distances_minkowski) == 0), msg +@pytest.mark.unit def test_plot_ecdf_distance(mock_config): # Create sample data df = pd.DataFrame({'minkowski': [1.5, 2.0, 1.8, 1.2, 1.6]}) diff --git a/tests/test_prep.py b/tests/test_prep.py index f646314..508a184 100644 --- a/tests/test_prep.py +++ b/tests/test_prep.py @@ -3,6 +3,7 @@ import pandas as pd from pathlib import Path +from tempfile import mkdtemp from unittest.mock import MagicMock, patch from deepfacility.data.inputs import DataInputs @@ -21,6 +22,7 @@ def mock_data_inputs(mock_config): # prepare_country_shapes tests +@pytest.mark.unit def test_prepare_country_shapes_when_shape_files_exist(mock_data_inputs): mock_data_inputs.cfg.inputs.shape_files = [Path('shape1.shp'), Path('shape2.shp')] with patch('pathlib.Path.is_file', return_value=True): @@ -28,8 +30,9 @@ def test_prepare_country_shapes_when_shape_files_exist(mock_data_inputs): assert result == mock_data_inputs.cfg.inputs.shape_files +@pytest.mark.unit def test_prepare_country_shapes_when_shape_files_do_not_exist(mock_data_inputs): - mock_data_inputs.cfg.inputs.shape_files = [Path('shape1.shp'), Path('shape2.shp')] + mock_data_inputs.cfg.inputs.shape_files = [Path('shape1.shp'), Path('shape2.shp')] with patch.object(Path, 'is_file') as mock_is_file: mock_is_file.return_value = True @@ -43,6 +46,7 @@ def test_prepare_country_shapes_when_shape_files_do_not_exist(mock_data_inputs): assert result == mock_data_inputs.cfg.inputs.shape_files +@pytest.mark.unit def test_prepare_country_shapes_when_zip_file_does_not_exist(mock_data_inputs): with pytest.raises(AssertionError, match="Raw shape file not found."): mock_data_inputs.prepare_country_shapes(Path('nonexistent.zip')) @@ -51,6 +55,7 @@ def test_prepare_country_shapes_when_zip_file_does_not_exist(mock_data_inputs): # prepare_households tests +@pytest.mark.unit def test_households_preparation_when_file_exists(mock_data_inputs): mock_data_inputs.cfg.inputs.households.file = Path('existing_file.csv') with patch.object(Path, 'is_file', return_value=True): @@ -58,8 +63,9 @@ def test_households_preparation_when_file_exists(mock_data_inputs): assert result == mock_data_inputs.cfg.inputs.households.file +@pytest.mark.unit def test_households_preparation_when_file_does_not_exist(mock_data_inputs): - mock_data_inputs.cfg.inputs.households.file = Path('nonexistent_file.csv') + mock_data_inputs.cfg.inputs.households.file = Path(mkdtemp()) / Path('nonexistent_file.csv') with patch.object(Path, 'is_file', return_value=False), \ patch('geopandas.read_file', return_value=MagicMock()), \ patch('pandas.read_feather', return_value=MagicMock()), \ @@ -88,12 +94,14 @@ def mock_df_xy(): }) +@pytest.mark.unit def test_buildings_processing_happy_path(mock_data_inputs, mock_gdf_shapes, mock_df_xy): with patch('deepfacility.data.inputs.process_google_buildings', return_value=mock_df_xy): result = mock_data_inputs.process_buildings(mock_gdf_shapes, ['x', 'y'], mock_df_xy, ['x', 'y']) assert result.equals(mock_df_xy) +@pytest.mark.unit def test_buildings_processing_with_empty_dataframe(mock_data_inputs, mock_gdf_shapes): with patch('deepfacility.data.inputs.process_google_buildings', return_value=pd.DataFrame()): result = mock_data_inputs.process_buildings(mock_gdf_shapes, ['x', 'y'], pd.DataFrame(), ['x', 'y']) @@ -113,8 +121,9 @@ def mock_shape_files(): return [Path('shape1.shp'), Path('shape2.shp')] +@pytest.mark.unit def test_village_locality_preparation(mock_data_inputs, mock_village_locality, mock_shape_files): - mock_data_inputs.cfg.inputs.village_centers.file = Path('nonexistent_file.csv') + mock_data_inputs.cfg.inputs.village_centers.file = Path(mkdtemp()) / Path('nonexistent_file.csv') with patch('pandas.read_csv', return_value=MagicMock()), \ patch('geopandas.read_file', return_value=MagicMock()), \ patch('deepfacility.data.inputs.DataInputs.prepare_village_centers', return_value=MagicMock()), \ @@ -153,6 +162,7 @@ def mock_baseline_file(): return Path('baseline.csv') +@pytest.mark.unit def test_village_centers_preparation_happy_path( mock_data_inputs, mock_village_locality, @@ -191,6 +201,7 @@ def test_village_centers_preparation_happy_path( # prepare_baseline_facilities tests +@pytest.mark.unit def test_baseline_facilities_preparation( mock_data_inputs, mock_baseline_file, diff --git a/tests/test_session.py b/tests/test_session.py index 65605c0..3c74d78 100644 --- a/tests/test_session.py +++ b/tests/test_session.py @@ -1,3 +1,4 @@ +import pytest from requests import Request from unittest.mock import Mock, MagicMock, patch @@ -5,16 +6,19 @@ from deepfacility.ux.session import Session +@pytest.mark.unit def test_get_session_id_from_query_string(): request: Request = MagicMock(query_params={'sid': 'test123'}, cookies={}) assert Session.get_session_id(request) == 'test123' +@pytest.mark.unit def test_get_session_id_from_cookie(): request: Request = MagicMock(query_params={}, cookies={'session_id': 'test123'}) assert Session.get_session_id(request) == 'test123' +@pytest.mark.unit def test_generate_new_session_id_when_no_source_provided(): request: Request = MagicMock(query_params={}, cookies={}) assert Session.get_session_id(request) is not None diff --git a/tests/test_utils.py b/tests/test_utils.py index cba0d65..7cb7024 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,4 +1,5 @@ import geopandas as gpd +import requests import shutil import tempfile @@ -53,9 +54,15 @@ def tmp_dir() -> Path: # download_url tests +def has_internet(): + try: + requests.get("https://gadm.org/", timeout=5) + return True + except requests.exceptions.RequestException: + return False -@pytest.mark.network_dependent +@pytest.mark.skipif(not has_internet(), reason="No internet connection") def test_download_url_ok(tmp_dir, url_pattern): expected, actual = download_url_test(tmp_dir, url_pattern, 0) assert expected == actual, "File name is not expected." @@ -66,7 +73,7 @@ def test_download_url_ok(tmp_dir, url_pattern): return False -@pytest.mark.network_dependent +@pytest.mark.skipif(not has_internet(), reason="No internet connection") def test_download_url_404(tmp_dir, url_pattern): expected, actual = download_url_test(tmp_dir, url_pattern, level=4) assert actual is None, "404 didn't return None."