udata 12.0.2.dev10__py3-none-any.whl → 13.0.1.dev21__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.

Files changed (272) hide show
  1. udata/api/__init__.py +1 -0
  2. udata/api_fields.py +10 -4
  3. udata/app.py +11 -10
  4. udata/auth/__init__.py +9 -10
  5. udata/auth/mails.py +137 -45
  6. udata/auth/views.py +5 -12
  7. udata/commands/__init__.py +2 -4
  8. udata/commands/info.py +1 -3
  9. udata/commands/tests/test_fixtures.py +6 -3
  10. udata/core/access_type/api.py +18 -0
  11. udata/core/access_type/constants.py +98 -0
  12. udata/core/access_type/models.py +44 -0
  13. udata/core/activity/models.py +1 -1
  14. udata/core/badges/models.py +1 -1
  15. udata/core/badges/tasks.py +35 -1
  16. udata/core/badges/tests/test_commands.py +2 -4
  17. udata/core/badges/tests/test_model.py +2 -2
  18. udata/core/badges/tests/test_tasks.py +55 -0
  19. udata/core/constants.py +1 -0
  20. udata/core/contact_point/models.py +8 -0
  21. udata/core/dataservices/api.py +10 -12
  22. udata/core/dataservices/apiv2.py +3 -1
  23. udata/core/dataservices/constants.py +0 -29
  24. udata/core/dataservices/models.py +44 -44
  25. udata/core/dataservices/rdf.py +2 -1
  26. udata/core/dataservices/search.py +5 -9
  27. udata/core/dataservices/tasks.py +33 -0
  28. udata/core/dataset/api.py +15 -24
  29. udata/core/dataset/api_fields.py +11 -0
  30. udata/core/dataset/apiv2.py +11 -0
  31. udata/core/dataset/constants.py +0 -1
  32. udata/core/dataset/forms.py +29 -0
  33. udata/core/dataset/models.py +24 -42
  34. udata/core/dataset/rdf.py +2 -1
  35. udata/core/dataset/search.py +2 -2
  36. udata/core/dataset/tasks.py +86 -8
  37. udata/core/discussions/mails.py +63 -0
  38. udata/core/discussions/tasks.py +4 -18
  39. udata/core/metrics/__init__.py +0 -6
  40. udata/core/organization/api.py +20 -14
  41. udata/core/organization/mails.py +144 -0
  42. udata/core/organization/models.py +2 -1
  43. udata/core/organization/rdf.py +3 -3
  44. udata/core/organization/search.py +1 -1
  45. udata/core/organization/tasks.py +21 -49
  46. udata/core/pages/tests/test_api.py +0 -2
  47. udata/core/reuse/api.py +29 -3
  48. udata/core/reuse/mails.py +21 -0
  49. udata/core/reuse/models.py +10 -1
  50. udata/core/reuse/search.py +1 -1
  51. udata/core/reuse/tasks.py +2 -3
  52. udata/core/site/api.py +27 -19
  53. udata/core/site/models.py +2 -6
  54. udata/core/site/rdf.py +2 -2
  55. udata/core/spatial/tests/test_api.py +17 -20
  56. udata/core/spatial/tests/test_models.py +3 -3
  57. udata/core/user/mails.py +54 -0
  58. udata/core/user/models.py +2 -3
  59. udata/core/user/tasks.py +8 -23
  60. udata/core/user/tests/test_user_model.py +2 -6
  61. udata/entrypoints.py +0 -6
  62. udata/features/identicon/tests/test_backends.py +3 -13
  63. udata/forms/fields.py +3 -3
  64. udata/forms/widgets.py +2 -2
  65. udata/frontend/__init__.py +3 -32
  66. udata/harvest/actions.py +4 -9
  67. udata/harvest/api.py +5 -14
  68. udata/harvest/backends/__init__.py +20 -11
  69. udata/harvest/backends/base.py +2 -2
  70. udata/harvest/backends/ckan/harvesters.py +2 -1
  71. udata/harvest/backends/dcat.py +3 -0
  72. udata/harvest/backends/maaf.py +1 -0
  73. udata/harvest/commands.py +6 -4
  74. udata/harvest/forms.py +9 -6
  75. udata/harvest/tasks.py +3 -5
  76. udata/harvest/tests/ckan/test_ckan_backend.py +300 -337
  77. udata/harvest/tests/ckan/test_ckan_backend_errors.py +94 -99
  78. udata/harvest/tests/ckan/test_ckan_backend_filters.py +128 -122
  79. udata/harvest/tests/ckan/test_dkan_backend.py +39 -51
  80. udata/harvest/tests/dcat/bnodes.xml +17 -1
  81. udata/harvest/tests/dcat/datara--5a26b0f6-0ccf-46ad-ac58-734054b91977.rdf.xml +255 -0
  82. udata/harvest/tests/dcat/datara--f40c3860-7236-4b30-a141-23b8ae33f7b2.rdf.xml +289 -0
  83. udata/harvest/tests/factories.py +1 -1
  84. udata/harvest/tests/test_actions.py +11 -9
  85. udata/harvest/tests/test_api.py +4 -5
  86. udata/harvest/tests/test_base_backend.py +5 -4
  87. udata/harvest/tests/test_dcat_backend.py +72 -16
  88. udata/harvest/tests/test_models.py +2 -4
  89. udata/harvest/tests/test_notifications.py +2 -4
  90. udata/harvest/tests/test_tasks.py +2 -3
  91. udata/mail.py +90 -53
  92. udata/migrations/2025-01-05-dataservices-fields-changes.py +8 -14
  93. udata/migrations/2025-10-21-remove-ckan-harvest-modified-at.py +28 -0
  94. udata/migrations/2025-10-29-harvesters-sources-integrity.py +27 -0
  95. udata/models/__init__.py +0 -2
  96. udata/mongo/extras_fields.py +4 -3
  97. udata/mongo/taglist_field.py +3 -3
  98. udata/rdf.py +65 -20
  99. udata/sentry.py +3 -4
  100. udata/settings.py +15 -13
  101. udata/tags.py +5 -5
  102. udata/tasks.py +3 -3
  103. udata/templates/mail/message.html +65 -0
  104. udata/templates/mail/message.txt +16 -0
  105. udata/tests/__init__.py +40 -58
  106. udata/tests/api/__init__.py +87 -2
  107. udata/tests/api/test_activities_api.py +17 -23
  108. udata/tests/api/test_auth_api.py +2 -4
  109. udata/tests/api/test_contact_points.py +48 -54
  110. udata/tests/api/test_dataservices_api.py +65 -97
  111. udata/tests/api/test_datasets_api.py +171 -56
  112. udata/tests/api/test_me_api.py +4 -6
  113. udata/tests/api/test_organizations_api.py +19 -38
  114. udata/tests/api/test_reports_api.py +0 -4
  115. udata/tests/api/test_reuses_api.py +99 -23
  116. udata/tests/api/test_security_api.py +124 -0
  117. udata/tests/api/test_swagger.py +2 -3
  118. udata/tests/api/test_tags_api.py +6 -7
  119. udata/tests/api/test_transfer_api.py +0 -2
  120. udata/tests/api/test_user_api.py +8 -10
  121. udata/tests/apiv2/test_datasets.py +0 -4
  122. udata/tests/apiv2/test_me_api.py +0 -2
  123. udata/tests/apiv2/test_organizations.py +0 -2
  124. udata/tests/apiv2/test_swagger.py +2 -3
  125. udata/tests/apiv2/test_topics.py +0 -2
  126. udata/tests/cli/test_cli_base.py +14 -12
  127. udata/tests/cli/test_db_cli.py +51 -54
  128. udata/tests/contact_point/test_contact_point_models.py +2 -2
  129. udata/tests/dataservice/test_csv_adapter.py +2 -5
  130. udata/tests/dataservice/test_dataservice_rdf.py +64 -4
  131. udata/tests/dataservice/test_dataservice_tasks.py +36 -38
  132. udata/tests/dataset/test_csv_adapter.py +2 -5
  133. udata/tests/dataset/test_dataset_actions.py +2 -4
  134. udata/tests/dataset/test_dataset_commands.py +2 -4
  135. udata/tests/dataset/test_dataset_events.py +3 -3
  136. udata/tests/dataset/test_dataset_model.py +6 -7
  137. udata/tests/dataset/test_dataset_rdf.py +205 -16
  138. udata/tests/dataset/test_dataset_recommendations.py +2 -2
  139. udata/tests/dataset/test_dataset_tasks.py +66 -68
  140. udata/tests/dataset/test_resource_preview.py +39 -48
  141. udata/tests/dataset/test_transport_tasks.py +2 -2
  142. udata/tests/features/territories/__init__.py +0 -6
  143. udata/tests/features/territories/test_territories_api.py +25 -24
  144. udata/tests/forms/test_current_user_field.py +2 -2
  145. udata/tests/forms/test_dict_field.py +2 -4
  146. udata/tests/forms/test_extras_fields.py +2 -3
  147. udata/tests/forms/test_image_field.py +2 -2
  148. udata/tests/forms/test_model_field.py +2 -4
  149. udata/tests/forms/test_publish_as_field.py +2 -4
  150. udata/tests/forms/test_user_forms.py +26 -29
  151. udata/tests/frontend/test_auth.py +2 -3
  152. udata/tests/frontend/test_csv.py +5 -6
  153. udata/tests/frontend/test_error_handlers.py +2 -3
  154. udata/tests/frontend/test_hooks.py +5 -7
  155. udata/tests/frontend/test_markdown.py +3 -4
  156. udata/tests/helpers.py +2 -7
  157. udata/tests/metrics/test_metrics.py +52 -48
  158. udata/tests/metrics/test_tasks.py +154 -150
  159. udata/tests/organization/test_csv_adapter.py +2 -5
  160. udata/tests/organization/test_notifications.py +2 -4
  161. udata/tests/organization/test_organization_model.py +3 -4
  162. udata/tests/organization/test_organization_rdf.py +6 -12
  163. udata/tests/plugin.py +6 -110
  164. udata/tests/reuse/test_reuse_model.py +3 -4
  165. udata/tests/site/test_site_api.py +0 -2
  166. udata/tests/site/test_site_csv_exports.py +0 -2
  167. udata/tests/site/test_site_metrics.py +2 -4
  168. udata/tests/site/test_site_model.py +2 -2
  169. udata/tests/site/test_site_rdf.py +85 -29
  170. udata/tests/test_activity.py +3 -3
  171. udata/tests/test_api_fields.py +6 -9
  172. udata/tests/test_cors.py +0 -2
  173. udata/tests/test_dcat_commands.py +2 -3
  174. udata/tests/test_discussions.py +2 -7
  175. udata/tests/test_mail.py +150 -114
  176. udata/tests/test_migrations.py +413 -419
  177. udata/tests/test_model.py +10 -11
  178. udata/tests/test_notifications.py +2 -3
  179. udata/tests/test_owned.py +3 -3
  180. udata/tests/test_rdf.py +19 -15
  181. udata/tests/test_routing.py +5 -5
  182. udata/tests/test_storages.py +6 -5
  183. udata/tests/test_tags.py +2 -4
  184. udata/tests/test_topics.py +2 -4
  185. udata/tests/test_transfer.py +4 -5
  186. udata/tests/topic/test_topic_tasks.py +25 -27
  187. udata/tests/user/test_user_rdf.py +2 -8
  188. udata/tests/user/test_user_tasks.py +3 -5
  189. udata/tests/workers/test_jobs_commands.py +2 -2
  190. udata/tests/workers/test_tasks_routing.py +27 -27
  191. udata/translations/ar/LC_MESSAGES/udata.mo +0 -0
  192. udata/translations/ar/LC_MESSAGES/udata.po +369 -435
  193. udata/translations/de/LC_MESSAGES/udata.mo +0 -0
  194. udata/translations/de/LC_MESSAGES/udata.po +371 -437
  195. udata/translations/es/LC_MESSAGES/udata.mo +0 -0
  196. udata/translations/es/LC_MESSAGES/udata.po +369 -435
  197. udata/translations/fr/LC_MESSAGES/udata.mo +0 -0
  198. udata/translations/fr/LC_MESSAGES/udata.po +381 -447
  199. udata/translations/it/LC_MESSAGES/udata.mo +0 -0
  200. udata/translations/it/LC_MESSAGES/udata.po +371 -437
  201. udata/translations/pt/LC_MESSAGES/udata.mo +0 -0
  202. udata/translations/pt/LC_MESSAGES/udata.po +371 -437
  203. udata/translations/sr/LC_MESSAGES/udata.mo +0 -0
  204. udata/translations/sr/LC_MESSAGES/udata.po +372 -438
  205. udata/translations/udata.pot +379 -440
  206. udata/utils.py +66 -4
  207. {udata-12.0.2.dev10.dist-info → udata-13.0.1.dev21.dist-info}/METADATA +1 -4
  208. {udata-12.0.2.dev10.dist-info → udata-13.0.1.dev21.dist-info}/RECORD +212 -256
  209. udata/linkchecker/__init__.py +0 -0
  210. udata/linkchecker/backends.py +0 -31
  211. udata/linkchecker/checker.py +0 -75
  212. udata/linkchecker/commands.py +0 -21
  213. udata/linkchecker/models.py +0 -9
  214. udata/linkchecker/tasks.py +0 -55
  215. udata/templates/mail/account_deleted.html +0 -5
  216. udata/templates/mail/account_deleted.txt +0 -6
  217. udata/templates/mail/account_inactivity.html +0 -40
  218. udata/templates/mail/account_inactivity.txt +0 -31
  219. udata/templates/mail/badge_added_association.html +0 -33
  220. udata/templates/mail/badge_added_association.txt +0 -11
  221. udata/templates/mail/badge_added_certified.html +0 -33
  222. udata/templates/mail/badge_added_certified.txt +0 -11
  223. udata/templates/mail/badge_added_company.html +0 -33
  224. udata/templates/mail/badge_added_company.txt +0 -11
  225. udata/templates/mail/badge_added_local_authority.html +0 -33
  226. udata/templates/mail/badge_added_local_authority.txt +0 -11
  227. udata/templates/mail/badge_added_public_service.html +0 -33
  228. udata/templates/mail/badge_added_public_service.txt +0 -11
  229. udata/templates/mail/discussion_closed.html +0 -47
  230. udata/templates/mail/discussion_closed.txt +0 -16
  231. udata/templates/mail/inactive_account_deleted.html +0 -5
  232. udata/templates/mail/inactive_account_deleted.txt +0 -6
  233. udata/templates/mail/membership_refused.html +0 -20
  234. udata/templates/mail/membership_refused.txt +0 -11
  235. udata/templates/mail/membership_request.html +0 -46
  236. udata/templates/mail/membership_request.txt +0 -12
  237. udata/templates/mail/new_discussion.html +0 -44
  238. udata/templates/mail/new_discussion.txt +0 -15
  239. udata/templates/mail/new_discussion_comment.html +0 -45
  240. udata/templates/mail/new_discussion_comment.txt +0 -16
  241. udata/templates/mail/new_member.html +0 -27
  242. udata/templates/mail/new_member.txt +0 -11
  243. udata/templates/mail/new_reuse.html +0 -37
  244. udata/templates/mail/new_reuse.txt +0 -9
  245. udata/templates/mail/test.html +0 -6
  246. udata/templates/mail/test.txt +0 -6
  247. udata/templates/mail/user_mail_card.html +0 -26
  248. udata/templates/security/email/base.html +0 -105
  249. udata/templates/security/email/base.txt +0 -6
  250. udata/templates/security/email/button.html +0 -3
  251. udata/templates/security/email/change_notice.html +0 -22
  252. udata/templates/security/email/change_notice.txt +0 -8
  253. udata/templates/security/email/confirmation_instructions.html +0 -20
  254. udata/templates/security/email/confirmation_instructions.txt +0 -7
  255. udata/templates/security/email/login_instructions.html +0 -19
  256. udata/templates/security/email/login_instructions.txt +0 -7
  257. udata/templates/security/email/reset_instructions.html +0 -24
  258. udata/templates/security/email/reset_instructions.txt +0 -9
  259. udata/templates/security/email/reset_notice.html +0 -11
  260. udata/templates/security/email/reset_notice.txt +0 -4
  261. udata/templates/security/email/welcome.html +0 -24
  262. udata/templates/security/email/welcome.txt +0 -9
  263. udata/templates/security/email/welcome_existing.html +0 -32
  264. udata/templates/security/email/welcome_existing.txt +0 -14
  265. udata/terms.md +0 -6
  266. udata/tests/frontend/__init__.py +0 -23
  267. udata/tests/metrics/conftest.py +0 -15
  268. udata/tests/test_linkchecker.py +0 -277
  269. {udata-12.0.2.dev10.dist-info → udata-13.0.1.dev21.dist-info}/WHEEL +0 -0
  270. {udata-12.0.2.dev10.dist-info → udata-13.0.1.dev21.dist-info}/entry_points.txt +0 -0
  271. {udata-12.0.2.dev10.dist-info → udata-13.0.1.dev21.dist-info}/licenses/LICENSE +0 -0
  272. {udata-12.0.2.dev10.dist-info → udata-13.0.1.dev21.dist-info}/top_level.txt +0 -0
