def test_files_access(draft_access,
                              submitted_access,
                              published_access,
                              user=None):
            def get_file(deposit, file_access):
                with app.test_client() as client:
                    if user is not None:
                        login_user(user, client)
                    subtest_file_bucket_permissions(client,
                                                    deposit.files.bucket,
                                                    access_level=file_access,
                                                    is_authenticated=user
                                                    is not None)

            deposit = create_deposit(test_record_data, creator, uploaded_files)
            get_file(deposit, file_access=draft_access)

            deposit = create_deposit(test_record_data, creator, uploaded_files)
            deposit.submit()
            get_file(deposit, file_access=submitted_access)

            deposit = create_deposit(test_record_data, creator, uploaded_files)
            deposit.submit()
            deposit.publish()
            get_file(deposit, file_access=published_access)
Пример #2
0
def test_deposit_create_with_invalid_fields_fails(app, test_records_data):
    """Test deposit creation without or with an invalid field fails."""
    data = test_records_data[0]
    with app.app_context():
        data['publication_state'] = 'published'
        with pytest.raises(InvalidDepositError):
            deposit = create_deposit(data=data)

    with app.app_context():
        data['$schema'] = '__garbage__'
        with pytest.raises(InvalidDepositError):
            deposit = create_deposit(data=data)
Пример #3
0
def test_deposit_create_with_invalid_community_fails(app,
                                                     test_records_data):
    """Test deposit creation without or with an invalid community fails."""
    data = test_records_data[0]
    with app.app_context():
        # test with no community
        del data['community']
        with pytest.raises(ValidationError):
            deposit = create_deposit(data=data)

    with app.app_context():
        # test with an invalid community
        data['community'] = str(uuid.uuid4())
        with pytest.raises(InvalidDepositError):
            deposit = create_deposit(data=data)
 def test_publish(status, user=None):
     deposit = create_deposit(record_data, creator)
     deposit.submit()
     headers = [('Content-Type', 'application/json-patch+json'),
                ('Accept', 'application/json')]
     with app.test_client() as client:
         if user is not None:
             login_user(user, client)
         request_res = client.patch(url_for(
             'b2share_deposit_rest.b2dep_item',
             pid_value=deposit.pid.pid_value),
                                    data=json.dumps([{
                                        "op":
                                        "replace",
                                        "path":
                                        "/publication_state",
                                        "value":
                                        PublicationStates.published.name
                                    }, {
                                        "op":
                                        "replace",
                                        "path":
                                        "/titles",
                                        "value": [{
                                            'title': 'newtitle'
                                        }]
                                    }]),
                                    headers=headers)
         assert request_res.status_code == status
Пример #5
0
def test_generation_of_external_pids(app, records_data_with_external_pids,
                                     deposit_with_external_pids, test_users):
    """Test the generate_external_pids function."""
    expected_output = records_data_with_external_pids[
        'external_pids'][:]
    with app.app_context():
        deposit = Deposit.get_record(deposit_with_external_pids.deposit_id)
        output = generate_external_pids(deposit)
        assert output == expected_output

        all_files = list(deposit.files)
        for f in all_files:
            if f.obj.key == \
                    records_data_with_external_pids['external_pids'][0]['key']:
                f.delete(f.obj.bucket, f.obj.key)
        output = generate_external_pids(deposit)
        expected_output_sorted = \
            records_data_with_external_pids['external_pids'][1:]
        expected_output_sorted.sort(key=lambda f: f['key'])
        assert output == expected_output_sorted

        records_data_with_external_pids['external_pids'].reverse()
        deposit2 = create_deposit(records_data_with_external_pids,
                                  test_users['deposits_creator'])
        output = generate_external_pids(deposit2)
        assert output == expected_output
def test_generation_of_external_pids(app, records_data_with_external_pids,
                                     deposit_with_external_pids, test_users):
    """Test the generate_external_pids function."""
    expected_output = records_data_with_external_pids['external_pids'][:]
    with app.app_context():
        deposit = Deposit.get_record(deposit_with_external_pids.deposit_id)
        output = generate_external_pids(deposit)
        assert output == expected_output

        all_files = list(deposit.files)
        for f in all_files:
            if f.obj.key == \
                    records_data_with_external_pids['external_pids'][0]['key']:
                f.delete(f.obj.bucket, f.obj.key)
        output = generate_external_pids(deposit)
        expected_output_sorted = \
            records_data_with_external_pids['external_pids'][1:]
        expected_output_sorted.sort(key=lambda f: f['key'])
        assert output == expected_output_sorted

        records_data_with_external_pids['external_pids'].reverse()
        deposit2 = create_deposit(records_data_with_external_pids,
                                  test_users['deposits_creator'])
        output = generate_external_pids(deposit2)
        assert output == expected_output
def test_deposit_delete(app, draft_deposits, test_records, test_users,
                        test_communities):
    """Test deposit deletion."""
    with app.app_context():
        # create a deposit with a parent pid which has no other children
        first_deposit = create_deposit(
            {'community': str(test_communities['MyTestCommunity1'])},
            test_users['deposits_creator'])
        parent_pid = first_deposit['_pid'][0]['value']
        deposit_pid_value = first_deposit['_deposit']['id']
        # delete the deposit
        first_deposit.delete()
        deposit = PersistentIdentifier.query.filter_by(
            pid_value=deposit_pid_value).first()
        parent = PersistentIdentifier.query.filter_by(
            pid_value=parent_pid).one_or_none()
        # check that deleting it deletes parent from the db because there are
        # no remaining published versions and draft.
        assert not parent
        assert deposit.status == PIDStatus.DELETED

        # check that the buckets are removed
        assert not db.session.query(
            Bucket.query.join(RecordsBuckets).filter(
                RecordsBuckets.bucket_id == Bucket.id, RecordsBuckets.record_id
                == first_deposit.id).exists()).scalar()

        # create a deposit with a parent which has other children
        v1 = test_records[0].data
        v1_rec = B2ShareRecord.get_record(test_records[0].record_id)
        v1_pid, v1_id = pid_of(v1)
        data = copy_data_from_previous(v1_rec.model.json)
        v2 = create_deposit(data,
                            test_users['deposits_creator'],
                            version_of=v1_id)
        deposit_pid_value = v2['_deposit']['id']
        parent_pid = v2['_pid'][0]['value']

        v2.delete()
        deposit = PersistentIdentifier.query.filter_by(
            pid_value=deposit_pid_value).first()
        parent = PersistentIdentifier.query.filter_by(
            pid_value=parent_pid).first()
        # check that the parent status is not changed after deleting
        assert parent.status != PIDStatus.DELETED
        assert parent.get_redirect() == v1_pid
        assert deposit.status == PIDStatus.DELETED
Пример #8
0
def test_deposit_delete_permissions(app, test_records_data, login_user,
                                    test_users):
    """Test deposit delete with HTTP DELETE."""
    with app.app_context():
        admin = test_users['admin']

        def test_delete(deposit, status, user=None):
            with app.test_client() as client:
                if user is not None:
                    login_user(user, client)
                headers = [('Accept', 'application/json')]
                request_res = client.delete(url_for(
                    'b2share_deposit_rest.b2dep_item',
                    pid_value=deposit.pid.pid_value),
                                            headers=headers)
                assert request_res.status_code == status

        creator = create_user('creator')
        non_creator = create_user('non-creator')
        # test with anonymous user
        deposit = create_deposit(test_records_data[0], creator)
        test_delete(deposit, 401)
        deposit.submit()
        test_delete(deposit, 401)
        deposit.publish()
        test_delete(deposit, 401)

        # test with non creator user
        deposit = create_deposit(test_records_data[0], creator)
        test_delete(deposit, 403, non_creator)
        deposit.submit()
        test_delete(deposit, 403, non_creator)
        deposit.publish()
        test_delete(deposit, 403, non_creator)

        # test with creator user
        deposit = create_deposit(test_records_data[0], creator)
        test_delete(deposit, 204, creator)
        deposit = create_deposit(test_records_data[0], creator)
        deposit.submit()
        test_delete(deposit, 204, creator)
        deposit = create_deposit(test_records_data[0], creator)
        deposit.submit()
        deposit.publish()
        test_delete(deposit, 403, creator)

        # test with admin user
        deposit = create_deposit(test_records_data[0], creator)
        test_delete(deposit, 204, admin)
        deposit = create_deposit(test_records_data[0], creator)
        deposit.submit()
        test_delete(deposit, 204, admin)
        deposit = create_deposit(test_records_data[0], creator)
        deposit.submit()
        deposit.publish()
        # FIXME: handle the deletion of published deposits
        test_delete(deposit, 403, admin)
