invenio-app-rdm 13.0.0b2.dev2__py2.py3-none-any.whl → 13.0.0b2.dev4__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_rdm/__init__.py +1 -1
- invenio_app_rdm/config.py +4 -2
- invenio_app_rdm/records_ui/templates/semantic-ui/invenio_app_rdm/records/details/meta.html +3 -1
- invenio_app_rdm/records_ui/views/decorators.py +91 -8
- invenio_app_rdm/records_ui/views/records.py +7 -4
- invenio_app_rdm/tasks.py +6 -2
- invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/components/CompareRevisionsDropdown.js +6 -0
- invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/components/RevisionsDiffViewer.js +5 -0
- invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/records/CompareRevisions.js +10 -9
- invenio_app_rdm/theme/assets/semantic-ui/less/invenio_app_rdm/theme/globals/site.overrides +1 -1
- invenio_app_rdm/theme/templates/semantic-ui/invenio_app_rdm/help/search.en.html +1 -1
- invenio_app_rdm/urls.py +11 -1
- {invenio_app_rdm-13.0.0b2.dev2.dist-info → invenio_app_rdm-13.0.0b2.dev4.dist-info}/METADATA +20 -1
- {invenio_app_rdm-13.0.0b2.dev2.dist-info → invenio_app_rdm-13.0.0b2.dev4.dist-info}/RECORD +50 -18
- {invenio_app_rdm-13.0.0b2.dev2.dist-info → invenio_app_rdm-13.0.0b2.dev4.dist-info}/WHEEL +1 -1
- {invenio_app_rdm-13.0.0b2.dev2.dist-info → invenio_app_rdm-13.0.0b2.dev4.dist-info}/top_level.txt +1 -0
- tests/__init__.py +8 -0
- tests/api/__init__.py +8 -0
- tests/api/conftest.py +24 -0
- tests/api/test_protect_files_rest.py +73 -0
- tests/api/test_record_api.py +175 -0
- tests/api/test_stats_api.py +26 -0
- tests/conftest.py +313 -0
- tests/fixtures/__init__.py +8 -0
- tests/fixtures/app_data/oai_sets.yaml +3 -0
- tests/fixtures/app_data/pages/about.html +1 -0
- tests/fixtures/app_data/pages.yaml +4 -0
- tests/fixtures/conftest.py +27 -0
- tests/fixtures/test_cli.py +25 -0
- tests/fixtures/test_fixtures.py +46 -0
- tests/mock_module/__init__.py +7 -0
- tests/mock_module/templates/mock_mail.html +28 -0
- tests/mock_module/views.py +32 -0
- tests/redirector/__init__.py +8 -0
- tests/redirector/conftest.py +54 -0
- tests/redirector/test_redirector.py +28 -0
- tests/test_tasks.py +104 -0
- tests/test_utils.py +67 -0
- tests/test_version.py +16 -0
- tests/test_views.py +43 -0
- tests/ui/__init__.py +8 -0
- tests/ui/conftest.py +105 -0
- tests/ui/test_deposits.py +115 -0
- tests/ui/test_export_formats.py +37 -0
- tests/ui/test_filters.py +10 -0
- tests/ui/test_signposting_ui.py +95 -0
- tests/ui/test_static.py +25 -0
- tests/ui/test_stats_ui.py +92 -0
- {invenio_app_rdm-13.0.0b2.dev2.dist-info → invenio_app_rdm-13.0.0b2.dev4.dist-info}/LICENSE +0 -0
- {invenio_app_rdm-13.0.0b2.dev2.dist-info → invenio_app_rdm-13.0.0b2.dev4.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2021 CERN.
|
|
4
|
+
# Copyright (C) 2021 Northwestern University.
|
|
5
|
+
#
|
|
6
|
+
# Invenio-RDM-Records is free software; you can redistribute it and/or modify
|
|
7
|
+
# it under the terms of the MIT License; see LICENSE file for more details.
|
|
8
|
+
|
|
9
|
+
"""Test files-rest is protected."""
|
|
10
|
+
|
|
11
|
+
from io import BytesIO
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def create_draft(client, record, headers):
|
|
15
|
+
"""Create draft and return its id."""
|
|
16
|
+
response = client.post("/records", json=record, headers=headers)
|
|
17
|
+
assert response.status_code == 201
|
|
18
|
+
return response.json["id"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def init_file(client, recid, headers):
|
|
22
|
+
"""Init a file for draft with given recid."""
|
|
23
|
+
return client.post(
|
|
24
|
+
f"/records/{recid}/draft/files", headers=headers, json=[{"key": "test.pdf"}]
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def upload_file(client, recid):
|
|
29
|
+
"""Create draft and return its id."""
|
|
30
|
+
return client.put(
|
|
31
|
+
f"/records/{recid}/draft/files/test.pdf/content",
|
|
32
|
+
headers={
|
|
33
|
+
"content-type": "application/octet-stream",
|
|
34
|
+
"accept": "application/json",
|
|
35
|
+
},
|
|
36
|
+
data=BytesIO(b"testfile"),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def commit_file(client, recid, headers):
|
|
41
|
+
"""Create draft and return its id."""
|
|
42
|
+
return client.post(f"/records/{recid}/draft/files/test.pdf/commit", headers=headers)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# NOTE: It seems like it was already the case that a logged in user wouldn't be
|
|
46
|
+
# able to access files-rest. We are just making doubly-clear.
|
|
47
|
+
def test_files_rest_endpoint_is_protected(
|
|
48
|
+
running_app, client_with_login, headers, es_clear, minimal_record
|
|
49
|
+
):
|
|
50
|
+
client = client_with_login
|
|
51
|
+
|
|
52
|
+
# Create draft with file
|
|
53
|
+
minimal_record["files"] = {"enabled": True}
|
|
54
|
+
recid = create_draft(client, minimal_record, headers)
|
|
55
|
+
init_file(client, recid, headers)
|
|
56
|
+
upload_file(client, recid)
|
|
57
|
+
commit_file(client, recid, headers)
|
|
58
|
+
|
|
59
|
+
# Get bucket information
|
|
60
|
+
url = f"/records/{recid}/draft/files/test.pdf"
|
|
61
|
+
response = client.get(url, headers=headers)
|
|
62
|
+
bucket_id = response.json["bucket_id"]
|
|
63
|
+
|
|
64
|
+
# Nobody is allowed to use the invenio-files-rest endpoints
|
|
65
|
+
# (even logged-in user). Just testing for the GET of each is enough
|
|
66
|
+
|
|
67
|
+
bucket_url = f"/files/{bucket_id}"
|
|
68
|
+
response = client.get(bucket_url, headers=headers)
|
|
69
|
+
assert 404 == response.status_code # because of files-rest hiding feature
|
|
70
|
+
|
|
71
|
+
bucket_key_url = f"/files/{bucket_id}/test.pdf"
|
|
72
|
+
response = client.get(bucket_key_url, headers=headers)
|
|
73
|
+
assert 404 == response.status_code
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2019-2021 CERN.
|
|
4
|
+
# Copyright (C) 2019-2021 Northwestern University.
|
|
5
|
+
# Copyright (C) 2024 Graz University of Technology.
|
|
6
|
+
#
|
|
7
|
+
# Invenio-RDM-Records is free software; you can redistribute it and/or modify
|
|
8
|
+
# it under the terms of the MIT License; see LICENSE file for more details.
|
|
9
|
+
|
|
10
|
+
"""Module tests."""
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
from invenio_pidstore.models import PersistentIdentifier
|
|
14
|
+
|
|
15
|
+
SINGLE_RECORD_API_URL = "/records/{}"
|
|
16
|
+
LIST_RECORDS_API_URL = "/records"
|
|
17
|
+
DRAFT_API_URL = "/records/{}/draft"
|
|
18
|
+
DRAFT_ACTION_API_URL = "/records/{}/draft/actions/{}"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_record_read_non_existing_pid(client, location, minimal_record, es_clear):
|
|
22
|
+
"""Retrieve a non existing record."""
|
|
23
|
+
# retrieve unknown record
|
|
24
|
+
response = client.get(SINGLE_RECORD_API_URL.format("notfound"))
|
|
25
|
+
assert response.status_code == 404
|
|
26
|
+
assert response.json["status"] == 404
|
|
27
|
+
assert response.json["message"] == "The persistent identifier does not exist."
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_record_draft_create_and_read(
|
|
31
|
+
client_with_login, running_app, minimal_record, es_clear
|
|
32
|
+
):
|
|
33
|
+
"""Test draft creation of a non-existing record."""
|
|
34
|
+
# create a record
|
|
35
|
+
client = client_with_login
|
|
36
|
+
response = client.post(LIST_RECORDS_API_URL, json=minimal_record)
|
|
37
|
+
|
|
38
|
+
assert response.status_code == 201
|
|
39
|
+
|
|
40
|
+
response_fields = response.json.keys()
|
|
41
|
+
fields_to_check = ["id", "metadata", "revision_id", "created", "updated", "links"]
|
|
42
|
+
|
|
43
|
+
for field in fields_to_check:
|
|
44
|
+
assert field in response_fields
|
|
45
|
+
|
|
46
|
+
recid = response.json["id"]
|
|
47
|
+
|
|
48
|
+
# retrieve record draft
|
|
49
|
+
response = client.get(DRAFT_API_URL.format(recid))
|
|
50
|
+
assert response.status_code == 200
|
|
51
|
+
assert response.json is not None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_record_draft_publish(
|
|
55
|
+
client_with_login, headers, running_app, minimal_record, es_clear
|
|
56
|
+
):
|
|
57
|
+
"""Test draft publication of a non-existing record.
|
|
58
|
+
|
|
59
|
+
It has to first create said draft and includes record read.
|
|
60
|
+
"""
|
|
61
|
+
# Create the draft
|
|
62
|
+
client = client_with_login
|
|
63
|
+
response = client.post(LIST_RECORDS_API_URL, json=minimal_record, headers=headers)
|
|
64
|
+
|
|
65
|
+
assert response.status_code == 201
|
|
66
|
+
recid = response.json["id"]
|
|
67
|
+
|
|
68
|
+
# Publish it
|
|
69
|
+
response = client.post(
|
|
70
|
+
DRAFT_ACTION_API_URL.format(recid, "publish"), headers=headers
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
assert response.status_code == 202
|
|
74
|
+
response_fields = response.json.keys()
|
|
75
|
+
fields_to_check = ["id", "metadata", "revision_id", "created", "updated", "links"]
|
|
76
|
+
|
|
77
|
+
for field in fields_to_check:
|
|
78
|
+
assert field in response_fields
|
|
79
|
+
|
|
80
|
+
response = client.get(DRAFT_API_URL.format(recid), headers=headers)
|
|
81
|
+
assert response.status_code == 404
|
|
82
|
+
|
|
83
|
+
# Test record exists
|
|
84
|
+
response = client.get(SINGLE_RECORD_API_URL.format(recid), headers=headers)
|
|
85
|
+
|
|
86
|
+
assert response.status_code == 200
|
|
87
|
+
|
|
88
|
+
response_fields = response.json.keys()
|
|
89
|
+
fields_to_check = ["id", "metadata", "revision_id", "created", "updated", "links"]
|
|
90
|
+
|
|
91
|
+
for field in fields_to_check:
|
|
92
|
+
assert field in response_fields
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_read_record_with_redirected_pid(
|
|
96
|
+
client_with_login, headers, running_app, minimal_record, es_clear
|
|
97
|
+
):
|
|
98
|
+
"""Test read a record with a redirected pid."""
|
|
99
|
+
# Create dummy record
|
|
100
|
+
client = client_with_login
|
|
101
|
+
response = client.post(LIST_RECORDS_API_URL, headers=headers, json=minimal_record)
|
|
102
|
+
assert response.status_code == 201
|
|
103
|
+
# Publish it
|
|
104
|
+
pid1_value = response.json["id"]
|
|
105
|
+
response = client.post(
|
|
106
|
+
DRAFT_ACTION_API_URL.format(pid1_value, "publish"), headers=headers
|
|
107
|
+
)
|
|
108
|
+
assert response.status_code == 202
|
|
109
|
+
|
|
110
|
+
# Create another dummy record
|
|
111
|
+
response = client.post(LIST_RECORDS_API_URL, headers=headers, json=minimal_record)
|
|
112
|
+
assert response.status_code == 201
|
|
113
|
+
pid2_value = response.json["id"]
|
|
114
|
+
# Publish it
|
|
115
|
+
response = client.post(
|
|
116
|
+
DRAFT_ACTION_API_URL.format(pid2_value, "publish"), headers=headers
|
|
117
|
+
)
|
|
118
|
+
assert response.status_code == 202
|
|
119
|
+
|
|
120
|
+
# redirect pid1 to pid2
|
|
121
|
+
pid1 = PersistentIdentifier.get("recid", pid1_value)
|
|
122
|
+
pid2 = PersistentIdentifier.get("recid", pid2_value)
|
|
123
|
+
pid1.redirect(pid2)
|
|
124
|
+
|
|
125
|
+
response = client.get(SINGLE_RECORD_API_URL.format(pid1.pid_value), headers=headers)
|
|
126
|
+
assert response.status_code == 301
|
|
127
|
+
|
|
128
|
+
assert response.json["status"] == 301
|
|
129
|
+
assert response.json["message"] == "Moved Permanently."
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@pytest.mark.skip()
|
|
133
|
+
def test_read_deleted_record(
|
|
134
|
+
client_with_login, headers, location, minimal_record, es_clear, administration_user
|
|
135
|
+
):
|
|
136
|
+
"""Test read a deleted record."""
|
|
137
|
+
client = client_with_login
|
|
138
|
+
|
|
139
|
+
# Create dummy record to test delete
|
|
140
|
+
response = client.post(LIST_RECORDS_API_URL, headers=headers, json=minimal_record)
|
|
141
|
+
assert response.status_code == 201
|
|
142
|
+
recid = response.json["id"]
|
|
143
|
+
# Publish it
|
|
144
|
+
response = client.post(
|
|
145
|
+
DRAFT_ACTION_API_URL.format(recid, "publish"), headers=headers
|
|
146
|
+
)
|
|
147
|
+
assert response.status_code == 202
|
|
148
|
+
|
|
149
|
+
# Delete the record
|
|
150
|
+
response = client.delete(SINGLE_RECORD_API_URL.format(recid), headers=headers)
|
|
151
|
+
assert response.status_code == 204
|
|
152
|
+
|
|
153
|
+
# Read the deleted record
|
|
154
|
+
response = client.get(SINGLE_RECORD_API_URL.format(recid), headers=headers)
|
|
155
|
+
assert response.status_code == 410
|
|
156
|
+
assert response.json["message"] == "The record has been deleted."
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def test_record_search(client, headers, running_app, es_clear):
|
|
160
|
+
"""Test record search."""
|
|
161
|
+
expected_response_keys = set(["hits", "links", "aggregations"])
|
|
162
|
+
expected_metadata_keys = set(["resource_type", "creators", "titles"])
|
|
163
|
+
|
|
164
|
+
# Get published bibliographic records
|
|
165
|
+
response = client.get(LIST_RECORDS_API_URL, headers=headers)
|
|
166
|
+
|
|
167
|
+
assert response.status_code == 200
|
|
168
|
+
response_keys = set(response.json.keys())
|
|
169
|
+
# The datamodel has other tests (jsonschemas, mappings, schemas)
|
|
170
|
+
# Here we just want to crosscheck the important ones are there.
|
|
171
|
+
assert expected_response_keys.issubset(response_keys)
|
|
172
|
+
|
|
173
|
+
for r in response.json["hits"]["hits"]:
|
|
174
|
+
metadata_keys = set(r["metadata"])
|
|
175
|
+
assert expected_metadata_keys.issubset(metadata_keys)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2023 CERN.
|
|
4
|
+
# Copyright (C) 2024 Graz University of Technology.
|
|
5
|
+
#
|
|
6
|
+
# Invenio App RDM is free software; you can redistribute it and/or modify it
|
|
7
|
+
# under the terms of the MIT License; see LICENSE file for more details.
|
|
8
|
+
|
|
9
|
+
"""Test the statistics integration."""
|
|
10
|
+
|
|
11
|
+
from invenio_accounts.testutils import login_user_via_session
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_ui_event_emission(running_app, headers, client, administration_user):
|
|
15
|
+
"""It is expected that the REST API endpoint for the statistics is disabled."""
|
|
16
|
+
login_user_via_session(client, email=administration_user.email)
|
|
17
|
+
|
|
18
|
+
# NOTE: the permissions are only relevant per requested query type ("stat")
|
|
19
|
+
data = {"my-query": {"stat": "record-view", "params": {"recid": "doesnt-matter"}}}
|
|
20
|
+
result = client.post("/stats", headers=headers, json=data)
|
|
21
|
+
assert result.status_code == 403
|
|
22
|
+
|
|
23
|
+
# i.e. if no queries are requested, nothing will be denied
|
|
24
|
+
result = client.post("/stats", headers=headers, json={})
|
|
25
|
+
assert result.status_code == 200
|
|
26
|
+
assert result.json == {}
|
tests/conftest.py
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2019-2025 CERN.
|
|
4
|
+
# Copyright (C) 2019-2021 Northwestern University.
|
|
5
|
+
# Copyright (C) 2024-2025 Graz University of Technology.
|
|
6
|
+
#
|
|
7
|
+
# Invenio App RDM is free software; you can redistribute it and/or modify it
|
|
8
|
+
# under the terms of the MIT License; see LICENSE file for more details.
|
|
9
|
+
|
|
10
|
+
"""Common pytest fixtures and plugins."""
|
|
11
|
+
|
|
12
|
+
# Monkey patch Werkzeug 2.1
|
|
13
|
+
# Flask-Login uses the safe_str_cmp method which has been removed in Werkzeug
|
|
14
|
+
# 2.1. Flask-Login v0.6.0 (yet to be released at the time of writing) fixes the
|
|
15
|
+
# issue. Once we depend on Flask-Login v0.6.0 as the minimal version in
|
|
16
|
+
# Flask-Security-Invenio/Invenio-Accounts we can remove this patch again.
|
|
17
|
+
try:
|
|
18
|
+
# Werkzeug <2.1
|
|
19
|
+
from werkzeug import security
|
|
20
|
+
|
|
21
|
+
security.safe_str_cmp
|
|
22
|
+
except AttributeError:
|
|
23
|
+
# Werkzeug >=2.1
|
|
24
|
+
import hmac
|
|
25
|
+
|
|
26
|
+
from werkzeug import security
|
|
27
|
+
|
|
28
|
+
security.safe_str_cmp = hmac.compare_digest
|
|
29
|
+
|
|
30
|
+
import shutil
|
|
31
|
+
import tempfile
|
|
32
|
+
from collections import namedtuple
|
|
33
|
+
|
|
34
|
+
import pytest
|
|
35
|
+
from flask_security import login_user
|
|
36
|
+
from flask_security.utils import hash_password
|
|
37
|
+
from invenio_access.models import ActionUsers
|
|
38
|
+
from invenio_access.permissions import system_identity
|
|
39
|
+
from invenio_access.proxies import current_access
|
|
40
|
+
from invenio_accounts.proxies import current_datastore
|
|
41
|
+
from invenio_accounts.testutils import login_user_via_session
|
|
42
|
+
from invenio_app.factory import create_app as _create_app
|
|
43
|
+
from invenio_db import db
|
|
44
|
+
from invenio_files_rest.models import Bucket, FileInstance, Location, ObjectVersion
|
|
45
|
+
from invenio_records_resources.proxies import current_service_registry
|
|
46
|
+
from invenio_vocabularies.contrib.subjects.api import Subject
|
|
47
|
+
from invenio_vocabularies.proxies import current_service as vocabulary_service
|
|
48
|
+
from invenio_vocabularies.records.api import Vocabulary
|
|
49
|
+
from invenio_vocabularies.records.models import VocabularyScheme
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@pytest.fixture(scope="module")
|
|
53
|
+
def create_app(entry_points):
|
|
54
|
+
"""Creates a test app."""
|
|
55
|
+
return _create_app
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@pytest.fixture(scope="module")
|
|
59
|
+
def app_config(app_config):
|
|
60
|
+
"""Override pytest-invenio app_config fixture to disable CSRF check."""
|
|
61
|
+
# Variable not used. We set it to silent warnings
|
|
62
|
+
app_config["REST_CSRF_ENABLED"] = False
|
|
63
|
+
|
|
64
|
+
return app_config
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@pytest.fixture(scope="module")
|
|
68
|
+
def subjects_service(app):
|
|
69
|
+
"""Subjects service."""
|
|
70
|
+
return current_service_registry.get("subjects")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
pytest_plugins = ("celery.contrib.pytest",)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@pytest.fixture(scope="module")
|
|
77
|
+
def extra_entry_points():
|
|
78
|
+
"""Register extra entry point."""
|
|
79
|
+
return {
|
|
80
|
+
"invenio_base.blueprints": [
|
|
81
|
+
"mock_module = tests.mock_module.views:create_blueprint"
|
|
82
|
+
],
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@pytest.fixture(scope="function")
|
|
87
|
+
def minimal_record(users):
|
|
88
|
+
"""Minimal record data as dict coming from the external world."""
|
|
89
|
+
return {
|
|
90
|
+
"access": {
|
|
91
|
+
"record": "public",
|
|
92
|
+
"files": "public",
|
|
93
|
+
},
|
|
94
|
+
"files": {"enabled": False}, # Most tests don't care about file upload
|
|
95
|
+
"metadata": {
|
|
96
|
+
"publication_date": "2020-06-01",
|
|
97
|
+
"resource_type": {
|
|
98
|
+
"id": "image-photo",
|
|
99
|
+
},
|
|
100
|
+
# Technically not required
|
|
101
|
+
"creators": [
|
|
102
|
+
{
|
|
103
|
+
"person_or_org": {
|
|
104
|
+
"type": "personal",
|
|
105
|
+
"name": "Doe, John",
|
|
106
|
+
"given_name": "John Doe",
|
|
107
|
+
"family_name": "Doe",
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
"title": "A Romans story",
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@pytest.fixture()
|
|
117
|
+
def users(app, db):
|
|
118
|
+
"""Create users."""
|
|
119
|
+
password = "123456"
|
|
120
|
+
with db.session.begin_nested():
|
|
121
|
+
datastore = app.extensions["security"].datastore
|
|
122
|
+
# create users
|
|
123
|
+
hashed_password = hash_password(password)
|
|
124
|
+
user1 = datastore.create_user(
|
|
125
|
+
email="user1@test.com", password=hashed_password, active=True
|
|
126
|
+
)
|
|
127
|
+
user2 = datastore.create_user(
|
|
128
|
+
email="user2@test.com", password=hashed_password, active=True
|
|
129
|
+
)
|
|
130
|
+
# Give role to administration-access
|
|
131
|
+
db.session.add(ActionUsers(action="administration-access", user=user1))
|
|
132
|
+
db.session.commit()
|
|
133
|
+
return {
|
|
134
|
+
"user1": user1,
|
|
135
|
+
"user2": user2,
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@pytest.fixture()
|
|
140
|
+
def roles(app, db):
|
|
141
|
+
"""Create some roles."""
|
|
142
|
+
with db.session.begin_nested():
|
|
143
|
+
datastore = app.extensions["security"].datastore
|
|
144
|
+
role1 = datastore.create_role(
|
|
145
|
+
name="administration", description="administration role"
|
|
146
|
+
)
|
|
147
|
+
role2 = datastore.create_role(name="test", description="tests are coming")
|
|
148
|
+
|
|
149
|
+
db.session.commit()
|
|
150
|
+
return {"administration": role1, "test": role2}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@pytest.fixture()
|
|
154
|
+
def administration_user(users, roles):
|
|
155
|
+
"""Give administration rights to a user."""
|
|
156
|
+
user = users["user1"]
|
|
157
|
+
role = roles["administration"]
|
|
158
|
+
current_datastore.add_role_to_user(user, role)
|
|
159
|
+
action = current_access.actions["superuser-access"]
|
|
160
|
+
db.session.add(ActionUsers.allow(action, user_id=user.id))
|
|
161
|
+
|
|
162
|
+
return user
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@pytest.fixture()
|
|
166
|
+
def client_with_login(client, users):
|
|
167
|
+
"""Log in a user to the client."""
|
|
168
|
+
user = users["user1"]
|
|
169
|
+
login_user(user, remember=True)
|
|
170
|
+
login_user_via_session(client, email=user.email)
|
|
171
|
+
return client
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@pytest.fixture(scope="module")
|
|
175
|
+
def resource_type_type(app):
|
|
176
|
+
"""Resource type vocabulary type."""
|
|
177
|
+
return vocabulary_service.create_type(system_identity, "resourcetypes", "rsrct")
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@pytest.fixture(scope="module")
|
|
181
|
+
def resource_type_item(app, resource_type_type):
|
|
182
|
+
"""Resource type vocabulary record."""
|
|
183
|
+
rst = vocabulary_service.create(
|
|
184
|
+
system_identity,
|
|
185
|
+
{
|
|
186
|
+
"id": "image-photo",
|
|
187
|
+
"icon": "chart bar outline",
|
|
188
|
+
"props": {
|
|
189
|
+
"csl": "graphic",
|
|
190
|
+
"datacite_general": "Image",
|
|
191
|
+
"datacite_type": "Photo",
|
|
192
|
+
"eurepo": "info:eu-repo/semantic/image-photo",
|
|
193
|
+
"openaire_resourceType": "25",
|
|
194
|
+
"openaire_type": "dataset",
|
|
195
|
+
"schema.org": "https://schema.org/Photograph",
|
|
196
|
+
"subtype": "image-photo",
|
|
197
|
+
"type": "image",
|
|
198
|
+
},
|
|
199
|
+
"title": {"en": "Photo"},
|
|
200
|
+
"tags": ["depositable", "linkable"],
|
|
201
|
+
"type": "resourcetypes",
|
|
202
|
+
},
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
Vocabulary.index.refresh()
|
|
206
|
+
|
|
207
|
+
return rst
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@pytest.fixture(scope="module")
|
|
211
|
+
def languages_type(app):
|
|
212
|
+
"""Language vocabulary type."""
|
|
213
|
+
return vocabulary_service.create_type(system_identity, "languages", "lng")
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@pytest.fixture(scope="module")
|
|
217
|
+
def language_item(app, languages_type):
|
|
218
|
+
"""Language vocabulary record."""
|
|
219
|
+
lang = vocabulary_service.create(
|
|
220
|
+
system_identity,
|
|
221
|
+
{
|
|
222
|
+
"id": "eng",
|
|
223
|
+
"props": {
|
|
224
|
+
"alpha_2": "",
|
|
225
|
+
},
|
|
226
|
+
"title": {"en": "English"},
|
|
227
|
+
"type": "languages",
|
|
228
|
+
},
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
Vocabulary.index.refresh()
|
|
232
|
+
|
|
233
|
+
return lang
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
@pytest.fixture
|
|
237
|
+
def subjects_mesh_scheme(app, db):
|
|
238
|
+
"""Subject Scheme for MeSH."""
|
|
239
|
+
scheme = VocabularyScheme.create(
|
|
240
|
+
id="MeSH",
|
|
241
|
+
parent_id="subjects",
|
|
242
|
+
name="Medical Subject Headings",
|
|
243
|
+
uri="https://www.nlm.nih.gov/mesh/meshhome.html",
|
|
244
|
+
)
|
|
245
|
+
db.session.commit()
|
|
246
|
+
return scheme
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
@pytest.fixture
|
|
250
|
+
def subject_item(app, subjects_mesh_scheme, subjects_service):
|
|
251
|
+
"""Subject vocabulary record."""
|
|
252
|
+
subj = subjects_service.create(
|
|
253
|
+
system_identity,
|
|
254
|
+
{
|
|
255
|
+
"id": "https://id.nlm.nih.gov/mesh/D000015",
|
|
256
|
+
"scheme": "MeSH",
|
|
257
|
+
"subject": "Abnormalities, Multiple",
|
|
258
|
+
},
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
Subject.index.refresh()
|
|
262
|
+
|
|
263
|
+
return subj
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
RunningApp = namedtuple(
|
|
267
|
+
"RunningApp",
|
|
268
|
+
["app", "location", "resource_type_item", "language_item", "subject_item"],
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
@pytest.fixture
|
|
273
|
+
def running_app(app, location, resource_type_item, language_item, subject_item):
|
|
274
|
+
"""Fixture mimicking a running app."""
|
|
275
|
+
return RunningApp(app, location, resource_type_item, language_item, subject_item)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
@pytest.yield_fixture()
|
|
279
|
+
def dummy_location(db):
|
|
280
|
+
"""File system location."""
|
|
281
|
+
tmppath = tempfile.mkdtemp()
|
|
282
|
+
|
|
283
|
+
loc = Location(name="testloc", uri=tmppath, default=True)
|
|
284
|
+
db.session.add(loc)
|
|
285
|
+
db.session.commit()
|
|
286
|
+
|
|
287
|
+
yield loc
|
|
288
|
+
|
|
289
|
+
shutil.rmtree(tmppath)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
@pytest.fixture
|
|
293
|
+
def invalid_file_instance(db, dummy_location):
|
|
294
|
+
"""Creates a file instance."""
|
|
295
|
+
# Create a Bucket and ObjectVersion
|
|
296
|
+
b1 = Bucket.create(location=dummy_location)
|
|
297
|
+
with open("README.rst", "rb") as fp:
|
|
298
|
+
obj = ObjectVersion.create(b1, "README.rst", stream=fp)
|
|
299
|
+
db.session.commit()
|
|
300
|
+
file_id = obj.file_id
|
|
301
|
+
|
|
302
|
+
# Get FileInstance from file ID
|
|
303
|
+
f = FileInstance.query.get(file_id)
|
|
304
|
+
|
|
305
|
+
# Force an invalid checksum
|
|
306
|
+
f.checksum = "invalid"
|
|
307
|
+
f.verify_checksum()
|
|
308
|
+
db.session.commit()
|
|
309
|
+
|
|
310
|
+
# Retrieve the file instance (with updated last_check)
|
|
311
|
+
f = FileInstance.query.get(file_id)
|
|
312
|
+
|
|
313
|
+
return f
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
About page
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2022 CERN.
|
|
4
|
+
#
|
|
5
|
+
# Invenio App RDM 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 fixtures for application vocabulary fixtures."""
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
from invenio_app.factory import create_api
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.fixture(scope="module")
|
|
15
|
+
def create_app():
|
|
16
|
+
"""Application factory fixture."""
|
|
17
|
+
return create_api
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@pytest.fixture(scope="module")
|
|
21
|
+
def cli_runner(base_app):
|
|
22
|
+
"""Create a CLI runner for testing a CLI command."""
|
|
23
|
+
|
|
24
|
+
def cli_invoke(command, *args, input=None):
|
|
25
|
+
return base_app.test_cli_runner().invoke(command, args, input=input)
|
|
26
|
+
|
|
27
|
+
return cli_invoke
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2022 CERN.
|
|
4
|
+
#
|
|
5
|
+
# Invenio App RDM is free software; you can redistribute it and/or modify
|
|
6
|
+
# it under the terms of the MIT License; see LICENSE file for more details.
|
|
7
|
+
|
|
8
|
+
"""Tests for the CLI."""
|
|
9
|
+
|
|
10
|
+
from invenio_access.permissions import system_identity
|
|
11
|
+
from invenio_rdm_records.proxies import current_oaipmh_server_service
|
|
12
|
+
|
|
13
|
+
from invenio_app_rdm.cli import create_fixtures
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_create_fixtures(app, db, cli_runner):
|
|
17
|
+
"""Assert that fixtures are created."""
|
|
18
|
+
result = cli_runner(create_fixtures)
|
|
19
|
+
assert result.exit_code == 0
|
|
20
|
+
|
|
21
|
+
service = current_oaipmh_server_service
|
|
22
|
+
res_set = service.search(system_identity, params={"q": f"%"})
|
|
23
|
+
|
|
24
|
+
# oai_sets.yaml file left empty
|
|
25
|
+
assert res_set.total == 2
|