Example #1
0
def test_id_acl_get_record_acl(app, db, es, es_acl_prepare, test_users):

    pid, record = create_record({'$schema': RECORD_SCHEMA},
                                clz=SchemaEnforcingRecord)
    pid1, record1 = create_record({'$schema': RECORD_SCHEMA},
                                  clz=SchemaEnforcingRecord)

    with db.session.begin_nested():
        acl = IdACL(name='test',
                    schemas=[RECORD_SCHEMA],
                    priority=0,
                    operation='get',
                    originator=test_users.u1,
                    record_id=str(record.id))
        db.session.add(acl)
        acl2 = IdACL(name='test 2',
                     schemas=[ANOTHER_SCHEMA],
                     priority=0,
                     operation='get',
                     originator=test_users.u1,
                     record_id=str(record1.id))
        db.session.add(acl2)

    acls = list(IdACL.get_record_acls(record))
    assert len(acls) == 1
    assert isinstance(acls[0], IdACL)
    assert acls[0].id == acl.id
Example #2
0
def test_id_acl_get_matching_resources(app, db, es, es_acl_prepare,
                                       test_users):
    pid, record = create_record({'$schema': RECORD_SCHEMA},
                                clz=SchemaEnforcingRecord)
    pid1, record1 = create_record({'$schema': RECORD_SCHEMA},
                                  clz=SchemaEnforcingRecord)
    RecordIndexer().index(record)
    RecordIndexer().index(record1)
    current_search_client.indices.refresh()
    current_search_client.indices.flush()

    with db.session.begin_nested():
        acl = IdACL(name='test',
                    schemas=[RECORD_SCHEMA],
                    priority=0,
                    operation='get',
                    originator=test_users.u1,
                    record_id=str(record.id))
        db.session.add(acl)

        acl1 = IdACL(name='test',
                     schemas=[RECORD_SCHEMA],
                     priority=0,
                     operation='get',
                     originator=test_users.u1,
                     record_id=str(record1.id))
        db.session.add(acl1)

    ids = list(acl.get_matching_resources())
    assert len(ids) == 1
    assert ids[0] == str(pid.object_uuid)

    ids = list(acl1.get_matching_resources())
    assert len(ids) == 1
    assert ids[0] == str(pid1.object_uuid)
def test_valid_delete(app):
    """Test VALID record delete request (DELETE .../records/<record_id>)."""
    with app.app_context():
        # create the record using the internal API
        pid, record = create_record(test_data)

        with app.test_client() as client:
            headers = [('Accept', 'application/json')]
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=pid.pid_value),
                                headers=headers)
            assert res.status_code == 204
            # check database state
            with pytest.raises(NoResultFound):
                Record.get_record(record.id)
            assert pid.is_deleted()

            # check that DELETE with non JSON accepted format will work
            # as it returns nothing
            pid, record = create_record(test_data)
            headers = [('Accept', 'video/mp4')]
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=pid.pid_value),
                                headers=headers)
            assert res.status_code == 204
Example #4
0
def test_valid_delete(app):
    """Test VALID record delete request (DELETE .../records/<record_id>)."""
    with app.app_context():
        # create the record using the internal API
        pid, record = create_record(test_data)

        with app.test_client() as client:
            headers = [('Accept', 'application/json')]
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=pid.pid_value),
                                headers=headers)
            assert res.status_code == 204
            # check database state
            with pytest.raises(NoResultFound):
                Record.get_record(record.id)
            assert pid.is_deleted()

            # check that DELETE with non JSON accepted format will work
            # as it returns nothing
            pid, record = create_record(test_data)
            headers = [('Accept', 'video/mp4')]
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=pid.pid_value),
                                headers=headers)
            assert res.status_code == 204
Example #5
0
def test_identify_earliest_date(app, schema):

    with app.test_client() as c:
        result = c.get('/oai2d?verb=Identify')
        assert 200 == result.status_code

        tree = etree.fromstring(result.data)
        earliestDatestamp = tree.xpath(
            '/x:OAI-PMH/x:Identify/x:earliestDatestamp', namespaces=NAMESPACES)
        assert earliestDatestamp[0].text == '0001-01-01T00:00:00Z'

    first_record = create_record(
        app, {
            '_oai': {
                'sets': ['a']
            },
            'title_statement': {
                'title': 'Test0'
            },
            '_oai_id': 1,
            '$schema': schema
        })

    first_record.model.created = datetime(2000, 1, 1, 13, 0, 0)
    RecordIndexer().index(first_record)

    create_record(
        app, {
            '_oai': {
                'sets': ['a']
            },
            'title_statement': {
                'title': 'Test1'
            },
            '_oai_id': 2,
            '$schema': schema
        })
    create_record(
        app, {
            '_oai': {
                'sets': ['a']
            },
            'title_statement': {
                'title': 'Test2'
            },
            '_oai_id': 3,
            '$schema': schema
        })
    app.extensions['invenio-search'].flush_and_refresh('records')

    with app.test_client() as c:
        result = c.get('/oai2d?verb=Identify')
        assert 200 == result.status_code

        tree = etree.fromstring(result.data)
        earliestDatestamp = tree.xpath(
            '/x:OAI-PMH/x:Identify/x:earliestDatestamp', namespaces=NAMESPACES)
        assert earliestDatestamp[0].text == '2000-01-01T13:00:00Z'
Example #6
0
def test_propertyvalue_acl_get_record_match_acl(app, db, es, es_acl_prepare,
                                                test_users):
    pid, record = create_record(
        {
            '$schema': RECORD_SCHEMA,
            'keywords': ['blah'],
            'title': 'Hello world'
        },
        clz=SchemaEnforcingRecord)
    pid1, record1 = create_record(
        {
            '$schema': RECORD_SCHEMA,
            'keywords': ['test'],
            'title': 'Goodbye world'
        },
        clz=SchemaEnforcingRecord)

    with db.session.begin_nested():
        acl = PropertyValueACL(name='test',
                               schemas=[RECORD_SCHEMA],
                               priority=0,
                               operation='get',
                               originator=test_users.u1)
        propval = PropertyValue(name='title',
                                value='hello',
                                acl=acl,
                                originator=test_users.u1,
                                match_operation=MatchOperation.match)

        db.session.add(acl)
        db.session.add(propval)

        acl2 = PropertyValueACL(name='test 2',
                                schemas=[RECORD_SCHEMA],
                                priority=0,
                                operation='get',
                                originator=test_users.u1)
        propval2 = PropertyValue(name='title',
                                 value='goodbye',
                                 acl=acl2,
                                 originator=test_users.u1,
                                 match_operation=MatchOperation.match)
        db.session.add(acl2)
        db.session.add(propval2)

    acl.update()
    acl2.update()

    acls = list(PropertyValueACL.get_record_acls(record))
    assert len(acls) == 1
    assert isinstance(acls[0], PropertyValueACL)
    assert acls[0].id == acl.id

    acls = list(PropertyValueACL.get_record_acls(record1))
    assert len(acls) == 1
    assert isinstance(acls[0], PropertyValueACL)
    assert acls[0].id == acl2.id