Пример #9
0
def test_deposit_delete_permissions(app, test_records_data,
                                    login_user, test_users):
    """Test deposit delete with HTTP DELETE."""
    with app.app_context():
        admin = test_users['admin']
        def test_delete(deposit, status, user=None):
            with app.test_client() as client:
                if user is not None:
                    login_user(user, client)
                headers = [('Accept', 'application/json')]
                request_res = client.delete(
                    url_for('b2share_deposit_rest.b2dep_item',
                            pid_value=deposit.pid.pid_value),
                    headers=headers)
                assert request_res.status_code == status

        creator = create_user('creator')
        non_creator = create_user('non-creator')
        # test with anonymous user
        deposit = create_deposit(test_records_data[0], creator)
        test_delete(deposit, 401)
        deposit.submit()
        test_delete(deposit, 401)
        deposit.publish()
        test_delete(deposit, 401)

        # test with non creator user
        deposit = create_deposit(test_records_data[0], creator)
        test_delete(deposit, 403, non_creator)
        deposit.submit()
        test_delete(deposit, 403, non_creator)
        deposit.publish()
        test_delete(deposit, 403, non_creator)

        # test with creator user
        deposit = create_deposit(test_records_data[0], creator)
        test_delete(deposit, 204, creator)
        deposit = create_deposit(test_records_data[0], creator)
        deposit.submit()
        test_delete(deposit, 204, creator)
        deposit = create_deposit(test_records_data[0], creator)
        deposit.submit()
        deposit.publish()
        test_delete(deposit, 403, creator)

        # test with admin user
        deposit = create_deposit(test_records_data[0], creator)
        test_delete(deposit, 204, admin)
        deposit = create_deposit(test_records_data[0], creator)
        deposit.submit()
        test_delete(deposit, 204, admin)
        deposit = create_deposit(test_records_data[0], creator)
        deposit.submit()
        deposit.publish()
        test_delete(deposit, 204, admin)
Пример #10
0
def test_deposit_create_with_incomplete_metadata(app,
                                                 test_incomplete_records_data):
    """Test deposit creation with incomplete metadata succeeds."""
    with app.app_context():
        for data in test_incomplete_records_data:
            deposit = create_deposit(data=data.incomplete_data)
            assert (deposit['publication_state']
                    == PublicationStates.draft.name)
            assert (deposit['_deposit']['status'] == 'draft')
def test_deposit_delete(app, draft_deposits, test_records,
                        test_users, test_communities):
    """Test deposit deletion."""
    with app.app_context():
        # create a deposit with a parent pid which has no other children
        first_deposit = create_deposit(
            {'community': str(test_communities['MyTestCommunity1'])},
            test_users['deposits_creator'])
        parent_pid = first_deposit['_pid'][0]['value']
        deposit_pid_value = first_deposit['_deposit']['id']
        # delete the deposit
        first_deposit.delete()
        deposit = PersistentIdentifier.query.filter_by(pid_value=deposit_pid_value).first()
        parent = PersistentIdentifier.query.filter_by(pid_value=parent_pid).one_or_none()
        # check that deleting it deletes parent from the db because there are
        # no remaining published versions and draft.
        assert not parent
        assert deposit.status == PIDStatus.DELETED

        # check that the buckets are removed
        assert not db.session.query(
            Bucket.query.join(RecordsBuckets).
            filter(RecordsBuckets.bucket_id == Bucket.id,
                   RecordsBuckets.record_id == first_deposit.id).exists()
        ).scalar()

        # create a deposit with a parent which has other children
        v1 = test_records[0].data
        v1_rec = B2ShareRecord.get_record(test_records[0].record_id)
        v1_pid, v1_id = pid_of(v1)
        data = copy_data_from_previous(v1_rec.model.json)
        v2 = create_deposit(data, test_users['deposits_creator'],
                            version_of=v1_id)
        deposit_pid_value = v2['_deposit']['id']
        parent_pid = v2['_pid'][0]['value']

        v2.delete()
        deposit = PersistentIdentifier.query.filter_by(pid_value=deposit_pid_value).first()
        parent = PersistentIdentifier.query.filter_by(pid_value=parent_pid).first()
        # check that the parent status is not changed after deleting
        assert parent.status != PIDStatus.DELETED
        assert parent.get_redirect() == v1_pid
        assert deposit.status == PIDStatus.DELETED
Пример #12
0
def test_deposit_submit_with_incomplete_metadata(app,
                                                 test_incomplete_records_data):
    """Test deposit submission with incomplete metadata fails."""
    for data in test_incomplete_records_data:
        with app.app_context():
            deposit = create_deposit(data=data.complete_data)
            deposit.commit()
            # make the data incomplete
            deposit = deposit.patch(data.patch)
            with pytest.raises(ValidationError):
                deposit.submit()
Пример #13
0
def test_records_type_helpers(app, test_records_data):
    """Test record util functions retrieving the record type."""
    with app.app_context():
        creator = create_user('creator')
        deposit = create_deposit(test_records_data[0], creator)
        deposit.submit()
        deposit.publish()
        _, record = deposit.fetch_published()
        assert is_deposit(deposit.model)
        assert not is_deposit(record.model)
        assert is_publication(record.model)
        assert not is_publication(deposit.model)
Пример #14
0
def test_records_type_helpers(app, test_records_data):
    """Test record util functions retrieving the record type."""
    with app.app_context():
        creator = create_user('creator')
        deposit = create_deposit(test_records_data[0], creator)
        deposit.submit()
        deposit.publish()
        _, record = deposit.fetch_published()
        assert is_deposit(deposit.model)
        assert not is_deposit(record.model)
        assert is_publication(record.model)
        assert not is_publication(deposit.model)
Пример #15
0
def test_deposit_publish_with_incomplete_metadata(app,
                                                  test_incomplete_records_data):
    """Test publication of an incomplete deposit fails."""
    for data in test_incomplete_records_data:
        with app.app_context():
            deposit = create_deposit(data=data.complete_data)
            deposit.submit()
            deposit.commit()
            # make the data incomplete
            deposit = deposit.patch(data.patch)
            with pytest.raises(ValidationError):
                deposit.publish()
Пример #16
0
def test_create_deposit_with_external_pids_errors(
        app, records_data_with_external_pids):
    """Test errors when a deposit is created with invalid external files."""
    data_without_pid = deepcopy(records_data_with_external_pids)
    del data_without_pid['external_pids'][0]['ePIC_PID']
    with app.app_context():
        with pytest.raises(ValidationError,
                           match="'ePIC_PID' is a required property.*"):
            create_deposit(data_without_pid)

    data_without_key = deepcopy(records_data_with_external_pids)
    del data_without_key['external_pids'][0]['key']
    with app.app_context():
        with pytest.raises(ValidationError,
                           match="'key' is a required property.*"):
            create_deposit(data_without_key)


    data_with_unknown_key = deepcopy(records_data_with_external_pids)
    data_with_unknown_key['external_pids'][0]['unknown'] = 'value'
    with app.app_context():
        with pytest.raises(ValidationError,
                           match="Additional properties are not allowed "
                           "\('unknown' was unexpected\).*"):
            create_deposit(data_with_unknown_key)
