invenio-app-ils 4.5.0__py2.py3-none-any.whl → 5.0.0__py2.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.
Files changed (115) hide show
  1. invenio_app_ils/__init__.py +1 -1
  2. invenio_app_ils/assets/semantic-ui/less/theme.config +103 -0
  3. invenio_app_ils/circulation/api.py +9 -5
  4. invenio_app_ils/circulation/config.py +1 -0
  5. invenio_app_ils/circulation/loaders/schemas/json/loan_checkout.py +1 -1
  6. invenio_app_ils/circulation/loaders/schemas/json/loan_request.py +2 -2
  7. invenio_app_ils/circulation/notifications/api.py +9 -1
  8. invenio_app_ils/circulation/notifications/messages.py +2 -1
  9. invenio_app_ils/circulation/templates/invenio_app_ils_circulation/notifications/update_dates.html +19 -0
  10. invenio_app_ils/cli.py +92 -64
  11. invenio_app_ils/config.py +10 -2
  12. invenio_app_ils/documents/loaders/jsonschemas/document.py +1 -1
  13. invenio_app_ils/eitems/loaders/jsonschemas/eitems.py +2 -2
  14. invenio_app_ils/patrons/anonymization.py +0 -4
  15. invenio_app_ils/series/loaders/jsonschemas/series.py +1 -1
  16. invenio_app_ils/webpack.py +27 -0
  17. {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/METADATA +56 -43
  18. {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/RECORD +24 -112
  19. {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/entry_points.txt +3 -0
  20. {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/top_level.txt +0 -1
  21. invenio_app_ils/notifications/admin.py +0 -57
  22. tests/__init__.py +0 -8
  23. tests/api/__init__.py +0 -8
  24. tests/api/acquisition/__init__.py +0 -8
  25. tests/api/acquisition/test_acq_orders_crud.py +0 -99
  26. tests/api/acquisition/test_acq_orders_permissions.py +0 -104
  27. tests/api/acquisition/test_acq_providers_permissions.py +0 -83
  28. tests/api/circulation/__init__.py +0 -8
  29. tests/api/circulation/test_expired_loans.py +0 -88
  30. tests/api/circulation/test_loan_bulk_extend.py +0 -169
  31. tests/api/circulation/test_loan_checkout.py +0 -422
  32. tests/api/circulation/test_loan_default_durations.py +0 -43
  33. tests/api/circulation/test_loan_document_resolver.py +0 -18
  34. tests/api/circulation/test_loan_extend.py +0 -122
  35. tests/api/circulation/test_loan_item_permissions.py +0 -135
  36. tests/api/circulation/test_loan_item_resolver.py +0 -26
  37. tests/api/circulation/test_loan_list_permissions.py +0 -98
  38. tests/api/circulation/test_loan_patron_resolver.py +0 -38
  39. tests/api/circulation/test_loan_request.py +0 -338
  40. tests/api/circulation/test_loan_update.py +0 -150
  41. tests/api/circulation/test_notifications.py +0 -221
  42. tests/api/conftest.py +0 -227
  43. tests/api/document_requests/__init__.py +0 -8
  44. tests/api/document_requests/test_document_requests.py +0 -131
  45. tests/api/document_requests/test_document_requests_permissions.py +0 -159
  46. tests/api/document_requests/test_notifications_filter.py +0 -35
  47. tests/api/ill/__init__.py +0 -8
  48. tests/api/ill/test_ill_brw_crud.py +0 -74
  49. tests/api/ill/test_ill_brw_reqs_patron_loan_create_action.py +0 -198
  50. tests/api/ill/test_ill_brw_reqs_patron_loan_extension_actions.py +0 -280
  51. tests/api/ill/test_ill_brw_reqs_permissions.py +0 -163
  52. tests/api/ill/test_ill_providers_permissions.py +0 -82
  53. tests/api/ils/__init__.py +0 -8
  54. tests/api/ils/documents/__init__.py +0 -8
  55. tests/api/ils/documents/test_document_crud.py +0 -57
  56. tests/api/ils/documents/test_document_permissions.py +0 -100
  57. tests/api/ils/documents/test_document_resolvers.py +0 -35
  58. tests/api/ils/eitems/__init__.py +0 -8
  59. tests/api/ils/eitems/test_eitems_crud.py +0 -42
  60. tests/api/ils/eitems/test_eitems_permissions.py +0 -85
  61. tests/api/ils/eitems/test_files.py +0 -162
  62. tests/api/ils/items/__init__.py +0 -8
  63. tests/api/ils/items/test_apis.py +0 -43
  64. tests/api/ils/items/test_items_crud.py +0 -99
  65. tests/api/ils/items/test_items_permissions.py +0 -107
  66. tests/api/ils/records_relations/__init__.py +0 -8
  67. tests/api/ils/records_relations/helpers.py +0 -115
  68. tests/api/ils/records_relations/test_records_relations_parentchild.py +0 -560
  69. tests/api/ils/records_relations/test_records_relations_sequence.py +0 -294
  70. tests/api/ils/records_relations/test_records_relations_siblings.py +0 -751
  71. tests/api/ils/series/__init__.py +0 -8
  72. tests/api/ils/series/test_series_permissions.py +0 -95
  73. tests/api/ils/test_anonymization.py +0 -181
  74. tests/api/ils/test_apis.py +0 -76
  75. tests/api/ils/test_closures.py +0 -353
  76. tests/api/ils/test_errors.py +0 -125
  77. tests/api/ils/test_facets.py +0 -88
  78. tests/api/ils/test_internal_locations.py +0 -96
  79. tests/api/ils/test_loaders.py +0 -51
  80. tests/api/ils/test_metadata_extensions.py +0 -206
  81. tests/api/ils/test_notifications.py +0 -173
  82. tests/api/ils/test_notifications_mails.py +0 -37
  83. tests/api/ils/test_notifications_permissions.py +0 -55
  84. tests/api/ils/test_patrons.py +0 -102
  85. tests/api/ils/test_record_delete.py +0 -42
  86. tests/api/ils/test_record_permissions.py +0 -132
  87. tests/api/ils/test_resolvers.py +0 -205
  88. tests/api/ils/test_stats.py +0 -142
  89. tests/api/ils/test_tasks.py +0 -209
  90. tests/api/ils/test_vocabularies.py +0 -35
  91. tests/conftest.py +0 -221
  92. tests/data/acq_orders.json +0 -110
  93. tests/data/acq_providers.json +0 -12
  94. tests/data/document_requests.json +0 -77
  95. tests/data/documents.json +0 -238
  96. tests/data/eitems.json +0 -71
  97. tests/data/ill_borrowing_requests.json +0 -77
  98. tests/data/ill_providers.json +0 -12
  99. tests/data/internal_locations.json +0 -22
  100. tests/data/items.json +0 -304
  101. tests/data/loans.json +0 -133
  102. tests/data/loans_most_loaned.json +0 -128
  103. tests/data/locations.json +0 -26
  104. tests/data/series.json +0 -33
  105. tests/helpers.py +0 -107
  106. tests/templates/notifications/title_body.html +0 -8
  107. tests/templates/notifications/title_body_html.html +0 -13
  108. tests/templates/notifications/title_body_html_ctx.html +0 -13
  109. tests/templates/notifications/title_only.html +0 -3
  110. tests/test_post_logout_redirect.py +0 -23
  111. tests/test_version.py +0 -17
  112. /tests/templates/notifications/blank.html → /invenio_app_ils/assets/semantic-ui/templates/.gitkeep +0 -0
  113. {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/WHEEL +0 -0
  114. {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/licenses/AUTHORS.rst +0 -0
  115. {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,353 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- #
3
- # Copyright (C) 2020 CERN.
4
- #
5
- # invenio-app-ils is free software; you can redistribute it and/or modify it
6
- # under the terms of the MIT License; see LICENSE file for more details.
7
-
8
- """Location closures tests."""
9
-
10
- import json
11
-
12
- import arrow
13
- import pytest
14
- from flask import url_for
15
-
16
- from invenio_app_ils.closures.api import find_next_open_date
17
- from invenio_app_ils.proxies import current_app_ils
18
- from tests.helpers import user_login
19
-
20
- _HTTP_OK = [200, 201, 204]
21
- _LOCATION_PID = "locid-1"
22
- _LOCATION_NAME = "Location name"
23
- _ITEM_ENDPOINT = "invenio_records_rest.locid_item"
24
- _LIST_ENDPOINT = "invenio_records_rest.locid_list"
25
- _CLOSURE_PERIODS_ENDPOINT = "invenio_app_ils_closures.location_closure_periods"
26
- _WEEKDAYS = [
27
- "monday",
28
- "tuesday",
29
- "wednesday",
30
- "thursday",
31
- "friday",
32
- "saturday",
33
- "sunday",
34
- ]
35
- _DEFAULT_TIMES = [
36
- {"start_time": "08:00", "end_time": "12:00"},
37
- {"start_time": "13:00", "end_time": "18:00"},
38
- ]
39
-
40
-
41
- def _build_location_closures_data(closed_weekdays, exceptions):
42
- opening_weekdays = []
43
- for name in _WEEKDAYS:
44
- is_open = name not in closed_weekdays
45
- obj = {"weekday": name, "is_open": is_open}
46
- if is_open:
47
- obj["times"] = _DEFAULT_TIMES
48
- opening_weekdays.append(obj)
49
-
50
- opening_exceptions = []
51
- for start_date, end_date, is_open in exceptions:
52
- opening_exceptions.append(
53
- {
54
- "title": "%s - %s" % (start_date, end_date),
55
- "start_date": start_date,
56
- "end_date": end_date,
57
- "is_open": is_open,
58
- }
59
- )
60
-
61
- return {
62
- "opening_weekdays": opening_weekdays,
63
- "opening_exceptions": opening_exceptions,
64
- }
65
-
66
-
67
- def _date_from_string(date_string):
68
- return arrow.get(date_string).date()
69
-
70
-
71
- def test_location_permissions(client, testdata, json_headers, users):
72
- """Test location endpoints permissions."""
73
- dummy_location = dict(
74
- name=_LOCATION_NAME,
75
- opening_weekdays=[
76
- {"weekday": w, "is_open": True, "times": _DEFAULT_TIMES} for w in _WEEKDAYS
77
- ],
78
- opening_exceptions=[],
79
- )
80
- tests = [
81
- ("admin", _HTTP_OK, dummy_location),
82
- ("librarian", _HTTP_OK, dummy_location),
83
- ("patron1", [403], dummy_location),
84
- ("anonymous", [401], dummy_location),
85
- ]
86
- read_statuses = [200]
87
-
88
- def _test_list(expected_status):
89
- """Test get list."""
90
- url = url_for(_LIST_ENDPOINT)
91
- res = client.get(url, headers=json_headers)
92
- assert res.status_code in expected_status
93
-
94
- def _test_create(expected_status, data):
95
- """Test record creation."""
96
- url = url_for(_LIST_ENDPOINT)
97
- res = client.post(url, headers=json_headers, data=json.dumps(data))
98
- assert res.status_code in expected_status
99
-
100
- if res.status_code < 400:
101
- record = res.get_json()["metadata"]
102
- assert record["name"] == _LOCATION_NAME
103
- return record["pid"]
104
-
105
- def _test_update(expected_status, data, pid):
106
- """Test record update."""
107
- pid_value = pid or _LOCATION_PID
108
- url = url_for(_ITEM_ENDPOINT, pid_value=pid_value)
109
- res = client.put(url, headers=json_headers, data=json.dumps(data))
110
- assert res.status_code in expected_status
111
- if res.status_code < 400:
112
- record = res.get_json()["metadata"]
113
- assert record["name"] == _LOCATION_NAME
114
-
115
- def _test_read(expected_status, pid):
116
- """Test record read."""
117
- pid_value = pid or _LOCATION_PID
118
- url = url_for(_ITEM_ENDPOINT, pid_value=pid_value)
119
- res = client.get(url, headers=json_headers)
120
- assert res.status_code in expected_status
121
-
122
- def _test_delete(expected_status, pid):
123
- """Test record delete."""
124
- pid_value = pid or _LOCATION_PID
125
- url = url_for(_ITEM_ENDPOINT, pid_value=pid_value)
126
- res = client.delete(url, headers=json_headers)
127
- assert res.status_code in expected_status
128
-
129
- for username, expected_status, data in tests:
130
- user_login(client, username, users)
131
- _test_list(read_statuses)
132
- pid = _test_create(expected_status, data)
133
- _test_update(expected_status, data, pid)
134
- _test_read(read_statuses, pid)
135
- _test_delete(expected_status, pid)
136
-
137
-
138
- def test_location_validation(client, json_headers, users, testdata):
139
- def _test_update_location_closures(data, expected_code):
140
- url = url_for("invenio_records_rest.locid_item", pid_value=_LOCATION_PID)
141
- res = client.get(url, headers=json_headers)
142
- assert res.status_code == 200
143
- metadata = res.get_json()["metadata"]
144
- metadata.update(data)
145
- res = client.put(url, headers=json_headers, data=json.dumps(metadata))
146
- assert res.status_code == expected_code
147
-
148
- def _test_update_weekdays(weekdays, expected_code):
149
- data = [
150
- {
151
- "weekday": name,
152
- "is_open": is_open,
153
- **({"times": _DEFAULT_TIMES} if is_open else {}),
154
- }
155
- for name, is_open in weekdays
156
- ]
157
- _test_update_location_closures(
158
- {"opening_weekdays": data, "opening_exceptions": []}, expected_code
159
- )
160
-
161
- def _test_update_times(times, expected_code):
162
- ref = "monday"
163
- times_data = [{"start_time": start, "end_time": end} for start, end in times]
164
- data = [
165
- {
166
- "weekday": w,
167
- "is_open": w == ref,
168
- **({"times": times_data} if w == ref else {}),
169
- }
170
- for w in _WEEKDAYS
171
- ]
172
- _test_update_location_closures(
173
- {"opening_weekdays": data, "opening_exceptions": []}, expected_code
174
- )
175
-
176
- def _test_update_exceptions(closed_weekdays, exceptions, expected_code):
177
- _test_update_location_closures(
178
- _build_location_closures_data(closed_weekdays, exceptions),
179
- expected_code,
180
- )
181
-
182
- user_login(client, "librarian", users)
183
-
184
- # Weekdays
185
-
186
- _test_update_weekdays([[w, True] for w in _WEEKDAYS], 200)
187
- _test_update_weekdays([[w, True] for w in _WEEKDAYS[::-1]], 200)
188
- _test_update_weekdays([[w, False] for w in _WEEKDAYS], 400)
189
- _test_update_weekdays([[w, True] for w in _WEEKDAYS[:6]], 400)
190
- _test_update_weekdays([[w, True] for w in _WEEKDAYS[:6] + ["monday"]], 400)
191
- _test_update_weekdays([["foobar", True]], 400)
192
-
193
- # Hours
194
-
195
- _test_update_times([["08:00", "12:00"], ["13:00", "18:00"]], 200)
196
- _test_update_times([["13:00", "18:00"], ["08:00", "12:00"]], 200)
197
- _test_update_times([["08:00", "18:00"]], 200)
198
- _test_update_times([], 400)
199
- _test_update_times(
200
- [["08:00", "10:00"], ["11:00", "12:00"], ["13:00", "18:00"]], 400
201
- )
202
- _test_update_times([["8:00", "12:00"], ["13:00", "18:00"]], 400)
203
- _test_update_times([["08:00", "12:0"], ["13:00", "18:00"]], 400)
204
- _test_update_times([["08:00", ""], ["13:00", "18:00"]], 400)
205
- _test_update_times([["08:00", "12:00"], ["12:00", "18:00"]], 400)
206
- _test_update_times([["08:00", "12:00"], ["09:00", "11:00"]], 400)
207
-
208
- # Exceptions
209
-
210
- _test_update_exceptions(
211
- ["saturday", "sunday"],
212
- [
213
- ["2000-01-01", "2000-01-05", False],
214
- ["2000-01-07", "2000-01-09", True],
215
- ["2000-01-10", "2000-01-15", True],
216
- ],
217
- 200,
218
- )
219
-
220
- _test_update_exceptions(
221
- ["saturday", "sunday"],
222
- [
223
- ["2000-01-12", "2000-01-17", True],
224
- ["2000-01-07", "2000-01-11", False],
225
- ["2000-01-02", "2000-01-04", True],
226
- ],
227
- 200,
228
- )
229
-
230
- _test_update_exceptions(
231
- [],
232
- [
233
- ["2000-01-01", "2000-01-05", False],
234
- ["2000-01-04", "2000-01-08", False],
235
- ],
236
- 400,
237
- )
238
-
239
- _test_update_exceptions(
240
- [],
241
- [
242
- ["2000-01-01", "2000-01-05", False],
243
- ["2000-01-04", "2000-01-08", True],
244
- ],
245
- 400,
246
- )
247
-
248
- _test_update_exceptions(
249
- [],
250
- [
251
- ["2000-01-01", "2000-01-01", False],
252
- ["2000-01-01", "2000-01-01", False],
253
- ],
254
- 400,
255
- )
256
-
257
- _test_update_exceptions(
258
- [],
259
- [
260
- ["2000-01-02", "2000-01-01", True],
261
- ],
262
- 400,
263
- )
264
-
265
-
266
- @pytest.fixture
267
- def location_closures_testdata(db, testdata):
268
- def _update_location_closures_data(closed_weekdays, exceptions):
269
- Location = current_app_ils.location_record_cls
270
- record = Location.get_record_by_pid(_LOCATION_PID)
271
-
272
- record.update(_build_location_closures_data(closed_weekdays, exceptions))
273
- record.commit()
274
- db.session.commit()
275
- current_app_ils.location_indexer.index(record)
276
-
277
- return record
278
-
279
- """
280
- Mon. Tue. Wed. Thu. Fri. Sat. Sun.
281
- 27 28 29 30 31 -01 -02
282
- x03 x04 x05 06 07 -08 -09
283
- x10 x11 x12 x13 x14 x15 x16
284
- x17 x18 x19 x20 x21 x22 -23
285
- x24 25 26 x27 x28 o29 o30
286
- 31 01 02 03 04 -05 -06
287
- """
288
-
289
- closed_weekdays = ["saturday", "sunday"]
290
- exceptions = [
291
- ["2000-01-03", "2000-01-05", False], # Mon. - Wed.
292
- ["2000-01-09", "2000-01-22", False], # Sun. - Sat.
293
- ["2000-01-24", "2000-01-24", False], # Mon.
294
- ["2000-01-27", "2000-01-28", False], # Thu. - Fri.
295
- ["2000-01-29", "2000-01-30", True], # Sat. - Sun.
296
- ]
297
- _update_location_closures_data(closed_weekdays, exceptions)
298
-
299
-
300
- def test_find_next_open_date(app, location_closures_testdata):
301
- def _test(start_date, expected_next_open_date):
302
- next_open = find_next_open_date(_LOCATION_PID, _date_from_string(start_date))
303
- if expected_next_open_date:
304
- assert next_open == _date_from_string(expected_next_open_date)
305
- else:
306
- assert next_open is None
307
-
308
- _test("2000-01-01", "2000-01-06")
309
- _test("2000-01-04", "2000-01-06")
310
- _test("2000-01-06", "2000-01-06")
311
- _test("2000-01-07", "2000-01-07")
312
-
313
- _test("2000-01-09", "2000-01-25")
314
- _test("2000-01-13", "2000-01-25")
315
-
316
- _test("2000-01-26", "2000-01-26")
317
- _test("2000-01-27", "2000-01-29")
318
- _test("2000-01-30", "2000-01-30")
319
-
320
- _test("2000-02-05", "2000-02-07")
321
-
322
-
323
- def test_get_closure_periods(client, location_closures_testdata, json_headers):
324
- """Test get closure periods."""
325
- url = url_for(
326
- _CLOSURE_PERIODS_ENDPOINT,
327
- pid_value=_LOCATION_PID,
328
- year=2000,
329
- )
330
- res = client.get(url, headers=json_headers)
331
- assert res.status_code in _HTTP_OK
332
-
333
- hits = json.loads(res.data.decode("utf-8"))["closure_periods"]
334
- hits = [
335
- (_date_from_string(hit["start"]), _date_from_string(hit["end"])) for hit in hits
336
- ]
337
-
338
- def _is_in_interval(date, interval):
339
- return interval[0] <= date <= interval[1]
340
-
341
- def _test(date, closed):
342
- date = _date_from_string(date)
343
- assert (
344
- any(_is_in_interval(date, hit) for hit in hits) == closed
345
- ), f"Date {date} is wrongfully marked as {'Open' if closed else 'Closed'}"
346
-
347
- _test("2000-01-01", True)
348
- _test("2000-01-06", False)
349
- _test("2000-01-22", True)
350
- _test("2000-01-23", True)
351
- _test("2000-01-30", False)
352
- _test("2000-02-01", False)
353
- _test("2000-02-05", True)
@@ -1,125 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- #
3
- # Copyright (C) 2018-2020 CERN.
4
- #
5
- # invenio-app-ils is free software; you can redistribute it and/or modify it
6
- # under the terms of the MIT License; see LICENSE file for more details.
7
-
8
- """Test errors."""
9
-
10
- import pytest
11
-
12
- from invenio_app_ils.errors import (
13
- ItemHasActiveLoanError,
14
- NotImplementedConfigurationError,
15
- PatronHasLoanOnItemError,
16
- PatronNotFoundError,
17
- RecordHasReferencesError,
18
- SearchQueryError,
19
- UnauthorizedSearchError,
20
- )
21
-
22
-
23
- def test_unauthorized_search_with_patron_pid(app):
24
- """Test UnauthorizedSearchError with patron_pid defined."""
25
- query = "test query"
26
- pid = "1"
27
- msg = "Search '{query}' not allowed by 'patron_pid:{patron_pid}'"
28
- with pytest.raises(UnauthorizedSearchError) as ex:
29
- raise UnauthorizedSearchError(query=query, patron_pid=pid)
30
- assert ex.value.code == 403
31
- assert ex.value.description == msg.format(query=query, patron_pid=pid)
32
-
33
-
34
- def test_unauthorized_search_without_patron_pid(app):
35
- """Test UnauthorizedSearchError without patron_pid defined."""
36
- query = "test query"
37
- msg = "Search '{query}' not allowed by 'patron_pid:None'"
38
- with pytest.raises(UnauthorizedSearchError) as ex:
39
- raise UnauthorizedSearchError(query=query)
40
- assert ex.value.code == 401
41
- assert ex.value.description == msg.format(query=query)
42
-
43
-
44
- def test_invalid_search_query(app):
45
- """Test SearchQueryError."""
46
- query = "invalid query"
47
- msg = "Invalid query syntax: '{query}'"
48
- with pytest.raises(SearchQueryError) as ex:
49
- raise SearchQueryError(query=query)
50
- assert ex.value.code == SearchQueryError.code
51
- assert ex.value.description == msg.format(query=query)
52
-
53
-
54
- def test_not_implemented(app):
55
- """Test NotImplementedConfigurationError."""
56
- config_variable = "CONFIG_VAR"
57
- msg = (
58
- "Function is not implemented. Implement this function in your module "
59
- "and pass it to the config variable '{}'".format(config_variable)
60
- )
61
- with pytest.raises(NotImplementedConfigurationError) as ex:
62
- raise NotImplementedConfigurationError(config_variable="CONFIG_VAR")
63
- assert ex.value.code == NotImplementedConfigurationError.code
64
- assert ex.value.description == msg
65
-
66
-
67
- def test_patron_not_found(app):
68
- """Test PatronNotFoundError."""
69
- patron_pid = "1"
70
- msg = "Patron with ID '{patron_pid}' was not found."
71
- with pytest.raises(PatronNotFoundError) as ex:
72
- raise PatronNotFoundError(patron_pid)
73
- assert ex.value.code == PatronNotFoundError.code
74
- assert ex.value.description == msg.format(patron_pid=patron_pid)
75
-
76
-
77
- def test_patron_has_loan_on_item(app):
78
- """Test PatronHasLoanOnItemError."""
79
- patron_pid = "1"
80
- item_pid = dict(type="pitmid", value="2")
81
- msg = "Patron '{0}' has already an active loan on item '{1}:{2}'"
82
- with pytest.raises(PatronHasLoanOnItemError) as ex:
83
- raise PatronHasLoanOnItemError(patron_pid, item_pid)
84
- assert ex.value.code == PatronHasLoanOnItemError.code
85
- assert ex.value.description == msg.format(
86
- patron_pid, item_pid["type"], item_pid["value"]
87
- )
88
-
89
-
90
- def test_record_has_references_error(app):
91
- """Test RecordHasReferencesError."""
92
- record_type = "Item"
93
- record_id = "item-1"
94
- ref_type = "RecordType"
95
- ref_ids = ["1", "2", "3"]
96
- msg = (
97
- "Cannot delete the {record_type} {record_id} record because it has "
98
- "references from {ref_type} records with ids: {ref_ids}."
99
- ).format(
100
- record_type=record_type,
101
- record_id=record_id,
102
- ref_type=ref_type,
103
- ref_ids=ref_ids,
104
- )
105
-
106
- with pytest.raises(RecordHasReferencesError) as ex:
107
- raise RecordHasReferencesError(
108
- record_type=record_type,
109
- record_id=record_id,
110
- ref_type=ref_type,
111
- ref_ids=ref_ids,
112
- )
113
- assert ex.value.code == RecordHasReferencesError.code
114
- assert ex.value.description == msg
115
-
116
-
117
- def test_item_has_active_loan_error(app):
118
- loan_pid = "1"
119
- msg = (
120
- "Could not update item because it has an active loan with " "pid: {loan_pid}."
121
- ).format(loan_pid=loan_pid)
122
- with pytest.raises(ItemHasActiveLoanError) as ex:
123
- raise ItemHasActiveLoanError(loan_pid=loan_pid)
124
- assert ex.value.code == ItemHasActiveLoanError.code
125
- assert ex.value.description == msg
@@ -1,88 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- #
3
- # Copyright (C) 2018 CERN.
4
- #
5
- # invenio-app-ils is free software; you can redistribute it and/or modify it
6
- # under the terms of the MIT License; see LICENSE file for more details.
7
-
8
- """Facets tests."""
9
-
10
- from datetime import timedelta
11
-
12
- import arrow
13
- import pytest
14
- from flask import current_app
15
- from invenio_search.engine import dsl
16
-
17
- from invenio_app_ils.facets import (
18
- date_range_filter,
19
- default_value_when_missing_filter,
20
- keyed_range_filter,
21
- overdue_loans_filter,
22
- )
23
-
24
-
25
- def test_keyed_range_filter():
26
- """Test range filter."""
27
- range_query = {"None": {"lt": 1}, "1+": {"gte": 1}}
28
- rfilter = keyed_range_filter("field", range_query)
29
-
30
- assert rfilter(["None"]) == dsl.query.Range(field={"lt": 1})
31
- assert rfilter(["1+"]) == dsl.query.Range(field={"gte": 1})
32
- assert rfilter(["None", "1+"]) == dsl.query.Range(field={"gte": 1, "lt": 1})
33
-
34
-
35
- def test_current_ranged_loans_filter(app):
36
- """Test ranged current loans filter."""
37
- with app.app_context():
38
- rfilter = overdue_loans_filter("field")
39
-
40
- current_loans_query = dsl.query.Terms(
41
- state=current_app.config["CIRCULATION_STATES_LOAN_ACTIVE"]
42
- )
43
-
44
- overdue = rfilter(["Overdue"])
45
- field = {"lt": str(arrow.utcnow().date())}
46
- assert overdue == dsl.query.Bool(
47
- must=[dsl.query.Range(field=field), current_loans_query]
48
- )
49
-
50
- upcoming = rfilter(["Upcoming return"])
51
- field = {
52
- "gte": str(arrow.utcnow().date()),
53
- "lte": str((arrow.utcnow() + timedelta(days=7)).date()),
54
- }
55
- assert upcoming == dsl.query.Bool(
56
- must=[dsl.query.Range(field=field), current_loans_query]
57
- )
58
-
59
-
60
- def test_default_value_when_missing_filter(app):
61
- """Test custom exists filter."""
62
- with app.app_context():
63
- rfilter = default_value_when_missing_filter("field", "missing val")
64
-
65
- assert rfilter("test") == dsl.query.Terms(field="test")
66
- assert rfilter("missing val") == dsl.query.Bool(
67
- **{"must_not": {"exists": {"field": "field"}}}
68
- )
69
-
70
-
71
- def test_date_range_filter(app):
72
- """Test date range filter date validation and query."""
73
-
74
- tests = ["", "a string", "2020-02-02"]
75
-
76
- for input_date in tests:
77
- from_filter = date_range_filter("field", "gte")
78
- to_filter = date_range_filter("field", "lte")
79
-
80
- try:
81
- assert from_filter([input_date]) == dsl.RangeField(
82
- field={"gte": input_date}
83
- )
84
- assert to_filter([input_date]) == dsl.RangeField(field={"lte": input_date})
85
- except (ValueError, AssertionError):
86
- with pytest.raises(ValueError):
87
- from_filter([input_date])
88
- to_filter([input_date])
@@ -1,96 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- #
3
- # Copyright (C) 2018-2021 CERN.
4
- #
5
- # invenio-app-ils is free software; you can redistribute it and/or modify it
6
- # under the terms of the MIT License; see LICENSE file for more details.
7
-
8
- """Test internal locations."""
9
-
10
- import json
11
-
12
- import pytest
13
- from flask import url_for
14
-
15
- from invenio_app_ils.errors import LocationNotFoundError
16
- from invenio_app_ils.internal_locations.api import InternalLocation
17
- from tests.helpers import user_login
18
-
19
- _HTTP_OK = [200, 201, 204]
20
- _INTERNAL_LOCATION_PID = "ilocid-1"
21
- _INTERNAL_LOCATION_NAME = "A location"
22
- _ITEM_ENDPOINT = "invenio_records_rest.ilocid_item"
23
- _LIST_ENDPOINT = "invenio_records_rest.ilocid_list"
24
-
25
-
26
- def test_internal_locations_validation(db, testdata):
27
- """Test validation when updating an Internal Location."""
28
- intloc_pid = testdata["internal_locations"][0]["pid"]
29
- intloc = InternalLocation.get_record_by_pid(intloc_pid)
30
-
31
- intloc["location_pid"] = "not_found_pid"
32
- with pytest.raises(LocationNotFoundError):
33
- intloc.commit()
34
-
35
-
36
- def test_internal_locations_permissions(client, testdata, json_headers, users):
37
- """Test internal locations endpoints permissions."""
38
- dummy_internal_location = dict(
39
- location_pid=testdata["locations"][0]["pid"],
40
- name=_INTERNAL_LOCATION_NAME,
41
- )
42
- tests = [
43
- ("admin", _HTTP_OK, dummy_internal_location),
44
- ("librarian", _HTTP_OK, dummy_internal_location),
45
- ("patron1", [403], dummy_internal_location),
46
- ("anonymous", [401], dummy_internal_location),
47
- ]
48
-
49
- def _test_list(expected_status):
50
- """Test get list."""
51
- url = url_for(_LIST_ENDPOINT)
52
- res = client.get(url, headers=json_headers)
53
- assert res.status_code in expected_status
54
-
55
- def _test_create(expected_status, data):
56
- """Test record creation."""
57
- url = url_for(_LIST_ENDPOINT)
58
- res = client.post(url, headers=json_headers, data=json.dumps(data))
59
- assert res.status_code in expected_status
60
-
61
- if res.status_code < 400:
62
- record = res.get_json()["metadata"]
63
- assert record["name"] == _INTERNAL_LOCATION_NAME
64
- return record["pid"]
65
-
66
- def _test_update(expected_status, data, pid):
67
- """Test record update."""
68
- pid_value = pid or _INTERNAL_LOCATION_PID
69
- url = url_for(_ITEM_ENDPOINT, pid_value=pid_value)
70
- res = client.put(url, headers=json_headers, data=json.dumps(data))
71
- assert res.status_code in expected_status
72
- if res.status_code < 400:
73
- record = res.get_json()["metadata"]
74
- assert record["name"] == _INTERNAL_LOCATION_NAME
75
-
76
- def _test_read(expected_status, pid):
77
- """Test record read."""
78
- pid_value = pid or _INTERNAL_LOCATION_PID
79
- url = url_for(_ITEM_ENDPOINT, pid_value=pid_value)
80
- res = client.get(url, headers=json_headers)
81
- assert res.status_code in expected_status
82
-
83
- def _test_delete(expected_status, pid):
84
- """Test record delete."""
85
- pid_value = pid or _INTERNAL_LOCATION_PID
86
- url = url_for(_ITEM_ENDPOINT, pid_value=pid_value)
87
- res = client.delete(url, headers=json_headers)
88
- assert res.status_code in expected_status
89
-
90
- for username, expected_status, data in tests:
91
- user_login(client, username, users)
92
- _test_list(expected_status)
93
- pid = _test_create(expected_status, data)
94
- _test_update(expected_status, data, pid)
95
- _test_read(expected_status, pid)
96
- _test_delete(expected_status, pid)