Repository: dov-vlaanderen/pydov
Issue: sort_by is broken for fields named differently between pydov and WFS

<!-- You can ask questions about the DOV webservices or about the `pydov` package. If you have a question about the `pydov` Python package, please use following template. -->

* PyDOV version: 3.0.0
* Python version: 3.10.6
* Operating System: Ubuntu

### Description

Sorting on fields having a different name in pydov compared to the WFS service is broken in pydov 3.0.0

### What I Did

```python
from pydov.search.bodemclassificatie import BodemclassificatieSearch
from owslib.fes2 import SortBy, SortProperty

s = BodemclassificatieSearch()

df = s.search(max_features=1, sort_by=SortBy([SortProperty('x')]))
```
```
Traceback (most recent call last):
  File "/home/roel/Sideprojects/pydov/example_bodemclassificaties.py", line 7, in <module>
    df = s.search(max_features=1, sort_by=SortBy([SortProperty('x')]))
  File "/home/roel/Sideprojects/pydov/pydov/search/abstract.py", line 988, in search
    trees = self._search(location=location, query=query, sort_by=sort_by,
  File "/home/roel/Sideprojects/pydov/pydov/search/abstract.py", line 833, in _search
    tree = _get_remote_wfs(
  File "/home/roel/Sideprojects/pydov/pydov/search/abstract.py", line 814, in _get_remote_wfs
    raise WfsGetFeatureError(
pydov.util.errors.WfsGetFeatureError: Error retrieving features of layer 'bodem:bodemclassificaties' from DOV WFS server:
<ows:ExceptionReport xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0.0" xsi:schemaLocation="http://www.opengis.net/ows/1.1 https://www.dov.vlaanderen.be/geoserver/schemas/ows/1.1.0/owsAll.xsd">
<ows:Exception exceptionCode="InvalidParameterValue" locator="GetFeature">
<ows:ExceptionText>Illegal property name: x for feature type bodem:bodemclassificaties</ows:ExceptionText>
</ows:Exception>
</ows:ExceptionReport>
```

Changed files:
tests/test_search_wfs_paging.py, tests/abstract.py, tests/conftest.py, pydov/search/abstract.py

diff --git a/pydov/search/abstract.py b/pydov/search/abstract.py
index 463297d..38e2de3 100644
--- a/pydov/search/abstract.py
+++ b/pydov/search/abstract.py
@@ -8,7 +8,6 @@ import warnings
 import re
 
 import owslib
-import owslib.fes
 import owslib.fes2
 from owslib.etree import etree
 from owslib.feature import get_schema
@@ -858,7 +857,7 @@ class AbstractSearch(AbstractCommon):
         if sort_by is not None:
             sort_by_xml = sort_by.toXML()
             for property_name in sort_by_xml.findall(
-                    './/{http://www.opengis.net/ogc}PropertyName'):
+                    './/{http://www.opengis.net/fes/2.0}ValueReference'):
                 property_name.text = self._map_df_wfs_source.get(
                     property_name.text, property_name.text)
 
diff --git a/tests/abstract.py b/tests/abstract.py
index 1dcfb12..1c18e5b 100644
--- a/tests/abstract.py
+++ b/tests/abstract.py
@@ -471,25 +471,14 @@ class AbstractTestSearch(object):
 
         assert list(df) == list(self.valid_returnfields_extra.get_names())
 
-    def test_search_sortby_valid(self, mp_get_schema,
-                                 mp_remote_describefeaturetype,
-                                 mp_remote_wfs_feature, mp_dov_xml):
+    @pytest.mark.online
+    @pytest.mark.skipif(not ServiceCheck.service_ok(),
+                        reason="DOV service is unreachable")
+    def test_search_sortby_valid(self):
         """Test the search method with the query parameter and the sort_by
         parameter with a valid sort field.
 
         Test whether a dataframe is returned.
-
-        Parameters
-        ----------
-        mp_get_schema : pytest.fixture
-            Monkeypatch the call to a remote OWSLib schema.
-        mp_remote_describefeaturetype : pytest.fixture
-            Monkeypatch the call to a remote DescribeFeatureType.
-        mp_remote_wfs_feature : pytest.fixture
-            Monkeypatch the call to get WFS features.
-        mp_dov_xml : pytest.fixture
-            Monkeypatch the call to get the remote XML data.
-
         """
         df = self.search_instance.search(
             query=self.valid_query_single,
diff --git a/tests/conftest.py b/tests/conftest.py
index 523dfcd..8cf0f4f 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -5,9 +5,7 @@ import glob
 import os
 import tempfile
 
-import owslib
 import pytest
-from _pytest.monkeypatch import MonkeyPatch
 from owslib.etree import etree
 from owslib.feature.schema import _construct_schema, _get_elements
 from owslib.feature.wfs200 import ContentMetadata
@@ -31,14 +29,7 @@ def pytest_configure(config):
                             "online: mark test that requires internet access")
 
 