Пример #17
0
        def test_files_access(draft_access, submitted_access,
                              published_access, user=None):
            def get_file(deposit, file_access):
                with app.test_client() as client:
                    if user is not None:
                        login_user(user, client)
                    subtest_file_bucket_permissions(
                        client, deposit.files.bucket,
                        access_level=file_access,
                        is_authenticated=user is not None
                    )
            deposit = create_deposit(test_record_data, creator, uploaded_files)
            get_file(deposit, file_access=draft_access)

            deposit = create_deposit(test_record_data, creator, uploaded_files)
            deposit.submit()
            get_file(deposit, file_access=submitted_access)

            deposit = create_deposit(test_record_data, creator, uploaded_files)
            deposit.submit()
            deposit.publish()
            get_file(deposit, file_access=published_access)
def test_new_deposit_versions_preserve_schema(app, test_records, test_users):
    """Creating new versions of existing records."""
    with app.app_context():
        # Retrieve a record which will be the first version, and its deposit
        v1_rec = B2ShareRecord.get_record(test_records[0].record_id)
        _, v1_id = pid_of(test_records[0].data)

        # update the community's schema
        community_id = v1_rec.model.json['community']
        old_schema = CommunitySchema.get_community_schema(community_id)
        json_schema = json.loads(old_schema.model.community_schema)
        new_schema = CommunitySchema.create_version(community_id, json_schema)
        assert new_schema.version > old_schema.version

        # create new, unversioned, draft and records
        data = copy_data_from_previous(v1_rec.model.json)
        unver_dep = create_deposit(data, test_users['deposits_creator'])
        unver_dep.submit()
        unver_dep.publish()
        _, unver_rec = unver_dep.fetch_published()

        # the unversioned draft or record in a version chain have the updated schema
        assert unver_dep['$schema'] != Deposit._build_deposit_schema(
            v1_rec.model.json)
        assert unver_rec.model.json['$schema'] != v1_rec.model.json['$schema']

        # create new draft and record in the version chain of v1
        data = copy_data_from_previous(v1_rec.model.json)
        v2_dep = create_deposit(data,
                                test_users['deposits_creator'],
                                version_of=v1_id)
        v2_dep.submit()
        v2_dep.publish()
        _, v2_rec = v2_dep.fetch_published()

        # the new draft and record in a version chain preserve the version of the schema
        assert v2_dep['$schema'] == Deposit._build_deposit_schema(
            v1_rec.model.json)
        assert v2_rec.model.json['$schema'] == v1_rec.model.json['$schema']
Пример #19
0
def test_verify_checksum_in_deposit(app, test_communities, login_user,
                                    test_users):
    """Test uploading and reading deposit files."""
    with app.app_context():
        creator = create_user('creator')
        uploaded_files = {
            'myfile1.dat': b'contents1',
            'myfile2.dat': b'contents2',
            'replaced.dat': b'old_content',
        }
        test_record_data = generate_record_data()
        deposit = create_deposit(test_record_data, creator, uploaded_files)
        uploaded_file_name = 'additional.dat'
        uploaded_file_content = b'additional content'
        headers = [('Accept', '*/*')]
        with app.test_client() as client:
            login_user(creator, client)

            # try uploading a new file
            file_url = url_for_file(deposit.files.bucket.id,
                                    uploaded_file_name)
            client.put(file_url,
                       input_stream=BytesIO(uploaded_file_content),
                       headers=headers)
            uploaded_files[uploaded_file_name] = uploaded_file_content

            # get file content by its uri
            file_instance = deposit.files['replaced.dat'].obj.file
            file_reader = urlopen('file://' + file_instance.uri)
            content = file_reader.read()
            assert content == b'old_content'

            # first make it writeable
            file_instance.writable = True
            file_instance.set_contents(BytesIO(b'test'))
            db.session.add(file_instance)
            db.session.commit()

            # changing the contents this way should be okay
            # as the checksum is updated after writing
            verify_checksum.apply([str(file_instance.id)])
            assert file_instance.last_check

            # directly changing the contents at the uri though
            # will cause verify_checksum to change the last_check to
            # False as the checksum will be different now
            with open(file_instance.uri, 'w') as file_writer:
                file_writer.write('modified content')

            verify_checksum.apply([str(file_instance.id)])
            assert not file_instance.last_check
Пример #20
0
def test_deposit_files(app, test_communities, login_user, test_users):
    """Test uploading and reading deposit files."""
    with app.app_context():
        admin = test_users['admin']
        creator = create_user('creator')
        uploaded_files = {
            'myfile1.dat': b'contents1',
            'myfile2.dat': b'contents2',
            'replaced.dat': b'old_content',
        }
        test_record_data = generate_record_data()
        # test with anonymous user
        deposit = create_deposit(test_record_data, creator, uploaded_files)
        uploaded_file_name = 'additional.dat'
        uploaded_file_content = b'additional content'
        # Test file upload
        headers = [('Accept', '*/*')]
        with app.test_client() as client:
            login_user(creator, client)

            # try uploading a new file
            file_url = url_for_file(deposit.files.bucket.id, uploaded_file_name)
            file_put_res = client.put(
                file_url,
                input_stream=BytesIO(uploaded_file_content),
                headers=headers
            )
            uploaded_files[uploaded_file_name] = uploaded_file_content

            # try replacing an existing file
            file_url = url_for_file(deposit.files.bucket.id, 'replaced.dat')
            file_put_res = client.put(
                file_url,
                input_stream=BytesIO(b'new_content'),
                headers=headers
            )
            uploaded_files['replaced.dat'] = b'new_content'

            # try removing a file
            file_url2 = url_for_file(deposit.files.bucket.id, 'myfile2.dat')
            file_put_res = client.delete(
                file_url2,
                input_stream=BytesIO(uploaded_file_content),
                headers=headers
            )
            del uploaded_files['myfile2.dat']

            # check that the files can be retrieved properly
            subtest_file_bucket_content(client, deposit.files.bucket,
                                        uploaded_files)
Пример #21
0
def test_deposit_files(app, test_communities, login_user, test_users):
    """Test uploading and reading deposit files."""
    with app.app_context():
        admin = test_users['admin']
        creator = create_user('creator')
        uploaded_files = {
            'myfile1.dat': b'contents1',
            'myfile2.dat': b'contents2',
            'replaced.dat': b'old_content',
        }
        test_record_data = generate_record_data()
        # test with anonymous user
        deposit = create_deposit(test_record_data, creator, uploaded_files)
        uploaded_file_name = 'additional.dat'
        uploaded_file_content = b'additional content'
        # Test file upload
        headers = [('Accept', '*/*')]
        with app.test_client() as client:
            login_user(creator, client)

            # try uploading a new file
            file_url = url_for_file(deposit.files.bucket.id, uploaded_file_name)
            file_put_res = client.put(
                file_url,
                input_stream=BytesIO(uploaded_file_content),
                headers=headers
            )
            uploaded_files[uploaded_file_name] = uploaded_file_content

            # try replacing an existing file
            file_url = url_for_file(deposit.files.bucket.id, 'replaced.dat')
            file_put_res = client.put(
                file_url,
                input_stream=BytesIO(b'new_content'),
                headers=headers
            )
            uploaded_files['replaced.dat'] = b'new_content'

            # try removing a file
            file_url2 = url_for_file(deposit.files.bucket.id, 'myfile2.dat')
            file_put_res = client.delete(
                file_url2,
                input_stream=BytesIO(uploaded_file_content),
                headers=headers
            )
            del uploaded_files['myfile2.dat']

            # check that the files can be retrieved properly
            subtest_file_bucket_content(client, deposit.files.bucket,
                                        uploaded_files)
def test_deposit_modify_published_permissions(app, login_user,
                                              test_communities, test_users):
    """Test deposit edition after its publication.

    FIXME: This test should evolve when we allow deposit edition.
    """
    with app.app_context():
        community_name = 'MyTestCommunity1'
        record_data = generate_record_data(community=community_name)

        admin = test_users['admin']
        creator = create_user('creator')
        non_creator = create_user('non-creator')

        community = Community.get(name=community_name)
        com_member = create_user('com_member', roles=[community.member_role])
        com_admin = create_user('com_admin', roles=[community.admin_role])

        deposit = create_deposit(record_data, creator)
        deposit.submit()
        deposit.publish()

        def test_edit(status, user=None):
            headers = [('Content-Type', 'application/json-patch+json'),
                       ('Accept', 'application/json')]
            with app.test_client() as client:
                if user is not None:
                    login_user(user, client)
                request_res = client.patch(url_for(
                    'b2share_deposit_rest.b2dep_item',
                    pid_value=deposit.pid.pid_value),
                                           data=json.dumps([{
                                               "op":
                                               "replace",
                                               "path":
                                               "/publication_state",
                                               "value":
                                               PublicationStates.draft.name
                                           }]),
                                           headers=headers)
                assert request_res.status_code == status

        # test with anonymous user
        test_edit(401)
        test_edit(403, non_creator)
        test_edit(403, creator)
        test_edit(403, admin)
        test_edit(403, com_member)
        test_edit(403, com_admin)
