Repository: CS-SI/eodag (python)

Issue:
[eodag-server] setting provider parameter in a request, default this provider as preferred for future requests
**Describe the bug**
A clear and concise description of what the bug is.

You first request a product type from a specific provider to EODAG Server.
This provider is set as the preferred one for all future requests to this EODAG Server instance.

Setting the provider parameter in requests should not impact the preferred provider defined in the configuration.

**Code To Reproduce**
We assume the default provider is not set to WEkEO but another one like PEPS

1. Request using the default provider
  ```shell
  curl http://localhost:8000/search?collections=S1_SAR_GRD
  ```

3. Request specifying a custom provider
  ```shell
  curl http://localhost:8000/search?collections=S1_SAR_GRD&provider=wekeo
  ```

4. Request without specifying a provider
  ```shell
  curl http://localhost:8000/search?collections=S1_SAR_GRD
  ```

The 3rd request use the same provider as the 2nd (a.k.a WEkEO)

**Additional context**

This behavior is caused by

```python
# https://github.com/CS-SI/eodag/blob/develop/eodag/rest/stac.py#L86-L87

        if self.provider and self.eodag_api.get_preferred_provider() != self.provider:
            self.eodag_api.set_preferred_provider(self.provider)
```

It has been introduced in commit: https://github.com/CS-SI/eodag/commit/05b24fc692cc5b55400ed8339b638b1eafe4d0e9



Pull request:
fix: search preferred provider and wekeo job error handling
Contains the following changes:

- The preferred provider is not changed anymore when a search or download is executed. If a provider is given in the request, it will be used (if it offers the selected product type) but the preferred provider is not changed. If no provider is given, the configured preferred provider is used.

Fixes #783 

- an error is raised when the creation of a data request job fails for wekeo


Patch:
diff --git a/eodag/api/core.py b/eodag/api/core.py
index 0d589122..9b394f4a 100644
--- a/eodag/api/core.py
+++ b/eodag/api/core.py
@@ -832,6 +832,7 @@ class EODataAccessGateway(object):
         end=None,
         geom=None,
         locations=None,
+        provider=None,
         **kwargs,
     ):
         """Look for products matching criteria on known providers.
@@ -877,6 +878,9 @@ class EODataAccessGateway(object):
         :type locations: dict
         :param kwargs: Some other criteria that will be used to do the search,
                        using paramaters compatibles with the provider
+        :param provider: (optional) the provider to be used, if not set, the configured
+                         default provider will be used
+        :type provider: str
         :type kwargs: Union[int, str, bool, dict]
         :returns: A collection of EO products matching the criteria and the total
                   number of results found
@@ -888,7 +892,12 @@ class EODataAccessGateway(object):
             enforced here.
         """
         search_kwargs = self._prepare_search(
-            start=start, end=end, geom=geom, locations=locations, **kwargs
+            start=start,
+            end=end,
+            geom=geom,
+            locations=locations,
+            provider=provider,
+            **kwargs,
         )
         search_plugins = search_kwargs.pop("search_plugins", [])
         if search_kwargs.get("id"):
@@ -899,7 +908,9 @@ class EODataAccessGateway(object):
             )
             # remove auth from search_kwargs as a loop over providers will be performed
             search_kwargs.pop("auth", None)
