udata 12.0.2.dev16__py3-none-any.whl → 12.0.2.dev18__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of udata might be problematic. Click here for more details.
- udata/commands/tests/test_fixtures.py +6 -3
- udata/core/badges/tests/test_commands.py +2 -4
- udata/core/badges/tests/test_model.py +2 -2
- udata/core/pages/tests/test_api.py +0 -2
- udata/core/spatial/tests/test_api.py +17 -20
- udata/core/spatial/tests/test_models.py +3 -3
- udata/core/user/tests/test_user_model.py +2 -6
- udata/features/identicon/tests/test_backends.py +3 -13
- udata/forms/fields.py +3 -3
- udata/forms/widgets.py +2 -2
- udata/harvest/tests/ckan/test_ckan_backend.py +300 -337
- udata/harvest/tests/ckan/test_ckan_backend_errors.py +94 -99
- udata/harvest/tests/ckan/test_ckan_backend_filters.py +128 -122
- udata/harvest/tests/ckan/test_dkan_backend.py +39 -51
- udata/harvest/tests/test_actions.py +7 -7
- udata/harvest/tests/test_api.py +2 -4
- udata/harvest/tests/test_base_backend.py +3 -4
- udata/harvest/tests/test_dcat_backend.py +5 -16
- udata/harvest/tests/test_models.py +2 -4
- udata/harvest/tests/test_notifications.py +2 -4
- udata/harvest/tests/test_tasks.py +2 -3
- udata/mongo/taglist_field.py +3 -3
- udata/settings.py +3 -0
- udata/tags.py +5 -5
- udata/tests/__init__.py +40 -58
- udata/tests/api/__init__.py +87 -2
- udata/tests/api/test_activities_api.py +17 -23
- udata/tests/api/test_auth_api.py +2 -4
- udata/tests/api/test_contact_points.py +48 -54
- udata/tests/api/test_dataservices_api.py +0 -2
- udata/tests/api/test_datasets_api.py +27 -49
- udata/tests/api/test_me_api.py +4 -6
- udata/tests/api/test_organizations_api.py +19 -38
- udata/tests/api/test_reports_api.py +0 -4
- udata/tests/api/test_reuses_api.py +9 -19
- udata/tests/api/test_swagger.py +2 -3
- udata/tests/api/test_tags_api.py +6 -7
- udata/tests/api/test_transfer_api.py +0 -2
- udata/tests/api/test_user_api.py +8 -10
- udata/tests/apiv2/test_datasets.py +0 -4
- udata/tests/apiv2/test_me_api.py +0 -2
- udata/tests/apiv2/test_organizations.py +0 -2
- udata/tests/apiv2/test_swagger.py +2 -3
- udata/tests/apiv2/test_topics.py +0 -2
- udata/tests/cli/test_cli_base.py +14 -12
- udata/tests/cli/test_db_cli.py +51 -54
- udata/tests/contact_point/test_contact_point_models.py +2 -2
- udata/tests/dataservice/test_csv_adapter.py +2 -5
- udata/tests/dataservice/test_dataservice_rdf.py +3 -6
- udata/tests/dataservice/test_dataservice_tasks.py +36 -38
- udata/tests/dataset/test_csv_adapter.py +2 -5
- udata/tests/dataset/test_dataset_actions.py +2 -4
- udata/tests/dataset/test_dataset_commands.py +2 -4
- udata/tests/dataset/test_dataset_events.py +3 -3
- udata/tests/dataset/test_dataset_model.py +6 -7
- udata/tests/dataset/test_dataset_rdf.py +6 -9
- udata/tests/dataset/test_dataset_recommendations.py +2 -2
- udata/tests/dataset/test_dataset_tasks.py +66 -68
- udata/tests/dataset/test_resource_preview.py +39 -48
- udata/tests/dataset/test_transport_tasks.py +2 -2
- udata/tests/features/territories/__init__.py +0 -6
- udata/tests/features/territories/test_territories_api.py +25 -24
- udata/tests/forms/test_current_user_field.py +2 -2
- udata/tests/forms/test_dict_field.py +2 -4
- udata/tests/forms/test_extras_fields.py +2 -3
- udata/tests/forms/test_image_field.py +2 -2
- udata/tests/forms/test_model_field.py +2 -4
- udata/tests/forms/test_publish_as_field.py +2 -4
- udata/tests/forms/test_user_forms.py +26 -29
- udata/tests/frontend/test_auth.py +2 -3
- udata/tests/frontend/test_csv.py +5 -6
- udata/tests/frontend/test_error_handlers.py +2 -3
- udata/tests/frontend/test_hooks.py +5 -7
- udata/tests/frontend/test_markdown.py +3 -4
- udata/tests/helpers.py +2 -7
- udata/tests/metrics/test_metrics.py +52 -48
- udata/tests/metrics/test_tasks.py +154 -150
- udata/tests/organization/test_csv_adapter.py +2 -5
- udata/tests/organization/test_notifications.py +2 -4
- udata/tests/organization/test_organization_model.py +3 -4
- udata/tests/organization/test_organization_rdf.py +2 -8
- udata/tests/plugin.py +6 -110
- udata/tests/reuse/test_reuse_model.py +3 -4
- udata/tests/site/test_site_api.py +0 -2
- udata/tests/site/test_site_csv_exports.py +0 -2
- udata/tests/site/test_site_metrics.py +2 -4
- udata/tests/site/test_site_model.py +2 -2
- udata/tests/site/test_site_rdf.py +3 -6
- udata/tests/test_activity.py +3 -3
- udata/tests/test_api_fields.py +6 -9
- udata/tests/test_cors.py +0 -2
- udata/tests/test_dcat_commands.py +2 -3
- udata/tests/test_discussions.py +2 -7
- udata/tests/test_mail.py +4 -10
- udata/tests/test_migrations.py +413 -419
- udata/tests/test_model.py +10 -11
- udata/tests/test_notifications.py +2 -3
- udata/tests/test_owned.py +3 -3
- udata/tests/test_routing.py +5 -5
- udata/tests/test_storages.py +6 -5
- udata/tests/test_tags.py +2 -4
- udata/tests/test_topics.py +2 -4
- udata/tests/test_transfer.py +4 -5
- udata/tests/topic/test_topic_tasks.py +25 -27
- udata/tests/user/test_user_rdf.py +2 -8
- udata/tests/workers/test_jobs_commands.py +2 -2
- udata/tests/workers/test_tasks_routing.py +27 -27
- udata/utils.py +1 -1
- {udata-12.0.2.dev16.dist-info → udata-12.0.2.dev18.dist-info}/METADATA +1 -1
- {udata-12.0.2.dev16.dist-info → udata-12.0.2.dev18.dist-info}/RECORD +114 -116
- udata/tests/frontend/__init__.py +0 -23
- udata/tests/metrics/conftest.py +0 -15
- {udata-12.0.2.dev16.dist-info → udata-12.0.2.dev18.dist-info}/WHEEL +0 -0
- {udata-12.0.2.dev16.dist-info → udata-12.0.2.dev18.dist-info}/entry_points.txt +0 -0
- {udata-12.0.2.dev16.dist-info → udata-12.0.2.dev18.dist-info}/licenses/LICENSE +0 -0
- {udata-12.0.2.dev16.dist-info → udata-12.0.2.dev18.dist-info}/top_level.txt +0 -0
|
@@ -10,6 +10,7 @@ from udata.core.dataset import tasks
|
|
|
10
10
|
from udata.core.dataset.factories import DatasetFactory
|
|
11
11
|
from udata.harvest.models import HarvestItem
|
|
12
12
|
from udata.models import Dataset
|
|
13
|
+
from udata.tests.api import PytestOnlyDBTestCase
|
|
13
14
|
from udata.tests.helpers import assert_equal_dates
|
|
14
15
|
from udata.utils import faker
|
|
15
16
|
|
|
@@ -93,8 +94,7 @@ class HarvestFilterTest:
|
|
|
93
94
|
HarvestFilter(faker.word(), faker.word(), type, faker.sentence())
|
|
94
95
|
|
|
95
96
|
|
|
96
|
-
|
|
97
|
-
class BaseBackendTest:
|
|
97
|
+
class BaseBackendTest(PytestOnlyDBTestCase):
|
|
98
98
|
def test_simple_harvest(self):
|
|
99
99
|
now = datetime.utcnow()
|
|
100
100
|
nb_datasets = 3
|
|
@@ -420,8 +420,7 @@ class BaseBackendTest:
|
|
|
420
420
|
assert dataset_reused_uri.harvest.source_id == str(source.id)
|
|
421
421
|
|
|
422
422
|
|
|
423
|
-
|
|
424
|
-
class BaseBackendValidateTest:
|
|
423
|
+
class BaseBackendValidateTest(PytestOnlyDBTestCase):
|
|
425
424
|
@pytest.fixture
|
|
426
425
|
def validate(self):
|
|
427
426
|
return FakeBackend(HarvestSourceFactory()).validate
|
|
@@ -18,6 +18,7 @@ from udata.harvest.models import HarvestJob
|
|
|
18
18
|
from udata.models import Dataset
|
|
19
19
|
from udata.rdf import DCAT, RDF, namespace_manager
|
|
20
20
|
from udata.storage.s3 import get_from_json
|
|
21
|
+
from udata.tests.api import PytestOnlyDBTestCase
|
|
21
22
|
|
|
22
23
|
from .. import actions
|
|
23
24
|
from ..backends.dcat import URIS_TO_REPLACE
|
|
@@ -67,9 +68,8 @@ def mock_csw_pagination(rmock, path, pattern):
|
|
|
67
68
|
return url
|
|
68
69
|
|
|
69
70
|
|
|
70
|
-
@pytest.mark.usefixtures("clean_db")
|
|
71
71
|
@pytest.mark.options(PLUGINS=["dcat"])
|
|
72
|
-
class DcatBackendTest:
|
|
72
|
+
class DcatBackendTest(PytestOnlyDBTestCase):
|
|
73
73
|
def test_simple_flat(self, rmock):
|
|
74
74
|
filename = "flat.jsonld"
|
|
75
75
|
url = mock_dcat(rmock, filename)
|
|
@@ -191,7 +191,6 @@ class DcatBackendTest:
|
|
|
191
191
|
|
|
192
192
|
def test_harvest_dataservices_keep_attached_associated_datasets(self, rmock):
|
|
193
193
|
"""It should update the existing list of dataservice.datasets and not overwrite existing ones"""
|
|
194
|
-
rmock.get("https://example.com/schemas", json=ResourceSchemaMockData.get_mock_data())
|
|
195
194
|
|
|
196
195
|
filename = "bnodes.xml"
|
|
197
196
|
url = mock_dcat(rmock, filename)
|
|
@@ -359,10 +358,8 @@ class DcatBackendTest:
|
|
|
359
358
|
is None
|
|
360
359
|
)
|
|
361
360
|
|
|
362
|
-
@pytest.mark.options(
|
|
361
|
+
@pytest.mark.options(HARVEST_MAX_ITEMS=2)
|
|
363
362
|
def test_harvest_max_items(self, rmock):
|
|
364
|
-
rmock.get("https://example.com/schemas", json=ResourceSchemaMockData.get_mock_data())
|
|
365
|
-
|
|
366
363
|
filename = "bnodes.xml"
|
|
367
364
|
url = mock_dcat(rmock, filename)
|
|
368
365
|
org = OrganizationFactory()
|
|
@@ -373,10 +370,7 @@ class DcatBackendTest:
|
|
|
373
370
|
assert Dataset.objects.count() == 2
|
|
374
371
|
assert HarvestJob.objects.first().status == "done"
|
|
375
372
|
|
|
376
|
-
@pytest.mark.options(SCHEMA_CATALOG_URL="https://example.com/schemas")
|
|
377
373
|
def test_harvest_spatial(self, rmock):
|
|
378
|
-
rmock.get("https://example.com/schemas", json=ResourceSchemaMockData.get_mock_data())
|
|
379
|
-
|
|
380
374
|
filename = "bnodes.xml"
|
|
381
375
|
url = mock_dcat(rmock, filename)
|
|
382
376
|
org = OrganizationFactory()
|
|
@@ -445,10 +439,7 @@ class DcatBackendTest:
|
|
|
445
439
|
assert resources_by_title["Resource 3-1"].schema.url is None
|
|
446
440
|
assert resources_by_title["Resource 3-1"].schema.version == "2.2.0"
|
|
447
441
|
|
|
448
|
-
@pytest.mark.options(SCHEMA_CATALOG_URL="https://example.com/schemas")
|
|
449
442
|
def test_harvest_inspire_themese(self, rmock):
|
|
450
|
-
rmock.get("https://example.com/schemas", json=ResourceSchemaMockData.get_mock_data())
|
|
451
|
-
|
|
452
443
|
filename = "bnodes.xml"
|
|
453
444
|
url = mock_dcat(rmock, filename)
|
|
454
445
|
org = OrganizationFactory()
|
|
@@ -893,9 +884,8 @@ class DcatBackendTest:
|
|
|
893
884
|
assert "404 Client Error" in job.errors[0].message
|
|
894
885
|
|
|
895
886
|
|
|
896
|
-
@pytest.mark.usefixtures("clean_db")
|
|
897
887
|
@pytest.mark.options(PLUGINS=["csw"])
|
|
898
|
-
class CswDcatBackendTest:
|
|
888
|
+
class CswDcatBackendTest(PytestOnlyDBTestCase):
|
|
899
889
|
def test_geonetworkv4(self, rmock):
|
|
900
890
|
url = mock_csw_pagination(rmock, "geonetwork/srv/eng/csw.rdf", "geonetworkv4-page-{}.xml")
|
|
901
891
|
org = OrganizationFactory()
|
|
@@ -1044,9 +1034,8 @@ class CswDcatBackendTest:
|
|
|
1044
1034
|
assert len(job.items) == 1
|
|
1045
1035
|
|
|
1046
1036
|
|
|
1047
|
-
@pytest.mark.usefixtures("clean_db")
|
|
1048
1037
|
@pytest.mark.options(PLUGINS=["csw"])
|
|
1049
|
-
class CswIso19139DcatBackendTest:
|
|
1038
|
+
class CswIso19139DcatBackendTest(PytestOnlyDBTestCase):
|
|
1050
1039
|
@pytest.mark.parametrize(
|
|
1051
1040
|
"remote_url_prefix",
|
|
1052
1041
|
[
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
|
|
3
|
+
from udata.tests.api import PytestOnlyDBTestCase
|
|
5
4
|
from udata.utils import faker
|
|
6
5
|
|
|
7
6
|
from ..models import HarvestSource
|
|
@@ -9,8 +8,7 @@ from ..models import HarvestSource
|
|
|
9
8
|
log = logging.getLogger(__name__)
|
|
10
9
|
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
class HarvestSourceTest:
|
|
11
|
+
class HarvestSourceTest(PytestOnlyDBTestCase):
|
|
14
12
|
def test_defaults(self):
|
|
15
13
|
source = HarvestSource.objects.create(name="Test", url=faker.url(), backend="factory")
|
|
16
14
|
assert source.name == "Test"
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
|
|
3
1
|
from udata.core.user.factories import AdminFactory, UserFactory
|
|
4
2
|
from udata.harvest.notifications import validate_harvester_notifications
|
|
3
|
+
from udata.tests.api import PytestOnlyDBTestCase
|
|
5
4
|
from udata.tests.helpers import assert_equal_dates
|
|
6
5
|
|
|
7
6
|
from .factories import HarvestSourceFactory
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
class HarvestNotificationsTest:
|
|
9
|
+
class HarvestNotificationsTest(PytestOnlyDBTestCase):
|
|
12
10
|
def test_pending_harvester_validations(self):
|
|
13
11
|
source = HarvestSourceFactory()
|
|
14
12
|
admin = AdminFactory()
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
from udata.tests.api import PytestOnlyDBTestCase
|
|
4
4
|
|
|
5
5
|
from ..tasks import purge_harvest_jobs, purge_harvest_sources
|
|
6
6
|
|
|
7
7
|
log = logging.getLogger(__name__)
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
class HarvestActionsTest:
|
|
10
|
+
class HarvestActionsTest(PytestOnlyDBTestCase):
|
|
12
11
|
def test_purge_sources(self, mocker):
|
|
13
12
|
"""It should purge from DB sources flagged as deleted"""
|
|
14
13
|
mock = mocker.patch("udata.harvest.actions.purge_sources")
|
udata/mongo/taglist_field.py
CHANGED
|
@@ -32,12 +32,12 @@ class TagListField(ListField):
|
|
|
32
32
|
super(TagListField, self).validate(values)
|
|
33
33
|
|
|
34
34
|
for tag in values:
|
|
35
|
-
if not tags.
|
|
35
|
+
if not tags.TAG_MIN_LENGTH <= len(tag) <= tags.TAG_MAX_LENGTH:
|
|
36
36
|
self.error(
|
|
37
37
|
_(
|
|
38
38
|
'Tag "%(tag)s" must be between %(min)d and %(max)d characters long.',
|
|
39
|
-
min=tags.
|
|
40
|
-
max=tags.
|
|
39
|
+
min=tags.TAG_MIN_LENGTH,
|
|
40
|
+
max=tags.TAG_MAX_LENGTH,
|
|
41
41
|
tag=tag,
|
|
42
42
|
)
|
|
43
43
|
)
|
udata/settings.py
CHANGED
|
@@ -658,6 +658,9 @@ class Testing(object):
|
|
|
658
658
|
} # Disables deliverability for email domain name
|
|
659
659
|
PUBLISH_ON_RESOURCE_EVENTS = False
|
|
660
660
|
HARVEST_ACTIVITY_USER_ID = None
|
|
661
|
+
SEARCH_SERVICE_API_URL = None
|
|
662
|
+
CDATA_BASE_URL = None
|
|
663
|
+
SCHEMA_CATALOG_URL = None
|
|
661
664
|
|
|
662
665
|
|
|
663
666
|
class Debug(Defaults):
|
udata/tags.py
CHANGED
|
@@ -2,8 +2,8 @@ from flask import current_app
|
|
|
2
2
|
from slugify import slugify
|
|
3
3
|
from werkzeug.local import LocalProxy
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
TAG_MIN_LENGTH = LocalProxy(lambda: current_app.config["TAG_MIN_LENGTH"])
|
|
6
|
+
TAG_MAX_LENGTH = LocalProxy(lambda: current_app.config["TAG_MAX_LENGTH"])
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def slug(value: str) -> str:
|
|
@@ -12,10 +12,10 @@ def slug(value: str) -> str:
|
|
|
12
12
|
|
|
13
13
|
def normalize(value: str) -> str:
|
|
14
14
|
value = slug(value)
|
|
15
|
-
if len(value) <
|
|
15
|
+
if len(value) < TAG_MIN_LENGTH:
|
|
16
16
|
value = ""
|
|
17
|
-
elif len(value) >
|
|
18
|
-
value = value[:
|
|
17
|
+
elif len(value) > TAG_MAX_LENGTH:
|
|
18
|
+
value = value[:TAG_MAX_LENGTH]
|
|
19
19
|
return value
|
|
20
20
|
|
|
21
21
|
|
udata/tests/__init__.py
CHANGED
|
@@ -1,24 +1,48 @@
|
|
|
1
1
|
import unittest
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
|
+
from werkzeug import Response
|
|
4
5
|
|
|
5
6
|
from udata import settings
|
|
7
|
+
from udata.app import UDataApp, create_app
|
|
8
|
+
from udata.tests.plugin import TestClient
|
|
6
9
|
|
|
7
10
|
from . import helpers
|
|
8
11
|
|
|
9
12
|
|
|
10
|
-
class
|
|
11
|
-
|
|
13
|
+
class TestCaseMixin:
|
|
14
|
+
app: UDataApp
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
def inject_app(self, app):
|
|
15
|
-
self.app = app
|
|
16
|
-
return self.create_app()
|
|
17
|
-
|
|
18
|
-
def create_app(self):
|
|
16
|
+
def get_settings(self, request):
|
|
19
17
|
"""
|
|
20
|
-
|
|
18
|
+
This seems really complicated for what it's doing. I think we want to create
|
|
19
|
+
an app with the default setting being `settings.Testing` and some overrides
|
|
20
|
+
from the "options" markers. But to do that `settings.Testing` should inherit from
|
|
21
|
+
`settings.Default`?
|
|
22
|
+
|
|
23
|
+
We may also want to prevent loading `udata.cfg` for testing to avoid failing tests
|
|
24
|
+
locally because some config is changed on our computer. Tests should work only with
|
|
25
|
+
default settings (or overrides for a specific test) but not with a local `udata.cfg`.
|
|
26
|
+
|
|
27
|
+
Not sure if the plugin situation is still relevant now that plugins are integrated
|
|
28
|
+
into udata.
|
|
21
29
|
"""
|
|
30
|
+
_settings = settings.Testing
|
|
31
|
+
# apply the options(plugins) marker from pytest_flask as soon as app is created
|
|
32
|
+
# https://github.com/pytest-dev/pytest-flask/blob/a62ea18cb0fe89e3f3911192ab9ea4f9b12f8a16/pytest_flask/plugin.py#L126
|
|
33
|
+
# this lets us have default settings for plugins applied while testing
|
|
34
|
+
plugins = getattr(_settings, "PLUGINS", [])
|
|
35
|
+
for options in request.node.iter_markers("options"):
|
|
36
|
+
option = options.kwargs.get("plugins", []) or options.kwargs.get("PLUGINS", [])
|
|
37
|
+
plugins += option
|
|
38
|
+
setattr(_settings, "PLUGINS", plugins)
|
|
39
|
+
return _settings
|
|
40
|
+
|
|
41
|
+
@pytest.fixture(autouse=True, name="app")
|
|
42
|
+
def _app(self, request):
|
|
43
|
+
test_settings = self.get_settings(request)
|
|
44
|
+
self.app = create_app(settings.Defaults, override=test_settings)
|
|
45
|
+
self.app.test_client_class = TestClient
|
|
22
46
|
return self.app
|
|
23
47
|
|
|
24
48
|
def assertEqualDates(self, datetime1, datetime2, limit=1): # Seconds.
|
|
@@ -26,58 +50,16 @@ class TestCase(unittest.TestCase):
|
|
|
26
50
|
__tracebackhide__ = True
|
|
27
51
|
helpers.assert_equal_dates(datetime1, datetime2, limit=1)
|
|
28
52
|
|
|
29
|
-
|
|
30
|
-
class WebTestMixin(object):
|
|
31
|
-
user = None
|
|
32
|
-
|
|
33
|
-
@pytest.fixture(autouse=True)
|
|
34
|
-
def inject_client(self, client):
|
|
35
|
-
"""
|
|
36
|
-
Inject test client for compatibility with Flask-Testing.
|
|
37
|
-
"""
|
|
38
|
-
self.client = client
|
|
39
|
-
|
|
40
|
-
def get(self, url, **kwargs):
|
|
41
|
-
return self.client.get(url, **kwargs)
|
|
42
|
-
|
|
43
|
-
def post(self, url, data=None, **kwargs):
|
|
44
|
-
return self.client.post(url, data=data, **kwargs)
|
|
45
|
-
|
|
46
|
-
def put(self, url, data=None, **kwargs):
|
|
47
|
-
return self.client.put(url, data=data, **kwargs)
|
|
48
|
-
|
|
49
|
-
def delete(self, url, data=None, **kwargs):
|
|
50
|
-
return self.client.delete(url, data=data, **kwargs)
|
|
51
|
-
|
|
52
|
-
def assertRedirects(self, response, location, message=None):
|
|
53
|
-
"""
|
|
54
|
-
Checks if response is an HTTP redirect to the
|
|
55
|
-
given location.
|
|
56
|
-
:param response: Flask response
|
|
57
|
-
:param location: relative URL path to SERVER_NAME or an absolute URL
|
|
58
|
-
"""
|
|
59
|
-
__tracebackhide__ = True
|
|
60
|
-
helpers.assert_redirects(response, location, message=message)
|
|
61
|
-
|
|
62
|
-
def assertStatus(self, response, status_code, message=None):
|
|
63
|
-
__tracebackhide__ = True
|
|
64
|
-
helpers.assert_status(response, status_code, message=message)
|
|
65
|
-
|
|
66
|
-
def full_url(self, *args, **kwargs):
|
|
53
|
+
def assertStreamEqual(self, response1: Response, response2: Response):
|
|
67
54
|
__tracebackhide__ = True
|
|
68
|
-
|
|
55
|
+
stream1 = list(response1.iter_encoded())
|
|
56
|
+
stream2 = list(response2.iter_encoded())
|
|
57
|
+
assert stream1 == stream2
|
|
69
58
|
|
|
70
|
-
def login(self, user=None):
|
|
71
|
-
self.user = self.client.login(user)
|
|
72
|
-
return self.user
|
|
73
59
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
name = "assert{0}".format(code)
|
|
77
|
-
helper = getattr(helpers, name)
|
|
78
|
-
setattr(WebTestMixin, name, lambda s, r, h=helper: h(r))
|
|
60
|
+
class TestCase(TestCaseMixin, unittest.TestCase):
|
|
61
|
+
pass
|
|
79
62
|
|
|
80
63
|
|
|
81
|
-
|
|
82
|
-
class DBTestMixin(object):
|
|
64
|
+
class PytestOnlyTestCase(TestCaseMixin):
|
|
83
65
|
pass
|
udata/tests/api/__init__.py
CHANGED
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
from contextlib import contextmanager
|
|
2
|
+
from urllib.parse import urlparse
|
|
2
3
|
|
|
3
4
|
import pytest
|
|
4
5
|
|
|
5
|
-
from
|
|
6
|
+
from udata.mongo import db
|
|
7
|
+
from udata.mongo.document import get_all_models
|
|
8
|
+
from udata.tests import PytestOnlyTestCase, TestCase, helpers
|
|
6
9
|
|
|
7
10
|
|
|
8
11
|
@pytest.mark.usefixtures("instance_path")
|
|
9
|
-
class
|
|
12
|
+
class APITestCaseMixin:
|
|
13
|
+
"""
|
|
14
|
+
See explanation about `get`, `post` overrides in :TestClientOverride
|
|
15
|
+
|
|
16
|
+
(switch from `data` in kwargs to `data` in args to avoid doing `data=data` and default to `json=True`)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
user = None
|
|
20
|
+
|
|
21
|
+
@pytest.fixture(autouse=True)
|
|
22
|
+
def load_api_routes(self, app):
|
|
23
|
+
from udata import api, frontend
|
|
24
|
+
|
|
25
|
+
api.init_app(app)
|
|
26
|
+
frontend.init_app(app)
|
|
27
|
+
|
|
10
28
|
@pytest.fixture(autouse=True)
|
|
11
29
|
def inject_api(self, api):
|
|
12
30
|
"""
|
|
@@ -14,11 +32,22 @@ class APITestCase(FrontTestCase):
|
|
|
14
32
|
"""
|
|
15
33
|
self.api = api
|
|
16
34
|
|
|
35
|
+
@pytest.fixture(autouse=True)
|
|
36
|
+
def inject_client(self, client):
|
|
37
|
+
"""
|
|
38
|
+
Inject test client for compatibility with Flask-Testing.
|
|
39
|
+
"""
|
|
40
|
+
self.client = client
|
|
41
|
+
|
|
17
42
|
@contextmanager
|
|
18
43
|
def api_user(self, user=None):
|
|
19
44
|
with self.api.user(user) as user:
|
|
20
45
|
yield user
|
|
21
46
|
|
|
47
|
+
def login(self, user=None):
|
|
48
|
+
self.user = self.client.login(user)
|
|
49
|
+
return self.user
|
|
50
|
+
|
|
22
51
|
def get(self, url, *args, **kwargs):
|
|
23
52
|
return self.api.get(url, *args, **kwargs)
|
|
24
53
|
|
|
@@ -36,3 +65,59 @@ class APITestCase(FrontTestCase):
|
|
|
36
65
|
|
|
37
66
|
def options(self, url, data=None, *args, **kwargs):
|
|
38
67
|
return self.api.options(url, data=data, *args, **kwargs)
|
|
68
|
+
|
|
69
|
+
def assertStatus(self, response, status_code, message=None):
|
|
70
|
+
__tracebackhide__ = True
|
|
71
|
+
helpers.assert_status(response, status_code, message=message)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
for code in 200, 201, 204, 400, 401, 403, 404, 410, 500:
|
|
75
|
+
name = "assert{0}".format(code)
|
|
76
|
+
helper = getattr(helpers, name)
|
|
77
|
+
setattr(APITestCaseMixin, name, lambda s, r, h=helper: h(r))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class _CleanDBMixin:
|
|
81
|
+
"""
|
|
82
|
+
This is only for internal use. We shouldn't inherit from this mixin but
|
|
83
|
+
from `DBTestCase` or `PytestOnlyDBTestCase` (or `*APITestCase`)
|
|
84
|
+
This is temporary while we have two hierarchies.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
def drop_db(self, app):
|
|
88
|
+
"""Clear the database"""
|
|
89
|
+
parsed_url = urlparse(app.config["MONGODB_HOST"])
|
|
90
|
+
|
|
91
|
+
# drop the leading /
|
|
92
|
+
db_name = parsed_url.path[1:]
|
|
93
|
+
db.connection.drop_database(db_name)
|
|
94
|
+
|
|
95
|
+
@pytest.fixture(autouse=True)
|
|
96
|
+
def _clean_db(self, app):
|
|
97
|
+
self.drop_db(app)
|
|
98
|
+
for model in get_all_models():
|
|
99
|
+
# When dropping the database, MongoEngine will keep the collection cached inside
|
|
100
|
+
# `_collection` (in memory). This cache is used to call `ensure_indexes` only on the
|
|
101
|
+
# first call to `_get_collection()`, on subsequent calls the value inside `_collection`
|
|
102
|
+
# is returned without calling `ensure_indexes`.
|
|
103
|
+
# In tests, the first test will have a clean memory state, so MongoEngine will initialise
|
|
104
|
+
# the collection and create the indexes, then the following test, with a clean database (no indexes)
|
|
105
|
+
# will have the collection cached, so MongoEngine will never create the indexes (except if `auto_create_index_on_save`
|
|
106
|
+
# is set on the model, which may be the reason it is present on most of the big models, we may remove it?)
|
|
107
|
+
model._collection = None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class DBTestCase(_CleanDBMixin, TestCase):
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class PytestOnlyDBTestCase(_CleanDBMixin, PytestOnlyTestCase):
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class APITestCase(APITestCaseMixin, DBTestCase):
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class PytestOnlyAPITestCase(APITestCaseMixin, PytestOnlyDBTestCase):
|
|
123
|
+
pass
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import pytest
|
|
2
1
|
from flask import url_for
|
|
3
2
|
from werkzeug.test import TestResponse
|
|
4
3
|
|
|
@@ -11,12 +10,9 @@ from udata.core.topic.factories import TopicFactory
|
|
|
11
10
|
from udata.core.topic.models import Topic
|
|
12
11
|
from udata.core.user.factories import AdminFactory, UserFactory
|
|
13
12
|
from udata.mongo import db
|
|
13
|
+
from udata.tests.api import APITestCase
|
|
14
14
|
from udata.tests.helpers import assert200, assert400
|
|
15
15
|
|
|
16
|
-
pytestmark = [
|
|
17
|
-
pytest.mark.usefixtures("clean_db"),
|
|
18
|
-
]
|
|
19
|
-
|
|
20
16
|
|
|
21
17
|
class FakeDatasetActivity(Activity):
|
|
22
18
|
key = "fakeDataset"
|
|
@@ -33,26 +29,24 @@ class FakeTopicActivity(Activity):
|
|
|
33
29
|
related_to = db.ReferenceField(Topic, required=True)
|
|
34
30
|
|
|
35
31
|
|
|
36
|
-
class ActivityAPITest:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def test_activity_api_list(self, api) -> None:
|
|
32
|
+
class ActivityAPITest(APITestCase):
|
|
33
|
+
def test_activity_api_list(self) -> None:
|
|
40
34
|
"""It should fetch an activity list from the API"""
|
|
41
35
|
activities: list[Activity] = [
|
|
42
36
|
FakeDatasetActivity.objects.create(actor=UserFactory(), related_to=DatasetFactory()),
|
|
43
37
|
FakeReuseActivity.objects.create(actor=UserFactory(), related_to=ReuseFactory()),
|
|
44
38
|
]
|
|
45
39
|
|
|
46
|
-
response: TestResponse =
|
|
40
|
+
response: TestResponse = self.get(url_for("api.activity"))
|
|
47
41
|
assert200(response)
|
|
48
42
|
assert len(response.json["data"]) == len(activities)
|
|
49
43
|
|
|
50
|
-
def test_activity_api_list_filter_by_bogus_related_to(self
|
|
44
|
+
def test_activity_api_list_filter_by_bogus_related_to(self) -> None:
|
|
51
45
|
"""It should return a 400 error if the `related_to` parameter isn't a valid ObjectId."""
|
|
52
|
-
response: TestResponse =
|
|
46
|
+
response: TestResponse = self.get(url_for("api.activity", related_to="foobar"))
|
|
53
47
|
assert400(response)
|
|
54
48
|
|
|
55
|
-
def test_activity_api_list_filtered_by_related_to(self
|
|
49
|
+
def test_activity_api_list_filtered_by_related_to(self) -> None:
|
|
56
50
|
"""It should only return activities that correspond to the `related_to` parameter."""
|
|
57
51
|
dataset1: Dataset = DatasetFactory()
|
|
58
52
|
dataset2: Dataset = DatasetFactory()
|
|
@@ -64,18 +58,18 @@ class ActivityAPITest:
|
|
|
64
58
|
FakeReuseActivity.objects.create(actor=UserFactory(), related_to=reuse),
|
|
65
59
|
]
|
|
66
60
|
|
|
67
|
-
response: TestResponse =
|
|
61
|
+
response: TestResponse = self.get(url_for("api.activity", related_to=dataset1.id))
|
|
68
62
|
assert200(response)
|
|
69
63
|
len(response.json["data"]) == 2
|
|
70
64
|
assert response.json["data"][0]["related_to"] == dataset1.title
|
|
71
65
|
assert response.json["data"][1]["related_to"] == dataset1.title
|
|
72
66
|
|
|
73
|
-
response: TestResponse =
|
|
67
|
+
response: TestResponse = self.get(url_for("api.activity", related_to=reuse.id))
|
|
74
68
|
assert200(response)
|
|
75
69
|
len(response.json["data"]) == 1
|
|
76
70
|
assert response.json["data"][0]["related_to"] == reuse.title
|
|
77
71
|
|
|
78
|
-
def test_activity_api_list_with_private(self
|
|
72
|
+
def test_activity_api_list_with_private(self) -> None:
|
|
79
73
|
"""It should fetch an activity list from the API"""
|
|
80
74
|
activities: list[Activity] = [
|
|
81
75
|
FakeDatasetActivity.objects.create(
|
|
@@ -87,28 +81,28 @@ class ActivityAPITest:
|
|
|
87
81
|
]
|
|
88
82
|
|
|
89
83
|
# Anonymised user won't see activities about private documents
|
|
90
|
-
response: TestResponse =
|
|
84
|
+
response: TestResponse = self.get(url_for("api.activity"))
|
|
91
85
|
assert200(response)
|
|
92
86
|
assert len(response.json["data"]) == 0
|
|
93
87
|
|
|
94
88
|
# Lambda user won't see activities about private documents
|
|
95
|
-
|
|
96
|
-
response: TestResponse =
|
|
89
|
+
self.login()
|
|
90
|
+
response: TestResponse = self.get(url_for("api.activity"))
|
|
97
91
|
assert200(response)
|
|
98
92
|
assert len(response.json["data"]) == 0
|
|
99
93
|
|
|
100
94
|
# Sysadmin user will see activities about private documents
|
|
101
|
-
|
|
102
|
-
response: TestResponse =
|
|
95
|
+
self.login(AdminFactory())
|
|
96
|
+
response: TestResponse = self.get(url_for("api.activity"))
|
|
103
97
|
assert200(response)
|
|
104
98
|
assert len(response.json["data"]) == len(activities)
|
|
105
99
|
|
|
106
|
-
def test_activity_api_with_topic(self
|
|
100
|
+
def test_activity_api_with_topic(self) -> None:
|
|
107
101
|
"""It should fetch topic activities from the API"""
|
|
108
102
|
topic: Topic = TopicFactory()
|
|
109
103
|
FakeTopicActivity.objects.create(actor=UserFactory(), related_to=topic)
|
|
110
104
|
|
|
111
|
-
response: TestResponse =
|
|
105
|
+
response: TestResponse = self.get(url_for("api.activity"))
|
|
112
106
|
assert200(response)
|
|
113
107
|
assert len(response.json["data"]) == 1
|
|
114
108
|
|
udata/tests/api/test_auth_api.py
CHANGED
|
@@ -14,6 +14,7 @@ from udata.api.oauth2 import OAuth2Client, OAuth2Token
|
|
|
14
14
|
from udata.auth import PermissionDenied
|
|
15
15
|
from udata.core.user.factories import UserFactory
|
|
16
16
|
from udata.forms import Form, fields, validators
|
|
17
|
+
from udata.tests.api import PytestOnlyAPITestCase
|
|
17
18
|
from udata.tests.helpers import (
|
|
18
19
|
assert200,
|
|
19
20
|
assert400,
|
|
@@ -65,10 +66,7 @@ def oauth(app, request):
|
|
|
65
66
|
return OAuth2Client.objects.create(**kwargs)
|
|
66
67
|
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
class APIAuthTest:
|
|
70
|
-
modules = []
|
|
71
|
-
|
|
69
|
+
class APIAuthTest(PytestOnlyAPITestCase):
|
|
72
70
|
def test_no_auth(self, api):
|
|
73
71
|
"""Should not return a content type if there is no content on delete"""
|
|
74
72
|
response = api.get(url_for("api.fake"))
|