Пример #23
0
def test_deposit_modify_published_permissions(app, login_user, test_communities,
                                              test_users):
    """Test deposit edition after its publication.

    FIXME: This test should evolve when we allow deposit edition.
    """
    with app.app_context():
        community_name = 'MyTestCommunity1'
        record_data = generate_record_data(community=community_name)

        admin = test_users['admin']
        creator = create_user('creator')
        non_creator = create_user('non-creator')

        community = Community.get(name=community_name)
        com_member = create_user('com_member', roles=[community.member_role])
        com_admin = create_user('com_admin', roles=[community.admin_role])

        deposit = create_deposit(record_data, creator)
        deposit.submit()
        deposit.publish()

        def test_edit(status, user=None):
            headers = [('Content-Type', 'application/json-patch+json'),
                       ('Accept', 'application/json')]
            with app.test_client() as client:
                if user is not None:
                    login_user(user, client)
                request_res = client.patch(
                    url_for('b2share_deposit_rest.b2dep_item',
                            pid_value=deposit.pid.pid_value),
                    data=json.dumps([{
                        "op": "replace", "path": "/publication_state",
                        "value": PublicationStates.draft.name
                    }]),
                    headers=headers)
                assert request_res.status_code == status

        # test with anonymous user
        test_edit(401)
        test_edit(403, non_creator)
        test_edit(403, creator)
        test_edit(403, admin)
        test_edit(403, com_member)
        test_edit(403, com_admin)
Пример #24
0
 def test_submit(status, user=None):
     deposit = create_deposit(record_data, creator)
     headers = [('Content-Type', 'application/json-patch+json'),
                ('Accept', 'application/json')]
     with app.test_client() as client:
         if user is not None:
             login_user(user, client)
         request_res = client.patch(
             url_for('b2share_deposit_rest.b2dep_item',
                     pid_value=deposit.pid.pid_value),
             data=json.dumps([{
                 "op": "replace", "path": "/publication_state",
                 "value": PublicationStates.submitted.name
             }, {
                 "op": "replace", "path": "/titles",
                 "value": [{'title':'newtitle'}]
             }]),
             headers=headers)
         assert request_res.status_code == status
def test_deposit_publish(app, test_users, test_communities, login_user):
    """Test record draft publication with HTTP PATCH."""
    with app.app_context():
        community_name = 'MyTestCommunity1'
        creator = test_users['deposits_creator']
        record_data = generate_record_data(community=community_name)
        community = Community.get(name=community_name)
        com_admin = create_user('com_admin', roles=[community.admin_role])

        deposit = create_deposit(record_data, creator)
        deposit_id = deposit.id
        deposit.submit()
        db.session.commit()

        with app.test_client() as client:
            login_user(com_admin, client)

            headers = [('Content-Type', 'application/json-patch+json'),
                       ('Accept', 'application/json')]
            draft_patch_res = client.patch(url_for(
                'b2share_deposit_rest.b2dep_item',
                pid_value=deposit.pid.pid_value),
                                           data=json.dumps([{
                                               "op":
                                               "replace",
                                               "path":
                                               "/publication_state",
                                               "value":
                                               PublicationStates.published.name
                                           }]),
                                           headers=headers)
            assert draft_patch_res.status_code == 200
            draft_patch_data = json.loads(
                draft_patch_res.get_data(as_text=True))
            expected_metadata = build_expected_metadata(
                record_data,
                PublicationStates.published.name,
                owners=[creator.id],
                draft=True,
                PID=draft_patch_data['metadata'].get('ePIC_PID'),
                DOI=draft_patch_data['metadata'].get('DOI'),
            )

    with app.app_context():
        deposit = Deposit.get_record(deposit_id)
        with app.test_client() as client:
            login_user(creator, client)
            assert expected_metadata == draft_patch_data['metadata']
            assert (deposit['publication_state'] ==
                    PublicationStates.published.name)
            subtest_self_link(draft_patch_data, draft_patch_res.headers,
                              client)

            pid, published = deposit.fetch_published()
            # check that the published record and the deposit are equal except
            # for the schema
            cleaned_deposit = {
                f: v
                for f, v in deposit.items() if f != '$schema'
            }
            cleaned_published = {
                f: v
                for f, v in deposit.items() if f != '$schema'
            }
            assert cleaned_published == cleaned_deposit
            # check "published" link
            assert draft_patch_data['links']['publication'] == \
                url_for('b2share_records_rest.{0}_item'.format(
                    RecordUUIDProvider.pid_type
                ), pid_value=pid.pid_value, _external=True)
            # check that the published record content match the deposit
            headers = [('Accept', 'application/json')]
            self_response = client.get(
                draft_patch_data['links']['publication'], headers=headers)
            assert self_response.status_code == 200
            published_data = json.loads(self_response.get_data(as_text=True))
            # we don't want to compare the links and dates
            cleaned_published_data = deepcopy(published_data)
            # the published record has an extra empty 'files' array
            assert cleaned_published_data['files'] == []
            del cleaned_published_data['files']
            cleaned_draft_data = deepcopy(draft_patch_data)
            for item in [cleaned_published_data, cleaned_draft_data]:
                del item['links']
                del item['created']
                del item['updated']
                del item['metadata']['$schema']
            assert cleaned_draft_data == cleaned_published_data