@@ -7,13 +7,14 @@ from werkzeug.test import TestResponse
7
7
 
8
8
  import udata.core.organization.constants as org_constants
9
9
  from udata.core.badges.factories import badge_factory
10
+ from udata.core.dataservices.factories import DataserviceFactory
10
11
  from udata.core.dataset.factories import DatasetFactory
11
12
  from udata.core.organization.factories import OrganizationFactory
12
13
  from udata.core.reuse.constants import REUSE_TOPICS, REUSE_TYPES
13
14
  from udata.core.reuse.factories import ReuseFactory
14
15
  from udata.core.user.factories import AdminFactory, UserFactory
15
16
  from udata.models import Follow, Member, Reuse
16
- from udata.tests.api import APITestCase
17
+ from udata.tests.api import APITestCase, PytestOnlyAPITestCase
17
18
  from udata.tests.helpers import (
18
19
  assert200,
19
20
  assert201,
@@ -24,19 +25,13 @@ from udata.tests.helpers import (
24
25
  )
25
26
  from udata.utils import faker
26
27
 
27
- pytestmark = [
28
- pytest.mark.usefixtures("clean_db"),
29
- ]
30
-
31
28
 
32
29
  def reuse_in_response(response: TestResponse, reuse: Reuse) -> bool:
33
30
  only_reuse = [r for r in response.json["data"] if r["id"] == str(reuse.id)]
34
31
  return len(only_reuse) > 0
35
32
 
36
33
 
37
- class ReuseAPITest:
38
- modules = []
39
-
34
+ class ReuseAPITest(PytestOnlyAPITestCase):
40
35
  def test_reuse_api_list(self, api):
41
36
  """It should fetch a reuse list from the API"""
42
37
  reuses = ReuseFactory.create_batch(3, visible=True)
@@ -381,6 +376,22 @@ class ReuseAPITest:
381
376
  response = api.delete(url_for("api.reuse", reuse=reuse))
382
377
  assert410(response)
383
378
 
379
+ def test_reuse_api_filter_by_dataset(self, api):
380
+ user = api.login()
381
+ dataset = DatasetFactory()
382
+ other_dataset = DatasetFactory()
383
+ ReuseFactory(owner=user, datasets=[dataset])
384
+
385
+ response = api.get(url_for("api.reuses", dataset=dataset.id))
386
+ assert200(response)
387
+ assert response.json["total"] == 1
388
+ assert len(response.json["data"][0]["datasets"]) == 1
389
+ assert response.json["data"][0]["datasets"][0]["title"] == dataset.title
390
+
391
+ response = api.get(url_for("api.reuses", dataset=other_dataset.id))
392
+ assert200(response)
393
+ assert response.json["total"] == 0
394
+
384
395
  def test_reuse_api_add_dataset(self, api):
385
396
  """It should add a dataset to a reuse from the API"""
386
397
  user = api.login()
@@ -431,6 +442,72 @@ class ReuseAPITest:
431
442
  reuse.reload()
432
443
  assert len(reuse.datasets) == 0
433
444
 
445
+ def test_reuse_api_filter_by_dataservice(self, api):
446
+ user = api.login()
447
+ dataservice = DataserviceFactory()
448
+ other_dataservice = DataserviceFactory()
449
+ ReuseFactory(owner=user, dataservices=[dataservice])
450
+
451
+ response = api.get(url_for("api.reuses", dataservice=dataservice.id))
452
+ assert200(response)
453
+ assert response.json["total"] == 1
454
+ assert len(response.json["data"][0]["dataservices"]) == 1
455
+ assert response.json["data"][0]["dataservices"][0]["title"] == dataservice.title
456
+
457
+ response = api.get(url_for("api.reuses", dataservice=other_dataservice.id))
458
+ assert200(response)
459
+ assert response.json["total"] == 0
460
+
461
+ def test_reuse_api_add_dataservice(self, api):
462
+ """It should add a dataset to a reuse from the API"""
463
+ user = api.login()
464
+ reuse = ReuseFactory(owner=user)
465
+
466
+ dataservice = DataserviceFactory()
467
+ data = {"id": dataservice.id, "class": "Dataservice"}
468
+ url = url_for("api.reuse_add_dataservice", reuse=reuse)
469
+ response = api.post(url, data)
470
+ assert201(response)
471
+ reuse.reload()
472
+ assert len(reuse.dataservices) == 1
473
+ assert reuse.dataservices[-1] == dataservice
474
+
475
+ dataservice = DataserviceFactory()
476
+ data = {"id": dataservice.id, "class": "dataservice"}
477
+ url = url_for("api.reuse_add_dataservice", reuse=reuse)
478
+ response = api.post(url, data)
479
+ assert201(response)
480
+ reuse.reload()
481
+ assert len(reuse.dataservices) == 2
482
+ assert reuse.dataservices[-1] == dataservice
483
+
484
+ def test_reuse_api_add_dataservice_twice(self, api):
485
+ """It should not add twice a dataservice to a reuse from the API"""
486
+ user = api.login()
487
+ dataservice = DataserviceFactory()
488
+ reuse = ReuseFactory(owner=user, dataservices=[dataservice])
489
+
490
+ data = {"id": dataservice.id, "class": "Dataservice"}
491
+ url = url_for("api.reuse_add_dataservice", reuse=reuse)
492
+ response = api.post(url, data)
493
+ assert200(response)
494
+ reuse.reload()
495
+ assert len(reuse.dataservices) == 1
496
+ assert reuse.dataservices[-1] == dataservice
497
+
498
+ def test_reuse_api_add_dataservice_not_found(self, api):
499
+ """It should return 404 when adding an unknown dataservice to a reuse"""
500
+ user = api.login()
501
+ reuse = ReuseFactory(owner=user)
502
+
503
+ data = {"id": "not-found", "class": "Dataservice"}
504
+ url = url_for("api.reuse_add_dataservice", reuse=reuse)
505
+ response = api.post(url, data)
506
+
507
+ assert404(response)
508
+ reuse.reload()
509
+ assert len(reuse.dataservices) == 0
510
+
434
511
  def test_reuse_api_feature(self, api):
435
512
  """It should mark the reuse featured on POST"""
436
513
  reuse = ReuseFactory(featured=False)
@@ -523,7 +600,7 @@ class ReuseAPITest:
523
600
  title="arealtestprefix-4", visible=True, metrics={"followers": 10}
524
601
  )
