diff --git a/swh/deposit/tests/api/test_deposit_status.py b/swh/deposit/tests/api/test_deposit_status.py index 0d1284cd..e2f8ae36 100644 --- a/swh/deposit/tests/api/test_deposit_status.py +++ b/swh/deposit/tests/api/test_deposit_status.py @@ -1,145 +1,130 @@ # Copyright (C) 2017-2019 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information from django.urls import reverse from io import BytesIO from rest_framework import status -from rest_framework.test import APITestCase -from swh.deposit.config import (COL_IRI, STATE_IRI, DEPOSIT_STATUS_DEPOSITED, +from swh.deposit.config import (STATE_IRI, DEPOSIT_STATUS_DEPOSITED, DEPOSIT_STATUS_REJECTED) -from swh.deposit.models import Deposit, DEPOSIT_STATUS_DETAIL -from swh.deposit.models import DEPOSIT_STATUS_LOAD_SUCCESS +from swh.deposit.models import ( + DEPOSIT_STATUS_DETAIL, DEPOSIT_STATUS_LOAD_SUCCESS +) from swh.deposit.parsers import parse_xml -from ..common import BasicTestCase, WithAuthTestCase, FileSystemCreationRoutine -from ..common import CommonCreationRoutine +def test_post_deposit_with_status_check( + authenticated_client, deposited_deposit): + """Successful but not loaded deposit should have a status 'deposited' -class DepositStatusTestCase(APITestCase, WithAuthTestCase, BasicTestCase, - FileSystemCreationRoutine, CommonCreationRoutine): - """Status on deposit + """ + deposit = deposited_deposit + status_url = reverse(STATE_IRI, + args=[deposit.collection.name, deposit.id]) + + # check status + status_response = authenticated_client.get(status_url) + + assert status_response.status_code == status.HTTP_200_OK + r = parse_xml(BytesIO(status_response.content)) + + assert int(r['deposit_id']) == deposit.id + assert r['deposit_status'] == DEPOSIT_STATUS_DEPOSITED + assert r['deposit_status_detail'] == \ + DEPOSIT_STATUS_DETAIL[DEPOSIT_STATUS_DEPOSITED] + assert r['deposit_external_id'] == deposit.external_id + + +def test_status_unknown_deposit(authenticated_client, deposit_collection): + """Unknown deposit status should return 404 response + + """ + unknown_deposit_id = 999 + status_url = reverse(STATE_IRI, + args=[deposit_collection.name, unknown_deposit_id]) + status_response = authenticated_client.get(status_url) + assert status_response.status_code == status.HTTP_404_NOT_FOUND + + +def test_status_unknown_collection( + authenticated_client, deposited_deposit): + """Unknown collection status should return 404 response""" + deposit = deposited_deposit + unknown_collection = 'something-unknown' + status_url = reverse(STATE_IRI, + args=[unknown_collection, deposit.id]) + status_response = authenticated_client.get(status_url) + assert status_response.status_code == status.HTTP_404_NOT_FOUND + + +def test_status_deposit_rejected(authenticated_client, rejected_deposit): + """Rejected deposit status should be 'rejected' with detailed summary + + """ + deposit = rejected_deposit + # _status_detail = {'url': {'summary': 'Wrong url'}} + + url = reverse(STATE_IRI, + args=[deposit.collection.name, deposit.id]) + + # when + status_response = authenticated_client.get(url) + + # then + assert status_response.status_code == status.HTTP_200_OK + r = parse_xml(BytesIO(status_response.content)) + assert int(r['deposit_id']) == deposit.id + assert r['deposit_status'] == DEPOSIT_STATUS_REJECTED + assert r['deposit_status_detail'] == 'Deposit failed the checks' + if deposit.swh_id: + assert r['deposit_swh_id'] == deposit.swh_id + + +def test_status_with_http_accept_header_should_not_break( + authenticated_client, partial_deposit): + """Asking deposit status with Accept header should return 200 + + """ + deposit = partial_deposit + + status_url = reverse(STATE_IRI, args=[ + deposit.collection.name, deposit.id]) + + response = authenticated_client.get(status_url) + assert response.status_code == status.HTTP_200_OK + + response = authenticated_client.get( + status_url, + HTTP_ACCEPT='text/html,application/xml;q=9,*/*,q=8') + assert response.status_code == status.HTTP_200_OK + + +def test_status_complete_deposit( + authenticated_client, complete_deposit): + """Successful and loaded deposit should be 'done' and have detailed swh ids """ - def test_post_deposit_with_status_check(self): - """Binary upload should be accepted - - """ - # given - url = reverse(COL_IRI, args=[self.collection.name]) - - external_id = 'some-external-id-1' - - # when - response = self.client.post( - url, - content_type='application/zip', # as zip - data=self.archive['data'], - # + headers - CONTENT_LENGTH=self.archive['length'], - HTTP_SLUG=external_id, - HTTP_CONTENT_MD5=self.archive['md5sum'], - HTTP_PACKAGING='http://purl.org/net/sword/package/SimpleZip', - HTTP_IN_PROGRESS='false', - HTTP_CONTENT_DISPOSITION='attachment; filename=filename0') - - # then - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - - deposit = Deposit.objects.get(external_id=external_id) - - status_url = reverse(STATE_IRI, - args=[self.collection.name, deposit.id]) - - # check status - status_response = self.client.get(status_url) - - self.assertEqual(status_response.status_code, status.HTTP_200_OK) - r = parse_xml(BytesIO(status_response.content)) - - self.assertEqual(int(r['deposit_id']), deposit.id) - self.assertEqual(r['deposit_status'], DEPOSIT_STATUS_DEPOSITED) - self.assertEqual(r['deposit_status_detail'], - DEPOSIT_STATUS_DETAIL[DEPOSIT_STATUS_DEPOSITED]) - self.assertEqual(r['deposit_external_id'], external_id) - - def test_status_with_swh_information(self): - _status = DEPOSIT_STATUS_LOAD_SUCCESS - _context = 'https://hal.archives-ouvertes.fr/hal-01727745' - _swh_id = 'swh:1:dir:42a13fc721c8716ff695d0d62fc851d641f3a12b' - _swh_id_context = '%s;%s' % (_swh_id, _context) - _swh_anchor_id = 'swh:rev:1:548b3c0a2bb43e1fca191e24b5803ff6b3bc7c10' - _swh_anchor_id_context = '%s;%s' % (_swh_anchor_id, _context) - - # given - deposit_id = self.create_deposit_with_status( - status=_status, - swh_id=_swh_id, - swh_id_context=_swh_id_context, - swh_anchor_id=_swh_anchor_id, - swh_anchor_id_context=_swh_anchor_id_context - ) - - url = reverse(STATE_IRI, args=[self.collection.name, deposit_id]) - - # when - status_response = self.client.get(url) - - # then - self.assertEqual(status_response.status_code, status.HTTP_200_OK) - r = parse_xml(BytesIO(status_response.content)) - self.assertEqual(int(r['deposit_id']), deposit_id) - self.assertEqual(r['deposit_status'], _status) - self.assertEqual(r['deposit_status_detail'], - DEPOSIT_STATUS_DETAIL[DEPOSIT_STATUS_LOAD_SUCCESS]) - self.assertEqual(r['deposit_swh_id'], _swh_id) - self.assertEqual(r['deposit_swh_id_context'], _swh_id_context) - self.assertEqual(r['deposit_swh_anchor_id'], _swh_anchor_id) - self.assertEqual(r['deposit_swh_anchor_id_context'], - _swh_anchor_id_context) - - def test_status_on_unknown_deposit(self): - """Asking for the status of unknown deposit returns 404 response""" - status_url = reverse(STATE_IRI, args=[self.collection.name, 999]) - status_response = self.client.get(status_url) - self.assertEqual(status_response.status_code, - status.HTTP_404_NOT_FOUND) - - def test_status_with_http_accept_header_should_not_break(self): - """Asking deposit status with Accept header should return 200 - - """ - deposit_id = self.create_deposit_partial() - - status_url = reverse(STATE_IRI, args=[ - self.collection.name, deposit_id]) - response = self.client.get( - status_url, - HTTP_ACCEPT='text/html,application/xml;q=9,*/*,q=8') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - - def test_status_on_deposit_rejected(self): - _status = DEPOSIT_STATUS_REJECTED - _swh_id = '548b3c0a2bb43e1fca191e24b5803ff6b3bc7c10' - _status_detail = {'url': {'summary': 'Wrong url'}} - - # given - deposit_id = self.create_deposit_with_status( - status=_status, swh_id=_swh_id, status_detail=_status_detail) - - url = reverse(STATE_IRI, args=[self.collection.name, deposit_id]) - - # when - status_response = self.client.get(url) - - # then - self.assertEqual(status_response.status_code, status.HTTP_200_OK) - r = parse_xml(BytesIO(status_response.content)) - self.assertEqual(int(r['deposit_id']), deposit_id) - self.assertEqual(r['deposit_status'], _status) - self.assertEqual(r['deposit_status_detail'], '- Wrong url') - self.assertEqual(r['deposit_swh_id'], _swh_id) + deposit = complete_deposit + url = reverse(STATE_IRI, args=[deposit.collection.name, deposit.id]) + + # when + status_response = authenticated_client.get(url) + + # then + assert status_response.status_code == status.HTTP_200_OK + r = parse_xml(BytesIO(status_response.content)) + assert int(r['deposit_id']) == deposit.id + assert r['deposit_status'] == DEPOSIT_STATUS_LOAD_SUCCESS + assert r['deposit_status_detail'] == \ + DEPOSIT_STATUS_DETAIL[DEPOSIT_STATUS_LOAD_SUCCESS] + assert deposit.swh_id is not None + assert r['deposit_swh_id'] == deposit.swh_id + assert deposit.swh_id_context is not None + assert r['deposit_swh_id_context'] == deposit.swh_id_context + assert deposit.swh_anchor_id is not None + assert r['deposit_swh_anchor_id'] == deposit.swh_anchor_id + assert deposit.swh_anchor_id_context is not None + assert r['deposit_swh_anchor_id_context'] == deposit.swh_anchor_id_context diff --git a/swh/deposit/tests/conftest.py b/swh/deposit/tests/conftest.py index 49ba4c4f..0ca26b00 100644 --- a/swh/deposit/tests/conftest.py +++ b/swh/deposit/tests/conftest.py @@ -1,127 +1,217 @@ # Copyright (C) 2019 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information import base64 import pytest import psycopg2 +from django.urls import reverse from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT +from rest_framework import status from rest_framework.test import APIClient +# , STATE_IRI, from swh.scheduler.tests.conftest import * # noqa +from swh.deposit.config import ( + COL_IRI, DEPOSIT_STATUS_DEPOSITED, DEPOSIT_STATUS_REJECTED, + DEPOSIT_STATUS_PARTIAL, DEPOSIT_STATUS_LOAD_SUCCESS +) +from swh.deposit.tests.common import create_arborescence_archive + TEST_USER = { 'username': 'test', 'password': 'password', 'email': 'test@example.org', 'provider_url': 'https://hal-test.archives-ouvertes.fr/', 'domain': 'archives-ouvertes.fr/', 'collection': { 'name': 'test' }, } def execute_sql(sql): """Execute sql to postgres db""" with psycopg2.connect(database='postgres') as conn: conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) cur = conn.cursor() cur.execute(sql) @pytest.hookimpl(tryfirst=True) def pytest_load_initial_conftests(early_config, parser, args): """This hook is done prior to django loading. Used to initialize the deposit's server db. """ import project.app.signals def prepare_db(*args, **kwargs): from django.conf import settings db_name = 'tests' print('before: %s' % settings.DATABASES) # work around db settings for django for k, v in [ ('ENGINE', 'django.db.backends.postgresql'), ('NAME', 'tests'), ('USER', postgresql_proc.user), # noqa ('HOST', postgresql_proc.host), # noqa ('PORT', postgresql_proc.port), # noqa ]: settings.DATABASES['default'][k] = v print('after: %s' % settings.DATABASES) execute_sql('DROP DATABASE IF EXISTS %s' % db_name) execute_sql('CREATE DATABASE %s TEMPLATE template0' % db_name) project.app.signals.something = prepare_db @pytest.fixture -def deposit_user(db): - """Create/Return the test_user "test" - - """ - from swh.deposit.models import DepositCollection, DepositClient - # UserModel = django_user_model +def deposit_collection(db): + from swh.deposit.models import DepositCollection collection_name = TEST_USER['collection']['name'] try: collection = DepositCollection._default_manager.get( name=collection_name) except DepositCollection.DoesNotExist: collection = DepositCollection(name=collection_name) collection.save() + return collection + - # Create a user +@pytest.fixture +def deposit_user(db, deposit_collection): + """Create/Return the test_user "test" + + """ + from swh.deposit.models import DepositClient try: user = DepositClient._default_manager.get( username=TEST_USER['username']) except DepositClient.DoesNotExist: user = DepositClient._default_manager.create_user( username=TEST_USER['username'], email=TEST_USER['email'], password=TEST_USER['password'], provider_url=TEST_USER['provider_url'], domain=TEST_USER['domain'], ) - user.collections = [collection.id] + user.collections = [deposit_collection.id] user.save() - return user -# @pytest.fixture -# def headers(deposit_user): - import base64 - _token = '%s:%s' % (deposit_user.username, TEST_USER['password']) - token = base64.b64encode(_token.encode('utf-8')) - authorization = 'Basic %s' % token.decode('utf-8') - return { - 'AUTHENTICATION': authorization, - } - - @pytest.fixture def client(): """Override pytest-django one which does not work for djangorestframework. """ return APIClient() # <- drf's client @pytest.yield_fixture def authenticated_client(client, deposit_user): """Returned a logged client """ _token = '%s:%s' % (deposit_user.username, TEST_USER['password']) token = base64.b64encode(_token.encode('utf-8')) authorization = 'Basic %s' % token.decode('utf-8') client.credentials(HTTP_AUTHORIZATION=authorization) yield client client.logout() + + +@pytest.fixture +def sample_archive(tmp_path): + """Returns a sample archive + + """ + tmp_path = str(tmp_path) # pytest version limitation in previous version + archive = create_arborescence_archive( + tmp_path, 'archive1', 'file1', b'some content in file') + + return archive + + +@pytest.fixture +def deposited_deposit( + sample_archive, deposit_collection, authenticated_client): + """Returns a deposit with status deposited. + + """ + collection = deposit_collection.name + url = reverse(COL_IRI, args=[collection]) + external_id = 'some-external-id-1' + + # when + response = authenticated_client.post( + url, + content_type='application/zip', # as zip + data=sample_archive['data'], + # + headers + CONTENT_LENGTH=sample_archive['length'], + HTTP_SLUG=external_id, + HTTP_CONTENT_MD5=sample_archive['md5sum'], + HTTP_PACKAGING='http://purl.org/net/sword/package/SimpleZip', + HTTP_IN_PROGRESS='false', + HTTP_CONTENT_DISPOSITION='attachment; filename=filename0') + + # then + assert response.status_code == status.HTTP_201_CREATED + + from swh.deposit.models import Deposit + deposit = Deposit._default_manager.get(external_id=external_id) + assert deposit.status == DEPOSIT_STATUS_DEPOSITED + return deposit + + +@pytest.fixture +def rejected_deposit( + deposited_deposit): + """Returns a deposit with status rejected. + + """ + deposit = deposited_deposit + deposit.status = DEPOSIT_STATUS_REJECTED + deposit.save() + assert deposit.status == DEPOSIT_STATUS_REJECTED + return deposit + + +@pytest.fixture +def partial_deposit( + deposited_deposit): + """Returns a deposit with status rejected. + + """ + deposit = deposited_deposit + deposit.status = DEPOSIT_STATUS_PARTIAL + deposit.save() + assert deposit.status == DEPOSIT_STATUS_PARTIAL + return deposit + + +@pytest.fixture +def complete_deposit(deposited_deposit): + """Returns a completed deposit (load success) + + """ + deposit = deposited_deposit + deposit.status = DEPOSIT_STATUS_LOAD_SUCCESS + _swh_id_context = 'https://hal.archives-ouvertes.fr/hal-01727745' + deposit.swh_id = 'swh:1:dir:42a13fc721c8716ff695d0d62fc851d641f3a12b' + deposit.swh_id_context = '%s;%s' % ( + deposit.swh_id, _swh_id_context) + deposit.swh_anchor_id = \ + 'swh:rev:1:548b3c0a2bb43e1fca191e24b5803ff6b3bc7c10' + deposit.swh_anchor_id_context = '%s;%s' % ( + deposit.swh_anchor_id, _swh_id_context) + + deposit.save() + return deposit