def test_getting_record_with_external_pids(app, login_user, test_users,
                                           deposit_with_external_pids,
                                           records_data_with_external_pids):
    """External pids are serialized in the metadata when it is allowed."""
    def test_get_deposit(deposit_pid_value, user):
        with app.test_client() as client:
            login_user(user, client)
            deposit_url = url_for('b2share_deposit_rest.b2dep_item',
                                  pid_value=deposit_pid_value)
            resp = client.get(deposit_url)
            deposit_data = json.loads(resp.get_data(as_text=True))
            return deposit_data

    def test_get_record(record_pid_value, user):
        with app.test_client() as client:
            login_user(user, client)
            record_url = url_for('b2share_records_rest.b2rec_item',
                                 pid_value=record_pid_value)
            resp = client.get(record_url)
            record_data = json.loads(resp.get_data(as_text=True))
            return record_data

    def test_search_deposits(user):
        with app.test_client() as client:
            login_user(user, client)
            search_deposits_url = url_for('b2share_records_rest.b2rec_list',
                                          drafts=1,
                                          size=100)
            headers = [('Content-Type', 'application/json'),
                       ('Accept', 'application/json')]
            resp = client.get(search_deposits_url, headers=headers)
            deposit_search_res = json.loads(resp.get_data(as_text=True))
            return deposit_search_res

    def test_search_records(user):
        with app.test_client() as client:
            login_user(user, client)
            search_records_url = url_for('b2share_records_rest.b2rec_list',
                                         size=100)
            headers = [('Content-Type', 'application/json'),
                       ('Accept', 'application/json')]
            resp = client.get(search_records_url, headers=headers)
            record_search_res = json.loads(resp.get_data(as_text=True))
            return record_search_res

    with app.app_context():
        deposit = Deposit.get_record(deposit_with_external_pids.deposit_id)

    with app.app_context():
        deposit_data = test_get_deposit(deposit.pid.pid_value,
                                        test_users['deposits_creator'])
        # assert that the external_pids are visible
        # when getting a specific deposit
        assert_external_files(deposit,
                              deposit_data['metadata']['external_pids'])
        current_search_client.indices.refresh('*')

    deposit_search_data = test_search_deposits(test_users['deposits_creator'])
    assert deposit_search_data['hits']['total'] == 1
    # external_pids are not shown in a deposit search because it would use
    # too much resources to generate it for each search hit.
    assert 'external_pids' not in deposit_search_data['hits']['hits'][0][
        'metadata']

    with app.app_context():
        deposit = Deposit.get_record(deposit_with_external_pids.deposit_id)
        deposit.submit()
        deposit.publish()
        record_resolver = Resolver(
            pid_type='b2rec',
            object_type='rec',
            getter=B2ShareRecord.get_record,
        )
        record_pid, record = record_resolver.resolve(deposit.pid.pid_value)
        current_search_client.indices.refresh('*')

        record_data = test_get_record(record_pid.pid_value,
                                      test_users['deposits_creator'])
        # when getting a specific record the owner sees the external_pids
        assert_external_files(record, record_data['metadata']['external_pids'])

    with app.app_context():
        record_data = test_get_record(record_pid.pid_value,
                                      test_users['normal'])
        # and all other users as well if it is open access
        assert_external_files(record, record_data['metadata']['external_pids'])

    deposit_search_data = test_search_deposits(test_users['deposits_creator'])
    assert deposit_search_data['hits']['total'] == 1
    # external_pids are not shown in deposit search even when published
    assert 'external_pids' not in deposit_search_data['hits']['hits'][0][
        'metadata']

    record_search_data = test_search_records(test_users['deposits_creator'])
    assert record_search_data['hits']['total'] == 1
    # external_pids are shown for record search if they are open access
    # for all users
    assert 'external_pids' in record_search_data['hits']['hits'][0]['metadata']
    record_search_data = test_search_records(test_users['normal'])
    assert record_search_data['hits']['total'] == 1
    assert 'external_pids' in record_search_data['hits']['hits'][0]['metadata']

    with app.app_context():
        deposit2 = create_deposit(records_data_with_external_pids,
                                  test_users['deposits_creator'])
        deposit2 = deposit2.patch([{
            'op':
            'add',
            'path':
            '/embargo_date',
            'value': (datetime.utcnow() + timedelta(days=1)).isoformat()
        }, {
            'op': 'replace',
            'path': '/open_access',
            'value': False
        }])
        deposit2.commit()
        deposit2.submit()
        deposit2.publish()

        record_resolver = Resolver(
            pid_type='b2rec',
            object_type='rec',
            getter=B2ShareRecord.get_record,
        )

        record_pid, record = record_resolver.resolve(deposit2.pid.pid_value)
        record_data = test_get_record(record_pid.pid_value,
                                      test_users['deposits_creator'])
        # owners of records have access to files and
        # external_pids of embargoed records
        assert_external_files(record, record_data['metadata']['external_pids'])

    with app.app_context():
        record_data = test_get_record(record_pid.pid_value,
                                      test_users['normal'])
        # normal users shouldn't have access to the
        # files and external_pids of an embargoed record
        assert 'metadata' in record_data
        assert 'external_pids' not in record_data['metadata']
Пример #27
0
def test_deposit_publish(app, test_users, test_communities,
                         login_user):
    """Test record draft publication with HTTP PATCH."""
    with app.app_context():
        community_name = 'MyTestCommunity1'
        creator = test_users['deposits_creator']
        record_data = generate_record_data(community=community_name)
        community = Community.get(name=community_name)
        com_admin = create_user('com_admin', roles=[community.admin_role])

        deposit = create_deposit(record_data, creator)
        deposit_id = deposit.id
        deposit.submit()
        db.session.commit()

        with app.test_client() as client:
            login_user(com_admin, client)

            headers = [('Content-Type', 'application/json-patch+json'),
                       ('Accept', 'application/json')]
            draft_patch_res = client.patch(
                url_for('b2share_deposit_rest.b2dep_item',
                        pid_value=deposit.pid.pid_value),
                data=json.dumps([{
                    "op": "replace", "path": "/publication_state",
                    "value": PublicationStates.published.name
                }]),
                headers=headers)
            assert draft_patch_res.status_code == 200
            draft_patch_data = json.loads(
                draft_patch_res.get_data(as_text=True))
            expected_metadata = build_expected_metadata(
                record_data,
                PublicationStates.published.name,
                owners=[creator.id],
                draft=True,
                PID=draft_patch_data['metadata'].get('ePIC_PID'),
                DOI=draft_patch_data['metadata'].get('DOI'),
            )

    with app.app_context():
        deposit = Deposit.get_record(deposit_id)
        with app.test_client() as client:
            login_user(creator, client)
            assert expected_metadata == draft_patch_data['metadata']
            assert (deposit['publication_state']
                    == PublicationStates.published.name)
            subtest_self_link(draft_patch_data,
                              draft_patch_res.headers,
                              client)

            pid, published = deposit.fetch_published()
            # check that the published record and the deposit are equal except
            # for the schema
            cleaned_deposit = {f: v for f, v in deposit.items()
                               if f != '$schema'}
            cleaned_published = {f: v for f, v in deposit.items()
                                 if f != '$schema'}
            assert cleaned_published == cleaned_deposit
            # check "published" link
            assert draft_patch_data['links']['publication'] == \
                url_for('b2share_records_rest.{0}_item'.format(
                    RecordUUIDProvider.pid_type
                ), pid_value=pid.pid_value, _external=True)
            # check that the published record content match the deposit
            headers = [('Accept', 'application/json')]
            self_response = client.get(
                draft_patch_data['links']['publication'],
                headers=headers
            )
            assert self_response.status_code == 200
            published_data = json.loads(self_response.get_data(
                as_text=True))
            # we don't want to compare the links and dates
            cleaned_published_data = deepcopy(published_data)
            # the published record has an extra empty 'files' array
            assert cleaned_published_data['files'] == []
            del cleaned_published_data['files']
            cleaned_draft_data = deepcopy(draft_patch_data)
            for item in [cleaned_published_data, cleaned_draft_data]:
                del item['links']
                del item['created']
                del item['updated']
                del item['metadata']['$schema']
            assert cleaned_draft_data == cleaned_published_data
Пример #28
0
def test_verify_checksum_in_deposit(app, test_communities, login_user,
                                    test_users):
    """Test checksum for files uploaded in a draft."""
    with app.app_context():
        creator = create_user('creator')
        uploaded_files = {
            'myfile1.dat': b'contents1',
            'myfile2.dat': b'contents2',
            'replaced.dat': b'old_content',
        }
        test_record_data = generate_record_data()
        deposit = create_deposit(test_record_data, creator, uploaded_files)
        uploaded_file_name = 'additional.dat'
        uploaded_file_content = b'additional content'
        headers = [('Accept', '*/*')]
        with app.test_client() as client:
            login_user(creator, client)

            # try uploading a new file
            file_url = url_for_file(deposit.files.bucket.id,
                                    uploaded_file_name)
            client.put(file_url,
                       input_stream=BytesIO(uploaded_file_content),
                       headers=headers)
            uploaded_files[uploaded_file_name] = uploaded_file_content

            # get file content by its uri
            file_instance = deposit.files['replaced.dat'].obj.file
            file_reader = urlopen('file://' + file_instance.uri)
            content = file_reader.read()
            assert content == b'old_content'

            # first make it writeable
            file_instance.writable = True
            file_instance.set_contents(BytesIO(b'test'))
            db.session.add(file_instance)
            db.session.commit()

            # changing the contents this way should be okay
            # as the checksum is updated after writing
            verify_checksum.apply([str(file_instance.id)])
            assert file_instance.last_check

            # directly changing the contents at the uri
            with open(file_instance.uri, 'w') as file_writer:
                file_writer.write('modified content')

            with app.extensions['mail'].record_messages() as outbox:
                verify_checksum.apply([str(file_instance.id)])
                # last_check=False as the checksum will be different now
                assert not file_instance.last_check

                schedule_failed_checksum_files(max_count=0,
                                               max_size=0,
                                               batch_interval={'seconds': 1},
                                               frequency={'seconds': 1})
                assert len(outbox) == 1
                # assert that an email is sent afterwards to the support email
                # mentioning the uri of the file with the different checksum
                email = outbox[0]

                assert file_instance.uri in email.body
                assert current_app.config['SUPPORT_EMAIL'] in email.recipients