-@pytest.fixture(scope='module')
-def monkeymodule():
-    mpatch = MonkeyPatch()
-    yield mpatch
-    mpatch.undo()
-
-
-@pytest.fixture(scope='module')
+@pytest.fixture
 def wfs_capabilities():
     """PyTest fixture providing the WFS GetCapabilities response based on a 
     local copy.
@@ -56,32 +47,34 @@ def wfs_capabilities():
     return data
 
 
-@pytest.fixture(scope='module')
-def mp_wfs(monkeymodule, wfs_capabilities):
+@pytest.fixture
+def mp_wfs(monkeypatch, wfs_capabilities):
     """Monkeypatch the call to the remote GetCapabilities request.
 
     Parameters
     ----------
-    monkeymodule : pytest.fixture
-        PyTest monkeypatch fixture with module scope.
+    monkeypatch : pytest.fixture
+        PyTest monkeypatch fixture.
+    wfs_capabilities: pytest.fixture
+        Fixture providing WFS capabilities response.
 
     """
     def read(*args, **kwargs):
         return wfs_capabilities
 
-    monkeymodule.setattr(
+    monkeypatch.setattr(
         owsutil, 'get_wfs_capabilities', read)
 
 
-@pytest.fixture(scope='module')
+@pytest.fixture
 def wfs(wfs_capabilities):
     """PyTest fixture providing an instance of a WebFeatureService based on
     a local copy of a GetCapabilities request.
 
     Parameters
     ----------
-    mp_wfs : pytest.fixture
-        Monkeypatch the call to the remote GetCapabilities request.
+    wfs_capabilities: pytest.fixture
+        Fixture providing WFS capabilities response.
 
     Returns
     -------
@@ -94,7 +87,7 @@ def wfs(wfs_capabilities):
         xml=wfs_capabilities)
 
 
-@pytest.fixture()
+@pytest.fixture
 def mp_remote_fc_notfound(monkeypatch):
     """Monkeypatch the call to get an inexistent remote featurecatalogue.
 
@@ -115,8 +108,8 @@ def mp_remote_fc_notfound(monkeypatch):
     monkeypatch.setattr(pydov.util.owsutil, '__get_remote_fc', __get_remote_fc)
 
 
-@pytest.fixture(scope='module')
-def mp_remote_md(wfs, monkeymodule, request):
+@pytest.fixture
+def mp_remote_md(wfs, monkeypatch, request):
     """Monkeypatch the call to get the remote metadata of the layer.
 
     This monkeypatch requires a module variable ``location_md_metadata``
@@ -126,9 +119,9 @@ def mp_remote_md(wfs, monkeymodule, request):
     ----------
     wfs : pytest.fixture returning owslib.wfs.WebFeatureService
         WebFeatureService based on the local GetCapabilities.
-    monkeymodule : pytest.fixture
-        PyTest monkeypatch fixture with module scope.
-    request : pytest.fixtue
+    monkeypatch : pytest.fixture
+        PyTest monkeypatch fixture.
+    request : pytest.fixture
         PyTest fixture providing request context.
 
     """
@@ -141,12 +134,12 @@ def mp_remote_md(wfs, monkeymodule, request):
         return MD_Metadata(etree.fromstring(data).find(
             './{http://www.isotc211.org/2005/gmd}MD_Metadata'))
 
-    monkeymodule.setattr(pydov.util.owsutil, 'get_remote_metadata',
-                         __get_remote_md)
+    monkeypatch.setattr(pydov.util.owsutil, 'get_remote_metadata',
+                        __get_remote_md)
 
 
-@pytest.fixture(scope='module')
-def mp_remote_fc(monkeymodule, request):
+@pytest.fixture
+def mp_remote_fc(monkeypatch, request):
     """Monkeypatch the call to get the remote feature catalogue.
 
     This monkeypatch requires a module variable
