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
@@ -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
- modules = []
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 = api.get(url_for("api.activity"))
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, api) -> None:
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 = api.get(url_for("api.activity", related_to="foobar"))
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, api) -> None:
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 = api.get(url_for("api.activity", related_to=dataset1.id))
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 = api.get(url_for("api.activity", related_to=reuse.id))
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, api) -> None:
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 = api.get(url_for("api.activity"))
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
- api.login()
96
- response: TestResponse = api.get(url_for("api.activity"))
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
- api.login(AdminFactory())
102
- response: TestResponse = api.get(url_for("api.activity"))
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, api) -> None:
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 = api.get(url_for("api.activity"))
105
+ response: TestResponse = self.get(url_for("api.activity"))
112
106
  assert200(response)
113
107
  assert len(response.json["data"]) == 1
114
108
 
@@ -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
- @pytest.mark.usefixtures("clean_db")
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"))
@@ -1,4 +1,3 @@
1
- import pytest
2
1
  from flask import url_for
3
2
 
4
3
  from udata.core.contact_point.factories import ContactPointFactory
@@ -7,17 +6,12 @@ from udata.core.organization.factories import OrganizationFactory
7
6
  from udata.core.organization.models import Member
8
7
  from udata.i18n import gettext as _
9
8
  from udata.models import ContactPoint
9
+ from udata.tests.api import APITestCase
10
10
  from udata.tests.helpers import assert200, assert201, assert204, assert400, assert403
11
11
  from udata.utils import faker
12
12
 
13
- pytestmark = [
14
- pytest.mark.usefixtures("clean_db"),
15
- ]
16
-
17
-
18
- class ContactPointAPITest:
19
- modules = []
20
13
 
14
+ class ContactPointAPITest(APITestCase):
21
15
  def test_get_or_create(self):
22
16
  org = OrganizationFactory()
23
17
  contact_point, created = ContactPoint.objects().get_or_create(
@@ -51,46 +45,46 @@ class ContactPointAPITest:
51
45
  assert created
52
46
  assert contact_point.name == "Another"
53
47
 
54
- def test_contact_point_api_create(self, api):
55
- user = api.login()
48
+ def test_contact_point_api_create(self):
49
+ user = self.login()
56
50
  data = {
57
51
  "name": faker.word(),
58
52
  "email": faker.email(),
59
53
  "contact_form": faker.url(),
60
54
  "role": "contact",
61
55
  }
62
- response = api.post(url_for("api.contact_points"), data=data)
56
+ response = self.post(url_for("api.contact_points"), data=data)
63
57
  assert201(response)
64
58
  assert ContactPoint.objects.count() == 1
65
59
 
66
60
  contact_point = ContactPoint.objects.first()
67
61
  assert contact_point.owner.id == user.id
68
62
 
69
- def test_contact_point_api_create_email_or_contact_form(self, api):
70
- api.login()
63
+ def test_contact_point_api_create_email_or_contact_form(self):
64
+ self.login()
71
65
  data = {"name": faker.word(), "contact_form": faker.url(), "role": "contact"}
72
- response = api.post(url_for("api.contact_points"), data=data)
66
+ response = self.post(url_for("api.contact_points"), data=data)
73
67
  assert201(response)
74
68
  assert ContactPoint.objects.count() == 1
75
69
 
76
70
  data = {"name": faker.word(), "email": faker.email(), "role": "contact"}
77
- response = api.post(url_for("api.contact_points"), data=data)
71
+ response = self.post(url_for("api.contact_points"), data=data)
78
72
  assert201(response)
79
73
  assert ContactPoint.objects.count() == 2
80
74
 
81
- def test_contact_point_duplicate_creation(self, api):
82
- api.login()
75
+ def test_contact_point_duplicate_creation(self):
76
+ self.login()
83
77
  data = {"name": faker.word(), "contact_form": faker.url(), "role": "contact"}
84
- response = api.post(url_for("api.contact_points"), data=data)
78
+ response = self.post(url_for("api.contact_points"), data=data)
85
79
  assert201(response)
86
80
  assert ContactPoint.objects.count() == 1
87
81
 
88
- response = api.post(url_for("api.contact_points"), data=data)
82
+ response = self.post(url_for("api.contact_points"), data=data)
89
83
  assert200(response)
90
84
  assert ContactPoint.objects.count() == 1
91
85
 
92
- def test_contact_point_for_different_org(self, api):
93
- user = api.login()
86
+ def test_contact_point_for_different_org(self):
87
+ user = self.login()
94
88
  member = Member(user=user, role="editor")
95
89
  org_a = OrganizationFactory(members=[member])
96
90
 
@@ -100,11 +94,11 @@ class ContactPointAPITest:
100
94
  "role": "contact",
101
95
  "organization": str(org_a.id),
102
96
  }