def test_deposit_read_permissions(app, login_user, test_users,
                                  test_communities):
    """Test deposit read with HTTP GET."""
    with app.app_context():
        community_name = 'MyTestCommunity1'
        record_data = generate_record_data(community=community_name)
        community = Community.get(name=community_name)

        admin = test_users['admin']
        creator = create_user('creator')
        non_creator = create_user('non-creator')
        com_member = create_user('com_member', roles=[community.member_role])
        com_admin = create_user('com_admin', roles=[community.admin_role])

        def test_get(deposit, status, user=None):
            with app.test_client() as client:
                if user is not None:
                    login_user(user, client)
                headers = [('Accept', 'application/json')]
                request_res = client.get(url_for(
                    'b2share_deposit_rest.b2dep_item',
                    pid_value=deposit.pid.pid_value),
                                         headers=headers)
                assert request_res.status_code == status

        # test with anonymous user
        deposit = create_deposit(record_data, creator)
        test_get(deposit, 401)
        deposit.submit()
        test_get(deposit, 401)
        deposit.publish()
        test_get(deposit, 401)

        deposit = create_deposit(record_data, creator)
        test_get(deposit, 403, non_creator)
        deposit.submit()
        test_get(deposit, 403, non_creator)
        deposit.publish()
        test_get(deposit, 403, non_creator)

        deposit = create_deposit(record_data, creator)
        test_get(deposit, 200, creator)
        deposit.submit()
        test_get(deposit, 200, creator)
        deposit.publish()
        test_get(deposit, 200, creator)

        deposit = create_deposit(record_data, creator)
        test_get(deposit, 200, admin)
        deposit.submit()
        test_get(deposit, 200, admin)
        deposit.publish()
        test_get(deposit, 200, admin)

        deposit = create_deposit(record_data, creator)
        test_get(deposit, 403, com_member)
        deposit.submit()
        test_get(deposit, 403, com_member)
        deposit.publish()
        test_get(deposit, 403, com_member)

        deposit = create_deposit(record_data, creator)
        test_get(deposit, 403, com_admin)
        deposit.submit()
        test_get(deposit, 200, com_admin)
        deposit.publish()
        test_get(deposit, 200, com_admin)
Пример #30
0
def test_getting_record_with_external_pids(app, login_user, test_users,
                                           deposit_with_external_pids,
                                           records_data_with_external_pids):
    """External pids are serialized in the metadata when it is allowed."""

    def test_get_deposit(deposit_pid_value, user):
        with app.test_client() as client:
            login_user(user, client)
            deposit_url = url_for('b2share_deposit_rest.b2dep_item',
                                  pid_value=deposit_pid_value)
            resp = client.get(deposit_url)
            deposit_data = json.loads(
                resp.get_data(as_text=True))
            return deposit_data

    def test_get_record(record_pid_value, user):
        with app.test_client() as client:
            login_user(user, client)
            record_url = url_for('b2share_records_rest.b2rec_item',
                                 pid_value=record_pid_value)
            resp = client.get(record_url)
            record_data = json.loads(
                resp.get_data(as_text=True))
            return record_data

    def test_search_deposits(user):
        with app.test_client() as client:
            login_user(user, client)
            search_deposits_url = url_for(
                'b2share_records_rest.b2rec_list', drafts=1, size=100)
            headers = [('Content-Type', 'application/json'),
                       ('Accept', 'application/json')]
            resp = client.get(
                search_deposits_url,
                headers=headers)
            deposit_search_res = json.loads(
                resp.get_data(as_text=True))
            return deposit_search_res

    def test_search_records(user):
        with app.test_client() as client:
            login_user(user, client)
            search_records_url = url_for(
                'b2share_records_rest.b2rec_list', size=100)
            headers = [('Content-Type', 'application/json'),
                       ('Accept', 'application/json')]
            resp = client.get(
                search_records_url,
                headers=headers)
            record_search_res = json.loads(
                resp.get_data(as_text=True))
            return record_search_res

    with app.app_context():
        deposit = Deposit.get_record(deposit_with_external_pids.deposit_id)

    with app.app_context():
        deposit_data = test_get_deposit(deposit.pid.pid_value,
                                        test_users['deposits_creator'])
        # assert that the external_pids are visible
        # when getting a specific deposit
        assert_external_files(
            deposit,
            deposit_data['metadata']['external_pids'])
        current_search_client.indices.refresh('*')

    deposit_search_data = test_search_deposits(
        test_users['deposits_creator'])
    assert deposit_search_data['hits']['total'] == 1
    # external_pids are not shown in a deposit search because it would use
    # too much resources to generate it for each search hit.
    assert 'external_pids' not in deposit_search_data[
        'hits']['hits'][0]['metadata']

    with app.app_context():
        deposit = Deposit.get_record(deposit_with_external_pids.deposit_id)
        deposit.submit()
        deposit.publish()
        record_resolver = Resolver(
            pid_type='b2rec',
            object_type='rec',
            getter=B2ShareRecord.get_record,
        )
        record_pid, record = record_resolver.resolve(deposit.pid.pid_value)
        current_search_client.indices.refresh('*')

        record_data = test_get_record(
            record_pid.pid_value, test_users['deposits_creator'])
        # when getting a specific record the owner sees the external_pids
        assert_external_files(record, record_data['metadata']['external_pids'])

    with app.app_context():
        record_data = test_get_record(
            record_pid.pid_value, test_users['normal'])
        # and all other users as well if it is open access
        assert_external_files(record, record_data['metadata']['external_pids'])

    deposit_search_data = test_search_deposits(
        test_users['deposits_creator'])
    assert deposit_search_data['hits']['total'] == 1
    # external_pids are not shown in deposit search even when published
    assert 'external_pids' not in deposit_search_data[
        'hits']['hits'][0]['metadata']

    record_search_data = test_search_records(
        test_users['deposits_creator'])
    assert record_search_data['hits']['total'] == 1
    # external_pids are shown for record search if they are open access
    # for all users
    assert 'external_pids' in record_search_data[
        'hits']['hits'][0]['metadata']
    record_search_data = test_search_records(test_users['normal'])
    assert record_search_data['hits']['total'] == 1
    assert 'external_pids' in record_search_data[
        'hits']['hits'][0]['metadata']

    with app.app_context():
        deposit2 = create_deposit(records_data_with_external_pids,
                                  test_users['deposits_creator'])
        deposit2 = deposit2.patch([
            {'op': 'add', 'path': '/embargo_date',
             'value': (datetime.utcnow() + timedelta(days=1)).isoformat()},
            {'op': 'replace', 'path': '/open_access',
             'value': False}
        ])
        deposit2.commit()
        deposit2.submit()
        deposit2.publish()

        record_resolver = Resolver(
            pid_type='b2rec',
            object_type='rec',
            getter=B2ShareRecord.get_record,
        )

        record_pid, record = record_resolver.resolve(deposit2.pid.pid_value)
        record_data = test_get_record(
            record_pid.pid_value, test_users['deposits_creator'])
        # owners of records have access to files and
        # external_pids of embargoed records
        assert_external_files(record, record_data['metadata']['external_pids'])

    with app.app_context():
        record_data = test_get_record(
            record_pid.pid_value, test_users['normal'])
        # normal users shouldn't have access to the
        # files and external_pids of an embargoed record
        assert 'metadata' in record_data
        assert 'external_pids' not in record_data['metadata']