def test_tombstone(app):
    """Test tomstones."""
    with app.app_context():
        # OK PID
        pid_ok, record = create_record({'title': 'test'})

        # Deleted PID
        pid_del, record = create_record({'title': 'deleted'})
        pid_del.delete()

        # Missing object PID
        pid_noobj = PersistentIdentifier.create(
            'recid', '100', status=PIDStatus.REGISTERED)
        db.session.commit()

        # Redirected PID
        pid_red = PersistentIdentifier.create(
            'recid', '101', status=PIDStatus.REGISTERED)
        pid_red.redirect(pid_ok)

        # Redirect PID - different endpoint
        pid_doi = PersistentIdentifier.create(
            'doi', '10.1234/foo', status=PIDStatus.REGISTERED)
        pid_red_doi = PersistentIdentifier.create(
            'recid', '102', status=PIDStatus.REGISTERED)
        pid_red_doi.redirect(pid_doi)
        db.session.commit()

        with app.test_client() as client:
            # PID deleted
            headers = [('Accept', 'application/json')]
            res = client.get(
                url_for('invenio_records_rest.recid_item',
                        pid_value=pid_del.pid_value),
                headers=headers)
            assert res.status_code == 410

            # PID missing object
            res = client.get(
                url_for('invenio_records_rest.recid_item',
                        pid_value=pid_noobj.pid_value),
                headers=headers)
            assert res.status_code == 500

            # Redirected invalid endpoint
            res = client.get(
                url_for('invenio_records_rest.recid_item',
                        pid_value=pid_red_doi.pid_value),
                headers=headers)
            assert res.status_code == 500

            # Redirected
            res = client.get(
                url_for('invenio_records_rest.recid_item',
                        pid_value=pid_red.pid_value),
                headers=headers)
            assert res.status_code == 301
Example #8
0
def test_tombstone(app):
    """Test tomstones."""
    with app.app_context():
        # OK PID
        pid_ok, record = create_record({'title': 'test'})

        # Deleted PID
        pid_del, record = create_record({'title': 'deleted'})
        pid_del.delete()

        # Missing object PID
        pid_noobj = PersistentIdentifier.create('recid',
                                                '100',
                                                status=PIDStatus.REGISTERED)
        db.session.commit()

        # Redirected PID
        pid_red = PersistentIdentifier.create('recid',
                                              '101',
                                              status=PIDStatus.REGISTERED)
        pid_red.redirect(pid_ok)

        # Redirect PID - different endpoint
        pid_doi = PersistentIdentifier.create('doi',
                                              '10.1234/foo',
                                              status=PIDStatus.REGISTERED)
        pid_red_doi = PersistentIdentifier.create('recid',
                                                  '102',
                                                  status=PIDStatus.REGISTERED)
        pid_red_doi.redirect(pid_doi)
        db.session.commit()

        with app.test_client() as client:
            # PID deleted
            headers = [('Accept', 'application/json')]
            res = client.get(url_for('invenio_records_rest.recid_item',
                                     pid_value=pid_del.pid_value),
                             headers=headers)
            assert res.status_code == 410

            # PID missing object
            res = client.get(url_for('invenio_records_rest.recid_item',
                                     pid_value=pid_noobj.pid_value),
                             headers=headers)
            assert res.status_code == 500

            # Redirected invalid endpoint
            res = client.get(url_for('invenio_records_rest.recid_item',
                                     pid_value=pid_red_doi.pid_value),
                             headers=headers)
            assert res.status_code == 500

            # Redirected
            res = client.get(url_for('invenio_records_rest.recid_item',
                                     pid_value=pid_red.pid_value),
                             headers=headers)
            assert res.status_code == 301
Example #9
0
def test_propertyvalue_acl_get_matching_resources(app, db, es, es_acl_prepare,
                                                  test_users):
    pid, record = create_record(
        {
            '$schema': RECORD_SCHEMA,
            'keywords': ['blah']
        },
        clz=SchemaEnforcingRecord)
    pid1, record1 = create_record(
        {
            '$schema': RECORD_SCHEMA,
            'keywords': ['test']
        },
        clz=SchemaEnforcingRecord)
    RecordIndexer().index(record)
    RecordIndexer().index(record1)
    current_search_client.indices.refresh()
    current_search_client.indices.flush()

    with db.session.begin_nested():
        acl = PropertyValueACL(name='test',
                               schemas=[RECORD_SCHEMA],
                               priority=0,
                               operation='get',
                               originator=test_users.u1)
        propval = PropertyValue(name='keywords',
                                value='blah',
                                acl=acl,
                                originator=test_users.u1)

        db.session.add(acl)
        db.session.add(propval)

        acl1 = PropertyValueACL(name='test',
                                schemas=[RECORD_SCHEMA],
                                priority=0,
                                operation='get',
                                originator=test_users.u1)
        propval2 = PropertyValue(name='keywords',
                                 value='test',
                                 acl=acl1,
                                 originator=test_users.u1)

        db.session.add(acl1)
        db.session.add(propval2)

    ids = list(acl.get_matching_resources())
    assert len(ids) == 1
    assert ids[0] == str(pid.object_uuid)

    ids = list(acl1.get_matching_resources())
    assert len(ids) == 1
    assert ids[0] == str(pid1.object_uuid)
Example #10
0
def subtest_search_aggregation_serialization(app, user_factory, expected):
    """Test the serialization of elasticsearch aggregations."""
    with app.app_context():
        # create the record using the internal API
        pid1, record1 = create_record(test_data)
        pid2, record2 = create_record(test_data2)
        pid3, record3 = create_record(test_data3)

        with user_factory('allowed') as allowed_user:
            # create one user allowed to delete the record
            allowed_user.read_access(True, str(record1.id))
            allowed_user.read_access(True, str(record2.id))
            allowed_user.read_access(True, str(record3.id))
            allowed_login = allowed_user.login_function()
        db.session.commit()

        app.config['RECORDS_REST_FACETS'] = {
            'invenio_records_rest_test_index': {
                'aggs': {
                    'stars': {'terms': {'field': 'stars'}}
                },
                'post_filters': {
                    'type': terms_filter('type'),
                }
            }
        }
        es_index = app.config["RECORDS_REST_DEFAULT_SEARCH_INDEX"]
        current_search_client.indices.flush(wait_if_ongoing=True,
                                            force=True,
                                            index=es_index)

        with app.test_client() as client:
            allowed_login(client)
            headers = [('Accept', 'application/json')]
            res = client.get(url_for('invenio_records_rest.recid_list',
                                     q='the', sort='year'),
                             headers=headers)
            assert res.status_code == 200
            data = json.loads(res.get_data(as_text=True))
            assert isinstance(data['hits']['hits'], list)
            assert data['hits']['total'] == 3

            subtest_expected_hits(data['hits']['hits'], [
                (pid3.pid_value, control_num(test_data3, 3)),
                (pid1.pid_value, control_num(test_data, 1)),
                (pid2.pid_value, control_num(test_data2, 2)),
            ], client)
            assert data['aggregations'] == expected
