Skip to content

Commit

Permalink
test: add a test where 'discover_queryables()' fails
Browse files Browse the repository at this point in the history
'fetch_data()' must have been exposed to use it correctly in the test
  • Loading branch information
anesson-cs committed Jan 22, 2025
1 parent 176e1ed commit 93182d0
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 25 deletions.
17 changes: 12 additions & 5 deletions eodag/plugins/search/build_search_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,9 +339,6 @@ class ECMWFSearch(PostJsonSearch):
"""

def __init__(self, provider: str, config: PluginConfig) -> None:
# cache fetching method
self.fetch_data = functools.lru_cache()(self._fetch_data)

config.metadata_mapping = {
**keywords_to_mdt(ECMWF_KEYWORDS + COP_DS_KEYWORDS, "ecmwf"),
**config.metadata_mapping,
Expand Down Expand Up @@ -580,7 +577,7 @@ def discover_queryables(
getattr(self.config, "discover_queryables", {}).get("form_url", ""),
**kwargs,
)
form = self.fetch_data(form_url)
form: list[dict[str, Any]] = self.fetch_data(form_url)

formated_kwargs = self.format_as_provider_keyword(
product_type, processed_kwargs
Expand Down Expand Up @@ -637,7 +634,7 @@ def discover_queryables(
return self.queryables_from_metadata_mapping(product_type)
if "{" in values_url:
values_url = values_url.format(productType=provider_product_type)
data = self.fetch_data(values_url)
data: list[dict[str, Any]] = self.fetch_data(values_url)
available_values = data["constraints"]
required_keywords = data.get("required", [])

Expand Down Expand Up @@ -960,6 +957,16 @@ def format_as_provider_keyword(
}
return format_query_params(product_type, self.config, available_properties)

@functools.lru_cache()
def fetch_data(self, url: str) -> Any:
"""
Fetches from a provider elements like constraints or forms by using the cache.
:param url: url from which the constraints can be fetched
:returns: cache containing the json file content fetched from the provider
"""
return self._fetch_data(url)

def _fetch_data(self, url: str) -> Any:
"""
fetches from a provider elements like constraints or forms.
Expand Down
80 changes: 60 additions & 20 deletions tests/units/test_search_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
from eodag.api.product import AssetsDict
from eodag.api.product.metadata_mapping import get_queryable_from_provider
from eodag.utils import deepcopy
from eodag.utils.exceptions import UnsupportedProductType
from eodag.utils.exceptions import UnsupportedProductType, ValidationError
from tests.context import (
DEFAULT_MISSION_START_DATE,
HTTP_REQ_TIMEOUT,
Expand Down Expand Up @@ -2342,8 +2342,10 @@ def test_plugins_search_ecmwfsearch_with_custom_producttype(self):
except Exception:
assert eoproduct.properties[param] == self.custom_query_params[param]

@mock.patch("eodag.utils.requests.requests.sessions.Session.get", autospec=True)
def test_plugins_search_ecmwfsearch_discover_queryables(self, mock_requests_get):
@mock.patch(
"eodag.plugins.search.build_search_result.ECMWFSearch.fetch_data", autospec=True
)
def test_plugins_search_ecmwfsearch_discover_queryables_ok(self, mock_fetch_data):
constraints_path = os.path.join(TEST_RESOURCES_PATH, "constraints.json")
with open(constraints_path) as f:
constraints = json.load(f)
Expand All @@ -2352,7 +2354,7 @@ def test_plugins_search_ecmwfsearch_discover_queryables(self, mock_requests_get)
form_path = os.path.join(TEST_RESOURCES_PATH, "form.json")
with open(form_path) as f:
form = json.load(f)
mock_requests_get.return_value.json.side_effect = [constraints, form]
mock_fetch_data.side_effect = [constraints, form]
product_type_config = {"missionStartDate": "2001-01-01T00:00:00Z"}
setattr(self.search_plugin.config, "product_type_config", product_type_config)

Expand Down Expand Up @@ -2405,28 +2407,16 @@ def test_plugins_search_ecmwfsearch_discover_queryables(self, mock_requests_get)
# no error was raised, as expected
self.assertIsNotNone(queryables)

mock_requests_get.assert_has_calls(
mock_fetch_data.assert_has_calls(
[
call(
mock.ANY,
"https://ads.atmosphere.copernicus.eu/api/catalogue/v1/collections/"
"cams-europe-air-quality-reanalyses/constraints.json",
headers=USER_AGENT,
auth=None,
timeout=5,
),
call().raise_for_status(),
call().json(),
call(
mock.ANY,
"https://ads.atmosphere.copernicus.eu/api/catalogue/v1/collections/"
"cams-europe-air-quality-reanalyses/form.json",
headers=USER_AGENT,
auth=None,
timeout=5,
),
call().raise_for_status(),
call().json(),
]
)

Expand Down Expand Up @@ -2476,16 +2466,28 @@ def test_plugins_search_ecmwfsearch_discover_queryables(self, mock_requests_get)
)

# reset mock
mock_requests_get.reset_mock()
mock_fetch_data.reset_mock()
mock_fetch_data.side_effect = [constraints, form]
# with additional param
params = deepcopy(default_values)
params["productType"] = "CAMS_EU_AIR_QUALITY_RE"
params["ecmwf:variable"] = "a"
queryables = self.search_plugin.discover_queryables(**params)
self.assertIsNotNone(queryables)

# mock not called because cached values are used
mock_requests_get.assert_not_called()
# cached values are not used to make the set of unit tests work then the mock is called again
mock_fetch_data.assert_has_calls(
[
call(
"https://ads.atmosphere.copernicus.eu/api/catalogue/v1/collections/"
"cams-europe-air-quality-reanalyses/constraints.json",
),
call(
"https://ads.atmosphere.copernicus.eu/api/catalogue/v1/collections/"
"cams-europe-air-quality-reanalyses/form.json",
),
]
)

self.assertEqual(12, len(queryables))
# default properties called in function arguments are added and must be default values of the queryables
Expand All @@ -2494,6 +2496,44 @@ def test_plugins_search_ecmwfsearch_discover_queryables(self, mock_requests_get)
self.assertEqual("a", queryable.__metadata__[0].get_default())
self.assertFalse(queryable.__metadata__[0].is_required())

@mock.patch(
"eodag.plugins.search.build_search_result.ECMWFSearch.fetch_data", autospec=True
)
def test_plugins_search_ecmwfsearch_discover_queryables_ko(self, mock_fetch_data):
constraints_path = os.path.join(TEST_RESOURCES_PATH, "constraints.json")
with open(constraints_path) as f:
constraints = json.load(f)
form_path = os.path.join(TEST_RESOURCES_PATH, "form.json")
with open(form_path) as f:
form = json.load(f)
mock_fetch_data.side_effect = [constraints, form]

default_values = deepcopy(
getattr(self.search_plugin.config, "products", {}).get(
"CAMS_EU_AIR_QUALITY_RE", {}
)
)
default_values.pop("metadata_mapping", None)
params = deepcopy(default_values)
params["productType"] = "CAMS_EU_AIR_QUALITY_RE"

# use a wrong parameter, e.g. it is not among the ones of the form file, not among
# the ones of the constraints file and not among the ones of default provider configuration
wrong_queryable = "foo"
self.assertNotIn(wrong_queryable, default_values)
self.assertNotIn(wrong_queryable, [param["name"] for param in form])
for constraint in constraints:
self.assertNotIn(wrong_queryable, constraint)
params[wrong_queryable] = "bar"

# Test the function, expecting ValidationError to be raised
with self.assertRaises(ValidationError) as context:
self.search_plugin.discover_queryables(**params)
self.assertEqual(
f"{wrong_queryable} is not a queryable parameter for {self.provider}",
str(context.exception),
)

@mock.patch("eodag.utils.requests.requests.sessions.Session.get", autospec=True)
def test_plugins_search_ecmwf_search_wekeo_discover_queryables(
self, mock_requests_get
Expand Down

0 comments on commit 93182d0

Please sign in to comment.