Пример #31
0
def test_deposit_read_permissions(app, login_user, test_users,
                                  test_communities):
    """Test deposit read with HTTP GET."""
    with app.app_context():
        community_name = 'MyTestCommunity1'
        record_data = generate_record_data(community=community_name)
        community = Community.get(name=community_name)

        admin = test_users['admin']
        creator = create_user('creator')
        non_creator = create_user('non-creator')
        com_member = create_user('com_member', roles=[community.member_role])
        com_admin = create_user('com_admin', roles=[community.admin_role])

        def test_get(deposit, status, user=None):
            with app.test_client() as client:
                if user is not None:
                    login_user(user, client)
                headers = [('Accept', 'application/json')]
                request_res = client.get(
                    url_for('b2share_deposit_rest.b2dep_item',
                            pid_value=deposit.pid.pid_value),
                    headers=headers)
                assert request_res.status_code == status

        # test with anonymous user
        deposit = create_deposit(record_data, creator)
        test_get(deposit, 401)
        deposit.submit()
        test_get(deposit, 401)
        deposit.publish()
        test_get(deposit, 401)

        deposit = create_deposit(record_data, creator)
        test_get(deposit, 403, non_creator)
        deposit.submit()
        test_get(deposit, 403, non_creator)
        deposit.publish()
        test_get(deposit, 403, non_creator)

        deposit = create_deposit(record_data, creator)
        test_get(deposit, 200, creator)
        deposit.submit()
        test_get(deposit, 200, creator)
        deposit.publish()
        test_get(deposit, 200, creator)

        deposit = create_deposit(record_data, creator)
        test_get(deposit, 200, admin)
        deposit.submit()
        test_get(deposit, 200, admin)
        deposit.publish()
        test_get(deposit, 200, admin)

        deposit = create_deposit(record_data, creator)
        test_get(deposit, 403, com_member)
        deposit.submit()
        test_get(deposit, 403, com_member)
        deposit.publish()
        test_get(deposit, 403, com_member)

        deposit = create_deposit(record_data, creator)
        test_get(deposit, 403, com_admin)
        deposit.submit()
        test_get(deposit, 200, com_admin)
        deposit.publish()
        test_get(deposit, 200, com_admin)
Пример #32
0
def test_verify_checksum_in_deposit(app, test_communities,
                                    login_user, test_users):
    """Test checksum for files uploaded in a draft."""
    with app.app_context():
        creator = create_user('creator')
        uploaded_files = {
            'myfile1.dat': b'contents1',
            'myfile2.dat': b'contents2',
            'replaced.dat': b'old_content',
        }
        test_record_data = generate_record_data()
        deposit = create_deposit(test_record_data, creator, uploaded_files)
        uploaded_file_name = 'additional.dat'
        uploaded_file_content = b'additional content'
        headers = [('Accept', '*/*')]
        with app.test_client() as client:
            login_user(creator, client)

            # try uploading a new file
            file_url = url_for_file(deposit.files.bucket.id,
                                    uploaded_file_name)
            client.put(
                file_url,
                input_stream=BytesIO(uploaded_file_content),
                headers=headers
            )
            uploaded_files[uploaded_file_name] = uploaded_file_content

            # get file content by its uri
            file_instance = deposit.files['replaced.dat'].obj.file
            file_reader = urlopen('file://' + file_instance.uri)
            content = file_reader.read()
            assert content == b'old_content'

            # first make it writeable
            file_instance.writable = True
            file_instance.set_contents(BytesIO(b'test'))
            db.session.add(file_instance)
            db.session.commit()

            # changing the contents this way should be okay
            # as the checksum is updated after writing
            verify_checksum.apply([str(file_instance.id)])
            assert file_instance.last_check

            # directly changing the contents at the uri
            with open(file_instance.uri, 'w') as file_writer:
                file_writer.write('modified content')

            with app.extensions['mail'].record_messages() as outbox:
                verify_checksum.apply([str(file_instance.id)])
                # last_check=False as the checksum will be different now
                assert not file_instance.last_check

                schedule_failed_checksum_files(
                    max_count=0, max_size=0,
                    batch_interval={'seconds': 1}, frequency={'seconds': 1}
                )
                assert len(outbox) == 1
                # assert that an email is sent afterwards to the support email
                # mentioning the uri of the file with the different checksum
                email = outbox[0]

                assert file_instance.uri in email.body
                assert current_app.config['SUPPORT_EMAIL'] in email.recipients
Пример #33
0
def test_record_delete_version(app, test_records, test_users):
    """Test deletion of a record version."""
    with app.app_context():
        resolver = Resolver(
            pid_type='b2rec',
            object_type='rec',
            getter=B2ShareRecord.get_record,
        )

        v1 = test_records[0].data
        v1_pid, v1_id = pid_of(v1)

        _, v1_rec = resolver.resolve(v1_id)
        data = copy_data_from_previous(v1_rec.model.json)
        v2 = create_deposit(data, test_users['deposits_creator'],
                            version_of=v1_id)
        ObjectVersion.create(v2.files.bucket, 'myfile1',
                             stream=BytesIO(b'mycontent'))
        v2.submit()
        v2.publish()
        v2_pid, v2_id = pid_of(v2)
        data = copy_data_from_previous(v2.model.json)
        v3 = create_deposit(data, test_users['deposits_creator'],
                            version_of=v2_id)
        v3.submit()
        v3.publish()
        v3_pid, v3_id = pid_of(v3)
        v3_pid, v3_rec = resolver.resolve(v3_pid.pid_value)
        # chain is now: [v1] -- [v2] -- [v3]
        version_child = PIDVersioning(child=v2_pid)
        version_master = PIDVersioning(parent=version_child.parent)
        assert len(version_master.children.all()) == 3
        v3_rec.delete()
        assert len(version_master.children.all()) == 2
        # chain is now [v1] -- [v2]
        # assert that we can create again a new version from v2
        data = copy_data_from_previous(v2.model.json)
        v3 = create_deposit(data, test_users['deposits_creator'],
                            version_of=v2_id)
        v3.submit()
        v3.publish()
        v3_pid, v3_id = pid_of(v3)
        v3_pid, v3_rec = resolver.resolve(v3_pid.pid_value)
        assert len(version_master.children.all()) == 3
        v2_pid, v2_rec = resolver.resolve(v2_pid.pid_value)
        # Delete an intermediate version
        v2_rec.delete()
        assert len(version_master.children.all()) == 2
        # chain is now [v1] -- [v3]
        # Add a new version
        data = copy_data_from_previous(v3.model.json)
        v4 = create_deposit(data, test_users['deposits_creator'],
                            version_of=v3_id)
        v4.submit()
        v4.publish()
        assert len(version_master.children.all()) == 3
        # final chain [v1] -- [v3] -- [v4]
        v4_pid, v4_id = pid_of(v4)
        v4_pid, v4_rec = resolver.resolve(v4_pid.pid_value)
        data = copy_data_from_previous(v4)
        draft_child = create_deposit(data, test_users['deposits_creator'],
                                     version_of=v4_id)
        draft_child.submit()

        # delete all children except the draft child
        assert len(version_master.children.all()) == 3
        v4_rec.delete()
        assert len(version_master.children.all()) == 2

        v3_rec.delete()
        assert len(version_master.children.all()) == 1

        v1_rec.delete()
        assert len(version_master.children.all()) == 0

        assert version_master.parent.status != PIDStatus.DELETED

        draft_child.publish()
        draft_child_pid, draft_child_id = pid_of(draft_child)
        draft_child_pid, draft_child_rec = \
            resolver.resolve(draft_child_pid.pid_value)
        # assert that we can create again a new version

        assert len(version_master.children.all()) == 1

        # no child remains and there is no draft_child
        draft_child_rec.delete()
        assert version_master.parent.status == PIDStatus.DELETED
