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
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
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'
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
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
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)
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
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'] == []
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
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)
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
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'
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_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_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()
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")
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
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'
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'
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"
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'
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"
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