@@ -155,9 +148,9 @@ def mp_remote_fc(monkeymodule, request):
 
     Parameters
     ----------
-    monkeymodule : pytest.fixture
-        PyTest monkeypatch fixture with module scope.
-    request : pytest.fixtue
+    monkeypatch : pytest.fixture
+        PyTest monkeypatch fixture.
+    request : pytest.fixture
         PyTest fixture providing request context.
 
     """
@@ -169,12 +162,12 @@ def mp_remote_fc(monkeymodule, request):
                 data = data.encode('utf-8')
         return data
 
-    monkeymodule.setattr(pydov.util.owsutil, '__get_remote_fc',
-                         __get_remote_fc)
+    monkeypatch.setattr(pydov.util.owsutil, '__get_remote_fc',
+                        __get_remote_fc)
 
 
-@pytest.fixture(scope='module')
-def mp_remote_describefeaturetype(monkeymodule, request):
+@pytest.fixture
+def mp_remote_describefeaturetype(monkeypatch, request):
     """Monkeypatch the call to a remote DescribeFeatureType.
 
     This monkeypatch requires a module variable
@@ -183,9 +176,9 @@ def mp_remote_describefeaturetype(monkeymodule, request):
 
     Parameters
     ----------
-    monkeymodule : pytest.fixture
-        PyTest monkeypatch fixture with module scope.
-    request : pytest.fixtue
+    monkeypatch : pytest.fixture
+        PyTest monkeypatch fixture.
+    request : pytest.fixture
         PyTest fixture providing request context.
 
     """
@@ -197,13 +190,23 @@ def mp_remote_describefeaturetype(monkeymodule, request):
                 data = data.encode('utf-8')
         return data
 
-    monkeymodule.setattr(pydov.util.owsutil,
-                         '__get_remote_describefeaturetype',
-                         __get_remote_describefeaturetype)
+    monkeypatch.setattr(pydov.util.owsutil,
+                        '__get_remote_describefeaturetype',
+                        __get_remote_describefeaturetype)
 
 
-@pytest.fixture(scope='module')
-def mp_get_schema(monkeymodule, request):
+@pytest.fixture
+def mp_get_schema(monkeypatch, request):
+    """Monkeypatch the call to the get_schema method.
+
+    Parameters
+    ----------
+    monkeypatch : pytest.fixture
+        PyTest monkeypatch fixture.
+    request : pytest.fixture
+        PyTest fixture providing request context.
+
+    """
     def __get_schema(*args, **kwargs):
         file_path = getattr(request.module, "location_wfs_describefeaturetype")
         with open(file_path, 'r') as f:
@@ -231,11 +234,11 @@ def mp_get_schema(monkeymodule, request):
             nsmap = root.nsmap
         return _construct_schema(elements, nsmap)
 
-    monkeymodule.setattr(pydov.search.abstract.AbstractSearch, '_get_schema',
-                         __get_schema)
+    monkeypatch.setattr(pydov.search.abstract.AbstractSearch, '_get_schema',
+                        __get_schema)
 
 
-@pytest.fixture(scope='module')
+@pytest.fixture
 def wfs_getfeature(request):
     """PyTest fixture providing a WFS GetFeature response.
 
@@ -244,7 +247,7 @@ def wfs_getfeature(request):
 
     Parameters
     ----------
-    request : pytest.fixtue
+    request : pytest.fixture
         PyTest fixture providing request context.
 
     Returns
@@ -259,7 +262,7 @@ def wfs_getfeature(request):
         return data
 
 
-@pytest.fixture(scope='module')
+@pytest.fixture
 def wfs_feature(request):
     """PyTest fixture providing an XML of a WFS feature element.
 
@@ -268,7 +271,7 @@ def wfs_feature(request):
 
     Parameters
     ----------
-    request : pytest.fixtue
+    request : pytest.fixture
         PyTest fixture providing request context.
 
     Returns
@@ -282,8 +285,8 @@ def wfs_feature(request):
         return etree.fromstring(f.read())
 
 
-@pytest.fixture(scope='module')
-def mp_remote_wfs_feature(monkeymodule, request):
+@pytest.fixture
+def mp_remote_wfs_feature(monkeypatch, request):
     """Monkeypatch the call to get WFS features.
 
     This monkeypatch requires a module variable ``location_wfs_getfeature``