103
- response = api.post(url_for("api.contact_points"), data=data)
97
+ response = self.post(url_for("api.contact_points"), data=data)
104
98
  assert201(response)
105
99
  assert ContactPoint.objects.count() == 1
106
100
 
107
- response = api.post(url_for("api.contact_points"), data=data)
101
+ response = self.post(url_for("api.contact_points"), data=data)
108
102
  assert200(response)
109
103
  assert ContactPoint.objects.count() == 1
110
104
 
@@ -114,39 +108,39 @@ class ContactPointAPITest:
114
108
 
115
109
  org_b = OrganizationFactory(members=[])
116
110
  data["organization"] = str(org_b.id)
117
- response = api.post(url_for("api.contact_points"), data=data)
111
+ response = self.post(url_for("api.contact_points"), data=data)
118
112
  assert400(response)
119
113
  assert ContactPoint.objects.count() == 1
120
114
 
121
115
  org_b.members = [Member(user=user, role="editor")]
122
116
  org_b.save()
123
117
 
124
- response = api.post(url_for("api.contact_points"), data=data)
118
+ response = self.post(url_for("api.contact_points"), data=data)
125
119
  assert201(response)
126
120
  assert ContactPoint.objects.count() == 2
127
121
 
128
- def test_contact_point_api_invalid_email(self, api):
129
- api.login()
122
+ def test_contact_point_api_invalid_email(self):
123
+ self.login()
130
124
  data = {"name": faker.word(), "email": faker.word(), "role": "contact"}
131
- response = api.post(url_for("api.contact_points"), data=data)
125
+ response = self.post(url_for("api.contact_points"), data=data)
132
126
  assert400(response)
133
127
  assert "email" in response.json["errors"]
134
128
  assert ContactPoint.objects.count() == 0
135
129
 
136
- def test_contact_point_missing_contact_information(self, api):
137
- api.login()
130
+ def test_contact_point_missing_contact_information(self):
131
+ self.login()
138
132
  data = {"name": faker.word(), "role": "contact"}
139
- response = api.post(url_for("api.contact_points"), data=data)
133
+ response = self.post(url_for("api.contact_points"), data=data)
140
134
  assert400(response)
141
135
  assert response.json["message"] == _(
142
136
  "At least an email or a contact form is required for a contact point"
143
137
  )
144
138
  assert ContactPoint.objects.count() == 0
145
139
 
146
- def test_contact_point_missing_role(self, api):
147
- api.login()
140
+ def test_contact_point_missing_role(self):
141
+ self.login()
148
142
  data = {"name": faker.word(), "email": faker.email()}
149
- response = api.post(url_for("api.contact_points"), data=data)
143
+ response = self.post(url_for("api.contact_points"), data=data)
150
144
  assert400(response)
151
145
  assert (
152
146
  response.json["message"]
@@ -154,70 +148,70 @@ class ContactPointAPITest:
154
148
  )
155
149
  assert ContactPoint.objects.count() == 0
156
150
 
157
- def test_contact_point_no_need_for_email_for_role_other_than_contact(self, api):
158
- api.login()
151
+ def test_contact_point_no_need_for_email_for_role_other_than_contact(self):
152
+ self.login()
159
153
  roles_other_than_contact = [role_ for role_ in CONTACT_ROLES.keys() if role_ != "contact"]