def test_deposit_versions_create(app, test_records, test_users):
    """Creating new versions of existing records."""
    with app.app_context():
        # Retrieve a record which will be the first version
        v1 = test_records[0].data
        v1_rec = B2ShareRecord.get_record(test_records[0].record_id)
        v1_pid, v1_id = pid_of(v1)
        assert list_published_pids(v1_pid) == [v1_pid]

        # create draft in version chain:
        # version chain becomes: [v1] -- [v2 draft]
        # v2 = create_deposit({}, version_of=v1_id)
        data = copy_data_from_previous(v1_rec.model.json)
        v2 = create_deposit(data, test_users['deposits_creator'],
                            version_of=v1_id)
        assert filenames(v2) == []
        ObjectVersion.create(v2.files.bucket, 'myfile1',
                             stream=BytesIO(b'mycontent'))
        assert filenames(v2) == ['myfile1']

        assert list_published_pids(v1_pid) == [v1_pid]

        # cannot create another draft if one exists
        # not possible: [v1] -- [v2 draft]
        #                    `- [new draft]
        with pytest.raises(DraftExistsVersioningError):
            data = copy_data_from_previous(v1_rec.model.json)
            create_deposit(data, test_users['deposits_creator'],
                           version_of=v1_id)

        # cannot create a version from a draft pid
        # not possible: [v1] -- [v2 draft] -- [new draft]
        with pytest.raises(IncorrectRecordVersioningError): # record pid not created yet
            data = copy_data_from_previous(v1_rec.model.json)
            create_deposit(data, test_users['deposits_creator'],
                           version_of=v2['_deposit']['id'])

        # publish previous draft
        # version chain becomes: [v1] -- [v2]
        v2.submit()
        v2.publish()
        v2_pid, v2_id = pid_of(v2)
        assert list_published_pids(v1_pid) == [v1_pid, v2_pid]

        # cannot create draft based on the first version in a chain
        # not possible: [v1] -- [v2]
        #                    `- [new draft]
        with pytest.raises(IncorrectRecordVersioningError):
            data = copy_data_from_previous(v1_rec.model.json)
            create_deposit(data, test_users['deposits_creator'],
                           version_of=v1_id)

        # create and publish other versions:
        # version chain becomes: [v1] -- [v2] -- [v3]
        data = copy_data_from_previous(v1_rec.model.json)
        v3 = create_deposit(data, test_users['deposits_creator'],
                            version_of=v2_id)
        # assert files are imported from v2
        assert filenames(v3) == ['myfile1']
        ObjectVersion.create(v3.files.bucket, 'myfile2',
                                stream=BytesIO(b'mycontent'))
        assert filenames(v3) == ['myfile1', 'myfile2']

        assert list_published_pids(v1_pid) == [v1_pid, v2_pid]

        v3.submit()
        v3.publish()
        v3_pid, v3_id = pid_of(v3)
        v3_rec = Record.get_record(v3_id)
        assert filenames(v3_rec) == ['myfile1', 'myfile2']
        assert list_published_pids(v1_pid) == [v1_pid, v2_pid, v3_pid]

        # cannot create draft based on an intermediate version in a chain
        # not possible: [v1] -- [v2] -- [v3]
        #                            `- [new draft]
        with pytest.raises(IncorrectRecordVersioningError):
            create_deposit({}, test_users['deposits_creator'],
                           version_of=v2_id)

        # Create yet another version
        # Version chain becomes: [v1] -- [v2] -- [v3] -- [v4]
        data = copy_data_from_previous(v1_rec.model.json)
        v4 = create_deposit(data, test_users['deposits_creator'],
                            version_of=v3_id)
        v4.submit()
        v4.publish()
        assert filenames(v4) == ['myfile1', 'myfile2']
        v4_pid, v4_id = pid_of(v4)
        assert list_published_pids(v1_pid) == [
            v1_pid, v2_pid, v3_pid, v4_pid]

        # assert that creating a new version from a deleted pid is not allowed
        resolver = Resolver(pid_type=v4_pid.pid_type, object_type='rec',
                            getter=partial(B2ShareRecord.get_record,
                                           with_deleted=True))
        v4_pid, v4_rec = LazyPIDValue(resolver, v4_pid.pid_value).data
        # delete [v4]
        v4_rec.delete()
        with pytest.raises(RecordNotFoundVersioningError):
            v5 = create_deposit(data, test_users['deposits_creator'],
                                version_of=v4_id)
def test_deposit_versions_create(app, test_records, test_users):
    """Creating new versions of existing records."""
    with app.app_context():
        # Retrieve a record which will be the first version
        v1 = test_records[0].data
        v1_rec = B2ShareRecord.get_record(test_records[0].record_id)
        v1_pid, v1_id = pid_of(v1)
        assert list_published_pids(v1_pid) == [v1_pid]

        # create draft in version chain:
        # version chain becomes: [v1] -- [v2 draft]
        # v2 = create_deposit({}, version_of=v1_id)
        data = copy_data_from_previous(v1_rec.model.json)
        v2 = create_deposit(data,
                            test_users['deposits_creator'],
                            version_of=v1_id)
        assert filenames(v2) == []
        ObjectVersion.create(v2.files.bucket,
                             'myfile1',
                             stream=BytesIO(b'mycontent'))
        assert filenames(v2) == ['myfile1']

        assert list_published_pids(v1_pid) == [v1_pid]

        # cannot create another draft if one exists
        # not possible: [v1] -- [v2 draft]
        #                    `- [new draft]
        with pytest.raises(DraftExistsVersioningError):
            data = copy_data_from_previous(v1_rec.model.json)
            create_deposit(data,
                           test_users['deposits_creator'],
                           version_of=v1_id)

        # cannot create a version from a draft pid
        # not possible: [v1] -- [v2 draft] -- [new draft]
        with pytest.raises(
                IncorrectRecordVersioningError):  # record pid not created yet
            data = copy_data_from_previous(v1_rec.model.json)
            create_deposit(data,
                           test_users['deposits_creator'],
                           version_of=v2['_deposit']['id'])

        # publish previous draft
        # version chain becomes: [v1] -- [v2]
        v2.submit()
        v2.publish()
        v2_pid, v2_id = pid_of(v2)
        assert list_published_pids(v1_pid) == [v1_pid, v2_pid]

        # cannot create draft based on the first version in a chain
        # not possible: [v1] -- [v2]
        #                    `- [new draft]
        with pytest.raises(IncorrectRecordVersioningError):
            data = copy_data_from_previous(v1_rec.model.json)
            create_deposit(data,
                           test_users['deposits_creator'],
                           version_of=v1_id)

        # create and publish other versions:
        # version chain becomes: [v1] -- [v2] -- [v3]
        data = copy_data_from_previous(v1_rec.model.json)
        v3 = create_deposit(data,
                            test_users['deposits_creator'],
                            version_of=v2_id)
        # assert files are imported from v2
        assert filenames(v3) == ['myfile1']
        ObjectVersion.create(v3.files.bucket,
                             'myfile2',
                             stream=BytesIO(b'mycontent'))
        assert filenames(v3) == ['myfile1', 'myfile2']

        assert list_published_pids(v1_pid) == [v1_pid, v2_pid]

        v3.submit()
        v3.publish()
        v3_pid, v3_id = pid_of(v3)
        v3_rec = Record.get_record(v3_id)
        assert filenames(v3_rec) == ['myfile1', 'myfile2']
        assert list_published_pids(v1_pid) == [v1_pid, v2_pid, v3_pid]

        # cannot create draft based on an intermediate version in a chain
        # not possible: [v1] -- [v2] -- [v3]
        #                            `- [new draft]
        with pytest.raises(IncorrectRecordVersioningError):
            create_deposit({},
                           test_users['deposits_creator'],
                           version_of=v2_id)

        # Create yet another version
        # Version chain becomes: [v1] -- [v2] -- [v3] -- [v4]
        data = copy_data_from_previous(v1_rec.model.json)
        v4 = create_deposit(data,
                            test_users['deposits_creator'],
                            version_of=v3_id)
        v4.submit()
        v4.publish()
        assert filenames(v4) == ['myfile1', 'myfile2']
        v4_pid, v4_id = pid_of(v4)
        assert list_published_pids(v1_pid) == [v1_pid, v2_pid, v3_pid, v4_pid]

        # assert that creating a new version from a deleted pid is not allowed
        resolver = Resolver(pid_type=v4_pid.pid_type,
                            object_type='rec',
                            getter=partial(B2ShareRecord.get_record,
                                           with_deleted=True))
        v4_pid, v4_rec = LazyPIDValue(resolver, v4_pid.pid_value).data
        # delete [v4]
        v4_rec.delete()
        with pytest.raises(RecordNotFoundVersioningError):
            v5 = create_deposit(data,
                                test_users['deposits_creator'],
                                version_of=v4_id)