Example #11
0
def test_search_pattern_change(app, without_oaiset_signals, schema):
    """Test search pattern change."""
    record0 = create_record(app, {
        '_oai': {'sets': ['a']}, 'title_statement': {'title': 'Test0'},
        '$schema': schema
    })
    rec_uuid = record0.id
    oaiset = OAISet(spec="a", search_pattern="title_statement.title:Test0")
    db.session.add(oaiset)
    db.session.commit()
    run_after_insert_oai_set()
    sleep(2)
    record = Record.get_record(rec_uuid)
    assert record['_oai']['sets'] == ['a']

    # change search pattern: record0 will not inside it anymore
    oaiset = OAISet.query.first()
    oaiset.search_pattern = 'title_statement.title:Test1'
    db.session.merge(oaiset)
    db.session.commit()
    after_update_oai_set(None, None, oaiset)
    sleep(2)
    record = Record.get_record(rec_uuid)
    record.commit()
    assert record['_oai']['sets'] == []
Example #12
0
def test_rest_delete_record(app, db, es, es_acl_prepare, test_users):
    with app.test_client() as client:
        with db.session.begin_nested():
            acl = DefaultACL(name='default',
                             schemas=[RECORD_SCHEMA],
                             priority=0,
                             originator=test_users.u1,
                             operation='update')
            actor = UserActor(name='u1',
                              users=[test_users.u1],
                              acl=acl,
                              originator=test_users.u1)

            db.session.add(acl)
            db.session.add(actor)

        pid, record = create_record({'keywords': ['blah']},
                                    clz=SchemaEnforcingRecord)
        RecordIndexer().index(record)
        current_search_client.indices.refresh()
        current_search_client.indices.flush()

        login(client, test_users.u2)
        response = client.delete(record_url(pid))
        assert response.status_code == 403

        login(client, test_users.u1)
        response = client.delete(record_url(pid))
        assert response.status_code == 204

        with pytest.raises(NoResultFound):
            Record.get_record(pid.object_uuid)
def test_get_record_no_acls_anonymous(app, db, es, es_acl_prepare, test_users):

    with db.session.begin_nested():
        # create an empty ACL in order to get the _invenio_explicit_acls filled
        acl = DefaultACL(name='test',
                         schemas=[RECORD_SCHEMA],
                         priority=0,
                         operation='get',
                         originator=test_users.u1)
        db.session.add(acl)
        actor = UserActor(name='test',
                          acl=acl,
                          users=[],
                          originator=test_users.u1)
        db.session.add(actor)

    pid, record = create_record({}, clz=SchemaEnforcingRecord)
    RecordIndexer().index(record)

    # make sure it is flushed
    current_search_client.indices.flush()

    # try to get it ...
    with app.test_client() as client:
        res = client.get(record_url(pid))
        assert res.status_code == 401  # unauthorized

    # get it directly from ES
    res = get_from_es(pid)['_source']
    assert res['control_number'] == pid.pid_value
    assert res['$schema'] == 'https://localhost/schemas/' + RECORD_SCHEMA
    assert '_invenio_explicit_acls' in res
Example #14
0
def test_cli_full_reindex(app, db, es, capsys, es_acl_prepare, test_users):
    pid, record = create_record(
        {
            '$schema': RECORD_SCHEMA,
            'keywords': ['blah']
        },
        clz=SchemaEnforcingRecord)
    RecordIndexer().index(record)
    current_search_client.indices.flush()
    with db.session.begin_nested():
        acl = ElasticsearchACL(name='test',
                               schemas=[RECORD_SCHEMA],
                               priority=0,
                               operation='get',
                               originator=test_users.u1,
                               record_selector={'term': {
                                   'keywords': 'blah'
                               }})
        db.session.add(acl)
        u = UserActor(name='test',
                      acl=acl,
                      originator=test_users.u1,
                      users=[test_users.u1])
        db.session.add(u)

    # now the record is not indexed and ACL is not in the helper index, check it ...
    retrieved = RecordsSearch(
        index=schema_to_index(RECORD_SCHEMA)[0]).get_record(
            record.id).execute().hits[0].to_dict()
    assert '_invenio_explicit_acls' not in retrieved

    # just a precaution test
    assert current_explicit_acls.enabled_schemas == {RECORD_SCHEMA}

    # and run the reindex - should reindex one record
    from invenio_explicit_acls.cli import full_reindex_impl
    full_reindex_impl(verbose=True, records=True, in_bulk=False)

    captured = capsys.readouterr()
    assert captured.out.strip() == """
Reindexing ACLs
Updating ACL representation for "test" (%s) on schemas ['records/record-v1.0.0.json']
Getting records for schema records/record-v1.0.0.json
   ... collected 1 records
Adding 1 records to indexing queue""".strip() % (acl.id)

    current_search_client.indices.flush()

    retrieved = RecordsSearch(
        index=schema_to_index(RECORD_SCHEMA)[0]).get_record(
            record.id).execute().hits[0].to_dict()
    assert clear_timestamp(retrieved['_invenio_explicit_acls']) == [{
        'id':
        str(acl.id),
        'operation':
        'get',
        'timestamp':
        'cleared',
        'user': [1]
    }]
def test_valid_put(app, resolver):
    """Test VALID record patch request (PATCH .../records/<record_id>)."""
    with app.app_context():
        # create the record using the internal API
        pid, internal_record = create_record(test_data)
        with app.test_client() as client:
            headers = [('Content-Type', 'application/json'),
                       ('Accept', 'application/json')]
            res = client.put(url_for('invenio_records_rest.recid_item',
                                     pid_value=pid.pid_value),
                             data=json.dumps(test_data_patched),
                             headers=headers)
            assert res.status_code == 200
            # check that the returned record matches the given data
            response_data = json.loads(res.get_data(as_text=True))
            assert response_data['metadata'] == test_data_patched

            # check that an internal record returned id and that it contains
            # the same data
            assert 'id' in response_data.keys()
            pid, internal_record = resolver.resolve(response_data['id'])
            assert internal_record == response_data['metadata']

            # check that the returned self link returns the same data
            subtest_self_link(response_data, res.headers, client)