160
154
  for index, role in enumerate(roles_other_than_contact):
161
155
  data = {"name": faker.word(), "role": role}
162
- response = api.post(url_for("api.contact_points"), data=data)
156
+ response = self.post(url_for("api.contact_points"), data=data)
163
157
  assert201(response)
164
158
  assert ContactPoint.objects.count() == index + 1
165
159
 
166
- def test_contact_point_api_update(self, api):
167
- user = api.login()
160
+ def test_contact_point_api_update(self):
161
+ user = self.login()
168
162
  member = Member(user=user, role="editor")
169
163
  org = OrganizationFactory(members=[member])
170
164
  contact_point = ContactPointFactory(organization=org)
171
165
  data = contact_point.to_dict()
172
166
  data["email"] = "new.email@newdomain.com"
173
- response = api.put(url_for("api.contact_point", contact_point=contact_point), data)
167
+ response = self.put(url_for("api.contact_point", contact_point=contact_point), data)
174
168
  assert200(response)
175
169
  assert ContactPoint.objects.count() == 1
176
170
  assert ContactPoint.objects.first().email == "new.email@newdomain.com"
177
171
 
178
- def test_contact_point_api_update_to_existing_contact_point(self, api):
179
- user = api.login()
172
+ def test_contact_point_api_update_to_existing_contact_point(self):
173
+ user = self.login()
180
174
  contact_point_a = ContactPointFactory(email="a@example.org", owner=user)
181
175
  contact_point_b = ContactPointFactory(email="b@example.org", owner=user)
182
176
 
183
177
  data_b = contact_point_b.to_dict()
184
- response = api.put(url_for("api.contact_point", contact_point=contact_point_a), data_b)
178
+ response = self.put(url_for("api.contact_point", contact_point=contact_point_a), data_b)
185
179
  assert400(response)
186
180
  assert ContactPoint.objects.count() == 2
187
181
 
188
182
  contact_point_a.reload()
189
183
  assert contact_point_a.email == "a@example.org"
190
184
 
191
- def test_contact_point_api_update_forbidden(self, api):
192
- api.login()
185
+ def test_contact_point_api_update_forbidden(self):
186
+ self.login()
193
187
  org = OrganizationFactory()
194
188
  contact_point = ContactPointFactory(organization=org)
195
189
  data = contact_point.to_dict()
196
190
  data["email"] = "new.email@newdomain.com"
197
- response = api.put(url_for("api.contact_point", contact_point=contact_point), data)
191
+ response = self.put(url_for("api.contact_point", contact_point=contact_point), data)
198
192
  assert403(response)
199
193
  assert ContactPoint.objects.count() == 1
200
194
  assert ContactPoint.objects.first().email == contact_point.email
201
195
 
202
- def test_contact_point_api_delete(self, api):
203
- user = api.login()
196
+ def test_contact_point_api_delete(self):
197
+ user = self.login()
204
198
  member = Member(user=user, role="editor")
205
199
  org = OrganizationFactory(members=[member])
206
200
  contact_point = ContactPointFactory(organization=org)
207
- response = api.delete(url_for("api.contact_point", contact_point=contact_point))
201
+ response = self.delete(url_for("api.contact_point", contact_point=contact_point))
208
202
  assert204(response)
209
203
  assert ContactPoint.objects.count() == 0
210
204
 
211
- def test_contact_point_roles_list(self, api):
205
+ def test_contact_point_roles_list(self):
212
206
  """It should fetch the contact point roles list from the API"""
213
- response = api.get(url_for("api.contact_point_roles"))
207
+ response = self.get(url_for("api.contact_point_roles"))
214
208
  assert200(response)
215
209
  assert len(response.json) == len(CONTACT_ROLES)
216
210
 
217
- def test_contact_point_api_delete_forbidden(self, api):
218
- api.login()
211
+ def test_contact_point_api_delete_forbidden(self):
212
+ self.login()
219
213
  org = OrganizationFactory()
220
214
  contact_point = ContactPointFactory(organization=org)
