Changeset View
Changeset View
Standalone View
Standalone View
swh/deposit/tests/api/test_deposit_update.py
# Copyright (C) 2017-2020 The Software Heritage developers | # Copyright (C) 2017-2020 The Software Heritage developers | ||||
# See the AUTHORS file at the top-level directory of this distribution | # See the AUTHORS file at the top-level directory of this distribution | ||||
# License: GNU General Public License version 3, or any later version | # License: GNU General Public License version 3, or any later version | ||||
# See top-level LICENSE file for more information | # See top-level LICENSE file for more information | ||||
from io import BytesIO | from io import BytesIO | ||||
import attr | import attr | ||||
from django.core.files.uploadedfile import InMemoryUploadedFile | from django.core.files.uploadedfile import InMemoryUploadedFile | ||||
from django.urls import reverse | from django.urls import reverse | ||||
from rest_framework import status | from rest_framework import status | ||||
from swh.deposit.api.common import ACCEPT_ARCHIVE_CONTENT_TYPES | from swh.deposit.api.common import ACCEPT_ARCHIVE_CONTENT_TYPES | ||||
from swh.deposit.config import ( | from swh.deposit.config import ( | ||||
DEPOSIT_STATUS_DEPOSITED, | DEPOSIT_STATUS_DEPOSITED, | ||||
DEPOSIT_STATUS_PARTIAL, | DEPOSIT_STATUS_PARTIAL, | ||||
EDIT_SE_IRI, | EDIT_IRI, | ||||
EM_IRI, | EM_IRI, | ||||
SE_IRI, | |||||
APIConfig, | APIConfig, | ||||
) | ) | ||||
from swh.deposit.models import Deposit, DepositCollection, DepositRequest | from swh.deposit.models import Deposit, DepositCollection, DepositRequest | ||||
from swh.deposit.parsers import parse_xml | from swh.deposit.parsers import parse_xml | ||||
from swh.deposit.tests.common import check_archive, create_arborescence_archive | from swh.deposit.tests.common import check_archive, create_arborescence_archive | ||||
from swh.model.hashutil import hash_to_bytes | from swh.model.hashutil import hash_to_bytes | ||||
from swh.model.identifiers import parse_swhid, swhid | from swh.model.identifiers import parse_swhid, swhid | ||||
from swh.model.model import ( | from swh.model.model import ( | ||||
Show All 25 Lines | ): | ||||
assert len(list(requests)) == 1 | assert len(list(requests)) == 1 | ||||
check_archive(sample_archive["name"], requests[0].archive.name) | check_archive(sample_archive["name"], requests[0].archive.name) | ||||
# we have no metadata for that deposit | # we have no metadata for that deposit | ||||
requests = list(DepositRequest.objects.filter(deposit=deposit, type="metadata")) | requests = list(DepositRequest.objects.filter(deposit=deposit, type="metadata")) | ||||
assert len(requests) == 0 | assert len(requests) == 0 | ||||
response = authenticated_client.post( | response = authenticated_client.post( | ||||
reverse(EDIT_SE_IRI, args=[deposit_collection.name, deposit.id]), | reverse(SE_IRI, args=[deposit_collection.name, deposit.id]), | ||||
content_type="application/atom+xml;type=entry", | content_type="application/atom+xml;type=entry", | ||||
data=atom_dataset["entry-data1"], | data=atom_dataset["entry-data1"], | ||||
HTTP_SLUG=deposit.external_id, | HTTP_SLUG=deposit.external_id, | ||||
HTTP_IN_PROGRESS=True, | HTTP_IN_PROGRESS=True, | ||||
) | ) | ||||
requests = list(DepositRequest.objects.filter(deposit=deposit, type="metadata")) | requests = list(DepositRequest.objects.filter(deposit=deposit, type="metadata")) | ||||
assert len(requests) == 1 | assert len(requests) == 1 | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | ): | ||||
requests_meta = DepositRequest.objects.filter(deposit=deposit, type="metadata") | requests_meta = DepositRequest.objects.filter(deposit=deposit, type="metadata") | ||||
assert len(requests_meta) == 1 | assert len(requests_meta) == 1 | ||||
request_meta0 = requests_meta[0] | request_meta0 = requests_meta[0] | ||||
assert request_meta0.raw_metadata == raw_metadata0 | assert request_meta0.raw_metadata == raw_metadata0 | ||||
requests_archive0 = DepositRequest.objects.filter(deposit=deposit, type="archive") | requests_archive0 = DepositRequest.objects.filter(deposit=deposit, type="archive") | ||||
assert len(requests_archive0) == 1 | assert len(requests_archive0) == 1 | ||||
update_uri = reverse(EDIT_SE_IRI, args=[deposit_collection.name, deposit.id]) | update_uri = reverse(EDIT_IRI, args=[deposit_collection.name, deposit.id]) | ||||
response = authenticated_client.put( | response = authenticated_client.put( | ||||
update_uri, | update_uri, | ||||
content_type="application/atom+xml;type=entry", | content_type="application/atom+xml;type=entry", | ||||
data=atom_dataset["entry-data1"], | data=atom_dataset["entry-data1"], | ||||
) | ) | ||||
assert response.status_code == status.HTTP_204_NO_CONTENT | assert response.status_code == status.HTTP_204_NO_CONTENT | ||||
▲ Show 20 Lines • Show All 84 Lines • ▼ Show 20 Lines | ): | ||||
deposit = partial_deposit_with_metadata | deposit = partial_deposit_with_metadata | ||||
requests = DepositRequest.objects.filter(deposit=deposit, type="metadata") | requests = DepositRequest.objects.filter(deposit=deposit, type="metadata") | ||||
assert len(requests) == 1 | assert len(requests) == 1 | ||||
requests_archive0 = DepositRequest.objects.filter(deposit=deposit, type="archive") | requests_archive0 = DepositRequest.objects.filter(deposit=deposit, type="archive") | ||||
assert len(requests_archive0) == 1 | assert len(requests_archive0) == 1 | ||||
update_uri = reverse(EDIT_SE_IRI, args=[deposit_collection.name, deposit.id]) | update_uri = reverse(SE_IRI, args=[deposit_collection.name, deposit.id]) | ||||
atom_entry = atom_dataset["entry-data1"] | atom_entry = atom_dataset["entry-data1"] | ||||
response = authenticated_client.post( | response = authenticated_client.post( | ||||
update_uri, content_type="application/atom+xml;type=entry", data=atom_entry | update_uri, content_type="application/atom+xml;type=entry", data=atom_entry | ||||
) | ) | ||||
assert response.status_code == status.HTTP_201_CREATED | assert response.status_code == status.HTTP_201_CREATED | ||||
Show All 29 Lines | ): | ||||
""" | """ | ||||
deposit = partial_deposit_with_metadata | deposit = partial_deposit_with_metadata | ||||
requests = DepositRequest.objects.filter(deposit=deposit, type="metadata") | requests = DepositRequest.objects.filter(deposit=deposit, type="metadata") | ||||
assert len(requests) == 1 | assert len(requests) == 1 | ||||
requests_archive0 = DepositRequest.objects.filter(deposit=deposit, type="archive") | requests_archive0 = DepositRequest.objects.filter(deposit=deposit, type="archive") | ||||
assert len(requests_archive0) == 1 | assert len(requests_archive0) == 1 | ||||
update_uri = reverse(EDIT_SE_IRI, args=[deposit_collection.name, deposit.id]) | update_uri = reverse(EDIT_IRI, args=[deposit_collection.name, deposit.id]) | ||||
archive = InMemoryUploadedFile( | archive = InMemoryUploadedFile( | ||||
BytesIO(sample_archive["data"]), | BytesIO(sample_archive["data"]), | ||||
field_name=sample_archive["name"], | field_name=sample_archive["name"], | ||||
name=sample_archive["name"], | name=sample_archive["name"], | ||||
content_type="application/x-tar", | content_type="application/x-tar", | ||||
size=sample_archive["length"], | size=sample_archive["length"], | ||||
charset=None, | charset=None, | ||||
) | ) | ||||
data_atom_entry = atom_dataset["entry-data1"] | data_atom_entry = atom_dataset["entry-data1"] | ||||
atom_entry = InMemoryUploadedFile( | atom_entry = InMemoryUploadedFile( | ||||
BytesIO(data_atom_entry.encode("utf-8")), | BytesIO(data_atom_entry.encode("utf-8")), | ||||
field_name="atom0", | field_name="atom0", | ||||
name="atom0", | name="atom0", | ||||
content_type='application/atom+xml; charset="utf-8"', | content_type='application/atom+xml; charset="utf-8"', | ||||
size=len(data_atom_entry), | size=len(data_atom_entry), | ||||
charset="utf-8", | charset="utf-8", | ||||
) | ) | ||||
update_uri = reverse(EDIT_SE_IRI, args=[deposit_collection.name, deposit.id]) | update_uri = reverse(SE_IRI, args=[deposit_collection.name, deposit.id]) | ||||
response = authenticated_client.post( | response = authenticated_client.post( | ||||
update_uri, | update_uri, | ||||
format="multipart", | format="multipart", | ||||
data={"archive": archive, "atom_entry": atom_entry,}, | data={"archive": archive, "atom_entry": atom_entry,}, | ||||
) | ) | ||||
assert response.status_code == status.HTTP_201_CREATED | assert response.status_code == status.HTTP_201_CREATED | ||||
requests = DepositRequest.objects.filter(deposit=deposit, type="metadata").order_by( | requests = DepositRequest.objects.filter(deposit=deposit, type="metadata").order_by( | ||||
Show All 23 Lines | """Empty atom post entry with header in-progress to false transitions deposit to | ||||
'deposited' status | 'deposited' status | ||||
Response: 200 | Response: 200 | ||||
""" | """ | ||||
deposit = partial_deposit_with_metadata | deposit = partial_deposit_with_metadata | ||||
assert deposit.status == DEPOSIT_STATUS_PARTIAL | assert deposit.status == DEPOSIT_STATUS_PARTIAL | ||||
update_uri = reverse(EDIT_SE_IRI, args=[deposit_collection.name, deposit.id]) | update_uri = reverse(SE_IRI, args=[deposit_collection.name, deposit.id]) | ||||
response = authenticated_client.post( | response = authenticated_client.post( | ||||
update_uri, | update_uri, | ||||
content_type="application/atom+xml;type=entry", | content_type="application/atom+xml;type=entry", | ||||
data="", | data="", | ||||
size=0, | size=0, | ||||
HTTP_IN_PROGRESS=False, | HTTP_IN_PROGRESS=False, | ||||
) | ) | ||||
Show All 9 Lines | ): | ||||
""" | """ | ||||
unknown_deposit_id = 1000 | unknown_deposit_id = 1000 | ||||
try: | try: | ||||
Deposit.objects.get(pk=unknown_deposit_id) | Deposit.objects.get(pk=unknown_deposit_id) | ||||
except Deposit.DoesNotExist: | except Deposit.DoesNotExist: | ||||
assert True | assert True | ||||
url = reverse(EDIT_SE_IRI, args=[deposit_collection, unknown_deposit_id]) | url = reverse(SE_IRI, args=[deposit_collection, unknown_deposit_id]) | ||||
response = authenticated_client.post( | response = authenticated_client.post( | ||||
url, | url, | ||||
content_type="application/atom+xml;type=entry", | content_type="application/atom+xml;type=entry", | ||||
data=atom_dataset["entry-data1"], | data=atom_dataset["entry-data1"], | ||||
) | ) | ||||
assert response.status_code == status.HTTP_404_NOT_FOUND | assert response.status_code == status.HTTP_404_NOT_FOUND | ||||
response_content = parse_xml(response.content) | response_content = parse_xml(response.content) | ||||
assert "Unknown collection name" in response_content["sword:error"]["summary"] | assert "Unknown collection name" in response_content["sword:error"]["summary"] | ||||
def test_add_metadata_to_unknown_collection( | def test_add_metadata_to_unknown_collection( | ||||
partial_deposit, authenticated_client, atom_dataset | partial_deposit, authenticated_client, atom_dataset | ||||
): | ): | ||||
"""Replacing metadata to unknown deposit should return a 404 response | """Replacing metadata to unknown deposit should return a 404 response | ||||
""" | """ | ||||
deposit = partial_deposit | deposit = partial_deposit | ||||
unknown_collection_name = "unknown-collection" | unknown_collection_name = "unknown-collection" | ||||
try: | try: | ||||
DepositCollection.objects.get(name=unknown_collection_name) | DepositCollection.objects.get(name=unknown_collection_name) | ||||
except DepositCollection.DoesNotExist: | except DepositCollection.DoesNotExist: | ||||
assert True | assert True | ||||
url = reverse(EDIT_SE_IRI, args=[unknown_collection_name, deposit.id]) | url = reverse(SE_IRI, args=[unknown_collection_name, deposit.id]) | ||||
response = authenticated_client.post( | response = authenticated_client.post( | ||||
url, | url, | ||||
content_type="application/atom+xml;type=entry", | content_type="application/atom+xml;type=entry", | ||||
data=atom_dataset["entry-data1"], | data=atom_dataset["entry-data1"], | ||||
) | ) | ||||
assert response.status_code == status.HTTP_404_NOT_FOUND | assert response.status_code == status.HTTP_404_NOT_FOUND | ||||
response_content = parse_xml(response.content) | response_content = parse_xml(response.content) | ||||
assert "Unknown collection name" in response_content["sword:error"]["summary"] | assert "Unknown collection name" in response_content["sword:error"]["summary"] | ||||
def test_replace_metadata_to_unknown_deposit( | def test_replace_metadata_to_unknown_deposit( | ||||
authenticated_client, deposit_collection, atom_dataset | authenticated_client, deposit_collection, atom_dataset | ||||
): | ): | ||||
"""Adding metadata to unknown deposit should return a 404 response | """Adding metadata to unknown deposit should return a 404 response | ||||
""" | """ | ||||
unknown_deposit_id = 998 | unknown_deposit_id = 998 | ||||
try: | try: | ||||
Deposit.objects.get(pk=unknown_deposit_id) | Deposit.objects.get(pk=unknown_deposit_id) | ||||
except Deposit.DoesNotExist: | except Deposit.DoesNotExist: | ||||
assert True | assert True | ||||
url = reverse(EDIT_SE_IRI, args=[deposit_collection.name, unknown_deposit_id]) | url = reverse(EDIT_IRI, args=[deposit_collection.name, unknown_deposit_id]) | ||||
response = authenticated_client.put( | response = authenticated_client.put( | ||||
url, | url, | ||||
content_type="application/atom+xml;type=entry", | content_type="application/atom+xml;type=entry", | ||||
data=atom_dataset["entry-data1"], | data=atom_dataset["entry-data1"], | ||||
) | ) | ||||
assert response.status_code == status.HTTP_404_NOT_FOUND | assert response.status_code == status.HTTP_404_NOT_FOUND | ||||
response_content = parse_xml(response.content) | response_content = parse_xml(response.content) | ||||
assert ( | assert ( | ||||
▲ Show 20 Lines • Show All 130 Lines • ▼ Show 20 Lines | atom_entry = InMemoryUploadedFile( | ||||
BytesIO(data_atom_entry.encode("utf-8")), | BytesIO(data_atom_entry.encode("utf-8")), | ||||
field_name="atom0", | field_name="atom0", | ||||
name="atom0", | name="atom0", | ||||
content_type='application/atom+xml; charset="utf-8"', | content_type='application/atom+xml; charset="utf-8"', | ||||
size=len(data_atom_entry), | size=len(data_atom_entry), | ||||
charset="utf-8", | charset="utf-8", | ||||
) | ) | ||||
update_uri = reverse(EDIT_SE_IRI, args=[deposit_collection.name, deposit.id]) | update_uri = reverse(EDIT_IRI, args=[deposit_collection.name, deposit.id]) | ||||
response = authenticated_client.put( | response = authenticated_client.put( | ||||
update_uri, | update_uri, | ||||
format="multipart", | format="multipart", | ||||
data={"archive": archive, "atom_entry": atom_entry,}, | data={"archive": archive, "atom_entry": atom_entry,}, | ||||
) | ) | ||||
assert response.status_code == status.HTTP_204_NO_CONTENT | assert response.status_code == status.HTTP_204_NO_CONTENT | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | actual_existing_requests_archive = DepositRequest.objects.filter( | ||||
deposit=complete_deposit, type="archive" | deposit=complete_deposit, type="archive" | ||||
) | ) | ||||
nb_archives = len(actual_existing_requests_archive) | nb_archives = len(actual_existing_requests_archive) | ||||
actual_existing_requests_metadata = DepositRequest.objects.filter( | actual_existing_requests_metadata = DepositRequest.objects.filter( | ||||
deposit=complete_deposit, type="metadata" | deposit=complete_deposit, type="metadata" | ||||
) | ) | ||||
nb_metadata = len(actual_existing_requests_metadata) | nb_metadata = len(actual_existing_requests_metadata) | ||||
update_uri = reverse( | update_uri = reverse(EDIT_IRI, args=[deposit_collection.name, complete_deposit.id]) | ||||
EDIT_SE_IRI, args=[deposit_collection.name, complete_deposit.id] | |||||
) | |||||
response = authenticated_client.put( | response = authenticated_client.put( | ||||
update_uri, | update_uri, | ||||
content_type="application/atom+xml;type=entry", | content_type="application/atom+xml;type=entry", | ||||
data=atom_dataset["entry-data1"], | data=atom_dataset["entry-data1"], | ||||
HTTP_X_CHECK_SWHID=complete_deposit.swhid, | HTTP_X_CHECK_SWHID=complete_deposit.swhid, | ||||
) | ) | ||||
assert response.status_code == status.HTTP_204_NO_CONTENT | assert response.status_code == status.HTTP_204_NO_CONTENT | ||||
▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | ): | ||||
"""failure: client updates metadata on deposit with SWHID not matching the deposit's. | """failure: client updates metadata on deposit with SWHID not matching the deposit's. | ||||
Response: 400 | Response: 400 | ||||
""" | """ | ||||
incorrect_swhid = "swh:1:dir:ef04a768181417fbc5eef4243e2507915f24deea" | incorrect_swhid = "swh:1:dir:ef04a768181417fbc5eef4243e2507915f24deea" | ||||
assert complete_deposit.swhid != incorrect_swhid | assert complete_deposit.swhid != incorrect_swhid | ||||
update_uri = reverse( | update_uri = reverse(EDIT_IRI, args=[deposit_collection.name, complete_deposit.id]) | ||||
EDIT_SE_IRI, args=[deposit_collection.name, complete_deposit.id] | |||||
) | |||||
response = authenticated_client.put( | response = authenticated_client.put( | ||||
update_uri, | update_uri, | ||||
content_type="application/atom+xml;type=entry", | content_type="application/atom+xml;type=entry", | ||||
data=atom_dataset["entry-data1"], | data=atom_dataset["entry-data1"], | ||||
HTTP_X_CHECK_SWHID=incorrect_swhid, | HTTP_X_CHECK_SWHID=incorrect_swhid, | ||||
) | ) | ||||
assert response.status_code == status.HTTP_400_BAD_REQUEST | assert response.status_code == status.HTTP_400_BAD_REQUEST | ||||
assert b"Mismatched provided SWHID" in response.content | assert b"Mismatched provided SWHID" in response.content | ||||
def test_put_update_metadata_done_deposit_failure_malformed_xml( | def test_put_update_metadata_done_deposit_failure_malformed_xml( | ||||
tmp_path, | tmp_path, | ||||
authenticated_client, | authenticated_client, | ||||
complete_deposit, | complete_deposit, | ||||
deposit_collection, | deposit_collection, | ||||
atom_dataset, | atom_dataset, | ||||
swh_storage, | swh_storage, | ||||
): | ): | ||||
"""failure: client updates metadata on deposit done with a malformed xml | """failure: client updates metadata on deposit done with a malformed xml | ||||
Response: 400 | Response: 400 | ||||
""" | """ | ||||
update_uri = reverse( | update_uri = reverse(EDIT_IRI, args=[deposit_collection.name, complete_deposit.id]) | ||||
EDIT_SE_IRI, args=[deposit_collection.name, complete_deposit.id] | |||||
) | |||||
response = authenticated_client.put( | response = authenticated_client.put( | ||||
update_uri, | update_uri, | ||||
content_type="application/atom+xml;type=entry", | content_type="application/atom+xml;type=entry", | ||||
data=atom_dataset["entry-data-ko"], | data=atom_dataset["entry-data-ko"], | ||||
HTTP_X_CHECK_SWHID=complete_deposit.swhid, | HTTP_X_CHECK_SWHID=complete_deposit.swhid, | ||||
) | ) | ||||
assert response.status_code == status.HTTP_400_BAD_REQUEST | assert response.status_code == status.HTTP_400_BAD_REQUEST | ||||
assert b"Malformed xml metadata" in response.content | assert b"Malformed xml metadata" in response.content | ||||
def test_put_update_metadata_done_deposit_failure_empty_xml( | def test_put_update_metadata_done_deposit_failure_empty_xml( | ||||
tmp_path, | tmp_path, | ||||
authenticated_client, | authenticated_client, | ||||
complete_deposit, | complete_deposit, | ||||
deposit_collection, | deposit_collection, | ||||
atom_dataset, | atom_dataset, | ||||
swh_storage, | swh_storage, | ||||
): | ): | ||||
"""failure: client updates metadata on deposit done with an empty xml. | """failure: client updates metadata on deposit done with an empty xml. | ||||
Response: 400 | Response: 400 | ||||
""" | """ | ||||
update_uri = reverse( | update_uri = reverse(EDIT_IRI, args=[deposit_collection.name, complete_deposit.id]) | ||||
EDIT_SE_IRI, args=[deposit_collection.name, complete_deposit.id] | |||||
) | |||||
for atom_key in ["entry-data-empty-body", "entry-data-empty-body-no-namespace"]: | for atom_key in ["entry-data-empty-body", "entry-data-empty-body-no-namespace"]: | ||||
atom_content = atom_dataset[atom_key] | atom_content = atom_dataset[atom_key] | ||||
response = authenticated_client.put( | response = authenticated_client.put( | ||||
update_uri, | update_uri, | ||||
content_type="application/atom+xml;type=entry", | content_type="application/atom+xml;type=entry", | ||||
data=atom_content, | data=atom_content, | ||||
HTTP_X_CHECK_SWHID=complete_deposit.swhid, | HTTP_X_CHECK_SWHID=complete_deposit.swhid, | ||||
Show All 11 Lines | def test_put_update_metadata_done_deposit_failure_functional_checks( | ||||
atom_dataset, | atom_dataset, | ||||
swh_storage, | swh_storage, | ||||
): | ): | ||||
"""failure: client updates metadata on deposit done without required incomplete metadata | """failure: client updates metadata on deposit done without required incomplete metadata | ||||
Response: 400 | Response: 400 | ||||
""" | """ | ||||
update_uri = reverse( | update_uri = reverse(EDIT_IRI, args=[deposit_collection.name, complete_deposit.id]) | ||||
EDIT_SE_IRI, args=[deposit_collection.name, complete_deposit.id] | |||||
) | |||||
response = authenticated_client.put( | response = authenticated_client.put( | ||||
update_uri, | update_uri, | ||||
content_type="application/atom+xml;type=entry", | content_type="application/atom+xml;type=entry", | ||||
# no title, nor author, nor name fields | # no title, nor author, nor name fields | ||||
data=atom_dataset["entry-data-fail-metadata-functional-checks"], | data=atom_dataset["entry-data-fail-metadata-functional-checks"], | ||||
HTTP_X_CHECK_SWHID=complete_deposit.swhid, | HTTP_X_CHECK_SWHID=complete_deposit.swhid, | ||||
) | ) | ||||
assert response.status_code == status.HTTP_400_BAD_REQUEST | assert response.status_code == status.HTTP_400_BAD_REQUEST | ||||
assert b"Functional metadata checks failure" in response.content | assert b"Functional metadata checks failure" in response.content | ||||
# detail on the errors | # detail on the errors | ||||
assert b"- Mandatory fields are missing (author)" in response.content | assert b"- Mandatory fields are missing (author)" in response.content | ||||
assert ( | assert ( | ||||
b"- Mandatory alternate fields are missing (name or title)" in response.content | b"- Mandatory alternate fields are missing (name or title)" in response.content | ||||
) | ) |