-            return self._search_by_id(search_kwargs.pop("id"), **search_kwargs)
+            return self._search_by_id(
+                search_kwargs.pop("id"), provider=provider, **search_kwargs
+            )
         search_kwargs.update(
             page=page,
             items_per_page=items_per_page,
@@ -1254,12 +1265,16 @@ class EODataAccessGateway(object):
                   of EO products retrieved (0 or 1)
         :rtype: tuple(:class:`~eodag.api.search_result.SearchResult`, int)
         """
+        if not provider:
+            provider = self.get_preferred_provider()[0]
         get_search_plugins_kwargs = dict(
             provider=provider, product_type=kwargs.get("productType", None)
         )
-        for plugin in self._plugins_manager.get_search_plugins(
+        search_plugins = self._plugins_manager.get_search_plugins(
             **get_search_plugins_kwargs
-        ):
+        )
+
+        for plugin in search_plugins:
             logger.info(
                 "Searching product with id '%s' on provider: %s", uid, plugin.provider
             )
@@ -1294,7 +1309,7 @@ class EODataAccessGateway(object):
         return SearchResult([]), 0
 
     def _prepare_search(
-        self, start=None, end=None, geom=None, locations=None, **kwargs
+        self, start=None, end=None, geom=None, locations=None, provider=None, **kwargs
     ):
         """Internal method to prepare the search kwargs and get the search
         and auth plugins.
@@ -1323,6 +1338,9 @@ class EODataAccessGateway(object):
         :type geom: Union[str, dict, shapely.geometry.base.BaseGeometry]
         :param locations: (optional) Location filtering by name using locations configuration
         :type locations: dict
+        :param provider: provider to be used, if no provider is given or the product type
+                        is not available for the provider, the preferred provider is used
+        :type provider: str
         :param kwargs: Some other criteria
                        * id and/or a provider for a search by
                        * search criteria to guess the product type
@@ -1400,15 +1418,23 @@ class EODataAccessGateway(object):
         ):
             search_plugins.append(plugin)
 
-        if search_plugins[0].provider != self.get_preferred_provider()[0]:
+        if not provider:
+            provider = self.get_preferred_provider()[0]
+        providers = [plugin.provider for plugin in search_plugins]
+        if provider not in providers:
             logger.warning(
                 "Product type '%s' is not available with provider '%s'. "
                 "Searching it on provider '%s' instead.",
                 product_type,
-                self.get_preferred_provider()[0],
+                provider,
                 search_plugins[0].provider,
             )
         else:
