-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
GTC-2171 - Test Raster Tile Cache Assets Function (#361)
* ✅ test(RasterTileCacheAsset): Add collaborator tests This commit reflects the effort to assert how `raster_tile_cache_asset`'s collaborators are called. The goal is to use these tests to provide feedback during a refactor of this function. While there was code coverage around the function, there weren't any `assert`s for behavior around its dependencies. Now, we have the feedback for more in-depth structural change. * ✅ test: Use `call_args_list` with mocks I definitely think this is a better form than what I was doing previously with `ANY`. The diffs are MUCH nicer. https://stackoverflow.com/questions/21611559/assert-that-a-method-was-called-with-one-argument-out-of-several/56765027#56765027 * ✅ test: Add RasterTileSetSourceCreationOptions tests Found a nice way to leverage pytest diffing when asserting a subset of a dict. pytest-dev/pytest#2376 (comment) * 🎨 refactor: Move `patch` decorator to class declaration This removes lots of boilerplate code! From the python docs: Patch can be used as a TestCase class decorator. It works by decorating each test method in the class. This reduces the boilerplate code when your test methods share a common patchings set. patch() finds tests by looking for method names that start with patch.TEST_PREFIX. By default this is 'test', which matches the way unittest finds tests. You can specify an alternative prefix by setting patch.TEST_PREFIX.
- Loading branch information
Showing
9 changed files
with
1,262 additions
and
0 deletions.
There are no files selected for viewing
1 change: 1 addition & 0 deletions
1
tests_v2/unit/app/tasks/raster_tile_cache_assets/collaborators/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
MODULE_PATH_UNDER_TEST = "app.tasks.raster_tile_cache_assets.raster_tile_cache_assets" |
154 changes: 154 additions & 0 deletions
154
tests_v2/unit/app/tasks/raster_tile_cache_assets/collaborators/conftest.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
from datetime import datetime | ||
from unittest.mock import AsyncMock | ||
from uuid import UUID | ||
|
||
import pytest | ||
|
||
from app.models.orm.assets import Asset as ORMAsset | ||
from app.models.pydantic.change_log import ChangeLog | ||
from app.models.pydantic.creation_options import RasterTileSetSourceCreationOptions | ||
from app.models.pydantic.jobs import Job | ||
from app.models.pydantic.symbology import Symbology | ||
from app.tasks.raster_tile_cache_assets.symbology import ( | ||
SymbologyFuncType, | ||
SymbologyInfo, | ||
) | ||
|
||
|
||
@pytest.fixture(scope="module") | ||
def tile_cache_asset_uuid(): | ||
return UUID("e0a2dc44-aee1-4f71-963b-70a85869685d") | ||
|
||
|
||
@pytest.fixture(scope="module") | ||
def source_asset_uuid(): | ||
return UUID("a8230040-23ad-4def-aca3-a292b6161557") | ||
|
||
|
||
@pytest.fixture() | ||
def creation_options_dict(source_asset_uuid): | ||
return { | ||
"creation_options": { | ||
"min_zoom": 0, | ||
"max_zoom": 0, # make sure to test this at least with 1 | ||
"max_static_zoom": 0, | ||
"implementation": "default", | ||
"symbology": { | ||
"type": "date_conf_intensity", # try no_symbology | ||
}, | ||
"resampling": "nearest", | ||
"source_asset_id": source_asset_uuid, | ||
} | ||
} | ||
|
||
|
||
@pytest.fixture() | ||
def max_zoom_and_min_zoom_different_creation_options_dict(source_asset_uuid): | ||
return { | ||
"creation_options": { | ||
"min_zoom": 0, | ||
"max_zoom": 1, | ||
"max_static_zoom": 0, | ||
"implementation": "default", | ||
"symbology": { | ||
"type": "date_conf_intensity", # try no_symbology | ||
}, | ||
"resampling": "nearest", | ||
"source_asset_id": source_asset_uuid, | ||
} | ||
} | ||
|
||
|
||
@pytest.fixture() | ||
def source_asset(): | ||
return ORMAsset( | ||
creation_options={ | ||
"pixel_meaning": "test_pixels", | ||
"data_type": "boolean", | ||
"grid": "1/4000", | ||
} | ||
) | ||
|
||
|
||
@pytest.fixture() | ||
def raster_tile_set_source_creation_options(): | ||
return RasterTileSetSourceCreationOptions( | ||
pixel_meaning="test_pixels", | ||
data_type="boolean", | ||
grid="1/4000", | ||
source_type="raster", | ||
source_driver="GeoTIFF", | ||
symbology=Symbology(type="date_conf_intensity"), | ||
) | ||
|
||
|
||
@pytest.fixture() | ||
def tile_cache_job(): | ||
return Job( | ||
dataset="test_dataset", | ||
job_name="tile_cache_job", | ||
job_queue="tile_cache_job_queue", | ||
job_definition="tile cache job", | ||
command=["doit"], | ||
vcpus=1, | ||
memory=64, | ||
attempts=1, | ||
attempt_duration_seconds=1, | ||
) | ||
|
||
|
||
@pytest.fixture() | ||
def symbology_job(): | ||
return Job( | ||
dataset="test_dataset", | ||
job_name="symbology_job", | ||
job_queue="symbology_job_queue", | ||
job_definition="symbology job", | ||
command=["doit"], | ||
vcpus=1, | ||
memory=64, | ||
attempts=1, | ||
attempt_duration_seconds=1, | ||
) | ||
|
||
|
||
@pytest.fixture() | ||
def job(): | ||
return Job( | ||
dataset="test_dataset", | ||
job_name="reprojection_job", | ||
job_queue="reprojection_job_queue", | ||
job_definition="source reprojection job", | ||
command=["doit"], | ||
vcpus=1, | ||
memory=64, | ||
attempts=1, | ||
attempt_duration_seconds=1, | ||
) | ||
|
||
|
||
@pytest.fixture() | ||
def reprojection(job): | ||
return ( | ||
job, | ||
"https://some/asset/uri", | ||
) | ||
|
||
|
||
@pytest.fixture() | ||
def symbology_info(symbology_job): | ||
return SymbologyInfo( | ||
8, | ||
1, | ||
AsyncMock( | ||
spec_set=SymbologyFuncType, | ||
return_value=([symbology_job], "/dummy/symbology/uri"), | ||
), | ||
) | ||
|
||
|
||
@pytest.fixture() | ||
def change_log(): | ||
return ChangeLog( | ||
date_time=datetime(2022, 12, 6), status="success", message="All done!" | ||
) |
233 changes: 233 additions & 0 deletions
233
..._tile_cache_assets/collaborators/test_building_raster_tile_set_source_creation_options.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
from unittest.mock import patch | ||
|
||
import pytest | ||
|
||
from app.models.enum.creation_options import RasterDrivers | ||
from app.models.enum.sources import RasterSourceType | ||
from app.models.pydantic.symbology import Symbology | ||
from app.tasks.raster_tile_cache_assets import raster_tile_cache_asset | ||
|
||
from . import MODULE_PATH_UNDER_TEST | ||
|
||
|
||
def assert_contains_subset(kwargs, expected_subset): | ||
"""Simple assertion helper function for asserting fewer keys than what's in | ||
the entire dict.""" | ||
__tracebackhide__ = True | ||
assert expected_subset == {k: v for k, v in kwargs.items() if k in expected_subset} | ||
|
||
|
||
@patch(f"{MODULE_PATH_UNDER_TEST}.execute", autospec=True) | ||
@patch(f"{MODULE_PATH_UNDER_TEST}.symbology_constructor", autospec=True) | ||
@patch(f"{MODULE_PATH_UNDER_TEST}.reproject_to_web_mercator", autospec=True) | ||
@patch(f"{MODULE_PATH_UNDER_TEST}.RasterTileSetSourceCreationOptions", autospec=True) | ||
@patch(f"{MODULE_PATH_UNDER_TEST}.get_asset", autospec=True) | ||
class TestBuildingRasterTileSetSourceCreationOptionsFromSourceAsset: | ||
@pytest.mark.asyncio | ||
async def test_overrides_source_information( | ||
self, | ||
get_asset_dummy, | ||
raster_tile_set_source_creation_options_mock, | ||
web_mercator_dummy, | ||
symbology_constructor_dummy, | ||
execute_dummy, | ||
tile_cache_asset_uuid, | ||
creation_options_dict, | ||
source_asset, | ||
raster_tile_set_source_creation_options, | ||
reprojection, | ||
symbology_info, | ||
change_log, | ||
): | ||
get_asset_dummy.return_value = source_asset | ||
raster_tile_set_source_creation_options_mock.return_value = ( | ||
raster_tile_set_source_creation_options | ||
) | ||
symbology_constructor_dummy.__getitem__.return_value = symbology_info | ||
web_mercator_dummy.return_value = reprojection | ||
execute_dummy.return_value = change_log | ||
|
||
await raster_tile_cache_asset( | ||
"test_dataset", "2022", tile_cache_asset_uuid, creation_options_dict | ||
) | ||
|
||
_, kwargs = raster_tile_set_source_creation_options_mock.call_args_list[-1] | ||
expected = { | ||
"source_type": RasterSourceType.raster, | ||
"source_driver": RasterDrivers.geotiff, | ||
"source_uri": [ | ||
"s3://gfw-data-lake-test/test_dataset/2022/raster/epsg-4326/1/4000/test_pixels/geotiff/tiles.geojson" | ||
], | ||
} | ||
assert_contains_subset(kwargs, expected) | ||
|
||
@pytest.mark.asyncio | ||
async def test_sets_calc_to_none( | ||
self, | ||
get_asset_dummy, | ||
raster_tile_set_source_creation_options_mock, | ||
web_mercator_dummy, | ||
symbology_constructor_dummy, | ||
execute_dummy, | ||
tile_cache_asset_uuid, | ||
creation_options_dict, | ||
source_asset, | ||
raster_tile_set_source_creation_options, | ||
reprojection, | ||
symbology_info, | ||
change_log, | ||
): | ||
get_asset_dummy.return_value = source_asset | ||
raster_tile_set_source_creation_options_mock.return_value = ( | ||
raster_tile_set_source_creation_options | ||
) | ||
symbology_constructor_dummy.__getitem__.return_value = symbology_info | ||
web_mercator_dummy.return_value = reprojection | ||
execute_dummy.return_value = change_log | ||
|
||
await raster_tile_cache_asset( | ||
"test_dataset", "2022", tile_cache_asset_uuid, creation_options_dict | ||
) | ||
|
||
_, kwargs = raster_tile_set_source_creation_options_mock.call_args_list[-1] | ||
expected = {"calc": None} | ||
assert_contains_subset(kwargs, expected) | ||
|
||
@pytest.mark.asyncio | ||
async def test_sets_resampling_method_from_input_params( | ||
self, | ||
get_asset_dummy, | ||
raster_tile_set_source_creation_options_mock, | ||
web_mercator_dummy, | ||
symbology_constructor_dummy, | ||
execute_dummy, | ||
tile_cache_asset_uuid, | ||
creation_options_dict, | ||
source_asset, | ||
raster_tile_set_source_creation_options, | ||
reprojection, | ||
symbology_info, | ||
change_log, | ||
): | ||
get_asset_dummy.return_value = source_asset | ||
raster_tile_set_source_creation_options_mock.return_value = ( | ||
raster_tile_set_source_creation_options | ||
) | ||
symbology_constructor_dummy.__getitem__.return_value = symbology_info | ||
web_mercator_dummy.return_value = reprojection | ||
execute_dummy.return_value = change_log | ||
|
||
await raster_tile_cache_asset( | ||
"test_dataset", "2022", tile_cache_asset_uuid, creation_options_dict | ||
) | ||
|
||
_, kwargs = raster_tile_set_source_creation_options_mock.call_args_list[-1] | ||
expected = { | ||
"resampling": creation_options_dict["creation_options"]["resampling"] | ||
} | ||
assert_contains_subset(kwargs, expected) | ||
|
||
@pytest.mark.asyncio | ||
async def test_sets_compute_information_to_false( | ||
self, | ||
get_asset_dummy, | ||
raster_tile_set_source_creation_options_mock, | ||
web_mercator_dummy, | ||
symbology_constructor_dummy, | ||
execute_dummy, | ||
tile_cache_asset_uuid, | ||
creation_options_dict, | ||
source_asset, | ||
raster_tile_set_source_creation_options, | ||
reprojection, | ||
symbology_info, | ||
change_log, | ||
): | ||
get_asset_dummy.return_value = source_asset | ||
raster_tile_set_source_creation_options_mock.return_value = ( | ||
raster_tile_set_source_creation_options | ||
) | ||
symbology_constructor_dummy.__getitem__.return_value = symbology_info | ||
web_mercator_dummy.return_value = reprojection | ||
execute_dummy.return_value = change_log | ||
|
||
await raster_tile_cache_asset( | ||
"test_dataset", "2022", tile_cache_asset_uuid, creation_options_dict | ||
) | ||
|
||
_, kwargs = raster_tile_set_source_creation_options_mock.call_args_list[-1] | ||
expected = { | ||
"compute_stats": False, | ||
"compute_histogram": False, | ||
} | ||
assert_contains_subset(kwargs, expected) | ||
|
||
@pytest.mark.asyncio | ||
async def test_sets_symbology_from_input_data( | ||
self, | ||
get_asset_dummy, | ||
raster_tile_set_source_creation_options_mock, | ||
web_mercator_dummy, | ||
symbology_constructor_dummy, | ||
execute_dummy, | ||
tile_cache_asset_uuid, | ||
creation_options_dict, | ||
source_asset, | ||
raster_tile_set_source_creation_options, | ||
reprojection, | ||
symbology_info, | ||
change_log, | ||
): | ||
get_asset_dummy.return_value = source_asset | ||
raster_tile_set_source_creation_options_mock.return_value = ( | ||
raster_tile_set_source_creation_options | ||
) | ||
symbology_constructor_dummy.__getitem__.return_value = symbology_info | ||
web_mercator_dummy.return_value = reprojection | ||
execute_dummy.return_value = change_log | ||
|
||
await raster_tile_cache_asset( | ||
"test_dataset", "2022", tile_cache_asset_uuid, creation_options_dict | ||
) | ||
|
||
_, kwargs = raster_tile_set_source_creation_options_mock.call_args_list[-1] | ||
expected = { | ||
"symbology": Symbology( | ||
**creation_options_dict["creation_options"]["symbology"] | ||
), | ||
} | ||
assert_contains_subset(kwargs, expected) | ||
|
||
@pytest.mark.asyncio | ||
async def test_sets_subset_to_none( | ||
self, | ||
get_asset_dummy, | ||
raster_tile_set_source_creation_options_mock, | ||
web_mercator_dummy, | ||
symbology_constructor_dummy, | ||
execute_dummy, | ||
tile_cache_asset_uuid, | ||
creation_options_dict, | ||
source_asset, | ||
raster_tile_set_source_creation_options, | ||
reprojection, | ||
symbology_info, | ||
change_log, | ||
): | ||
get_asset_dummy.return_value = source_asset | ||
raster_tile_set_source_creation_options_mock.return_value = ( | ||
raster_tile_set_source_creation_options | ||
) | ||
symbology_constructor_dummy.__getitem__.return_value = symbology_info | ||
web_mercator_dummy.return_value = reprojection | ||
execute_dummy.return_value = change_log | ||
|
||
await raster_tile_cache_asset( | ||
"test_dataset", "2022", tile_cache_asset_uuid, creation_options_dict | ||
) | ||
|
||
_, kwargs = raster_tile_set_source_creation_options_mock.call_args_list[-1] | ||
expected = { | ||
"subset": None, | ||
} | ||
assert_contains_subset(kwargs, expected) |
Oops, something went wrong.