221
- response = api.delete(url_for("api.contact_point", contact_point=contact_point))
215
+ response = self.delete(url_for("api.contact_point", contact_point=contact_point))
222
216
  assert403(response)
223
217
  assert ContactPoint.objects.count() == 1
@@ -1,5 +1,4 @@
1
1
  from datetime import datetime, timedelta
2
- from xml.etree.ElementTree import XML
3
2
 
4
3
  import feedparser
5
4
  import pytest
@@ -7,24 +6,20 @@ from flask import url_for
7
6
  from werkzeug.test import TestResponse
8
7
 
9
8
  import udata.core.organization.constants as org_constants
10
- from udata.core.dataservices.constants import (
11
- DATASERVICE_ACCESS_AUDIENCE_ADMINISTRATION,
12
- DATASERVICE_ACCESS_AUDIENCE_COMPANY,
13
- DATASERVICE_ACCESS_AUDIENCE_UNDER_CONDITIONS,
14
- DATASERVICE_ACCESS_AUDIENCE_YES,
15
- DATASERVICE_ACCESS_TYPE_OPEN,
16
- DATASERVICE_ACCESS_TYPE_OPEN_WITH_ACCOUNT,
17
- DATASERVICE_ACCESS_TYPE_RESTRICTED,
9
+ from udata.core.access_type.constants import (
10
+ AccessAudienceCondition,
11
+ AccessAudienceType,
12
+ AccessType,
18
13
  )
19
14
  from udata.core.dataservices.factories import DataserviceFactory
20
15
  from udata.core.dataservices.models import Dataservice
21
16
  from udata.core.dataset.factories import DatasetFactory, LicenseFactory
22
17
  from udata.core.organization.factories import OrganizationFactory
23
18
  from udata.core.organization.models import Member
24
- from udata.core.topic.factories import TopicElementFactory, TopicFactory
19
+ from udata.core.topic.factories import ReuseFactory, TopicElementFactory, TopicFactory
25
20
  from udata.core.user.factories import AdminFactory, UserFactory
26
21
  from udata.i18n import gettext as _
27
- from udata.tests.helpers import assert200, assert400, assert410, assert_redirects
22
+ from udata.tests.helpers import assert200, assert400, assert410
28
23
 
29
24
  from . import APITestCase
30
25
 
@@ -35,8 +30,6 @@ def dataservice_in_response(response: TestResponse, dataservice: Dataservice) ->
35
30
 
36
31
 
37
32
  class DataserviceAPITest(APITestCase):
38
- modules = []
39
-
40
33
  def test_dataservice_api_get(self):
41
34
  """It should fetch a dataservice from the API"""
42
35
  dataservice = DataserviceFactory()
@@ -106,6 +99,34 @@ class DataserviceAPITest(APITestCase):
106
99
  assert len(response.json["data"]) == 1
107
100
  assert response.json["data"][0]["id"] == str(topic_dataservice.id)
108
101
 
102
+ # filter on reuse
103
+ reuse_dataservice = DataserviceFactory()
104
+ reuse = ReuseFactory(dataservices=[reuse_dataservice])
105
+ response = self.get(url_for("api.dataservices", reuse=reuse.id))
106
+ assert200(response)
107
+ assert len(response.json["data"]) == 1
108
+ assert response.json["data"][0]["id"] == str(reuse_dataservice.id)
109
+
110
+ def test_dataservices_topic_filter_errors(self):
111
+ # non-existing
112
+ response = self.get(url_for("api.dataservices", topic="690c7f48ec85adaa376c1e93"))
113
+ assert200(response)
114
+
115
+ # not an object ID
116
+ response = self.get(url_for("api.dataservices", topic="xxx"))
117
+ assert400(response)
118
+ assert "`topic` must be an identifier" in response.json["message"]
119
+
120
+ def test_dataservices_reuse_filter_errors(self):
121
+ # non-existing
122
+ response = self.get(url_for("api.dataservices", reuse="690c7f48ec85adaa376c1e93"))
123
+ assert200(response)
124
+
125
+ # not an object ID
126
+ response = self.get(url_for("api.dataservices", reuse="xxx"))
127
+ assert400(response)
128
+ assert "`reuse` must be an identifier" in response.json["message"]
129
+
109
130
  def test_dataservice_api_create(self):
110
131
  user = self.login()
111
132
  datasets = DatasetFactory.create_batch(3)
@@ -141,15 +162,15 @@ class DataserviceAPITest(APITestCase):
141
162
  "extras": {
142
163
  "foo": "bar",
143
164
  },
144
- "access_type": DATASERVICE_ACCESS_TYPE_RESTRICTED,
165
+ "access_type": AccessType.RESTRICTED,
145
166
  "access_audiences": [
146
167
  {
147
- "role": DATASERVICE_ACCESS_AUDIENCE_ADMINISTRATION,
148
- "condition": DATASERVICE_ACCESS_AUDIENCE_YES,
168
+ "role": AccessAudienceType.ADMINISTRATION,
169
+ "condition": AccessAudienceCondition.YES,
149
170
  },
150
171
  {
151
- "role": DATASERVICE_ACCESS_AUDIENCE_COMPANY,
152
- "condition": DATASERVICE_ACCESS_AUDIENCE_UNDER_CONDITIONS,
172
+ "role": AccessAudienceType.COMPANY,
173
+ "condition": AccessAudienceCondition.UNDER_CONDITIONS,
153
174
  },
154
175
  ],
155
176
  },
