invenio-app-ils 4.6.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.
- invenio_app_ils/__init__.py +1 -1
- invenio_app_ils/assets/semantic-ui/less/theme.config +103 -0
- invenio_app_ils/circulation/api.py +9 -5
- invenio_app_ils/circulation/config.py +1 -0
- invenio_app_ils/circulation/loaders/schemas/json/loan_checkout.py +1 -1
- invenio_app_ils/circulation/loaders/schemas/json/loan_request.py +2 -2
- invenio_app_ils/circulation/notifications/api.py +9 -1
- invenio_app_ils/circulation/notifications/messages.py +2 -1
- invenio_app_ils/circulation/templates/invenio_app_ils_circulation/notifications/update_dates.html +19 -0
- invenio_app_ils/cli.py +92 -64
- invenio_app_ils/config.py +10 -2
- invenio_app_ils/documents/loaders/jsonschemas/document.py +1 -1
- invenio_app_ils/eitems/loaders/jsonschemas/eitems.py +2 -2
- invenio_app_ils/patrons/anonymization.py +0 -4
- invenio_app_ils/series/loaders/jsonschemas/series.py +1 -1
- invenio_app_ils/webpack.py +27 -0
- {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/METADATA +52 -42
- {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/RECORD +24 -112
- {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/entry_points.txt +3 -0
- {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/top_level.txt +0 -1
- invenio_app_ils/notifications/admin.py +0 -57
- tests/__init__.py +0 -8
- tests/api/__init__.py +0 -8
- tests/api/acquisition/__init__.py +0 -8
- tests/api/acquisition/test_acq_orders_crud.py +0 -99
- tests/api/acquisition/test_acq_orders_permissions.py +0 -104
- tests/api/acquisition/test_acq_providers_permissions.py +0 -83
- tests/api/circulation/__init__.py +0 -8
- tests/api/circulation/test_expired_loans.py +0 -88
- tests/api/circulation/test_loan_bulk_extend.py +0 -169
- tests/api/circulation/test_loan_checkout.py +0 -422
- tests/api/circulation/test_loan_default_durations.py +0 -43
- tests/api/circulation/test_loan_document_resolver.py +0 -18
- tests/api/circulation/test_loan_extend.py +0 -122
- tests/api/circulation/test_loan_item_permissions.py +0 -135
- tests/api/circulation/test_loan_item_resolver.py +0 -26
- tests/api/circulation/test_loan_list_permissions.py +0 -98
- tests/api/circulation/test_loan_patron_resolver.py +0 -38
- tests/api/circulation/test_loan_request.py +0 -338
- tests/api/circulation/test_loan_update.py +0 -150
- tests/api/circulation/test_notifications.py +0 -221
- tests/api/conftest.py +0 -227
- tests/api/document_requests/__init__.py +0 -8
- tests/api/document_requests/test_document_requests.py +0 -131
- tests/api/document_requests/test_document_requests_permissions.py +0 -159
- tests/api/document_requests/test_notifications_filter.py +0 -35
- tests/api/ill/__init__.py +0 -8
- tests/api/ill/test_ill_brw_crud.py +0 -74
- tests/api/ill/test_ill_brw_reqs_patron_loan_create_action.py +0 -198
- tests/api/ill/test_ill_brw_reqs_patron_loan_extension_actions.py +0 -280
- tests/api/ill/test_ill_brw_reqs_permissions.py +0 -163
- tests/api/ill/test_ill_providers_permissions.py +0 -82
- tests/api/ils/__init__.py +0 -8
- tests/api/ils/documents/__init__.py +0 -8
- tests/api/ils/documents/test_document_crud.py +0 -57
- tests/api/ils/documents/test_document_permissions.py +0 -100
- tests/api/ils/documents/test_document_resolvers.py +0 -35
- tests/api/ils/eitems/__init__.py +0 -8
- tests/api/ils/eitems/test_eitems_crud.py +0 -42
- tests/api/ils/eitems/test_eitems_permissions.py +0 -85
- tests/api/ils/eitems/test_files.py +0 -162
- tests/api/ils/items/__init__.py +0 -8
- tests/api/ils/items/test_apis.py +0 -43
- tests/api/ils/items/test_items_crud.py +0 -99
- tests/api/ils/items/test_items_permissions.py +0 -107
- tests/api/ils/records_relations/__init__.py +0 -8
- tests/api/ils/records_relations/helpers.py +0 -115
- tests/api/ils/records_relations/test_records_relations_parentchild.py +0 -560
- tests/api/ils/records_relations/test_records_relations_sequence.py +0 -294
- tests/api/ils/records_relations/test_records_relations_siblings.py +0 -751
- tests/api/ils/series/__init__.py +0 -8
- tests/api/ils/series/test_series_permissions.py +0 -95
- tests/api/ils/test_anonymization.py +0 -181
- tests/api/ils/test_apis.py +0 -76
- tests/api/ils/test_closures.py +0 -353
- tests/api/ils/test_errors.py +0 -125
- tests/api/ils/test_facets.py +0 -88
- tests/api/ils/test_internal_locations.py +0 -96
- tests/api/ils/test_loaders.py +0 -51
- tests/api/ils/test_metadata_extensions.py +0 -206
- tests/api/ils/test_notifications.py +0 -173
- tests/api/ils/test_notifications_mails.py +0 -37
- tests/api/ils/test_notifications_permissions.py +0 -55
- tests/api/ils/test_patrons.py +0 -102
- tests/api/ils/test_record_delete.py +0 -42
- tests/api/ils/test_record_permissions.py +0 -132
- tests/api/ils/test_resolvers.py +0 -205
- tests/api/ils/test_stats.py +0 -142
- tests/api/ils/test_tasks.py +0 -209
- tests/api/ils/test_vocabularies.py +0 -35
- tests/conftest.py +0 -221
- tests/data/acq_orders.json +0 -110
- tests/data/acq_providers.json +0 -12
- tests/data/document_requests.json +0 -77
- tests/data/documents.json +0 -238
- tests/data/eitems.json +0 -71
- tests/data/ill_borrowing_requests.json +0 -77
- tests/data/ill_providers.json +0 -12
- tests/data/internal_locations.json +0 -22
- tests/data/items.json +0 -304
- tests/data/loans.json +0 -133
- tests/data/loans_most_loaned.json +0 -128
- tests/data/locations.json +0 -26
- tests/data/series.json +0 -33
- tests/helpers.py +0 -107
- tests/templates/notifications/title_body.html +0 -8
- tests/templates/notifications/title_body_html.html +0 -13
- tests/templates/notifications/title_body_html_ctx.html +0 -13
- tests/templates/notifications/title_only.html +0 -3
- tests/test_post_logout_redirect.py +0 -23
- tests/test_version.py +0 -17
- /tests/templates/notifications/blank.html → /invenio_app_ils/assets/semantic-ui/templates/.gitkeep +0 -0
- {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/WHEEL +0 -0
- {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/licenses/AUTHORS.rst +0 -0
- {invenio_app_ils-4.6.0.dist-info → invenio_app_ils-5.0.0.dist-info}/licenses/LICENSE +0 -0
tests/api/ils/test_loaders.py
DELETED
|
@@ -1,51 +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
|
-
"""Test accesibility of resource endpoints."""
|
|
9
|
-
|
|
10
|
-
import json
|
|
11
|
-
|
|
12
|
-
from flask import url_for
|
|
13
|
-
|
|
14
|
-
from tests.helpers import user_login, validate_response
|
|
15
|
-
|
|
16
|
-
NEW_INTERNAL_LOCATION = {
|
|
17
|
-
"location_pid": "locid-1",
|
|
18
|
-
"legacy_ids": ["Test legacy id"],
|
|
19
|
-
"name": "My test internal location",
|
|
20
|
-
"physical_location": "Left from the right building",
|
|
21
|
-
"notes": "In house",
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def test_post_internal_location(client, json_headers, testdata, users):
|
|
26
|
-
"""Test POST of internal_location."""
|
|
27
|
-
user_login(client, "admin", users)
|
|
28
|
-
url = url_for("invenio_records_rest.ilocid_list")
|
|
29
|
-
res = validate_response(
|
|
30
|
-
client, "post", url, json_headers, NEW_INTERNAL_LOCATION, 201
|
|
31
|
-
)
|
|
32
|
-
data = json.loads(res.data.decode("utf-8"))["metadata"]
|
|
33
|
-
assert "name" in data["location"]
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def test_post_partial_internal_location(client, json_headers, testdata, users):
|
|
37
|
-
"""Test POST of internal_location without all required data."""
|
|
38
|
-
user_login(client, "admin", users)
|
|
39
|
-
del NEW_INTERNAL_LOCATION["location_pid"]
|
|
40
|
-
url = url_for("invenio_records_rest.ilocid_list")
|
|
41
|
-
validate_response(client, "post", url, json_headers, NEW_INTERNAL_LOCATION, 400)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def test_post_item(client, json_headers, testdata, users, item_record):
|
|
45
|
-
"""Test POST of an item."""
|
|
46
|
-
user_login(client, "admin", users)
|
|
47
|
-
url = url_for("invenio_records_rest.pitmid_list")
|
|
48
|
-
del item_record["pid"]
|
|
49
|
-
res = validate_response(client, "post", url, json_headers, item_record, 201)
|
|
50
|
-
data = json.loads(res.data.decode("utf-8"))["metadata"]
|
|
51
|
-
assert "name" in data["internal_location"]
|
|
@@ -1,206 +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 metadata extensions."""
|
|
9
|
-
|
|
10
|
-
import pytest
|
|
11
|
-
from invenio_indexer.api import RecordIndexer
|
|
12
|
-
from invenio_records_rest.schemas.fields import DateString, SanitizedUnicode
|
|
13
|
-
from marshmallow import ValidationError
|
|
14
|
-
from marshmallow.fields import Bool, Integer, List
|
|
15
|
-
|
|
16
|
-
from invenio_app_ils.documents.api import Document
|
|
17
|
-
from invenio_app_ils.records.metadata_extensions import MetadataExtensions
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@pytest.fixture(scope="module")
|
|
21
|
-
def app_config(app_config):
|
|
22
|
-
"""Override conftest.py's app_config"""
|
|
23
|
-
app_config["ILS_RECORDS_METADATA_NAMESPACES"] = {
|
|
24
|
-
"document": {
|
|
25
|
-
"accelerator_experiments": {
|
|
26
|
-
"@context": "https://example.com/accelerator_experiments/terms"
|
|
27
|
-
},
|
|
28
|
-
"standard_status": {
|
|
29
|
-
"@context": "https://example.com/standard_status/terms"
|
|
30
|
-
},
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
app_config["ILS_RECORDS_METADATA_EXTENSIONS"] = {
|
|
35
|
-
"document": {
|
|
36
|
-
"schema": Document._schema,
|
|
37
|
-
"fields": {
|
|
38
|
-
"accelerator_experiments_accelerator": {
|
|
39
|
-
"elasticsearch": "keyword",
|
|
40
|
-
"marshmallow": SanitizedUnicode(required=True),
|
|
41
|
-
},
|
|
42
|
-
"accelerator_experiments_project": {
|
|
43
|
-
"elasticsearch": "text",
|
|
44
|
-
"marshmallow": SanitizedUnicode(),
|
|
45
|
-
},
|
|
46
|
-
"accelerator_experiments_number_in_sequence": {
|
|
47
|
-
"elasticsearch": "long",
|
|
48
|
-
"marshmallow": Integer(),
|
|
49
|
-
},
|
|
50
|
-
"accelerator_experiments_scientific_sequence": {
|
|
51
|
-
"elasticsearch": "long",
|
|
52
|
-
"marshmallow": List(Integer()),
|
|
53
|
-
},
|
|
54
|
-
"standard_status_original_presentation_date": {
|
|
55
|
-
"elasticsearch": "date",
|
|
56
|
-
"marshmallow": DateString(),
|
|
57
|
-
},
|
|
58
|
-
"standard_status_right_or_wrong": {
|
|
59
|
-
"elasticsearch": "boolean",
|
|
60
|
-
"marshmallow": Bool(),
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return app_config
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def assert_unordered_equality(array_dict1, array_dict2):
|
|
70
|
-
array1 = sorted(array_dict1, key=lambda d: d["key"])
|
|
71
|
-
array2 = sorted(array_dict2, key=lambda d: d["key"])
|
|
72
|
-
assert array1 == array2
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def test_metadata_extensions(db, es, testdata):
|
|
76
|
-
"""Tests that a Record is indexed into Elasticsearch properly.
|
|
77
|
-
- Tests that the before_record_index_hook is registered properly.
|
|
78
|
-
- Tests add_es_metadata_extensions.
|
|
79
|
-
- Tests jsonschema validates correctly
|
|
80
|
-
- Tests that retrieved record document is fine.
|
|
81
|
-
NOTE:
|
|
82
|
-
- es fixture depends on appctx fixture, so we are in app context
|
|
83
|
-
- this test requires a running ES instance
|
|
84
|
-
"""
|
|
85
|
-
document = testdata["documents"][0]
|
|
86
|
-
data = {
|
|
87
|
-
"extensions": {
|
|
88
|
-
"accelerator_experiments_accelerator": "LHCb",
|
|
89
|
-
"accelerator_experiments_project": "A project for experiment.",
|
|
90
|
-
"accelerator_experiments_number_in_sequence": 3,
|
|
91
|
-
"accelerator_experiments_scientific_sequence": [1, 1, 2, 3, 5, 8],
|
|
92
|
-
"standard_status_original_presentation_date": "2019-02-14",
|
|
93
|
-
"standard_status_right_or_wrong": True,
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
document.update(data)
|
|
97
|
-
document.validate()
|
|
98
|
-
db.session.commit()
|
|
99
|
-
indexer = RecordIndexer()
|
|
100
|
-
index_result = indexer.index(document)
|
|
101
|
-
|
|
102
|
-
_index = index_result["_index"]
|
|
103
|
-
_id = index_result["_id"]
|
|
104
|
-
es_doc = es.get(index=_index, id=_id)
|
|
105
|
-
source = es_doc["_source"]
|
|
106
|
-
expected_keywords = [
|
|
107
|
-
{"key": "accelerator_experiments_accelerator", "value": "LHCb"},
|
|
108
|
-
]
|
|
109
|
-
expected_texts = [
|
|
110
|
-
{
|
|
111
|
-
"key": "accelerator_experiments_project",
|
|
112
|
-
"value": "A project for experiment.",
|
|
113
|
-
},
|
|
114
|
-
]
|
|
115
|
-
expected_longs = [
|
|
116
|
-
{"key": "accelerator_experiments_number_in_sequence", "value": 3},
|
|
117
|
-
{
|
|
118
|
-
"key": "accelerator_experiments_scientific_sequence",
|
|
119
|
-
"value": [1, 1, 2, 3, 5, 8],
|
|
120
|
-
},
|
|
121
|
-
]
|
|
122
|
-
expected_dates = [
|
|
123
|
-
{
|
|
124
|
-
"key": "standard_status_original_presentation_date",
|
|
125
|
-
"value": "2019-02-14",
|
|
126
|
-
},
|
|
127
|
-
]
|
|
128
|
-
expected_booleans = [
|
|
129
|
-
{"key": "standard_status_right_or_wrong", "value": True},
|
|
130
|
-
]
|
|
131
|
-
assert_unordered_equality(source["extensions_keywords"], expected_keywords)
|
|
132
|
-
assert_unordered_equality(source["extensions_texts"], expected_texts)
|
|
133
|
-
assert_unordered_equality(source["extensions_longs"], expected_longs)
|
|
134
|
-
assert_unordered_equality(source["extensions_dates"], expected_dates)
|
|
135
|
-
assert_unordered_equality(source["extensions_booleans"], expected_booleans)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
def test_extensions():
|
|
139
|
-
"""Test metadata extensions schema."""
|
|
140
|
-
ILS_RECORDS_METADATA_NAMESPACES = {
|
|
141
|
-
"accelerator_experiments": {
|
|
142
|
-
"@context": "https://example.com/accelerator_experiments/terms"
|
|
143
|
-
},
|
|
144
|
-
"standard_status": {"@context": "https://example.com/standard_status/terms"},
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
ILS_RECORDS_METADATA_EXTENSIONS = {
|
|
148
|
-
"accelerator_experiments_accelerator": {
|
|
149
|
-
"elasticsearch": "keyword",
|
|
150
|
-
"marshmallow": SanitizedUnicode(required=True),
|
|
151
|
-
},
|
|
152
|
-
"accelerator_experiments_project": {
|
|
153
|
-
"elasticsearch": "text",
|
|
154
|
-
"marshmallow": SanitizedUnicode(),
|
|
155
|
-
},
|
|
156
|
-
"accelerator_experiments_number_in_sequence": {
|
|
157
|
-
"elasticsearch": "long",
|
|
158
|
-
"marshmallow": Integer(),
|
|
159
|
-
},
|
|
160
|
-
"accelerator_experiments_scientific_sequence": {
|
|
161
|
-
"elasticsearch": "long",
|
|
162
|
-
"marshmallow": List(Integer()),
|
|
163
|
-
},
|
|
164
|
-
"standard_status_original_presentation_date": {
|
|
165
|
-
"elasticsearch": "date",
|
|
166
|
-
"marshmallow": DateString(),
|
|
167
|
-
},
|
|
168
|
-
"standard_status_right_or_wrong": {
|
|
169
|
-
"elasticsearch": "boolean",
|
|
170
|
-
"marshmallow": Bool(),
|
|
171
|
-
},
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
extensions = MetadataExtensions(
|
|
175
|
-
ILS_RECORDS_METADATA_NAMESPACES, ILS_RECORDS_METADATA_EXTENSIONS
|
|
176
|
-
)
|
|
177
|
-
ExtensionsSchema = extensions.to_schema()
|
|
178
|
-
|
|
179
|
-
# Minimal if not absent
|
|
180
|
-
valid_minimal = {"accelerator_experiments_accelerator": "LHCb"}
|
|
181
|
-
|
|
182
|
-
data = ExtensionsSchema().load(valid_minimal)
|
|
183
|
-
|
|
184
|
-
assert data == valid_minimal
|
|
185
|
-
|
|
186
|
-
# Full
|
|
187
|
-
valid_full = {
|
|
188
|
-
"accelerator_experiments_accelerator": "LHCb",
|
|
189
|
-
"accelerator_experiments_project": "A project for experiment.",
|
|
190
|
-
"accelerator_experiments_number_in_sequence": 3,
|
|
191
|
-
"accelerator_experiments_scientific_sequence": [1, 1, 2, 3, 5, 8],
|
|
192
|
-
"standard_status_original_presentation_date": "2019-02-14",
|
|
193
|
-
"standard_status_right_or_wrong": True,
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
data = ExtensionsSchema().load(valid_full)
|
|
197
|
-
|
|
198
|
-
assert data == valid_full
|
|
199
|
-
|
|
200
|
-
# Invalid
|
|
201
|
-
invalid_number_in_sequence = {
|
|
202
|
-
"accelerator_experiments_accelerator": "LHCb",
|
|
203
|
-
"accelerator_experiments_scientific_sequence": [1, "l", 2, 3, 5, 8],
|
|
204
|
-
}
|
|
205
|
-
with pytest.raises(ValidationError):
|
|
206
|
-
data = ExtensionsSchema().load(invalid_number_in_sequence)
|
|
@@ -1,173 +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 notifications."""
|
|
9
|
-
|
|
10
|
-
import pytest
|
|
11
|
-
from flask import url_for
|
|
12
|
-
from jinja2.exceptions import TemplateError, TemplateNotFound
|
|
13
|
-
|
|
14
|
-
from invenio_app_ils.notifications.api import (
|
|
15
|
-
log_error_notification,
|
|
16
|
-
log_successful_notification,
|
|
17
|
-
send_notification,
|
|
18
|
-
)
|
|
19
|
-
from invenio_app_ils.notifications.messages import NotificationMsg
|
|
20
|
-
from invenio_app_ils.notifications.models import NotificationsLogs
|
|
21
|
-
from invenio_app_ils.patrons.api import Patron
|
|
22
|
-
from tests.helpers import user_login
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def test_message_full(app_with_notifs):
|
|
26
|
-
"""Test that the title, body and html are read correctly."""
|
|
27
|
-
with app_with_notifs.app_context():
|
|
28
|
-
# remove footer
|
|
29
|
-
app_with_notifs.config["ILS_NOTIFICATIONS_TEMPLATES"] = {}
|
|
30
|
-
|
|
31
|
-
full = NotificationMsg("notifications/title_body_html.html")
|
|
32
|
-
assert full.title == "Test title."
|
|
33
|
-
assert "Test body." == full.body_plain
|
|
34
|
-
assert "Test html." == full.body_html
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def test_message_body_same_as_html(app_with_notifs):
|
|
38
|
-
"""Test that the title and body are read correctly."""
|
|
39
|
-
with app_with_notifs.app_context():
|
|
40
|
-
# remove footer
|
|
41
|
-
app_with_notifs.config["ILS_NOTIFICATIONS_TEMPLATES"] = {}
|
|
42
|
-
|
|
43
|
-
blank = NotificationMsg("notifications/title_body.html")
|
|
44
|
-
assert blank.title == "Test title."
|
|
45
|
-
assert "Test body." == blank.body_plain
|
|
46
|
-
assert "Test body." == blank.body_html
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def test_invalid_message_templates(app_with_notifs):
|
|
50
|
-
"""Test invalid templates."""
|
|
51
|
-
with app_with_notifs.app_context():
|
|
52
|
-
with pytest.raises(TemplateError) as ex:
|
|
53
|
-
NotificationMsg("notifications/blank.html")
|
|
54
|
-
assert "No block with name 'title'" in str(ex.value)
|
|
55
|
-
|
|
56
|
-
with pytest.raises(TemplateError) as ex:
|
|
57
|
-
NotificationMsg("notifications/title_only.html")
|
|
58
|
-
assert "No block with name 'body_plain'" in str(ex.value)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def test_message_templates_escaping(app_with_notifs):
|
|
62
|
-
"""Test templates escaping."""
|
|
63
|
-
title = (
|
|
64
|
-
"Spèciâl chäráctèrs: "
|
|
65
|
-
"`,~,!,@,#,$,%,^,&,*,(,),_,-,+,=,{,[,},},|,\\,:,;,',<,>,.,?,/,º,ª "
|
|
66
|
-
"<script>alert('test');</script>"
|
|
67
|
-
)
|
|
68
|
-
ctx = dict(title=title)
|
|
69
|
-
with app_with_notifs.app_context():
|
|
70
|
-
# remove footer
|
|
71
|
-
app_with_notifs.config["ILS_NOTIFICATIONS_TEMPLATES"] = {}
|
|
72
|
-
|
|
73
|
-
full = NotificationMsg("notifications/title_body_html_ctx.html", ctx)
|
|
74
|
-
|
|
75
|
-
assert full.title == 'Test "{0}" title.'.format(title)
|
|
76
|
-
|
|
77
|
-
from markupsafe import escape
|
|
78
|
-
|
|
79
|
-
escaped = escape(title)
|
|
80
|
-
assert 'Test "{0}" body.'.format(escaped) == full.body_plain
|
|
81
|
-
assert 'Test "{0}" html.'.format(escaped) == full.body_html
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def test_message_with_missing_template(app_with_notifs):
|
|
85
|
-
"""Test that a missing template raises an exception."""
|
|
86
|
-
with app_with_notifs.app_context():
|
|
87
|
-
with pytest.raises(TemplateNotFound):
|
|
88
|
-
NotificationMsg("missing.html")
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
def test_send_message_backend(app_with_notifs, users, testdata, mocker):
|
|
92
|
-
"""Test that notifications backend is called when sending."""
|
|
93
|
-
send_mocked = mocker.patch("invenio_app_ils.notifications.backends.mail.send")
|
|
94
|
-
send_mocked.__name__ = "send"
|
|
95
|
-
send_mocked.__annotations__ = "send"
|
|
96
|
-
backends = mocker.patch(
|
|
97
|
-
"invenio_app_ils.notifications.api._get_notification_backends",
|
|
98
|
-
return_value=[send_mocked],
|
|
99
|
-
)
|
|
100
|
-
with app_with_notifs.app_context():
|
|
101
|
-
# remove footer
|
|
102
|
-
app_with_notifs.config["ILS_NOTIFICATIONS_TEMPLATES"] = {}
|
|
103
|
-
|
|
104
|
-
msg = NotificationMsg("notifications/title_body_html.html")
|
|
105
|
-
patrons = [Patron(users["patron1"].id)]
|
|
106
|
-
send_notification(patrons, msg)
|
|
107
|
-
|
|
108
|
-
assert send_mocked.apply_async.called
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
class FakeMessage(NotificationMsg):
|
|
112
|
-
def __init__(self, *args, **kwargs):
|
|
113
|
-
template = kwargs.pop("template", "notifications/title_body_html.html")
|
|
114
|
-
super().__init__(template, **kwargs)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
def test_log_successful_error_mail_task(app_with_notifs, users, testdata, mocker):
|
|
118
|
-
"""Test that a successfully sent email is logged."""
|
|
119
|
-
# !attention the mocker has to patch function where it is used, not defined
|
|
120
|
-
succ = mocker.patch("invenio_app_ils.notifications.api.log_successful_notification")
|
|
121
|
-
err = mocker.patch("invenio_app_ils.notifications.api.log_error_notification")
|
|
122
|
-
|
|
123
|
-
patron = Patron(users["patron1"].id)
|
|
124
|
-
|
|
125
|
-
assert not succ.s.called
|
|
126
|
-
assert not err.s.called
|
|
127
|
-
send_notification([patron], FakeMessage())
|
|
128
|
-
assert succ.s.called
|
|
129
|
-
assert err.s.called
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
def test_notifications_db_table_and_endpoint(
|
|
133
|
-
users, client, json_headers, app_with_notifs
|
|
134
|
-
):
|
|
135
|
-
"""Test logging of notifs in db table and read from endpoint."""
|
|
136
|
-
request = {"id": "test-id", "task": "test-task"}
|
|
137
|
-
data = {
|
|
138
|
-
"id": "test-id",
|
|
139
|
-
"recipients": ["patron1@test.com"],
|
|
140
|
-
"is_manually_triggered": True,
|
|
141
|
-
"message_id": "1",
|
|
142
|
-
"patron_id": "1",
|
|
143
|
-
}
|
|
144
|
-
exc = "An error occurred."
|
|
145
|
-
log_successful_notification(None, data)
|
|
146
|
-
|
|
147
|
-
log_error_notification(request=request, exc=exc, traceback=None, data=data)
|
|
148
|
-
|
|
149
|
-
assert NotificationsLogs.query.filter_by(id=1).one().send_log == "Success"
|
|
150
|
-
assert (
|
|
151
|
-
NotificationsLogs.query.filter_by(id=2).one().send_log
|
|
152
|
-
== "Error: 'An error occurred.'"
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
ITEM_ENDPOINT = "invenio_app_ils_notifications.get_notification"
|
|
156
|
-
LIST_ENDPOINT = "invenio_app_ils_notifications.get_notifications"
|
|
157
|
-
|
|
158
|
-
user_login(client, "librarian", users)
|
|
159
|
-
url = url_for(LIST_ENDPOINT)
|
|
160
|
-
res = client.get(url, headers=json_headers)
|
|
161
|
-
assert res.get_json()["hits"][0]["send_log"] == "Error: 'An error occurred.'"
|
|
162
|
-
assert res.get_json()["hits"][1]["send_log"] == "Success"
|
|
163
|
-
|
|
164
|
-
url = url_for(ITEM_ENDPOINT, id=1)
|
|
165
|
-
res = client.get(url, headers=json_headers)
|
|
166
|
-
assert res.get_json()["send_log"] == "Success"
|
|
167
|
-
assert res.get_json()["id"] == 1
|
|
168
|
-
|
|
169
|
-
url = url_for(ITEM_ENDPOINT, id=2)
|
|
170
|
-
res = client.get(url, headers=json_headers)
|
|
171
|
-
|
|
172
|
-
assert res.get_json()["send_log"] == "Error: 'An error occurred.'"
|
|
173
|
-
assert res.get_json()["id"] == 2
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Copyright (C) 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 notifications backend emails."""
|
|
9
|
-
|
|
10
|
-
from invenio_app_ils.notifications.api import send_notification
|
|
11
|
-
from invenio_app_ils.notifications.messages import NotificationMsg
|
|
12
|
-
from invenio_app_ils.patrons.api import Patron
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class FakeMessage(NotificationMsg):
|
|
16
|
-
def __init__(self, *args, **kwargs):
|
|
17
|
-
template = kwargs.pop("template", "notifications/title_body_html.html")
|
|
18
|
-
super().__init__(template, **kwargs)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def test_send_only_to_test_recipients(app_with_notifs, users, testdata, mocker):
|
|
22
|
-
"""Tests that send only to test recipients works."""
|
|
23
|
-
app_with_notifs.config["ILS_MAIL_ENABLE_TEST_RECIPIENTS"] = True
|
|
24
|
-
succ = mocker.patch("invenio_app_ils.notifications.api.log_successful_notification")
|
|
25
|
-
|
|
26
|
-
msg = FakeMessage()
|
|
27
|
-
patron = Patron(users["patron1"].id)
|
|
28
|
-
|
|
29
|
-
fake_recipients = app_with_notifs.config["ILS_MAIL_NOTIFY_TEST_RECIPIENTS"]
|
|
30
|
-
with app_with_notifs.extensions["mail"].record_messages() as outbox:
|
|
31
|
-
assert len(outbox) == 0
|
|
32
|
-
send_notification([patron], msg)
|
|
33
|
-
assert succ.s.called
|
|
34
|
-
assert len(outbox) == 1
|
|
35
|
-
assert outbox[0].recipients == fake_recipients
|
|
36
|
-
|
|
37
|
-
app_with_notifs.config["ILS_MAIL_ENABLE_TEST_RECIPIENTS"] = False
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# Copyright (C) 2021 CERN.
|
|
2
|
-
#
|
|
3
|
-
# invenio-app-ils is free software; you can redistribute it and/or modify it
|
|
4
|
-
# under the terms of the MIT License; see LICENSE file for more details.
|
|
5
|
-
|
|
6
|
-
"""Test notifications permissions."""
|
|
7
|
-
|
|
8
|
-
from flask import url_for
|
|
9
|
-
|
|
10
|
-
from tests.helpers import user_login
|
|
11
|
-
|
|
12
|
-
_HTTP_OK = [200, 201, 204]
|
|
13
|
-
_HTTP_UNAUTHORIZED = [401]
|
|
14
|
-
_HTTP_FORBIDDEN = [403]
|
|
15
|
-
_HTTP_NOT_FOUND = [404]
|
|
16
|
-
MAIL_ID = 1
|
|
17
|
-
ITEM_ENDPOINT = "invenio_app_ils_notifications.get_notification"
|
|
18
|
-
LIST_ENDPOINT = "invenio_app_ils_notifications.get_notifications"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def test_notifications_read_permissions(client, json_headers, users):
|
|
22
|
-
"""Test notifications endpoints read permissions."""
|
|
23
|
-
list_tests = [
|
|
24
|
-
("admin", _HTTP_OK),
|
|
25
|
-
("librarian", _HTTP_OK),
|
|
26
|
-
("patron1", _HTTP_FORBIDDEN),
|
|
27
|
-
("anonymous", _HTTP_UNAUTHORIZED),
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
item_tests = [
|
|
31
|
-
("admin", _HTTP_NOT_FOUND),
|
|
32
|
-
("librarian", _HTTP_NOT_FOUND),
|
|
33
|
-
("patron1", _HTTP_FORBIDDEN),
|
|
34
|
-
("anonymous", _HTTP_UNAUTHORIZED),
|
|
35
|
-
]
|
|
36
|
-
|
|
37
|
-
def _test_list(expected_status):
|
|
38
|
-
"""Test get list."""
|
|
39
|
-
url = url_for(LIST_ENDPOINT)
|
|
40
|
-
res = client.get(url, headers=json_headers)
|
|
41
|
-
assert res.status_code in expected_status
|
|
42
|
-
|
|
43
|
-
def _test_read(expected_status, id):
|
|
44
|
-
"""Test record read."""
|
|
45
|
-
url = url_for(ITEM_ENDPOINT, id=id)
|
|
46
|
-
res = client.get(url, headers=json_headers)
|
|
47
|
-
assert res.status_code in expected_status
|
|
48
|
-
|
|
49
|
-
for username, expected_status in list_tests:
|
|
50
|
-
user_login(client, username, users)
|
|
51
|
-
_test_list(expected_status)
|
|
52
|
-
|
|
53
|
-
for username, expected_status in item_tests:
|
|
54
|
-
user_login(client, username, users)
|
|
55
|
-
_test_read(expected_status, MAIL_ID)
|
tests/api/ils/test_patrons.py
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Copyright (C) 2019 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 patrons endpoint."""
|
|
9
|
-
|
|
10
|
-
import json
|
|
11
|
-
|
|
12
|
-
from flask import url_for
|
|
13
|
-
|
|
14
|
-
from tests.helpers import user_login
|
|
15
|
-
|
|
16
|
-
_LIST_ENDPOINT = "invenio_records_rest.patid_list"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def _patron_loans_request(client, json_headers, document_pid):
|
|
20
|
-
"""Perform a patron loans request."""
|
|
21
|
-
response = client.get(
|
|
22
|
-
url_for(
|
|
23
|
-
"invenio_app_ils_patrons.get_user_information",
|
|
24
|
-
document_pid=document_pid,
|
|
25
|
-
),
|
|
26
|
-
headers=json_headers,
|
|
27
|
-
)
|
|
28
|
-
return json.loads(response.data.decode("utf-8"))
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def _assert_patron_loans_information(client, json_headers, document_pid, expect):
|
|
32
|
-
"""Assert patron loan information."""
|
|
33
|
-
resp = _patron_loans_request(client, json_headers, document_pid)
|
|
34
|
-
assert resp == expect
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def _assert_error_when_not_authenticated(client, json_headers, document_pid):
|
|
38
|
-
"""Assert user is not logged in."""
|
|
39
|
-
resp = _patron_loans_request(client, json_headers, document_pid)
|
|
40
|
-
assert resp["status"] == 401
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def test_patrons_permissions(client, testdata, json_headers, users):
|
|
44
|
-
"""Test patron endpoints permissions."""
|
|
45
|
-
tests = [
|
|
46
|
-
("admin", [200]),
|
|
47
|
-
("librarian", [200]),
|
|
48
|
-
("patron1", [403]),
|
|
49
|
-
("anonymous", [401]),
|
|
50
|
-
]
|
|
51
|
-
|
|
52
|
-
def _test_list(expected_status):
|
|
53
|
-
"""Test get list."""
|
|
54
|
-
url = url_for(_LIST_ENDPOINT)
|
|
55
|
-
res = client.get(url, headers=json_headers)
|
|
56
|
-
assert res.status_code in expected_status
|
|
57
|
-
|
|
58
|
-
for username, expected_status in tests:
|
|
59
|
-
user_login(client, username, users)
|
|
60
|
-
_test_list(expected_status)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def test_patron_loans_information(client, json_headers, testdata_most_loaned, users):
|
|
64
|
-
"""Test patron loans information API endpoint."""
|
|
65
|
-
_assert_error_when_not_authenticated(client, json_headers, "docid-1")
|
|
66
|
-
user_login(client, "librarian", users)
|
|
67
|
-
|
|
68
|
-
_assert_patron_loans_information(
|
|
69
|
-
client,
|
|
70
|
-
json_headers,
|
|
71
|
-
"docid-1",
|
|
72
|
-
expect={
|
|
73
|
-
"has_active_loan": False,
|
|
74
|
-
"is_requested": False,
|
|
75
|
-
"last_loan": None,
|
|
76
|
-
},
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
user_login(client, "patron1", users)
|
|
80
|
-
|
|
81
|
-
_assert_patron_loans_information(
|
|
82
|
-
client,
|
|
83
|
-
json_headers,
|
|
84
|
-
"docid-1",
|
|
85
|
-
expect={
|
|
86
|
-
"has_active_loan": False,
|
|
87
|
-
"is_requested": True,
|
|
88
|
-
"last_loan": "2019-04-02",
|
|
89
|
-
},
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
user_login(client, "patron2", users)
|
|
93
|
-
_assert_patron_loans_information(
|
|
94
|
-
client,
|
|
95
|
-
json_headers,
|
|
96
|
-
"docid-3",
|
|
97
|
-
expect={
|
|
98
|
-
"has_active_loan": False,
|
|
99
|
-
"is_requested": True,
|
|
100
|
-
"last_loan": None,
|
|
101
|
-
},
|
|
102
|
-
)
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Copyright (C) 2018-19 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 record delete."""
|
|
9
|
-
|
|
10
|
-
from flask import url_for
|
|
11
|
-
|
|
12
|
-
from tests.helpers import user_login
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def test_delete_location(client, users, json_headers, testdata):
|
|
16
|
-
"""Test DELETE existing location."""
|
|
17
|
-
user_login(client, "admin", users)
|
|
18
|
-
|
|
19
|
-
location_pid = "locid-1"
|
|
20
|
-
url = url_for("invenio_records_rest.locid_item", pid_value=location_pid)
|
|
21
|
-
res = client.delete(url, headers=json_headers)
|
|
22
|
-
assert res.status_code == 400
|
|
23
|
-
|
|
24
|
-
location_pid = "locid-3"
|
|
25
|
-
url = url_for("invenio_records_rest.locid_item", pid_value=location_pid)
|
|
26
|
-
res = client.delete(url, headers=json_headers)
|
|
27
|
-
assert res.status_code == 204
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def test_delete_internal_location(client, users, json_headers, testdata):
|
|
31
|
-
"""Test DELETE existing internal_location."""
|
|
32
|
-
user_login(client, "admin", users)
|
|
33
|
-
|
|
34
|
-
internal_location_pid = "ilocid-1"
|
|
35
|
-
url = url_for("invenio_records_rest.ilocid_item", pid_value=internal_location_pid)
|
|
36
|
-
res = client.delete(url, headers=json_headers)
|
|
37
|
-
assert res.status_code == 400
|
|
38
|
-
|
|
39
|
-
internal_location_pid = "ilocid-3"
|
|
40
|
-
url = url_for("invenio_records_rest.ilocid_item", pid_value=internal_location_pid)
|
|
41
|
-
res = client.delete(url, headers=json_headers)
|
|
42
|
-
assert res.status_code == 204
|