Example #16
0
def test_records(db, test_data):
    """Load test records."""
    result = []
    for r in test_data:
        result.append(create_record(r))
    db.session.commit()
    yield result
Example #17
0
def test_update(app, db):
    pid, rec = create_record({}, clz=SchemaEnforcingRecord)
    with pytest.raises(AttributeError):
        rec.update({'$schema': 'http://blah'})

    rec.update({'title': 'blah'})
    assert rec['title'] == 'blah'
Example #18
0
def test_preprocessor_mixin_record(app, db):
    """Test preprocessor mixin."""
    pid, record = create_record({'title': 'test', 'aref': {'$ref': '#/title'}})
    record.model.created = datetime(2015, 10, 1, 11, 11, 11, 1)
    db.session.commit()

    data = PreprocessorMixin().preprocess_record(pid, record)
    for k in keys:
        assert k in data

    assert data['metadata']['title'] == 'test'
    assert data['metadata']['aref'] == {'$ref': '#/title'}
    assert data['created'] == '2015-10-01T11:11:11.000001+00:00'
    assert data['revision'] == 1

    data = PreprocessorMixin(replace_refs=True).preprocess_record(
        pid, Record({
            'title': 'test2',
            'aref': {
                '$ref': '#/title'
            }
        }))
    assert data['created'] is None
    assert data['updated'] is None
    assert data['metadata']['aref'] == 'test2'
Example #19
0
def test_valid_put(app, resolver):
    """Test VALID record patch request (PATCH .../records/<record_id>)."""
    with app.app_context():
        # create the record using the internal API
        pid, internal_record = create_record(test_data)
        with app.test_client() as client:
            headers = [('Content-Type', 'application/json'),
                       ('Accept', 'application/json')]
            res = client.put(url_for('invenio_records_rest.recid_item',
                                     pid_value=pid.pid_value),
                             data=json.dumps(test_data_patched),
                             headers=headers)
            assert res.status_code == 200
            # check that the returned record matches the given data
            response_data = json.loads(res.get_data(as_text=True))
            assert response_data['metadata'] == test_data_patched

            # check that an internal record returned id and that it contains
            # the same data
            assert 'id' in response_data.keys()
            pid, internal_record = resolver.resolve(response_data['id'])
            assert internal_record == response_data['metadata']

            # check that the returned self link returns the same data
            subtest_self_link(response_data, res.headers, client)
def test_video_records(db, test_videos_project):
    """Load test videos and project records."""
    result = []
    for r in test_videos_project:
        result.append(create_record(r))
    db.session.commit()
    yield result
def test_records(db, test_data):
    """Load test records."""
    result = []
    for r in test_data:
        result.append(create_record(r))
    db.session.commit()
    yield result
Example #22
0
def test_delete_with_sqldatabase_error(app):
    """Test VALID record delete request (GET .../records/<record_id>)."""
    with app.app_context():
        # create the record using the internal API
        pid, record = create_record(test_data)
        db.session.expire(record.model)
        pid_value = pid.pid_value
        pid_type = pid.pid_type
        record_id = record.id

        db.session.commit()
        Record.get_record(record_id)

        def raise_exception():
            raise SQLAlchemyError()

        with app.test_client() as client:
            # start a new SQLAlchemy session so that it will rollback
            # everything
            nested_transaction = db.session().transaction
            orig_rollback = nested_transaction.rollback
            flags = {'rollbacked': False}

            def custom_rollback(*args, **kwargs):
                flags['rollbacked'] = True
                orig_rollback(*args, **kwargs)

            nested_transaction.rollback = custom_rollback

            with patch.object(PersistentIdentifier,
                              'delete',
                              side_effect=raise_exception):
                headers = [('Accept', 'application/json')]
                res = client.delete(url_for('invenio_records_rest.recid_item',
                                            pid_value=pid_value),
                                    headers=headers)
                assert res.status_code == 500
            # check that the transaction is finished
            assert db.session().transaction is not nested_transaction
            # check that the session has rollbacked
            assert flags['rollbacked']

    with app.app_context():
        with app.test_client() as client:
            # check that the record and PID have not been deleted
            Record.get_record(record_id)
            assert not PersistentIdentifier.get(pid_type,
                                                pid_value).is_deleted()
            # try to delete without exception, the transaction should have been
            # rollbacked
            headers = [('Accept', 'application/json')]
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=pid_value),
                                headers=headers)
            assert res.status_code == 204
            # check database state
            with pytest.raises(NoResultFound):
                Record.get_record(record_id)
            assert PersistentIdentifier.get(pid_type, pid_value).is_deleted()
Example #23
0
def test_default_links_factory_with_additional(app, db):
    """Test links factory with additional links."""
    pid, record = create_record({'title': 'test'})
    with app.test_request_context('/records/1'):
        link_factory = default_links_factory_with_additional(
            dict(test_link='{scheme}://{host}/{pid.pid_value}'))
        links = link_factory(pid)
        assert links['test_link'] == 'http://localhost:5000/1'
def test0(app, without_oaiset_signals, schema):
    _ = create_record(app, {
        "title_statement": {
            "title": "Test0"
        },
        "$schema": schema
    })
    current_search.flush_and_refresh("records")
Example #25
0
def test_default_links_factory_with_additional(app, db):
    """Test links factory with additional links."""
    pid, record = create_record({'title': 'test'})
    with app.test_request_context('/records/1'):
        link_factory = default_links_factory_with_additional(
            dict(test_link='{scheme}://{host}/{pid.pid_value}'))
        links = link_factory(pid)
        assert links['test_link'] == 'http://localhost:5000/1'
def test_delete_with_sqldatabase_error(app):
    """Test VALID record delete request (GET .../records/<record_id>)."""
    with app.app_context():
        # create the record using the internal API
        pid, record = create_record(test_data)
        db.session.expire(record.model)
        pid_value = pid.pid_value
        pid_type = pid.pid_type
        record_id = record.id

        db.session.commit()
        Record.get_record(record_id)

        def raise_exception():
            raise SQLAlchemyError()

        with app.test_client() as client:
            # start a new SQLAlchemy session so that it will rollback
            # everything
            nested_transaction = db.session().transaction
            orig_rollback = nested_transaction.rollback
            flags = {'rollbacked': False}

            def custom_rollback(*args, **kwargs):
                flags['rollbacked'] = True
                orig_rollback(*args, **kwargs)
            nested_transaction.rollback = custom_rollback

            with patch.object(PersistentIdentifier, 'delete',
                              side_effect=raise_exception):
                headers = [('Accept', 'application/json')]
                res = client.delete(url_for('invenio_records_rest.recid_item',
                                            pid_value=pid_value),
                                    headers=headers)
                assert res.status_code == 500
            # check that the transaction is finished
            assert db.session().transaction is not nested_transaction
            # check that the session has rollbacked
            assert flags['rollbacked']

    with app.app_context():
        with app.test_client() as client:
            # check that the record and PID have not been deleted
            Record.get_record(record_id)
            assert not PersistentIdentifier.get(pid_type,
                                                pid_value).is_deleted()
            # try to delete without exception, the transaction should have been
            # rollbacked
            headers = [('Accept', 'application/json')]
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=pid_value),
                                headers=headers)
            assert res.status_code == 204
            # check database state
            with pytest.raises(NoResultFound):
                Record.get_record(record_id)
            assert PersistentIdentifier.get(pid_type,
                                            pid_value).is_deleted()