525
602
 
526
- response = api.get(url_for("api.suggest_reuses"), qs={"q": "arealtestpref", "size": "5"})
603
+ response = api.get(url_for("api.suggest_reuses", q="arealtestpref", size=5))
527
604
  assert200(response)
528
605
 
529
606
  assert len(response.json) <= 5
@@ -542,7 +619,7 @@ class ReuseAPITest:
542
619
  for i in range(4):
543
620
  ReuseFactory(title="testé-{0}".format(i) if i % 2 else faker.word(), visible=True)
544
621
 
545
- response = api.get(url_for("api.suggest_reuses"), qs={"q": "testé", "size": "5"})
622
+ response = api.get(url_for("api.suggest_reuses", q="testé", size=5))
546
623
  assert200(response)
547
624
 
548
625
  assert len(response.json) <= 5
@@ -559,20 +636,22 @@ class ReuseAPITest:
559
636
  """It should not provide reuse suggestion if no match"""
560
637
  ReuseFactory.create_batch(3, visible=True)
561
638
 
562
- response = api.get(url_for("api.suggest_reuses"), qs={"q": "xxxxxx", "size": "5"})
639
+ response = api.get(url_for("api.suggest_reuses", q="xxxxxx", size=5))
563
640
  assert200(response)
564
641
  assert len(response.json) == 0