+            provider_plugin = list(
+                filter(lambda p: p.provider == provider, search_plugins)
+            )[0]
+            search_plugins.remove(provider_plugin)
+            search_plugins.insert(0, provider_plugin)
             logger.info(
                 "Searching product type '%s' on provider: %s",
                 product_type,
@@ -1440,9 +1466,6 @@ class EODataAccessGateway(object):
             # Remove the ID since this is equal to productType.
             search_plugin.config.product_type_config.pop("ID", None)
 
-            logger.debug(
-                "Using plugin class for search: %s", search_plugin.__class__.__name__
-            )
             auth_plugin = self._plugins_manager.get_auth_plugin(search_plugin.provider)
             auth_plugins[search_plugin.provider] = auth_plugin
 
@@ -1493,6 +1516,7 @@ class EODataAccessGateway(object):
 
         results = SearchResult([])
         total_results = 0
+
         try:
             if "auth" in kwargs:
                 if isinstance(kwargs["auth"], dict):
@@ -1586,6 +1610,7 @@ class EODataAccessGateway(object):
                         pass
                     else:
                         eo_product.product_type = guesses[0]
+
                 if eo_product.search_intersection is not None:
                     download_plugin = self._plugins_manager.get_download_plugin(
                         eo_product
diff --git a/eodag/plugins/authentication/token.py b/eodag/plugins/authentication/token.py
index b1032426..377268c7 100644
--- a/eodag/plugins/authentication/token.py
+++ b/eodag/plugins/authentication/token.py
@@ -15,19 +15,28 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+import logging
 
 import requests
 from requests import RequestException
+from requests.adapters import HTTPAdapter
+from urllib3 import Retry
 
 from eodag.plugins.authentication.base import Authentication
 from eodag.utils import USER_AGENT, RequestsTokenAuth
 from eodag.utils.exceptions import AuthenticationError, MisconfiguredError
 from eodag.utils.stac_reader import HTTP_REQ_TIMEOUT
 
+logger = logging.getLogger("eodag.authentication.token")
+
 
 class TokenAuth(Authentication):
     """TokenAuth authentication plugin"""
 
+    def __init__(self, provider, config):
+        super(TokenAuth, self).__init__(provider, config)
+        self.token = ""
+
     def validate_config_credentials(self):
         """Validate configured credentials"""
         super(TokenAuth, self).validate_config_credentials()
@@ -56,10 +65,15 @@ class TokenAuth(Authentication):
             if hasattr(self.config, "headers")
             else {"headers": USER_AGENT}
         )
+        s = requests.Session()
+        retries = Retry(
+            total=3, backoff_factor=2, status_forcelist=[401, 429, 500, 502, 503, 504]
+        )
+        s.mount(self.config.auth_uri, HTTPAdapter(max_retries=retries))
         try:
             # First get the token
             if getattr(self.config, "request_method", "POST") == "POST":
-                response = requests.post(
+                response = s.post(
                     self.config.auth_uri,
                     data=self.config.credentials,
                     timeout=HTTP_REQ_TIMEOUT,
@@ -67,7 +81,7 @@ class TokenAuth(Authentication):
                 )
             else:
                 cred = self.config.credentials
-                response = requests.get(
+                response = s.get(
                     self.config.auth_uri,
                     auth=(cred["username"], cred["password"]),
                     timeout=HTTP_REQ_TIMEOUT,
@@ -84,11 +98,21 @@ class TokenAuth(Authentication):
                 token = response.json()[self.config.token_key]
             else:
                 token = response.text
+            headers = self._get_headers(token)
+            self.token = token
             # Return auth class set with obtained token
-            return RequestsTokenAuth(token, "header", headers=self._get_headers(token))
+            return RequestsTokenAuth(token, "header", headers=headers)
 
     def _get_headers(self, token):
         headers = self.config.headers
         if "Authorization" in headers and "$" in headers["Authorization"]:
             headers["Authorization"] = headers["Authorization"].replace("$token", token)
+        if (
+            self.token
+            and token != self.token
+            and self.token in headers["Authorization"]
+        ):
+            headers["Authorization"] = headers["Authorization"].replace(
+                self.token, token
+            )
         return headers
diff --git a/eodag/plugins/download/base.py b/eodag/plugins/download/base.py
index dce4db23..1e040cef 100644
--- a/eodag/plugins/download/base.py
+++ b/eodag/plugins/download/base.py
@@ -373,7 +373,7 @@ class Download(PluginTopic):
                 shutil.move(extraction_dir, outputs_dir)
 
             elif fs_path.endswith(".tar.gz"):
-                with tarfile.open(fs_path, "r:gz") as zfile:
+                with tarfile.open(fs_path, "r") as zfile:
                     progress_callback.reset(total=1)
                     zfile.extractall(path=extraction_dir)
                     progress_callback(1)
diff --git a/eodag/plugins/search/data_request_search.py b/eodag/plugins/search/data_request_search.py
index 1f6c586e..0bf2b043 100644
--- a/eodag/plugins/search/data_request_search.py
+++ b/eodag/plugins/search/data_request_search.py
@@ -11,6 +11,7 @@ from eodag.api.product.metadata_mapping import (
 )
 from eodag.plugins.search.base import Search
 from eodag.utils import GENERIC_PRODUCT_TYPE, string_to_jsonpath
+from eodag.utils.exceptions import RequestError
 
 logger = logging.getLogger("eodag.search.data_request_search")
 
@@ -91,10 +92,9 @@ class DataRequestSearch(Search):
             metadata = requests.get(metadata_url, headers=headers)
             metadata.raise_for_status()
         except requests.RequestException:
-            logger.error(
-                "metadata for product_type %s could not be retrieved", product_type
+            raise RequestError(
+                f"metadata for product_type {product_type} could not be retrieved"
             )
-            raise
         else:
             try:
                 url = self.config.data_request_url
@@ -107,11 +107,8 @@ class DataRequestSearch(Search):
                 request_job = requests.post(url, json=request_body, headers=headers)
                 request_job.raise_for_status()
             except requests.RequestException as e:
-                logger.error(
-                    "search job for product_type %s could not be created: %s, %s",
-                    product_type,
-                    str(e),
-                    request_job.text,
+                raise RequestError(
+                    f"search job for product_type {product_type} could not be created: {str(e)}, {request_job.text}"
                 )
             else:
                 logger.info("search job for product_type %s created", product_type)
@@ -122,13 +119,11 @@ class DataRequestSearch(Search):
         status_url = self.config.status_url + data_request_id
         status_data = requests.get(status_url, headers=self.auth.headers).json()
         if "status_code" in status_data and status_data["status_code"] == 403:
-            logger.error("authentication token expired during request")
-            raise requests.RequestException
+            raise RequestError("authentication token expired during request")
         if status_data["status"] == "failed":
-            logger.error(
-                "data request job has failed, message: %s", status_data["message"]
+            raise RequestError(
+                f"data request job has failed, message: {status_data['message']}"
             )
-            raise requests.RequestException
         return status_data["status"] == "completed"
 
     def _get_result_data(self, data_request_id):
diff --git a/eodag/rest/server.py b/eodag/rest/server.py
index 0c9ecbda..57cc4381 100755
--- a/eodag/rest/server.py
+++ b/eodag/rest/server.py
@@ -233,12 +233,13 @@ async def handle_resource_not_found(request: Request, error):
 
 @app.exception_handler(AuthenticationError)
 async def handle_auth_error(request: Request, error):
-    """Unauthorized [401] errors handle"""
+    """AuthenticationError should be sent as internal server error to the client"""
+    logger.error(f"{type(error).__name__}: {str(error)}")
     return await default_exception_handler(
         request,
         HTTPException(
-            status_code=401,
-            detail=f"{type(error).__name__}: {str(error)}",
+            status_code=500,
+            detail="Internal server error: please contact the administrator",
         ),
     )
 
@@ -409,12 +410,9 @@ def stac_collections_item_download(collection_id, item_id, request: Request):
     body = {}
     arguments = dict(request.query_params, **body)
     provider = arguments.pop("provider", None)
-    zipped = "True"
-    if "zip" in arguments:
-        zipped = arguments["zip"]
 
     return download_stac_item_by_id_stream(
-        catalogs=[collection_id], item_id=item_id, provider=provider, zip=zipped
+        catalogs=[collection_id], item_id=item_id, provider=provider
     )
 
 
diff --git a/eodag/rest/stac.py b/eodag/rest/stac.py
index 6e8fcbe5..afbcbce1 100644
--- a/eodag/rest/stac.py
+++ b/eodag/rest/stac.py
@@ -83,9 +83,6 @@ class StacCommon(object):
 
         self.data = {}
 
-        if self.provider and self.eodag_api.get_preferred_provider() != self.provider:
-            self.eodag_api.set_preferred_provider(self.provider)
-
     def update_data(self, data):
         """Updates data using given input STAC dict data
 
diff --git a/eodag/rest/utils.py b/eodag/rest/utils.py
index afa872ae..8fbda850 100644
--- a/eodag/rest/utils.py
+++ b/eodag/rest/utils.py
@@ -393,6 +393,10 @@ def search_products(product_type, arguments, stac_formatted=True):
 
     try:
         arg_product_type = arguments.pop("product_type", None)
+        provider = arguments.pop("provider", None)
+        if not provider:
+            provider = eodag_api.get_preferred_provider()[0]
+
         unserialized = arguments.pop("unserialized", None)
 
         page, items_per_page = get_pagination_info(arguments)
@@ -407,6 +411,7 @@ def search_products(product_type, arguments, stac_formatted=True):
             "start": dtstart,
             "end": dtend,
             "geom": geom,
+            "provider": provider,
         }
 
         if stac_formatted:
@@ -455,20 +460,24 @@ def search_products(product_type, arguments, stac_formatted=True):
     return response
 
 
-def search_product_by_id(uid, product_type=None):
+def search_product_by_id(uid, product_type=None, provider=None):
     """Search a product by its id
 
     :param uid: The uid of the EO product
     :type uid: str
     :param product_type: (optional) The product type
     :type product_type: str
+    :param provider: (optional) The provider to be used
+    :type provider: str
     :returns: A search result
     :rtype: :class:`~eodag.api.search_result.SearchResult`
     :raises: :class:`~eodag.utils.exceptions.ValidationError`
     :raises: RuntimeError
     """
     try:
-        products, total = eodag_api.search(id=uid, productType=product_type)
+        products, total = eodag_api.search(
+            id=uid, productType=product_type, provider=provider
+        )
         return products
     except ValidationError:
         raise
@@ -573,7 +582,7 @@ def get_stac_item_by_id(url, item_id, catalogs, root="/", provider=None):
         return None
 
 
-def download_stac_item_by_id_stream(catalogs, item_id, provider=None, zip="True"):
+def download_stac_item_by_id_stream(catalogs, item_id, provider=None):
     """Download item
 
     :param catalogs: Catalogs list (only first is used as product_type)
@@ -587,17 +596,16 @@ def download_stac_item_by_id_stream(catalogs, item_id, provider=None, zip="True"
     :returns: a stream of the downloaded data (either as a zip or the individual assets)
     :rtype: StreamingResponse
     """
-    if provider:
-        eodag_api.set_preferred_provider(provider)
-
-    product = search_product_by_id(item_id, product_type=catalogs[0])[0]
-
+    product = search_product_by_id(
+        item_id, product_type=catalogs[0], provider=provider
+    )[0]
     if product.downloader is None:
         download_plugin = eodag_api._plugins_manager.get_download_plugin(product)
         auth_plugin = eodag_api._plugins_manager.get_auth_plugin(
             download_plugin.provider
         )
         product.register_downloader(download_plugin, auth_plugin)
+
     auth = (
         product.downloader_auth.authenticate()
         if product.downloader_auth is not None
@@ -664,7 +672,7 @@ def get_stac_catalogs(url, root="/", catalogs=[], provider=None, fetch_providers
     :param fetch_providers: (optional) Whether to fetch providers for new product
                             types or not
     :type fetch_providers: bool
-    :returns: Catalog dictionnary
+    :returns: Catalog dictionary
     :rtype: dict
     """
     return StacCatalog(
@@ -734,10 +742,10 @@ def search_stac_items(url, arguments, root="/", catalogs=[], provider=None):
         ids = [ids]
     if ids:
         search_results = SearchResult([])
-        if provider:
-            eodag_api.set_preferred_provider(provider)
         for item_id in ids:
-            found_products = search_product_by_id(item_id, product_type=collections[0])
+            found_products = search_product_by_id(
+                item_id, product_type=collections[0], provider=provider
+            )
             if len(found_products) == 1:
                 search_results.extend(found_products)
         search_results.properties = {
@@ -769,7 +777,9 @@ def search_stac_items(url, arguments, root="/", catalogs=[], provider=None):
             arguments.pop("datetime")
 
         search_products_arguments = dict(
-            arguments, **result_catalog.search_args, **{"unserialized": "true"}
+            arguments,
+            **result_catalog.search_args,
+            **{"unserialized": "true", "provider": provider},
         )
 
         # check if time filtering appears twice
@@ -825,7 +835,6 @@ def search_stac_items(url, arguments, root="/", catalogs=[], provider=None):
                         **{"url": result_catalog.url, "root": result_catalog.root},
                     ),
                 )
-
         search_results = search_products(
             product_type=result_catalog.search_args["product_type"],
             arguments=search_products_arguments,


Test patch:
diff --git a/tests/integration/test_core_search_results.py b/tests/integration/test_core_search_results.py
index 8d94a465..1fff5fe7 100644
--- a/tests/integration/test_core_search_results.py
+++ b/tests/integration/test_core_search_results.py
@@ -264,3 +264,23 @@ class TestCoreSearchResults(EODagTestCase):
         for search_result in search_results:
             self.assertIsInstance(search_result.downloader, PluginTopic)
             self.assertIsInstance(search_result.downloader_auth, PluginTopic)
+
+    @mock.patch(
+        "eodag.plugins.search.qssearch.QueryStringSearch.do_search",
+        autospec=True,
+    )
+    def test_core_search_with_provider(self, mock_query):
+        """The core api must register search results downloaders"""
+        self.dag.set_preferred_provider("creodias")
+        search_results_file = os.path.join(
+            TEST_RESOURCES_PATH, "provider_responses/peps_search.json"
+        )
+        with open(search_results_file, encoding="utf-8") as f:
+            search_results_peps = json.load(f)
+
+        mock_query.return_value = search_results_peps["features"]
+        search_results, count = self.dag.search(
+            productType="S2_MSI_L1C", provider="peps"
+        )
+        # use given provider and not preferred provider
+        self.assertEqual("peps", search_results[0].provider)
diff --git a/tests/units/test_auth_plugins.py b/tests/units/test_auth_plugins.py
index 93c8d71d..70cf6c2d 100644
--- a/tests/units/test_auth_plugins.py
+++ b/tests/units/test_auth_plugins.py
@@ -129,7 +129,9 @@ class TestAuthPluginTokenAuth(BaseAuthPluginTest):
         auth_plugin.config.credentials = {"foo": "bar", "username": "john"}
         auth_plugin.validate_config_credentials()
 
-    @mock.patch("eodag.plugins.authentication.token.requests.post", autospec=True)
+    @mock.patch(
+        "eodag.plugins.authentication.token.requests.Session.post", autospec=True
+    )
     def test_plugins_auth_tokenauth_text_token_authenticate(self, mock_requests_post):
         """TokenAuth.authenticate must return a RequestsTokenAuth object using text token"""
         auth_plugin = self.get_auth_plugin("provider_text_token_header")
@@ -146,7 +148,7 @@ class TestAuthPluginTokenAuth(BaseAuthPluginTest):
 
         # check token post request call arguments
         args, kwargs = mock_requests_post.call_args
-        assert args[0] == auth_plugin.config.auth_uri
+        assert args[1] == auth_plugin.config.auth_uri
         assert kwargs["data"] == auth_plugin.config.credentials
         assert kwargs["headers"] == dict(auth_plugin.config.headers, **USER_AGENT)
 
@@ -156,7 +158,9 @@ class TestAuthPluginTokenAuth(BaseAuthPluginTest):
         assert req.headers["Authorization"] == "Bearer this_is_test_token"
         assert req.headers["foo"] == "bar"
 
-    @mock.patch("eodag.plugins.authentication.token.requests.post", autospec=True)
+    @mock.patch(
+        "eodag.plugins.authentication.token.requests.Session.post", autospec=True
+    )
     def test_plugins_auth_tokenauth_json_token_authenticate(self, mock_requests_post):
         """TokenAuth.authenticate must return a RequestsTokenAuth object using json token"""
         auth_plugin = self.get_auth_plugin("provider_json_token_simple_url")
@@ -179,7 +183,9 @@ class TestAuthPluginTokenAuth(BaseAuthPluginTest):
         auth(req)
         assert req.headers["Authorization"] == "Bearer this_is_test_token"
 
-    @mock.patch("eodag.plugins.authentication.token.requests.post", autospec=True)
+    @mock.patch(
+        "eodag.plugins.authentication.token.requests.Session.post", autospec=True
+    )
     def test_plugins_auth_tokenauth_request_error(self, mock_requests_post):
         """TokenAuth.authenticate must raise an AuthenticationError if a request error occurs"""
         auth_plugin = self.get_auth_plugin("provider_json_token_simple_url")
diff --git a/tests/units/test_core.py b/tests/units/test_core.py
index 092beac5..c3b4347e 100644
--- a/tests/units/test_core.py
+++ b/tests/units/test_core.py
@@ -1292,7 +1292,7 @@ class TestCoreSearch(TestCoreBase):
         """_prepare_search must handle a search by id"""
         base = {"id": "dummy-id", "provider": "creodias"}
         prepared_search = self.dag._prepare_search(**base)
-        expected = base
+        expected = {"id": "dummy-id"}
         self.assertDictEqual(expected, prepared_search)
 
     def test__prepare_search_preserve_additional_kwargs(self):
diff --git a/tests/units/test_http_server.py b/tests/units/test_http_server.py
index d1a8a9aa..9a144cd8 100644
--- a/tests/units/test_http_server.py
+++ b/tests/units/test_http_server.py
@@ -317,6 +317,7 @@ class RequestTestCase(unittest.TestCase):
         self._request_valid(
             f"search?collections={self.tested_product_type}",
             expected_search_kwargs=dict(
+                provider="peps",
                 productType=self.tested_product_type,
                 page=1,
                 items_per_page=DEFAULT_ITEMS_PER_PAGE,
@@ -326,6 +327,7 @@ class RequestTestCase(unittest.TestCase):
         self._request_valid(
             f"search?collections={self.tested_product_type}&bbox=0,43,1,44",
             expected_search_kwargs=dict(
+                provider="peps",
                 productType=self.tested_product_type,
                 page=1,
                 items_per_page=DEFAULT_ITEMS_PER_PAGE,
@@ -344,22 +346,26 @@ class RequestTestCase(unittest.TestCase):
         side_effect=AuthenticationError("you are no authorized"),
     )
     def test_auth_error(self, mock_search):
-        """A request to eodag server raising a Authentication error must return a 401 HTTP error code"""
-        response = self.app.get(
-            f"search?collections={self.tested_product_type}", follow_redirects=True
-        )
-        response_content = json.loads(response.content.decode("utf-8"))
+        """A request to eodag server raising a Authentication error must return a 500 HTTP error code"""
 
-        self.assertEqual(401, response.status_code)
-        self.assertIn("description", response_content)
-        self.assertIn("AuthenticationError", response_content["description"])
-        self.assertIn("you are no authorized", response_content["description"])
+        with self.assertLogs(level="ERROR") as cm_logs:
+            response = self.app.get(
+                f"search?collections={self.tested_product_type}", follow_redirects=True
+            )
+            response_content = json.loads(response.content.decode("utf-8"))
+
+            self.assertIn("description", response_content)
+            self.assertIn("AuthenticationError", str(cm_logs.output))
+            self.assertIn("you are no authorized", str(cm_logs.output))
+
+        self.assertEqual(500, response.status_code)
 
     def test_filter(self):
         """latestIntersect filter should only keep the latest products once search area is fully covered"""
         result1 = self._request_valid(
             f"search?collections={self.tested_product_type}&bbox=89.65,2.65,89.7,2.7",
             expected_search_kwargs=dict(
+                provider="peps",
                 productType=self.tested_product_type,
                 page=1,
                 items_per_page=DEFAULT_ITEMS_PER_PAGE,
@@ -371,6 +377,7 @@ class RequestTestCase(unittest.TestCase):
         result2 = self._request_valid(
             f"search?collections={self.tested_product_type}&bbox=89.65,2.65,89.7,2.7&filter=latestIntersect",
             expected_search_kwargs=dict(
+                provider="peps",
                 productType=self.tested_product_type,
                 page=1,
                 items_per_page=DEFAULT_ITEMS_PER_PAGE,
@@ -386,6 +393,7 @@ class RequestTestCase(unittest.TestCase):
         self._request_valid(
             f"search?collections={self.tested_product_type}&bbox=0,43,1,44&datetime=2018-01-20/2018-01-25",
             expected_search_kwargs=dict(
+                provider="peps",
                 productType=self.tested_product_type,
                 page=1,
                 items_per_page=DEFAULT_ITEMS_PER_PAGE,
@@ -401,6 +409,7 @@ class RequestTestCase(unittest.TestCase):
         self._request_valid(
             f"collections/{self.tested_product_type}/items?bbox=0,43,1,44",
             expected_search_kwargs=dict(
+                provider="peps",
                 productType=self.tested_product_type,
                 page=1,
                 items_per_page=DEFAULT_ITEMS_PER_PAGE,
@@ -411,6 +420,7 @@ class RequestTestCase(unittest.TestCase):
         self._request_valid(
             f"collections/{self.tested_product_type}/items?bbox=0,43,1,44&datetime=2018-01-20/2018-01-25",
             expected_search_kwargs=dict(
+                provider="peps",
                 productType=self.tested_product_type,
                 page=1,
                 items_per_page=DEFAULT_ITEMS_PER_PAGE,
@@ -426,6 +436,7 @@ class RequestTestCase(unittest.TestCase):
         results = self._request_valid(
             f"catalogs/{self.tested_product_type}/year/2018/month/01/items?bbox=0,43,1,44",
             expected_search_kwargs=dict(
+                provider="peps",
                 productType=self.tested_product_type,
                 page=1,
                 items_per_page=DEFAULT_ITEMS_PER_PAGE,
@@ -441,6 +452,7 @@ class RequestTestCase(unittest.TestCase):
             f"catalogs/{self.tested_product_type}/year/2018/month/01/items"
             "?bbox=0,43,1,44&datetime=2018-01-20/2018-01-25",
             expected_search_kwargs=dict(
+                provider="peps",
                 productType=self.tested_product_type,
                 page=1,
                 items_per_page=DEFAULT_ITEMS_PER_PAGE,
@@ -456,6 +468,7 @@ class RequestTestCase(unittest.TestCase):
             f"catalogs/{self.tested_product_type}/year/2018/month/01/items"
             "?bbox=0,43,1,44&datetime=2018-01-20/2019-01-01",
             expected_search_kwargs=dict(
+                provider="peps",
                 productType=self.tested_product_type,
                 page=1,
                 items_per_page=DEFAULT_ITEMS_PER_PAGE,
@@ -489,6 +502,7 @@ class RequestTestCase(unittest.TestCase):
             f"catalogs/{self.tested_product_type}/items/foo",
             expected_search_kwargs={
                 "id": "foo",
+                "provider": None,
                 "productType": self.tested_product_type,
             },
         )
@@ -499,6 +513,7 @@ class RequestTestCase(unittest.TestCase):
             f"collections/{self.tested_product_type}/items/foo",
             expected_search_kwargs={
                 "id": "foo",
+                "provider": None,
                 "productType": self.tested_product_type,
             },
         )
@@ -521,6 +536,7 @@ class RequestTestCase(unittest.TestCase):
                 "query": {"eo:cloud_cover": {"lte": 10}},
             },
             expected_search_kwargs=dict(
+                provider="peps",
                 productType=self.tested_product_type,
                 page=1,
                 items_per_page=DEFAULT_ITEMS_PER_PAGE,
diff --git a/tests/units/test_search_plugins.py b/tests/units/test_search_plugins.py
index 9e09dc6f..9d99ea9f 100644
--- a/tests/units/test_search_plugins.py
+++ b/tests/units/test_search_plugins.py
@@ -22,7 +22,6 @@ import unittest
 from pathlib import Path
 from unittest import mock
 
-import requests
 import responses
 from requests import RequestException
 
@@ -936,10 +935,11 @@ class MockResponse:
 
 
 class TestSearchPluginDataRequestSearch(BaseSearchPluginTest):
-    @mock.patch("eodag.plugins.authentication.token.requests.get", autospec=True)
+    @mock.patch(
+        "eodag.plugins.authentication.token.requests.Session.get", autospec=True
+    )
     def setUp(self, mock_requests_get):
 
-        # One of the providers that has a BuildPostSearchResult Search plugin
         provider = "wekeo"
         self.search_plugin = self.get_search_plugin(self.product_type, provider)
         self.auth_plugin = self.get_auth_plugin(provider)
@@ -973,7 +973,7 @@ class TestSearchPluginDataRequestSearch(BaseSearchPluginTest):
         mock_requests_get.return_value = MockResponse(
             {"status": "failed", "message": "failed"}, 500
         )
-        with self.assertRaises(requests.RequestException):
+        with self.assertRaises(RequestError):
             self.search_plugin._check_request_status("123")
 
     @mock.patch("eodag.plugins.search.data_request_search.requests.get", autospec=True)
diff --git a/tests/units/test_stac_utils.py b/tests/units/test_stac_utils.py
index 0c8f618d..50ea9dff 100644
--- a/tests/units/test_stac_utils.py
+++ b/tests/units/test_stac_utils.py
@@ -447,6 +447,7 @@ class TestStacUtils(unittest.TestCase):
         mock__request.return_value.json.return_value = (
             self.earth_search_resp_search_json
         )
+        self.rest_utils.eodag_api.set_preferred_provider("peps")
 
         response = self.rest_utils.search_stac_items(
             url="http://foo/search",
@@ -467,6 +468,8 @@ class TestStacUtils(unittest.TestCase):
             "assets"
         ].items():
             self.assertIn((k, v), response["features"][0]["assets"].items())
+        # preferred provider should not be changed
+        self.assertEqual("peps", self.rest_utils.eodag_api.get_preferred_provider()[0])
 
     @mock.patch(
         "eodag.plugins.search.qssearch.QueryStringSearch._request",