Example #27
0
def test_elasticsearch_acl_get_record_acl(app, db, es, es_acl_prepare,
                                          test_users):
    pid, record = create_record(
        {
            '$schema': 'https://localhost/schemas/' + RECORD_SCHEMA,
            'keywords': ['blah']
        },
        clz=SchemaEnforcingRecord)
    pid1, record1 = create_record(
        {
            '$schema': 'https://localhost/schemas/' + RECORD_SCHEMA,
            'keywords': ['test']
        },
        clz=SchemaEnforcingRecord)

    with db.session.begin_nested():
        acl = ElasticsearchACL(name='test',
                               schemas=[RECORD_SCHEMA],
                               priority=0,
                               operation='get',
                               originator=test_users.u1,
                               record_selector={'term': {
                                   'keywords': 'blah'
                               }})
        db.session.add(acl)
        acl2 = ElasticsearchACL(name='test 2',
                                schemas=[ANOTHER_SCHEMA],
                                priority=0,
                                operation='get',
                                originator=test_users.u1,
                                record_selector={'term': {
                                    'keywords': 'test'
                                }})
        db.session.add(acl2)

    acl.update()
    with pytest.raises(
            AttributeError,
            match='No index found for schema records/blah-v1.0.0.json'):
        acl2.update()

    acls = list(ElasticsearchACL.get_record_acls(record))
    assert len(acls) == 1
    assert isinstance(acls[0], ElasticsearchACL)
    assert acls[0].id == acl.id
Example #28
0
def test_set(app, db):
    pid, rec = create_record({}, clz=SchemaEnforcingRecord)
    with pytest.raises(AttributeError):
        rec['$schema'] = 'http://blah'

    # should pass as this is an allowed schema
    rec['$schema'] = 'records/record-v1.0.0.json'

    # should pass as absolute path is an allowed schema as well
    rec['$schema'] = 'https://localhost/schemas/records/record-v1.0.0.json'
Example #29
0
def test_default_serializer(app, user_factory):
    """Test default serializer."""
    with app.app_context():
        # create the record using the internal API
        pid1, record1 = create_record(test_data)
        pid2, record2 = create_record(test_data2)
        db.session.commit()

        es_index = app.config["RECORDS_REST_DEFAULT_SEARCH_INDEX"]
        current_search_client.indices.flush(wait_if_ongoing=True,
                                            force=True,
                                            index=es_index)

    accept_json = [('Accept', 'application/json')]
    accept_xml = [('Accept', 'application/xml')]

    with app.test_client() as client:
        res = client.get('/records/', headers=accept_json)
        assert res.status_code == 200
        assert res.content_type == 'application/json'

        res = client.get('/records/', headers=accept_xml)
        assert res.status_code == 200
        assert res.content_type == 'application/xml'

        res = client.get('/records/')
        assert res.status_code == 200
        assert res.content_type == 'application/xml'

        res = client.get('/records/1', headers=accept_json)
        assert res.status_code == 200
        assert res.content_type == 'application/json'

        res = client.get('/records/1', headers=accept_xml)
        assert res.status_code == 200
        assert res.content_type == 'application/xml'

        res = client.get('/records/1')
        assert res.status_code == 200
        assert res.content_type == 'application/xml'
Example #30
0
def test_id_acl_record_str(app, db, es, es_acl_prepare, test_users):

    pid1, record1 = create_record({})
    pid2, record2 = create_record({'title': 'blah'})
    pid3, record3 = create_record(
        {'title': {
            '_': 'blah',
            'cs': 'blah cs',
            'en': 'blah en'
        }})

    acl1 = IdACL(name='test',
                 schemas=[RECORD_SCHEMA],
                 priority=0,
                 operation='get',
                 originator=test_users.u1,
                 record_id=str(record1.id))
    acl2 = IdACL(name='test',
                 schemas=[RECORD_SCHEMA],
                 priority=0,
                 operation='get',
                 originator=test_users.u1,
                 record_id=str(record2.id))
    acl3 = IdACL(name='test',
                 schemas=[RECORD_SCHEMA],
                 priority=0,
                 operation='get',
                 originator=test_users.u1,
                 record_id=str(record3.id))
    acl4 = IdACL(name='test',
                 schemas=[RECORD_SCHEMA],
                 priority=0,
                 operation='get',
                 originator=test_users.u1,
                 record_id='1111-11111111-11111111-1111')

    assert acl1.record_str == "%s: {'control_number': '1'}" % record1.id
    assert acl2.record_str == "%s: blah" % record2.id
    assert acl3.record_str == "%s: blah" % record3.id
    assert acl4.record_str == "No record for ID ACL on 1111-11111111-11111111-1111"
Example #31
0
def test_invalid_patch(app):
    """Test INVALID record patch request (PATCH .../records/<record_id>)."""
    with app.app_context():
        with app.test_client() as client:
            # check that PATCH with non existing id will return 404
            headers = [('Content-Type', 'application/json-patch+json'),
                       ('Accept', 'application/json')]
            res = client.patch(url_for('invenio_records_rest.recid_item',
                                       pid_value=0),
                               headers=headers)
            assert res.status_code == 404

            # create the record using the internal API
            pid, internal_record = create_record(test_data)

            # check that PATCH with non accepted format will return 406
            headers = [('Content-Type', 'application/json-patch+json'),
                       ('Accept', 'video/mp4')]
            res = client.patch(url_for('invenio_records_rest.recid_item',
                                       pid_value=pid.pid_value),
                               data=json.dumps(test_patch),
                               headers=headers)
            assert res.status_code == 406

            # check that PATCH with non-json Content-Type will return 415
            headers = [('Content-Type', 'video/mp4'),
                       ('Accept', 'application/json')]
            res = client.patch(url_for('invenio_records_rest.recid_item',
                                       pid_value=pid.pid_value),
                               data=json.dumps(test_patch),
                               headers=headers)
            assert res.status_code == 415

            # check that PATCH with invalid json-patch will return 400
            headers = [('Content-Type', 'application/json-patch+json'),
                       ('Accept', 'application/json')]
            res = client.patch(url_for('invenio_records_rest.recid_item',
                                       pid_value=pid.pid_value),
                               data=json.dumps([{
                                   'invalid': 'json-patch'
                               }]),
                               headers=headers)
            assert res.status_code == 400

            # check that PATCH with invalid json will return 400
            headers = [('Content-Type', 'application/json-patch+json'),
                       ('Accept', 'application/json')]
            res = client.patch(url_for('invenio_records_rest.recid_item',
                                       pid_value=pid.pid_value),
                               data='{sdfsdf',
                               headers=headers)
            assert res.status_code == 400