565
642
 
566
643
  def test_suggest_reuses_api_empty(self, api):
567
644
  """It should not provide reuse suggestion if no data"""
568
645
  # self.init_search()
569
- response = api.get(url_for("api.suggest_reuses"), qs={"q": "xxxxxx", "size": "5"})
646
+ response = api.get(url_for("api.suggest_reuses", q="xxxxxx", size=5))
570
647
  assert200(response)
571
648
  assert len(response.json) == 0
572
649
 
573
650
 
574
651
  class ReusesFeedAPItest(APITestCase):
652
+ @pytest.mark.options(DELAY_BEFORE_APPEARING_IN_RSS_FEED=10)
575
653
  def test_recent_feed(self):
654
+ # We have a 10 hours delay for a new object to appear in feed. A newly created one shouldn't appear.
576
655
  ReuseFactory(title="A", datasets=[DatasetFactory()], created_at=datetime.utcnow())
577
656
  ReuseFactory(
578
657
  title="B", datasets=[DatasetFactory()], created_at=datetime.utcnow() - timedelta(days=2)
@@ -586,11 +665,11 @@ class ReusesFeedAPItest(APITestCase):
586
665
 
587
666
  feed = feedparser.parse(response.data)
588
667
 
589
- self.assertEqual(len(feed.entries), 3)
590
- self.assertEqual(feed.entries[0].title, "A")
591
- self.assertEqual(feed.entries[1].title, "C")
592
- self.assertEqual(feed.entries[2].title, "B")
668
+ self.assertEqual(len(feed.entries), 2)
669
+ self.assertEqual(feed.entries[0].title, "C")
670
+ self.assertEqual(feed.entries[1].title, "B")
593
671
 
672
+ @pytest.mark.options(DELAY_BEFORE_APPEARING_IN_RSS_FEED=0)
594
673
  def test_recent_feed_owner(self):
595
674
  owner = UserFactory()
596
675
  ReuseFactory(owner=owner, datasets=[DatasetFactory()])
@@ -608,6 +687,7 @@ class ReusesFeedAPItest(APITestCase):
608
687
  self.assertEqual(author.name, owner.fullname)
609
688
  self.assertEqual(author.href, owner.url_for())
610
689
 
690
+ @pytest.mark.options(DELAY_BEFORE_APPEARING_IN_RSS_FEED=0)
611
691
  def test_recent_feed_org(self):
612
692
  owner = UserFactory()
613
693
  org = OrganizationFactory()
@@ -627,11 +707,9 @@ class ReusesFeedAPItest(APITestCase):
627
707
  self.assertEqual(author.href, org.url_for())
628
708
 
629
709
 
630
- class ReuseBadgeAPITest:
631
- modules = []
632
-
710
+ class ReuseBadgeAPITest(PytestOnlyAPITestCase):
633
711
  @pytest.fixture(autouse=True)
634
- def setup(self, api, clean_db):
712
+ def setup_func(self, api):
635
713
  # Register at least two badges
636
714
  Reuse.__badges__["test-1"] = "Test 1"
637
715
  Reuse.__badges__["test-2"] = "Test 2"
@@ -692,9 +770,7 @@ class ReuseBadgeAPITest:
692
770
  assert404(response)
693
771
 
694
772
 
695
- class ReuseReferencesAPITest:
696
- modules = []
697
-
773
+ class ReuseReferencesAPITest(PytestOnlyAPITestCase):
698
774
  def test_reuse_types_list(self, api):
699
775
  """It should fetch the reuse types list from the API"""
700
776
  response = api.get(url_for("api.reuse_types"))
@@ -0,0 +1,124 @@
1
+ from datetime import datetime
2
+
3
+ import pytest
4
+ from flask import url_for
5
+ from flask_security.recoverable import generate_reset_password_token
6
+
7
+ from udata.commands.fixtures import UserFactory
8
+ from udata.i18n import lazy_gettext as _
9
+ from udata.tests.api import PytestOnlyAPITestCase
10
+ from udata.tests.helpers import capture_mails
11
+
12
+
13
+ class SecurityAPITest(PytestOnlyAPITestCase):
14
+ @pytest.mark.options(CAPTCHETAT_BASE_URL=None)
15
+ def test_register(self, api):
16
+ # We cannot test for mail sending since they are sent with Flask
17
+ # directly and not with our system but if the sending is working
18
+ # we test the rendering of the mail.
19
+
20
+ response = api.post(
21
+ url_for("security.register"),
22
+ {
23
+ "first_name": "Jane",
24
+ "last_name": "Doe",
25
+ "accept_conditions": True,
26
+ "email": "jane@example.org",
27
+ "password": "Password123",
28
+ "password_confirm": "Password123",
29
+ "submit": True,
30
+ },
31
+ )
32
+ self.assertStatus(response, 200)
33
+
34
+ @pytest.mark.options(CAPTCHETAT_BASE_URL=None, SECURITY_RETURN_GENERIC_RESPONSES=True)
35
+ def test_register_existing(self, api):
36
+ # We cannot test for mail sending since they are sent with Flask
37
+ # directly and not with our system but if the sending is working
38
+ # we test the rendering of the mail.
39
+
40
+ UserFactory(email="jane@example.org", confirmed_at=datetime.now())
41
+ response = api.post(
42
+ url_for("security.register"),
43
+ {
44
+ "first_name": "Jane",
45
+ "last_name": "Doe",
46
+ "accept_conditions": True,
47
+ "email": "jane@example.org",
48
+ "password": "Password123",
49
+ "password_confirm": "Password123",
50
+ "submit": True,
51
+ },
52
+ )
53
+ self.assertStatus(response, 200)
54
+
55
+ @pytest.mark.options(CAPTCHETAT_BASE_URL=None)
56
+ def test_ask_for_reset(self, api):
57
+ # We cannot test for mail sending since they are sent with Flask
58
+ # directly and not with our system but if the sending is working
59
+ # we test the rendering of the mail.
60
+
61
+ UserFactory(email="jane@example.org", confirmed_at=datetime.now())
62
+
63
+ response = api.post(
64
+ url_for("security.forgot_password"), {"email": "jane@example.org", "submit": True}
65
+ )
66
+ self.assertStatus(response, 200)
67
+
68
+ @pytest.mark.options(CAPTCHETAT_BASE_URL=None)
69
+ def test_change_notice_mail(self, api):
70
+ # We cannot test for mail sending since they are sent with Flask
71
+ # directly and not with our system but if the sending is working
72
+ # we test the rendering of the mail.
73
+
74
+ user = UserFactory(
75
+ email="jane@example.org", password="password", confirmed_at=datetime.now()
76
+ )
77
+ self.login(user)
78
+
79
+ response = api.post(
80
+ url_for("security.change_password"),
81
+ {
82
+ "password": "password",
83
+ "new_password": "New_password123",
84
+ "new_password_confirm": "New_password123",
85
+ "submit": True,
86
+ },
87
+ )
88
+ self.assertStatus(response, 200)
89
+
90
+ @pytest.mark.options(CAPTCHETAT_BASE_URL=None)
91
+ def test_change_email_confirmation(self, api):
92
+ user = UserFactory(email="jane@example.org", confirmed_at=datetime.now())
93
+ self.login(user)
94
+
95
+ with capture_mails() as mails:
96
+ response = api.post(
97
+ url_for("security.change_email"),
98
+ {
99
+ "new_email": "jane2@example.org",
100
+ "new_email_confirm": "jane2@example.org",
101
+ "submit": True,
102
+ },
103
+ )
104
+ self.assertStatus(response, 200)
105
+
106
+ assert len(mails) == 1
107
+ assert len(mails[0].recipients) == 1
108
+ assert mails[0].recipients[0] == "jane2@example.org"
109
+ assert mails[0].subject == _("Confirm your email address")
110
+
111
+ @pytest.mark.options(CAPTCHETAT_BASE_URL=None, SECURITY_RETURN_GENERIC_RESPONSES=True)
112
+ def test_reset_password(self, api):
113
+ user = UserFactory(email="jane@example.org", confirmed_at=datetime.now())
114
+ token = generate_reset_password_token(user)
115
+
116
+ response = api.post(
117
+ url_for("security.reset_password", token=token),
118
+ {
119
+ "password": "Password123",
120
+ "password_confirm": "Password123",
121
+ "submit": True,
122
+ },
123
+ )
124
+ self.assertStatus(response, 200)
@@ -3,12 +3,11 @@ import json
3
3
  from flask import url_for
4
4
  from flask_restx import schemas
5
5
 
6
+ from udata.tests.api import PytestOnlyAPITestCase
6
7
  from udata.tests.helpers import assert200
7
8
 
8
9
 
9
- class SwaggerBlueprintTest:
10
- modules = []
11
-
10
+ class SwaggerBlueprintTest(PytestOnlyAPITestCase):
12
11
  def test_swagger_resource_type(self, api):
13
12
  response = api.get(url_for("api.specs"))
14
13
  assert200(response)
@@ -1,15 +1,14 @@
1
- import pytest
2
1
  from flask import url_for
3
2
 
4
3
  from udata.core.dataset.factories import DatasetFactory
5
4
  from udata.core.reuse.factories import ReuseFactory
6
5
  from udata.core.tags.tasks import count_tags
6
+ from udata.tests.api import PytestOnlyAPITestCase
7
7
  from udata.tests.helpers import assert200
8
8
  from udata.utils import faker
9
9
 
10
10
 
11
- @pytest.mark.frontend
12
- class TagsAPITest:
11
+ class TagsAPITest(PytestOnlyAPITestCase):
13
12
  def test_suggest_tags_api(self, api):
14
13
  """It should suggest tags"""
15
14
  for i in range(3):
@@ -19,7 +18,7 @@ class TagsAPITest:
19
18
 
20
19
  count_tags()
21
20
 
22
- response = api.get(url_for("api.suggest_tags"), qs={"q": "tes", "size": "5"})
21
+ response = api.get(url_for("api.suggest_tags", q="tes", size=5))
23
22
  assert200(response)
24
23
 
25
24
  assert len(response.json) <= 5
@@ -39,7 +38,7 @@ class TagsAPITest:
39
38
 
40
39
  count_tags()
41
40
 
42
- response = api.get(url_for("api.suggest_tags"), qs={"q": "testé", "size": "5"})
41
+ response = api.get(url_for("api.suggest_tags", q="testé", size=5))
43
42
  assert200(response)
44
43
 
45
44
  assert len(response.json) <= 5
@@ -59,12 +58,12 @@ class TagsAPITest:
59
58
 
60
59
  count_tags()
61
60
 
62
- response = api.get(url_for("api.suggest_tags"), qs={"q": "bbbb", "size": "5"})
61
+ response = api.get(url_for("api.suggest_tags", q="bbbb", size=5))
63
62
  assert200(response)
64
63
  assert len(response.json) == 0
65
64
 
66
65
  def test_suggest_tags_api_empty(self, api):
67
66
  """It should not provide tag suggestion if no data"""
68
- response = api.get(url_for("api.suggest_tags"), qs={"q": "bbbb", "size": "5"})
67
+ response = api.get(url_for("api.suggest_tags", q="bbbb", size=5))
69
68
  assert200(response)
70
69
  assert len(response.json) == 0
@@ -12,8 +12,6 @@ from . import APITestCase
12
12
 
13
13
 
14
14
  class TransferAPITest(APITestCase):
15
- modules = []
16
-
17
15
  def test_request_dataset_transfer(self):
18
16
  user = self.login()
19
17
  recipient_user = UserFactory()
@@ -11,8 +11,6 @@ from . import APITestCase
11
11
 
12
12
 
13
13
  class UserAPITest(APITestCase):
14
- modules = []
15
-
16
14
  def test_follow_user(self):
17
15
  """It should follow an user on POST"""
18
16
  user = self.login()
@@ -65,7 +63,7 @@ class UserAPITest(APITestCase):
65
63
  for i in range(4):
66
64
  UserFactory(first_name="first-name-test-{0}".format(i) if i % 2 else faker.word())
67
65
 
68
- response = self.get(url_for("api.suggest_users"), qs={"q": "first-name-test", "size": "5"})
66
+ response = self.get(url_for("api.suggest_users", q="first-name-test", size=5))
69
67
  self.assert200(response)
70
68
 
71
69
  self.assertLessEqual(len(response.json), 5)
@@ -84,7 +82,7 @@ class UserAPITest(APITestCase):
84
82
  for i in range(4):
85
83
  UserFactory(last_name="last-name-test-{0}".format(i) if i % 2 else faker.word())
86
84
 
87
- response = self.get(url_for("api.suggest_users"), qs={"q": "last-name-test", "size": "5"})
85
+ response = self.get(url_for("api.suggest_users", q="last-name-test", size=5))
88
86
  self.assert200(response)
89
87
 
90
88
  self.assertLessEqual(len(response.json), 5)
@@ -102,7 +100,7 @@ class UserAPITest(APITestCase):
102
100
  for i in range(4):
103
101
  UserFactory(last_name="last-name-testé-{0}".format(i) if i % 2 else faker.word())
104
102
 
105
- response = self.get(url_for("api.suggest_users"), qs={"q": "last-name-testé", "size": "5"})
103
+ response = self.get(url_for("api.suggest_users", q="last-name-testé", size=5))
106
104
  self.assert200(response)
107
105
 
108
106
  self.assertLessEqual(len(response.json), 5)
@@ -119,13 +117,13 @@ class UserAPITest(APITestCase):
119
117
  """It should not provide user suggestion if no match"""
120
118
  UserFactory.create_batch(3)
121
119
 
122
- response = self.get(url_for("api.suggest_users"), qs={"q": "xxxxxx", "size": "5"})
120
+ response = self.get(url_for("api.suggest_users", q="xxxxxx", size=5))
123
121
  self.assert200(response)
124
122
  self.assertEqual(len(response.json), 0)
125
123
 
126
124
  def test_suggest_users_api_empty(self):
127
125
  """It should not provide user suggestion if no data"""
128
- response = self.get(url_for("api.suggest_users"), qs={"q": "xxxxxx", "size": "5"})
126
+ response = self.get(url_for("api.suggest_users", q="xxxxxx", size=5))
129
127
  self.assert200(response)
130
128
  self.assertEqual(len(response.json), 0)
131
129
 
@@ -133,7 +131,7 @@ class UserAPITest(APITestCase):
133
131
  """It should suggest users without deduplicating homonyms"""
134
132
  UserFactory.create_batch(2, first_name="test", last_name="homonym")
135
133
 
136
- response = self.get(url_for("api.suggest_users"), qs={"q": "homonym", "size": "5"})
134
+ response = self.get(url_for("api.suggest_users", q="homonym", size=5))
137
135
  self.assert200(response)
138
136
 
139
137
  self.assertEqual(len(response.json), 2)
@@ -144,11 +142,11 @@ class UserAPITest(APITestCase):
144
142
 
145
143
  def test_suggest_users_api_size_validation(self):
146
144
  """It should validate that the size parameter is between 1 and 20."""
147
- response = self.get(url_for("api.suggest_users"), qs={"q": "foobar", "size": "0"})
145
+ response = self.get(url_for("api.suggest_users", q="foobar", size=0))
148
146
  self.assert400(response)
149
147
  self.assertIn("between 1 and 20", response.json["errors"]["size"])
150
148
 
151
- response = self.get(url_for("api.suggest_users"), qs={"q": "foobar", "size": "21"})
149
+ response = self.get(url_for("api.suggest_users", q="foobar", size=21))
152
150
 
153
151
  self.assert400(response)
154
152
  self.assertIn("between 1 and 20", response.json["errors"]["size"])
@@ -367,8 +367,6 @@ class DatasetResourceAPIV2Test(APITestCase):
367
367
 
368
368
 
369
369
  class DatasetExtrasAPITest(APITestCase):
370
- modules = None
371
-
372
370
  def setUp(self):
373
371
  self.login()
374
372
  self.dataset = DatasetFactory(owner=self.user)
@@ -503,8 +501,6 @@ class DatasetExtrasAPITest(APITestCase):
503
501
 
504
502
 
505
503
  class DatasetResourceExtrasAPITest(APITestCase):
506
- modules = None
507
-
508
504
  def setUp(self):
509
505
  self.login()
510
506
  self.dataset = DatasetFactory(owner=self.user)
@@ -7,8 +7,6 @@ from udata.tests.api import APITestCase
7
7
 
8
8
 
9
9
  class MeAPIv2Test(APITestCase):
10
- modules = []
11
-
12
10
  def test_my_org_topics(self):
13
11
  user = self.login()
14
12
  member = Member(user=user, role="editor")
@@ -5,8 +5,6 @@ from udata.tests.api import APITestCase
5
5
 
6
6
 
7
7
  class OrganizationExtrasAPITest(APITestCase):
8
- modules = None
9
-
10
8
  def setUp(self):
11
9
  self.login()
12
10
  member = Member(user=self.user, role="admin")
@@ -3,12 +3,11 @@ import json
3
3
  from flask import url_for
4
4
  from flask_restx import schemas
5
5
 
6
+ from udata.tests.api import PytestOnlyAPITestCase
6
7
  from udata.tests.helpers import assert200
7
8
 
8
9
 
9
- class SwaggerBlueprintTest:
10
- modules = []
11
-
10
+ class SwaggerBlueprintTest(PytestOnlyAPITestCase):
12
11
  def test_swagger_resource_type(self, api):
13
12
  response = api.get(url_for("apiv2.specs"))
14
13
  assert200(response)
@@ -27,8 +27,6 @@ from udata.tests.features.territories import create_geozones_fixtures
27
27
 
28
28
 
29
29
  class TopicsListAPITest(APITestCase):
30
- modules = []
31
-
32
30
  def test_topic_api_list(self):
33
31
  """It should fetch a topic list from the API"""
34
32
  owner = UserFactory()
@@ -1,16 +1,18 @@
1
- def test_cli_help(cli):
2
- """Should display help without errors"""
3
- cli()
4
- cli("-?")
5
- cli("-h")
6
- cli("--help")
1
+ from udata.tests import PytestOnlyTestCase
7
2
 
8
3
 
9
- def test_cli_log_and_printing(cli):
10
- """Should properly log and print"""
11
- cli("test log")
4
+ class CliBaseTest(PytestOnlyTestCase):
5
+ def test_cli_help(self, cli):
6
+ """Should display help without errors"""
7
+ cli()
8
+ cli("-?")
9
+ cli("-h")
10
+ cli("--help")
12
11
 
12
+ def test_cli_log_and_printing(self, cli):
13
+ """Should properly log and print"""
14
+ cli("test log")
13
15
 
14
- def test_cli_version(cli):
15
- """Should display version without errors"""
16
- cli("--version")
16
+ def test_cli_version(self, cli):
17
+ """Should display version without errors"""
18
+ cli("--version")