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.
Files changed (50) hide show
  1. invenio_app_rdm/__init__.py +1 -1
  2. invenio_app_rdm/config.py +4 -2
  3. invenio_app_rdm/records_ui/templates/semantic-ui/invenio_app_rdm/records/details/meta.html +3 -1
  4. invenio_app_rdm/records_ui/views/decorators.py +91 -8
  5. invenio_app_rdm/records_ui/views/records.py +7 -4
  6. invenio_app_rdm/tasks.py +6 -2
  7. invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/components/CompareRevisionsDropdown.js +6 -0
  8. invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/components/RevisionsDiffViewer.js +5 -0
  9. invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/records/CompareRevisions.js +10 -9
  10. invenio_app_rdm/theme/assets/semantic-ui/less/invenio_app_rdm/theme/globals/site.overrides +1 -1
  11. invenio_app_rdm/theme/templates/semantic-ui/invenio_app_rdm/help/search.en.html +1 -1
  12. invenio_app_rdm/urls.py +11 -1
  13. {invenio_app_rdm-13.0.0b2.dev2.dist-info → invenio_app_rdm-13.0.0b2.dev4.dist-info}/METADATA +20 -1
  14. {invenio_app_rdm-13.0.0b2.dev2.dist-info → invenio_app_rdm-13.0.0b2.dev4.dist-info}/RECORD +50 -18
  15. {invenio_app_rdm-13.0.0b2.dev2.dist-info → invenio_app_rdm-13.0.0b2.dev4.dist-info}/WHEEL +1 -1
  16. {invenio_app_rdm-13.0.0b2.dev2.dist-info → invenio_app_rdm-13.0.0b2.dev4.dist-info}/top_level.txt +1 -0
  17. tests/__init__.py +8 -0
  18. tests/api/__init__.py +8 -0
  19. tests/api/conftest.py +24 -0
  20. tests/api/test_protect_files_rest.py +73 -0
  21. tests/api/test_record_api.py +175 -0
  22. tests/api/test_stats_api.py +26 -0
  23. tests/conftest.py +313 -0
  24. tests/fixtures/__init__.py +8 -0
  25. tests/fixtures/app_data/oai_sets.yaml +3 -0
  26. tests/fixtures/app_data/pages/about.html +1 -0
  27. tests/fixtures/app_data/pages.yaml +4 -0
  28. tests/fixtures/conftest.py +27 -0
  29. tests/fixtures/test_cli.py +25 -0
  30. tests/fixtures/test_fixtures.py +46 -0
  31. tests/mock_module/__init__.py +7 -0
  32. tests/mock_module/templates/mock_mail.html +28 -0
  33. tests/mock_module/views.py +32 -0
  34. tests/redirector/__init__.py +8 -0
  35. tests/redirector/conftest.py +54 -0
  36. tests/redirector/test_redirector.py +28 -0
  37. tests/test_tasks.py +104 -0
  38. tests/test_utils.py +67 -0
  39. tests/test_version.py +16 -0
  40. tests/test_views.py +43 -0
  41. tests/ui/__init__.py +8 -0
  42. tests/ui/conftest.py +105 -0
  43. tests/ui/test_deposits.py +115 -0
  44. tests/ui/test_export_formats.py +37 -0
  45. tests/ui/test_filters.py +10 -0
  46. tests/ui/test_signposting_ui.py +95 -0
  47. tests/ui/test_static.py +25 -0
  48. tests/ui/test_stats_ui.py +92 -0
  49. {invenio_app_rdm-13.0.0b2.dev2.dist-info → invenio_app_rdm-13.0.0b2.dev4.dist-info}/LICENSE +0 -0
  50. {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,37 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2021 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
+ """Test that all export formats are working."""
9
+
10
+ import pytest
11
+ from invenio_cache import current_cache
12
+
13
+
14
+ @pytest.fixture(scope="function")
15
+ def cache():
16
+ """Clean cache"""
17
+ try:
18
+ current_cache.clear()
19
+ yield current_cache
20
+ finally:
21
+ current_cache.clear()
22
+
23
+
24
+ def test_export_formats(client, running_app, cache, record):
25
+ """Test that all expected export formats are working."""
26
+ # Expected export formats:
27
+ formats = [
28
+ "json",
29
+ "json-ld",
30
+ "csl",
31
+ "datacite-json",
32
+ "datacite-xml",
33
+ "dublincore",
34
+ ]
35
+ for f in formats:
36
+ res = client.get(f"/records/{record.id}/export/{f}")
37
+ assert res.status_code == 200
@@ -0,0 +1,10 @@
1
+ from invenio_app_rdm.records_ui.views.filters import get_scheme_label
2
+
3
+
4
+ def test_get_scheme_label(app):
5
+ # just test a couple of schemes
6
+ assert "PMID" == get_scheme_label("pmid")
7
+
8
+ assert "arXiv" == get_scheme_label("arxiv")
9
+
10
+ assert "Bibcode" == get_scheme_label("ads")
@@ -0,0 +1,95 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2023 Northwestern University.
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
+ """Test Signposting.
9
+
10
+ See https://signposting.org/FAIR/#level2 for more information on Signposting
11
+ """
12
+ import pytest
13
+
14
+
15
+ @pytest.mark.parametrize("http_method", ["head", "get"])
16
+ @pytest.mark.parametrize("level_1_enabled", [True, False])
17
+ def test_link_in_landing_page_response_headers(
18
+ running_app, app, client, record_with_file, http_method, level_1_enabled
19
+ ):
20
+ previous_config = app.config[
21
+ "APP_RDM_RECORD_LANDING_PAGE_FAIR_SIGNPOSTING_LEVEL_1_ENABLED"
22
+ ]
23
+
24
+ if level_1_enabled:
25
+ app.config["APP_RDM_RECORD_LANDING_PAGE_FAIR_SIGNPOSTING_LEVEL_1_ENABLED"] = (
26
+ True
27
+ )
28
+
29
+ client_http_method = getattr(client, http_method)
30
+ res = client_http_method(f"/records/{record_with_file.id}")
31
+
32
+ # The link headers are already tested in details in `invenio-rdm-records` (see `test_signposting_serializer`).
33
+ # Here we still want to issue the HTTP call to the URL in order to make sure that the decorator is working properly,
34
+ # but the assertions are less detailed to avoid having to adapt this test every time we modify the logic in `invenio-rdm-records`.
35
+ link_headers = res.headers["Link"].split(" , ")
36
+
37
+ # The test record does not have:
38
+ # - an author with an identifier.
39
+ # - a cite-as since it has no DOI.
40
+ # - a license.
41
+
42
+ # There should be at least one link to a linkset (e.g. "application/linkset" and/or "application/linkset+json")
43
+ assert sum('; rel="linkset" ;' in header for header in link_headers) >= 1
44
+
45
+ if level_1_enabled:
46
+ # There should be at least 10 export formats supported (e.g. "application/dcat+xml", "application/x-bibtex", etc.).
47
+ assert sum('; rel="describedby" ;' in header for header in link_headers) >= 10
48
+
49
+ # There should be at least one file in the record.
50
+ assert sum('; rel="item" ;' in header for header in link_headers) >= 1
51
+
52
+ # There should be at least one description of the type of the record (e.g. "https://schema.org/Photograph").
53
+ assert sum('; rel="type"' in header for header in link_headers) >= 1
54
+ else:
55
+ # The only link headers should be linkset headers.
56
+ assert sum('; rel="linkset" ;' in header for header in link_headers) == len(
57
+ link_headers
58
+ )
59
+
60
+ app.config["APP_RDM_RECORD_LANDING_PAGE_FAIR_SIGNPOSTING_LEVEL_1_ENABLED"] = (
61
+ previous_config
62
+ )
63
+
64
+
65
+ @pytest.mark.parametrize("http_method", ["head", "get"])
66
+ def test_link_in_content_resource_response_headers(
67
+ running_app, client, record_with_file, http_method
68
+ ):
69
+ ui_url = f"https://127.0.0.1:5000/records/{record_with_file.id}"
70
+ api_url = f"https://127.0.0.1:5000/api/records/{record_with_file.id}"
71
+ filename = "article.txt"
72
+
73
+ client_http_method = getattr(client, http_method)
74
+ res = client_http_method(f"/records/{record_with_file.id}/files/{filename}")
75
+
76
+ assert res.headers["Link"].split(" , ") == [
77
+ f'<{ui_url}> ; rel="collection" ; type="text/html"',
78
+ f'<{api_url}> ; rel="linkset" ; type="application/linkset+json"',
79
+ ]
80
+
81
+
82
+ @pytest.mark.parametrize("http_method", ["head", "get"])
83
+ def test_link_in_metadata_resource_response_headers(
84
+ running_app, client, record, http_method
85
+ ):
86
+ ui_url = f"https://127.0.0.1:5000/records/{record.id}"
87
+ api_url = f"https://127.0.0.1:5000/api/records/{record.id}"
88
+
89
+ client_http_method = getattr(client, http_method)
90
+ res = client_http_method(f"/records/{record.id}/export/bibtex")
91
+
92
+ assert res.headers["Link"].split(" , ") == [
93
+ f'<{ui_url}> ; rel="describes" ; type="text/html"',
94
+ f'<{api_url}> ; rel="linkset" ; type="application/linkset+json"',
95
+ ]
@@ -0,0 +1,25 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2022 CERN.
4
+ #
5
+ # Invenio-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
+
9
+ def test_static(app, client, es_clear):
10
+ """Check if robots.txt returns a string response."""
11
+
12
+ # Save the current static folder to revert after finishing the test.
13
+ app_static_folder = app.static_folder
14
+ # Use as static folder the one of the blueprint we are testing.
15
+ app.static_folder = app.blueprints["invenio_app_rdm"].static_folder
16
+
17
+ response = client.get("/robots.txt")
18
+
19
+ assert response.status_code == 200
20
+ assert type(response.text) == str
21
+ assert "Disallow: /search" in response.text
22
+ assert "Disallow: /api" in response.text
23
+ assert "Disallow: /administration" in response.text
24
+
25
+ app.static_folder = app_static_folder
@@ -0,0 +1,92 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2023 TU Wien.
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
+ """Test the statistics integration."""
9
+
10
+ import time
11
+ from datetime import datetime, timedelta
12
+
13
+ import pytest
14
+ from flask import current_app
15
+ from invenio_search.engine import dsl
16
+ from invenio_stats.proxies import current_stats
17
+ from invenio_stats.tasks import process_events
18
+
19
+
20
+ @pytest.fixture()
21
+ def empty_event_queues(running_app):
22
+ """Make sure the event queues exist and are empty."""
23
+ for event in current_stats.events:
24
+ queue = current_stats.events[event].queue
25
+ queue.queue.declare()
26
+ queue.consume()
27
+
28
+
29
+ def test_record_view_events(
30
+ client, running_app, index_templates, record, empty_event_queues
31
+ ):
32
+ """Test that landing page visits trigger events."""
33
+ res = client.get(f"/records/{record.id}")
34
+ assert res.status_code == 200
35
+
36
+ # as per current default configuration, only UI-based visits should
37
+ # trigger events to be sent off to the queue
38
+ # (API calls such as the CSL export on the landing page are ignored)
39
+ queue = current_stats.events["record-view"].queue
40
+ events = list(queue.consume())
41
+ event = events[0]
42
+ assert len(events) == 1
43
+ assert event["recid"] == record.id
44
+ assert event["via_api"] is False
45
+
46
+
47
+ def test_record_view_statistics(
48
+ client, running_app, index_templates, record, empty_event_queues
49
+ ):
50
+ """Test that landing page visits triggers events and indexes them."""
51
+ event_cfg = current_stats.events["record-view"]
52
+ event = event_cfg.cls(**event_cfg.params, double_click_window=0)
53
+ agg_cfg = current_stats.aggregations["record-view-agg"]
54
+ agg = agg_cfg.cls(agg_cfg.name, **agg_cfg.params)
55
+ query_cfg = current_stats.queries["record-view"]
56
+ query = query_cfg.cls(name=query_cfg.name, **query_cfg.params)
57
+
58
+ for i in range(3):
59
+ if i != 0:
60
+ # we need to sleep for a little while here because invenio-stats trims
61
+ # the sub-second part from the events before indexing them,
62
+ # (and thus collapses similar events if they are too close in time)
63
+ time.sleep(1.25)
64
+
65
+ res = client.get(f"/records/{record.id}")
66
+ assert res.status_code == 200
67
+
68
+ # process the event
69
+ processing_result = event.run()
70
+ assert processing_result == (1, 0)
71
+
72
+ # refresh the index and check if the event was properly indexed
73
+ dsl.Index(event.index, using=event.client).refresh()
74
+ indexed_events = dsl.Search(using=event.client, index=event.index).execute(
75
+ ignore_cache=True
76
+ )
77
+ indexed_event = indexed_events.hits[0]
78
+ assert indexed_events.hits.total.value == (i + 1)
79
+ assert indexed_event["recid"] == record.id
80
+ assert indexed_event["parent_recid"] == record["parent"]["id"]
81
+ assert indexed_event["via_api"] is False
82
+
83
+ # calculate the aggregations and check if the query is correct
84
+ yesterday = datetime.today() - timedelta(days=1)
85
+ tomorrow = datetime.today() + timedelta(days=1)
86
+ agg.run(start_date=yesterday, end_date=tomorrow, update_bookmark=False)
87
+ dsl.Index(agg.index, using=agg.client).refresh()
88
+ query_result = query.run(recid=record.id)
89
+ assert query_result["recid"] == record.id
90
+ assert query_result["parent_recid"] == record["parent"]["id"]
91
+ assert query_result["views"] == (i + 1)
92
+ assert query_result["unique_views"] == 1