def test_invalid_patch(app):
    """Test INVALID record patch request (PATCH .../records/<record_id>)."""
    with app.app_context():
        with app.test_client() as client:
            # check that PATCH with non existing id will return 404
            headers = [('Content-Type', 'application/json-patch+json'),
                       ('Accept', 'application/json')]
            res = client.patch(url_for('invenio_records_rest.recid_item',
                                       pid_value=0),
                               headers=headers)
            assert res.status_code == 404

            # create the record using the internal API
            pid, internal_record = create_record(test_data)

            # check that PATCH with non accepted format will return 406
            headers = [('Content-Type', 'application/json-patch+json'),
                       ('Accept', 'video/mp4')]
            res = client.patch(url_for('invenio_records_rest.recid_item',
                                       pid_value=pid.pid_value),
                               data=json.dumps(test_patch),
                               headers=headers)
            assert res.status_code == 406

            # check that PATCH with non-json Content-Type will return 415
            headers = [('Content-Type', 'video/mp4'),
                       ('Accept', 'application/json')]
            res = client.patch(url_for('invenio_records_rest.recid_item',
                                       pid_value=pid.pid_value),
                               data=json.dumps(test_patch),
                               headers=headers)
            assert res.status_code == 415

            # check that PATCH with invalid json-patch will return 400
            headers = [('Content-Type', 'application/json-patch+json'),
                       ('Accept', 'application/json')]
            res = client.patch(url_for('invenio_records_rest.recid_item',
                                       pid_value=pid.pid_value),
                               data=json.dumps([
                                   {'invalid': 'json-patch'}
                               ]),
                               headers=headers)
            assert res.status_code == 400

            # check that PATCH with invalid json will return 400
            headers = [('Content-Type', 'application/json-patch+json'),
                       ('Accept', 'application/json')]
            res = client.patch(url_for('invenio_records_rest.recid_item',
                                       pid_value=pid.pid_value),
                               data='{sdfsdf',
                               headers=headers)
            assert res.status_code == 400
def test_get_record_no_acls_authenticated(app, db, es, es_acl_prepare,
                                          test_users):
    pid, record = create_record({}, clz=SchemaEnforcingRecord)
    RecordIndexer().index(record)

    # make sure it is flushed
    current_search_client.indices.flush()

    # try to get it ...
    with app.test_client() as client:
        login(client, test_users.u1)
        res = client.get(record_url(pid))
        assert res.status_code == 403  # Forbidden
def test_default_serializer(app, user_factory):
    """Test default serializer."""
    with app.app_context():
        # create the record using the internal API
        pid1, record1 = create_record(test_data)
        pid2, record2 = create_record(test_data2)
        db.session.commit()

        es_index = app.config["RECORDS_REST_DEFAULT_SEARCH_INDEX"]
        current_search_client.indices.flush(
            wait_if_ongoing=True, force=True, index=es_index)

    accept_json = [('Accept', 'application/json')]
    accept_xml = [('Accept', 'application/xml')]

    with app.test_client() as client:
        res = client.get('/records/', headers=accept_json)
        assert res.status_code == 200
        assert res.content_type == 'application/json'

        res = client.get('/records/', headers=accept_xml)
        assert res.status_code == 200
        assert res.content_type == 'application/xml'

        res = client.get('/records/')
        assert res.status_code == 200
        assert res.content_type == 'application/xml'

        res = client.get('/records/1', headers=accept_json)
        assert res.status_code == 200
        assert res.content_type == 'application/json'

        res = client.get('/records/1', headers=accept_xml)
        assert res.status_code == 200
        assert res.content_type == 'application/xml'

        res = client.get('/records/1')
        assert res.status_code == 200
        assert res.content_type == 'application/xml'
Example #35
0
def test_no_es_prepared_index(app, db, es, test_users):
    pid, record = create_record(
        {
            '$schema': RECORD_SCHEMA,
            'keywords': ['blah']
        },
        clz=SchemaEnforcingRecord)
    with pytest.raises(
            RuntimeError,
            match='Explicit ACLs were not prepared for the given schema. '
            'Please run invenio explicit-acls prepare '
            'https://localhost/schemas/records/record-v1.0.0.json'):
        list(ElasticsearchACL.get_record_acls(record))
def test_default_permissions(default_permissions):
    """Test default permissions."""
    app = default_permissions
    with app.app_context():
        pid, internal_record = create_record(test_data)
        headers = [('Content-Type', 'application/json')]
        fixtures = {'delete': 401, 'get': 200, 'post': 405, 'put': 401}
        with app.test_client() as client:
            for action, code in fixtures.items():
                request = getattr(client, action)
                res = request(url_for('invenio_records_rest.recid_item',
                                      pid_value=pid.pid_value),
                              headers=headers)
                assert code == res.status_code, action
def test_set_with_records(app, without_oaiset_signals, test0, schema):
    # create extra record
    _ = create_record(app, {
        "title_statement": {
            "title": "Test1"
        },
        "$schema": schema
    })
    current_search.flush_and_refresh("records")

    # create and query set
    _ = create_oaiset("test", "Test0")
    rec_in_set = get_records(set="test")
    assert rec_in_set.total == 1
    rec = next(rec_in_set.items)
    assert rec["json"]["_source"]["title_statement"]["title"] == "Test0"