@@ -165,20 +186,18 @@ class DataserviceAPITest(APITestCase):
165
186
  self.assertEqual(
166
187
  response.json["self_api_url"], "http://local.test/api/1/dataservices/updated-title/"
167
188
  )
168
- self.assertEqual(response.json["access_type"], DATASERVICE_ACCESS_TYPE_RESTRICTED)
189
+ self.assertEqual(response.json["access_type"], AccessType.RESTRICTED)
169
190
  self.assertEqual(len(response.json["access_audiences"]), 2)
170
191
  self.assertEqual(
171
- response.json["access_audiences"][0]["role"], DATASERVICE_ACCESS_AUDIENCE_ADMINISTRATION
192
+ response.json["access_audiences"][0]["role"], AccessAudienceType.ADMINISTRATION
172
193
  )
173
194
  self.assertEqual(
174
- response.json["access_audiences"][0]["condition"], DATASERVICE_ACCESS_AUDIENCE_YES
175
- )
176
- self.assertEqual(
177
- response.json["access_audiences"][1]["role"], DATASERVICE_ACCESS_AUDIENCE_COMPANY
195
+ response.json["access_audiences"][0]["condition"], AccessAudienceCondition.YES
178
196
  )
197
+ self.assertEqual(response.json["access_audiences"][1]["role"], AccessAudienceType.COMPANY)
179
198
  self.assertEqual(
180
199
  response.json["access_audiences"][1]["condition"],
181
- DATASERVICE_ACCESS_AUDIENCE_UNDER_CONDITIONS,
200
+ AccessAudienceCondition.UNDER_CONDITIONS,
182
201
  )
183
202
  # metadata_modified_at should have been updated during the patch
184
203
  self.assertNotEqual(
@@ -327,7 +346,7 @@ class DataserviceAPITest(APITestCase):
327
346
  "title": "B",
328
347
  "base_api_url": "https://example.org/B",
329
348
  "datasets": [dataset_b.id],
330
- "access_type": DATASERVICE_ACCESS_TYPE_OPEN,
349
+ "access_type": AccessType.OPEN,
331
350
  },
332
351
  )
333
352
  self.post(
@@ -336,7 +355,7 @@ class DataserviceAPITest(APITestCase):
336
355
  "title": "C",
337
356
  "base_api_url": "https://example.org/C",
338
357
  "datasets": [dataset_a.id, dataset_b.id],
339
- "access_type": DATASERVICE_ACCESS_TYPE_OPEN_WITH_ACCOUNT,
358
+ "access_type": AccessType.OPEN_WITH_ACCOUNT,
340
359
  },
341
360
  )
