Skip to content

Commit

Permalink
WIP: Add eumdac.py to mock the eumdac package
Browse files Browse the repository at this point in the history
  • Loading branch information
pkhalaj committed Dec 16, 2024
1 parent 004d7f2 commit a00ca3c
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 12 deletions.
11 changes: 6 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,16 @@ select = ["A", "B", "D", "E", "W", "F", "I", "N", "PT", "S", "TID", "C90", "Q",
# D102: missing docstrings in public method
# D103: missing docstrings in public function
# D104: missing docstrings in public packages
# D105: missing docstrings in magic method
# S101: use of assert is detected
# T201: use of print is detected
# TID252: relative imports are not prefered
# We supress the aformentioned Ruff rules on a per-file basis as follows:
"__init__.py" = ["D104"] # public packages
"src/**/models/*" = ["D100", "D101", "D102", "D103", "S101"] # pydantic models
"tests/*" = ["D100", "D103", "S101", "TID252"] # all tests
"docs/source/conf.py" = ["D100", "A001"] # sphinx [otherwise it misbehaves]
"examples/*" = ["D100", "D103", "S101", "TID252", "T201"] # all examples
"__init__.py" = ["D104"] # public packages
"src/**/models/*" = ["D100", "D101", "D102", "D103", "S101"] # pydantic models
"tests/*" = ["D100", "D101", "D102", "D103", "D105", "S101", "TID252"] # all tests
"docs/source/conf.py" = ["D100", "A001"] # sphinx [otherwise it misbehaves]
"examples/*" = ["D100", "D103", "S101", "TID252", "T201"] # all examples

[tool.ruff.lint.pydocstyle]
convention = "google"
Expand Down
8 changes: 8 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import pytest
from requests import HTTPError

from tests.utils.eumdac import EumdacPackage


@pytest.fixture
def get_token_or_skip():
Expand All @@ -27,3 +29,9 @@ def temp_dir() -> Path:
with tempfile.TemporaryDirectory() as tmpdir:
tmpdir = Path(tmpdir)
yield tmpdir


@pytest.fixture
def eumdac():
with EumdacPackage.mocked() as _eumdac:
yield _eumdac
17 changes: 10 additions & 7 deletions tests/query/test_api.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
from datetime import datetime, timedelta

import pytest
from eumdac.collection import SearchResults
from eumdac.product import Product

from monkey_wrench.date_time import SeviriIDParser
from monkey_wrench.query import EumetsatAPI, EumetsatCollection
from tests.utils import EnvironmentVariables


@pytest.fixture
def api(get_token_or_skip) -> EumetsatAPI:
def api(get_token_or_skip):
"""Get eumetsat api."""
from monkey_wrench.query import EumetsatAPI, EumetsatCollection
return EumetsatAPI(EumetsatCollection.amsu)


@pytest.fixture
def search_results(api: EumetsatAPI) -> SearchResults:
def search_results(api):
"""Get search results."""
start = datetime(2021, 1, 1, 0)
end = datetime(2021, 1, 1, 6)
Expand All @@ -32,6 +29,7 @@ def search_results(api: EumetsatAPI) -> SearchResults:

def test_api_init_raise():
"""Check that the API query raises an exception if the credentials are not set."""
from monkey_wrench.query import EumetsatAPI
k1, k2 = EumetsatAPI.credentials_env_vars.values()
for key1, key2 in [(k1, k2), (k2, k1)]:
with EnvironmentVariables(**{f"{key1}": "dummy", f"{key2}": None}):
Expand All @@ -44,12 +42,15 @@ def test_api_get_token_success(get_token_or_skip):


def test_api_query(get_token_or_skip):
from monkey_wrench.query import EumetsatAPI
start_datetime = datetime(2022, 1, 1, )
end_datetime = datetime(2022, 1, 2)
assert 96 == EumetsatAPI().query(start_datetime, end_datetime).total_results


def test_api_query_in_batches(get_token_or_skip):
from monkey_wrench.query import EumetsatAPI, EumetsatCollection

start_datetime = datetime(2022, 1, 1, )
end_datetime = datetime(2022, 1, 3)
batch_interval = timedelta(days=1)
Expand Down Expand Up @@ -83,13 +84,15 @@ def test_fetch(api, search_results, tmp_path):
assert outfiles[0].suffix == ".nc"


def seviri_product_datetime_is_correct(day: int, product: Product, end_datetime: datetime, start_datetime: datetime):
def seviri_product_datetime_is_correct(day: int, product, end_datetime: datetime, start_datetime: datetime):
"""Check that the product datetime is correct."""
from monkey_wrench.date_time import SeviriIDParser
datetime_obj = SeviriIDParser.parse(str(product))
return (start_datetime <= datetime_obj < end_datetime) and (day == datetime_obj.day)


def test_open_seviri_native_remotely(get_token_or_skip):
from monkey_wrench.query import EumetsatAPI
product_id = "MSG3-SEVI-MSG15-0100-NA-20230413164241.669000000Z-NA"
fs_file = EumetsatAPI.open_seviri_native_file_remotely(product_id)
assert f"{product_id}.nat" == fs_file.open().name
68 changes: 68 additions & 0 deletions tests/utils/eumdac.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"""Module to mock the ``eumdac`` package for testing purposes."""

import sys
from contextlib import contextmanager
from datetime import datetime, timedelta
from unittest.mock import MagicMock, patch


class AccessToken(MagicMock):
expiration = datetime.now() + timedelta(hours=1)

def __str__(self):
return "not-ok"


class EumdacPackage:
eumdac = None
access_token = AccessToken

@staticmethod
def _get_eumdac_namespace():
modules = [
"",
".token",
".collection",
".product",
".tailor_models"
]
return {f"eumdac{k}": MagicMock() for k in modules}

@classmethod
def __add_data_store(cls):
cls.eumdac.DataStore = MagicMock()
cls.eumdac.DataStore.get_collection = MagicMock()

@classmethod
def __add_product(cls):
cls.eumdac.Product = MagicMock()

@classmethod
def __add_access_token(cls):
cls.eumdac.token.AccessToken = MagicMock()
cls.eumdac.AccessToken = cls.eumdac.token.AccessToken

@classmethod
def __add_details(cls):
cls.__add_data_store()
cls.__add_product()
cls.__add_access_token()

@classmethod
def _add_sub_packages(cls, _namespace):
cls.eumdac = _namespace["eumdac"]
cls.eumdac.token = _namespace["eumdac.token"]
cls.eumdac.collection = _namespace["eumdac.collection"]
cls.eumdac.product = _namespace["eumdac.product"]
cls.eumdac.tailor_models = _namespace["eumdac.tailor_models"]
cls.__add_details()

@classmethod
@contextmanager
def mocked(cls):
try:
with patch.dict(sys.modules, EumdacPackage._get_eumdac_namespace()) as _namespace:
EumdacPackage._add_sub_packages(_namespace)
yield EumdacPackage.eumdac
finally:
pass

0 comments on commit a00ca3c

Please sign in to comment.