Example #38
0
def test_aclserializer(app, db, es, es_acl_prepare, test_users):
    with db.session.begin_nested():
        acl1 = DefaultACL(name='default',
                          schemas=[RECORD_SCHEMA],
                          priority=0,
                          originator=test_users.u1,
                          operation='get')
        actor1 = UserActor(name='auth',
                           users=[test_users.u1],
                           acl=acl1,
                           originator=test_users.u1)

        db.session.add(acl1)
        db.session.add(actor1)

    pid, rec = create_record({'title': 'blah'}, clz=SchemaEnforcingRecord)
    RecordIndexer().index(rec)
    current_search_client.indices.flush()

    assert current_jsonschemas.url_to_path(
        rec['$schema']) in current_explicit_acls.enabled_schemas
    assert list(DefaultACL.get_record_acls(rec)) != []

    index, doc_type = schema_to_index(RECORD_SCHEMA)
    data = current_search_client.get(index=index,
                                     doc_type=doc_type,
                                     id=str(pid.object_uuid))['_source']
    assert '_invenio_explicit_acls' in data
    assert len(data['_invenio_explicit_acls']) == 1

    with app.test_request_context():
        login_user(test_users.u1)
        set_identity(test_users.u1)

        acljson_serializer = ACLJSONSerializer(RecordSchemaV1,
                                               acl_rest_endpoint='recid',
                                               replace_refs=True)
        serialized = json.loads(acljson_serializer.serialize(pid, rec))
        assert serialized['invenio_explicit_acls'] == ["get"]

    with app.test_client() as client:
        login(client, test_users.u1)
        search_results = client.get(url_for('invenio_records_rest.recid_list'))
        search_results = get_json(search_results)
        hits = search_results['hits']['hits']
        assert len(hits) == 1
        assert hits[0]['invenio_explicit_acls'] == ["get"]
def test_delete_deleted(app):
    """Test deleting a perviously deleted record."""
    with app.app_context():
        # create the record using the internal API
        pid, record = create_record(test_data)
        delete_record(pid, app)

        with app.test_client() as client:
            headers = [('Accept', 'application/json')]
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=pid.pid_value),
                                headers=headers)
            assert res.status_code == 410
            # check that the returned record matches the given data
            response_data = json.loads(res.get_data(as_text=True))
            assert response_data['status'] == 410
            assert 'no longer available' in response_data['message']
def test_patch_one_permissions(app, user_factory, resolver):
    """Test patch permission."""
    with app.app_context():
        # create the record using the internal API
        pid, internal_record = create_record(test_data)
        with user_factory('allowed') as allowed_user, \
                user_factory('forbidden') as forbidden_user:
            # create one user allowed to update the record
            allowed_user.update_access(True, str(internal_record.id))
            allowed_login = allowed_user.login_function()
            # create one user who is not allowed to update the record
            forbidden_user.update_access(False, str(internal_record.id))
            forbidden_login = forbidden_user.login_function()
            db.session.commit()

        headers = [('Content-Type', 'application/json-patch+json'),
                   ('Accept', 'application/json')]
        # test get without being authenticated
        with app.test_client() as client:
            res = client.patch(url_for('invenio_records_rest.recid_item',
                                       pid_value=pid.pid_value),
                               data=json.dumps(test_patch),
                               headers=headers)
            assert res.status_code == 401
        # test not allowed get
        with app.test_client() as client:
            forbidden_login(client)
            res = client.patch(url_for('invenio_records_rest.recid_item',
                                       pid_value=pid.pid_value),
                               data=json.dumps(test_patch),
                               headers=headers)
            assert res.status_code == 403
        # test allowed get
        with app.test_client() as client:
            allowed_login(client)
            res = client.patch(url_for('invenio_records_rest.recid_item',
                                       pid_value=pid.pid_value),
                               data=json.dumps(test_patch),
                               headers=headers)
            assert res.status_code == 200
            # check that the returned record matches the given data
            response_data = json.loads(res.get_data(as_text=True))
            test = copy.deepcopy(test_data_patched)
            test['control_number'] = 1
            assert response_data['metadata'] == test
def test_preprocessor_mixin_record(app, db):
    """Test preprocessor mixin."""
    pid, record = create_record({'title': 'test', 'aref': {'$ref': '#/title'}})
    record.model.created = datetime(2015, 10, 1, 11, 11, 11, 1)
    db.session.commit()

    data = PreprocessorMixin().preprocess_record(pid, record)
    for k in keys:
        assert k in data

    assert data['metadata']['title'] == 'test'
    assert data['metadata']['aref'] == {'$ref': '#/title'}
    assert data['created'] == '2015-10-01T11:11:11.000001+00:00'
    assert data['revision'] == 1

    data = PreprocessorMixin(replace_refs=True).preprocess_record(
        pid, Record({'title': 'test2', 'aref': {'$ref': '#/title'}}))
    assert data['created'] is None
    assert data['updated'] is None
    assert data['metadata']['aref'] == 'test2'
def test_invalid_get(app):
    """Test INVALID record get request (GET .../records/<record_id>)."""
    with app.app_context():
        with app.test_client() as client:
            # check that GET with non existing id will return 404
            headers = [('Accept', 'application/json')]
            res = client.get(url_for('invenio_records_rest.recid_item',
                                     pid_value='0'),
                             headers=headers)
            assert res.status_code == 404

            # create the record using the internal API
            pid, record = create_record(test_data)

            # check that GET with non accepted format will return 406
            headers = [('Accept', 'video/mp4')]
            res = client.get(url_for('invenio_records_rest.recid_item',
                                     pid_value=pid.pid_value),
                             headers=headers)
            assert res.status_code == 406
def test_patch_deleted(app):
    """Test patching deleted record."""
    with app.app_context():
        # create the record using the internal API
        pid, record = create_record(test_data)
        delete_record(pid, app)

        with app.test_client() as client:
            headers = [('Content-Type', 'application/json-patch+json'),
                       ('Accept', 'application/json')]
            # check patch response for deleted resource
            patch_res = client.patch(url_for('invenio_records_rest.recid_item',
                                             pid_value=pid.pid_value),
                                     data=json.dumps(test_patch),
                                     headers=headers)
            assert patch_res.status_code == 410
            # check that the returned record matches the given data
            response_data = json.loads(patch_res.get_data(as_text=True))
            assert response_data['status'] == 410
            assert 'no longer available' in response_data['message']
def test_invalid_delete(app):
    """Test INVALID record delete request (DELETE .../records/<record_id>)."""
    with app.app_context():
        with app.test_client() as client:
            # check that GET with non existing id will return 404
            headers = [('Accept', 'application/json')]
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=0),
                                headers=headers)
            assert res.status_code == 404

            # check that deleting a deleted record returns 410
            pid, record = create_record(test_data)
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=pid.pid_value),
                                headers=headers)
            assert res.status_code == 204
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=pid.pid_value),
                                headers=headers)
            assert res.status_code == 410