342
361
  self.post(
@@ -345,7 +364,7 @@ class DataserviceAPITest(APITestCase):
345
364
  "title": "A",
346
365
  "base_api_url": "https://example.org/A",
347
366
  "datasets": [dataset_a.id],
348
- "access_type": DATASERVICE_ACCESS_TYPE_RESTRICTED,
367
+ "access_type": AccessType.RESTRICTED,
349
368
  },
350
369
  )
351
370
  self.post(
@@ -408,7 +427,7 @@ class DataserviceAPITest(APITestCase):
408
427
  self.assertEqual(response.json["data"][0]["title"], "A")
409
428
  self.assertEqual(response.json["data"][1]["title"], "C")
410
429
 
411
- response = self.get(url_for("api.dataservices", access_type=DATASERVICE_ACCESS_TYPE_OPEN))
430
+ response = self.get(url_for("api.dataservices", access_type=AccessType.OPEN))
412
431
  self.assert200(response)
413
432
 
414
433
  print(response.json)
@@ -599,15 +618,15 @@ class DataserviceAPITest(APITestCase):
599
618
  {
600
619
  "title": "My title",
601
620
  "base_api_url": "https://example.org",
602
- "access_type": DATASERVICE_ACCESS_TYPE_RESTRICTED,
621
+ "access_type": AccessType.RESTRICTED,
603
622
  "access_audiences": [
604
623
  {
605
- "role": DATASERVICE_ACCESS_AUDIENCE_ADMINISTRATION,
606
- "condition": DATASERVICE_ACCESS_AUDIENCE_YES,
624
+ "role": AccessAudienceType.ADMINISTRATION,
625
+ "condition": AccessAudienceCondition.YES,
607
626
  },
608
627
  {
609
- "role": DATASERVICE_ACCESS_AUDIENCE_ADMINISTRATION,
610
- "condition": DATASERVICE_ACCESS_AUDIENCE_UNDER_CONDITIONS,
628
+ "role": AccessAudienceType.ADMINISTRATION,
629
+ "condition": AccessAudienceCondition.UNDER_CONDITIONS,
611
630
  },
612
631
  ],
613
632
  },
@@ -623,15 +642,15 @@ class DataserviceAPITest(APITestCase):
623
642
  dataservice = DataserviceFactory(owner=user, organization=original_org)
624
643
 
625
644
  data = dataservice.to_dict()
626
- data["access_type"] = DATASERVICE_ACCESS_TYPE_RESTRICTED
645
+ data["access_type"] = AccessType.RESTRICTED
627
646
  data["access_audiences"] = [
628
647
  {
629
- "role": DATASERVICE_ACCESS_AUDIENCE_ADMINISTRATION,
630
- "condition": DATASERVICE_ACCESS_AUDIENCE_YES,
648
+ "role": AccessAudienceType.ADMINISTRATION,
649
+ "condition": AccessAudienceCondition.YES,
631
650
  },
632
651
  {
633
- "role": DATASERVICE_ACCESS_AUDIENCE_ADMINISTRATION,
634
- "condition": DATASERVICE_ACCESS_AUDIENCE_UNDER_CONDITIONS,
652
+ "role": AccessAudienceType.ADMINISTRATION,
653
+ "condition": AccessAudienceCondition.UNDER_CONDITIONS,
635
654
  },
636
655
  ]
637
656
  response = self.patch(url_for("api.dataservice", dataservice=dataservice), data)
@@ -664,62 +683,10 @@ class DataserviceAPITest(APITestCase):
664
683
  self.assertEqual(Dataservice.objects.first().organization.id, new_org.id)
665
684
 
666
685
 
667
- @pytest.mark.frontend
668
- class DataserviceRdfViewsTest:
669
- def test_rdf_default_to_jsonld(self, client):
670
- dataservice = DataserviceFactory()
671
- expected = url_for("api.dataservice_rdf_format", dataservice=dataservice.id, format="json")
672
- response = client.get(url_for("api.dataservice_rdf", dataservice=dataservice))
673
- assert_redirects(response, expected)
674
-
675
- def test_rdf_perform_content_negociation(self, client):
676
- dataservice = DataserviceFactory()
677
- expected = url_for("api.dataservice_rdf_format", dataservice=dataservice.id, format="xml")
678
- url = url_for("api.dataservice_rdf", dataservice=dataservice)
679
- headers = {"accept": "application/xml"}
680
- response = client.get(url, headers=headers)
681
- assert_redirects(response, expected)
682
-
683
- def test_rdf_perform_content_negociation_response(self, client):
684
- """Check we have valid XML as output"""
685
- dataservice = DataserviceFactory()
686
- url = url_for("api.dataservice_rdf", dataservice=dataservice)
687
- headers = {"accept": "application/xml"}
688
- response = client.get(url, headers=headers, follow_redirects=True)
689
- element = XML(response.data)
690
- assert element.tag == "{http://www.w3.org/1999/02/22-rdf-syntax-ns#}RDF"
691
-
692
- def test_dataservice_rdf_json_ld(self, client):
693
- dataservice = DataserviceFactory()
694
- for fmt in "json", "jsonld":
695
- url = url_for("api.dataservice_rdf_format", dataservice=dataservice, format=fmt)
696
- response = client.get(url, headers={"Accept": "application/ld+json"})
697
- assert200(response)
698
- assert response.content_type == "application/ld+json"
699
- assert response.json["@context"]["@vocab"] == "http://www.w3.org/ns/dcat#"
700
-
701
- @pytest.mark.parametrize(
702
- "fmt,mime",
703
- [
704
- ("n3", "text/n3"),
705
- ("nt", "application/n-triples"),
706
- ("ttl", "application/x-turtle"),
707
- ("xml", "application/rdf+xml"),
708
- ("rdf", "application/rdf+xml"),
709
- ("owl", "application/rdf+xml"),
710
- ("trig", "application/trig"),
711
- ],
712
- )
713
- def test_dataservice_rdf_formats(self, client, fmt, mime):
714
- dataservice = DataserviceFactory()
715
- url = url_for("api.dataservice_rdf_format", dataservice=dataservice, format=fmt)
716
- response = client.get(url, headers={"Accept": mime})
717
- assert200(response)
718
- assert response.content_type == mime
719
-
720
-
721
686
  class DataservicesFeedAPItest(APITestCase):
687
+ @pytest.mark.options(DELAY_BEFORE_APPEARING_IN_RSS_FEED=10)
722
688
  def test_recent_feed(self):
689
+ # We have a 10 hours delay for a new object to appear in feed. A newly created one shouldn't appear.
723
690
  DataserviceFactory(title="A", created_at=datetime.utcnow())
724
691
  DataserviceFactory(title="B", created_at=datetime.utcnow() - timedelta(days=2))
725
692
  DataserviceFactory(title="C", created_at=datetime.utcnow() - timedelta(days=1))
@@ -729,11 +696,11 @@ class DataservicesFeedAPItest(APITestCase):
729
696
 
730
697
  feed = feedparser.parse(response.data)
731
698
 
732
- self.assertEqual(len(feed.entries), 3)
733
- self.assertEqual(feed.entries[0].title, "A")
734
- self.assertEqual(feed.entries[1].title, "C")
735
- self.assertEqual(feed.entries[2].title, "B")
699
+ self.assertEqual(len(feed.entries), 2)
700
+ self.assertEqual(feed.entries[0].title, "C")
701
+ self.assertEqual(feed.entries[1].title, "B")
736
702
 
703
+ @pytest.mark.options(DELAY_BEFORE_APPEARING_IN_RSS_FEED=0)
737
704
  def test_recent_feed_owner(self):
738
705
  owner = UserFactory()
739
706
  DataserviceFactory(owner=owner)
@@ -751,6 +718,7 @@ class DataservicesFeedAPItest(APITestCase):
751
718
  self.assertEqual(author.name, owner.fullname)
752
719
  self.assertEqual(author.href, owner.url_for())
753
720
 
721
+ @pytest.mark.options(DELAY_BEFORE_APPEARING_IN_RSS_FEED=0)
754
722
  def test_recent_feed_org(self):
755
723
  owner = UserFactory()
756
724
  org = OrganizationFactory()