@@ -291,9 +294,9 @@ def mp_remote_wfs_feature(monkeymodule, request):
 
     Parameters
     ----------
-    monkeymodule : pytest.fixture
-        PyTest monkeypatch fixture with module scope.
-    request : pytest.fixtue
+    monkeypatch : pytest.fixture
+        PyTest monkeypatch fixture.
+    request : pytest.fixture
         PyTest fixture providing request context.
 
     """
@@ -305,13 +308,13 @@ def mp_remote_wfs_feature(monkeymodule, request):
                 data = data.encode('utf-8')
         return data
 
-    monkeymodule.setattr(pydov.util.owsutil,
-                         'wfs_get_feature',
-                         __get_remote_wfs_feature)
+    monkeypatch.setattr(pydov.util.owsutil,
+                        'wfs_get_feature',
+                        __get_remote_wfs_feature)
 
 
-@pytest.fixture(scope='module')
-def mp_dov_xml(monkeymodule, request):
+@pytest.fixture
+def mp_dov_xml(monkeypatch, request):
     """Monkeypatch the call to get the remote XML data.
 
     This monkeypatch requires a module variable ``location_dov_xml``
@@ -319,9 +322,9 @@ def mp_dov_xml(monkeymodule, request):
 
     Parameters
     ----------
-    monkeymodule : pytest.fixture
-        PyTest monkeypatch fixture with module scope.
-    request : pytest.fixtue
+    monkeypatch : pytest.fixture
+        PyTest monkeypatch fixture.
+    request : pytest.fixture
         PyTest fixture providing request context.
 
     """
@@ -334,11 +337,11 @@ def mp_dov_xml(monkeymodule, request):
                 data = data.encode('utf-8')
         return data
 
-    monkeymodule.setattr(pydov.types.abstract.AbstractDovType,
-                         '_get_xml_data', _get_xml_data)
+    monkeypatch.setattr(pydov.types.abstract.AbstractDovType,
+                        '_get_xml_data', _get_xml_data)
 
 
-@pytest.fixture()
+@pytest.fixture
 def mp_dov_xml_broken(monkeypatch):
     """Monkeypatch the call to break the fetching of remote XML data.
 
@@ -356,7 +359,7 @@ def mp_dov_xml_broken(monkeypatch):
                         '_get_xml_data', _get_xml_data)
 
 
-@pytest.fixture()
+@pytest.fixture
 def mp_geonetwork_broken(monkeypatch):
     """Monkeypatch the parsing of remote metadata to simulate the case where
     this would fail.
@@ -375,8 +378,8 @@ def mp_geonetwork_broken(monkeypatch):
                         'parse_remote_metadata', _parse_remote_metadata)
 
 
-@pytest.fixture()
-def mp_remote_xsd(monkeymodule, request):
+@pytest.fixture
+def mp_remote_xsd(monkeypatch, request):
     """Monkeypatch the call to get the remote XSD schemas.
 
     This monkeypatch requires a module variable ``location_xsd_base``
@@ -384,9 +387,9 @@ def mp_remote_xsd(monkeymodule, request):
 
     Parameters
     ----------
-    monkeymodule : pytest.fixture
-        PyTest monkeypatch fixture with module scope.
-    request : pytest.fixtue
+    monkeypatch : pytest.fixture
+        PyTest monkeypatch fixture.
+    request : pytest.fixture
         PyTest fixture providing request context.
 
     """
@@ -404,8 +407,8 @@ def mp_remote_xsd(monkeymodule, request):
 
         return schemas
 
-    monkeymodule.setattr(pydov.search.abstract.AbstractSearch,
-                         '_get_remote_xsd_schemas', _get_remote_xsd)
+    monkeypatch.setattr(pydov.search.abstract.AbstractSearch,
+                        '_get_remote_xsd_schemas', _get_remote_xsd)
 
 
 @pytest.fixture
@@ -460,8 +463,8 @@ def mp_gml_id(monkeypatch):
 
     Parameters
     ----------
-    monkeymodule : pytest.fixture
-        PyTest monkeypatch fixture with module scope.
+    monkeypatch : pytest.fixture
+        PyTest monkeypatch fixture.
     """
     def _get_id(*args, **kwargs):
         return 'pydov.gmlid'