def test_record_mementos(app, db):
    """Test resolution of record mementos."""
    modifications = [(datetime.now() + timedelta(days=-1), 'test')]
    pid, record = create_record({'title': 'test'})
    db.session.commit()
    assert len(record.revisions) == 1
    modifications.append((record.model.updated, 'test'))

    sleep(1)
    record.update({'title': 'test1'})
    record.commit()
    db.session.commit()
    assert len(record.revisions) == 2

    modifications.append((record.model.updated, 'test1'))
    modifications.append((datetime.now() + timedelta(days=1), 'test1'))

    headers = [('Accept', 'application/json')]
    with app.test_client() as client:
        # Normal request
        res = client.get(
            url_for('invenio_records_rest.recid_item',
                    pid_value=pid.pid_value),
            headers=headers)
        assert res.status_code == 200
        assert 'Memento-Datetime' not in res.headers

        for i, (accept_datetime, title) in enumerate(modifications):
            res = client.get(
                url_for('invenio_records_rest.recid_item',
                        pid_value=pid.pid_value),
                headers=headers + [
                    ('Accept-Datetime', http_date(accept_datetime))
                ],
            )
            assert res.status_code == 200, i
            assert parse_date(res.headers['Memento-Datetime']), i
            assert title == json.loads(
                res.data
            )['metadata']['title'], i
def test_valid_get(app):
    """Test VALID record get request (GET .../records/<record_id>)."""
    with app.app_context():
        # create the record using the internal API
        pid, record = create_record(test_data)

        with app.test_client() as client:
            headers = [('Accept', 'application/json')]
            res = client.get(url_for('invenio_records_rest.recid_item',
                                     pid_value=pid.pid_value),
                             headers=headers)
            assert res.status_code == 200
            # check that the returned record matches the given data
            response_data = json.loads(res.get_data(as_text=True))
            assert response_data['metadata'] == control_num(test_data)

            # check the returned id
            assert 'id' in response_data.keys()
            assert str(response_data['id']) == pid.pid_value

            # check that the returned self link returns the same data
            subtest_self_link(response_data, res.headers, client)
def test_delete_one_permissions(app, user_factory, resolver):
    """Test delete permission."""
    with app.app_context():
        # create the record using the internal API
        pid, internal_record = create_record(test_data)
        with user_factory('allowed') as allowed_user, \
                user_factory('forbidden') as forbidden_user:
            # create one user allowed to delete the record
            allowed_user.delete_access(True, str(internal_record.id))
            allowed_login = allowed_user.login_function()
            # create one user who is not allowed to delete the record
            forbidden_user.delete_access(False, str(internal_record.id))
            forbidden_login = forbidden_user.login_function()
            db.session.commit()

        headers = [('Content-Type', 'application/json')]
        # test get without being authenticated
        with app.test_client() as client:
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=pid.pid_value),
                                headers=headers)
            assert res.status_code == 401
        # test not allowed get
        with app.test_client() as client:
            forbidden_login(client)
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=pid.pid_value),
                                headers=headers)
            assert res.status_code == 403
        # test allowed get
        with app.test_client() as client:
            allowed_login(client)
            res = client.delete(url_for('invenio_records_rest.recid_item',
                                        pid_value=pid.pid_value),
                                headers=headers)
            assert res.status_code == 204
def test_valid_search(app, user_factory):
    """Test VALID record search request (GET .../records/?q=...)."""
    with app.app_context():
        # create the record using the internal API
        pid1, record1 = create_record(test_data)
        pid2, record2 = create_record(test_data2)
        pid3, record3 = create_record(test_data3)
        pid4, record4 = create_record(test_data4)

        with user_factory('allowed') as allowed_user, \
                user_factory('forbidden') as forbidden_user:
            # create one user allowed to delete the record
            allowed_user.read_access(True, str(record1.id))
            allowed_user.read_access(True, str(record2.id))
            allowed_user.read_access(True, str(record3.id))
            allowed_user.read_access(True, str(record4.id))
            allowed_login = allowed_user.login_function()
            # create one user who is not allowed to delete the record
            forbidden_user.read_access(False, str(record1.id))
            forbidden_user.read_access(False, str(record2.id))
            forbidden_user.read_access(False, str(record3.id))
            forbidden_user.read_access(False, str(record4.id))
            forbidden_login = forbidden_user.login_function()
            db.session.commit()

        es_index = app.config["RECORDS_REST_DEFAULT_SEARCH_INDEX"]
        current_search_client.indices.flush(wait_if_ongoing=True,
                                            force=True,
                                            index=es_index)

        with app.test_client() as client:
            forbidden_login(client)
            headers = [('Accept', 'application/json')]
            res = client.get(url_for('invenio_records_rest.recid_list',
                                     q='back'),
                             headers=headers)
            assert res.status_code == 200
            data = json.loads(res.get_data(as_text=True))
            assert isinstance(data['hits']['hits'], list)
            assert len(data['hits']['hits']) == 0

        with app.test_client() as client:
            allowed_login(client)
            headers = [('Accept', 'application/json')]
            res = client.get(url_for('invenio_records_rest.recid_list',
                                     q='back', sort='-year'),
                             headers=headers)
            assert res.status_code == 200
            data = json.loads(res.get_data(as_text=True))
            assert isinstance(data['hits']['hits'], list)
            assert data['hits']['total'] == 2

            subtest_expected_hits(data['hits']['hits'], [
                (pid2.pid_value, control_num(test_data2, 2)),
                (pid1.pid_value, control_num(test_data, 1)),
            ], client)

        # test pagination
        with app.test_client() as client:
            allowed_login(client)
            headers = [('Accept', 'application/json')]
            res = client.get(url_for('invenio_records_rest.recid_list',
                                     q='the', page='1', size='2', sort='year'),
                             headers=headers)
            assert res.status_code == 200
            data = json.loads(res.get_data(as_text=True))
            assert isinstance(data['hits']['hits'], list)
            assert data['hits']['total'] == 3

            subtest_expected_hits(data['hits']['hits'], [
                (pid3.pid_value, control_num(test_data3, 3)),
                (pid1.pid_value, control_num(test_data, 1)),
            ], client)
            assert 'next' in data['links'].keys()
            assert 'prev' not in data['links'].keys()

            # check next page
            url = external_to_relative_url(data['links']['next'])
            res2 = client.get(url)
            assert res2.status_code == 200
            data2 = json.loads(res2.get_data(as_text=True))
            assert isinstance(data2['hits']['hits'], list)
            assert data2['hits']['total'] == 3

            subtest_expected_hits(data2['hits']['hits'], [
                (pid2.pid_value, control_num(test_data2, 2)),
            ], client)
            assert 'next' not in data2['links'].keys()
            assert 'prev' in data2['links'].keys()

            # check previous page
            url = external_to_relative_url(data2['links']['prev'])
            res3 = client.get(url)
            assert res3.status_code == 200
            # check that the previous link returns the same response
            data3 = json.loads(res3.get_data(as_text=True))
            data3_copy = copy.deepcopy(data3)
            data3_copy['links'] = {
                k: normalise_url(v) for k, v in data3_copy['links'].items()
            }
            data_copy = copy.deepcopy(data)
            data_copy['links'] = {
                k: normalise_url(v) for k, v in data_copy['links'].items()
            }
            assert data3_copy == data_copy