@@ -479,6 +482,11 @@ def plaintext_cache(request):
 
     Parameters
     ----------
+    request : pytest.fixture
+        PyTest fixture providing request context.
+
+    Fixture parameters
+    ------------------
     max_age : datetime.timedelta
         The maximum age to use for the cache.
 
@@ -510,6 +518,11 @@ def gziptext_cache(request):
 
     Parameters
     ----------
+    request : pytest.fixture
+        PyTest fixture providing request context.
+
+    Fixture parameters
+    ------------------
     max_age : datetime.timedelta
         The maximum age to use for the cache.
 
@@ -541,10 +554,17 @@ def nocache():
     pydov.cache = orig_cache
 
 
-@pytest.fixture(autouse=True, scope='function')
+@pytest.fixture(autouse=True)
 def patch_owslib_openURL(monkeypatch):
     """Fixture to patch OWSLib's openURL function in favor of a GET request
-    using our pydov requests session."""
+    using our pydov requests session.
+
+    Parameters
+    ----------
+    monkeypatch : pytest.fixture
+        PyTest monkeypatch fixture.
+
+    """
     def _openURL(*args, **kwargs):
         """Patch function for owslib.util.openURL using our custom pydov
         requests session.
diff --git a/tests/test_search_wfs_paging.py b/tests/test_search_wfs_paging.py
index c6ae7ae..0bedba5 100644
--- a/tests/test_search_wfs_paging.py
+++ b/tests/test_search_wfs_paging.py
@@ -19,7 +19,7 @@ location_wfs_getfeature = 'tests/data/wfs_paging/wfsgetfeature.xml'
 page_size = 10
 
 
-@pytest.fixture(scope='module')
+@pytest.fixture
 def wfs_capabilities():
     """PyTest fixture providing the WFS GetCapabilities response based on a 
     local copy. Adjusts the CountDefault parameter to the configured 
@@ -47,16 +47,16 @@ def wfs_capabilities():
     return data
 
 
-@pytest.fixture(scope='module')
-def mp_wfs_max_features(monkeymodule, wfs_capabilities):
+@pytest.fixture
+def mp_wfs_max_features(monkeypatch, wfs_capabilities):
     """Monkeypatch the call to get the WFS capabilities maximum features.
 
     This ensures the maximum features are read from the testdata capabilities.
 
     Parameters
     ----------
-    monkeymodule : pytest.fixture
-        PyTest monkeypatch fixture with module scope.
+    monkeypatch : pytest.fixture
+        PyTest monkeypatch fixture.
     wfs_capabilities : pytest.fixture
         PyTest fixture providing the WFS capabilities from the testdata.
 
@@ -66,13 +66,13 @@ def mp_wfs_max_features(monkeymodule, wfs_capabilities):
     def __get_wfs_max_features(*args, **kwargs):
         return max_features
 
-    monkeymodule.setattr(pydov.util.owsutil,
-                         'get_wfs_max_features',
-                         __get_wfs_max_features)
+    monkeypatch.setattr(pydov.util.owsutil,
+                        'get_wfs_max_features',
+                        __get_wfs_max_features)
 
 
-@pytest.fixture(scope='module')
-def mp_remote_wfs_paged_feature(monkeymodule, request):
+@pytest.fixture
+def mp_remote_wfs_paged_feature(monkeypatch, request):
     """Monkeypatch the call to get WFS features.
 
     This monkeypatch requires a module variable ``location_wfs_getfeature``
@@ -83,9 +83,9 @@ def mp_remote_wfs_paged_feature(monkeymodule, request):
 
     Parameters
     ----------
-    monkeymodule : pytest.fixture
-        PyTest monkeypatch fixture with module scope.
-    request : pytest.fixtue
+    monkeypatch : pytest.fixture
+        PyTest monkeypatch fixture.
+    request : pytest.fixture
         PyTest fixture providing request context.
 
     """
@@ -120,9 +120,9 @@ def mp_remote_wfs_paged_feature(monkeymodule, request):
                 data = data.encode('utf-8')
         return data
 
-    monkeymodule.setattr(pydov.util.owsutil,
-                         'wfs_get_feature',
-                         __get_remote_wfs_feature)
+    monkeypatch.setattr(pydov.util.owsutil,
+                        'wfs_get_feature',
+                        __get_remote_wfs_feature)
 
 
 class TestSearchWfsPaging(object):