class FedoraTestCase(unittest.TestCase): def __init__(self, *args, **kwargs): unittest.TestCase.__init__(self, *args, **kwargs) self.fedora_fixtures_ingested = [] self.pidspace = FEDORA_PIDSPACE self.repo = Repository(FEDORA_ROOT, FEDORA_USER, FEDORA_PASSWORD) # fixture cleanup happens in tearDown, which doesn't always run # if a test fails - clean up stale test objects from a previous run here stale_objects = list(self.repo.find_objects(pid__contains="%s:*" % self.pidspace)) if stale_objects: print "Removing %d stale test object(s) in pidspace %s" % (len(stale_objects), self.pidspace) for obj in stale_objects: try: self.repo.purge_object(obj.pid) except RequestFailed as rf: logger.warn("Error purging stale test object %s (TestCase init): %s" % (obj.pid, rf)) def setUp(self): # NOTE: queries require RI flush=True or test objects will not show up in RI self.repo.risearch.RISEARCH_FLUSH_ON_QUERY = True self.opener = self.repo.opener self.api = ApiFacade(self.opener) fixtures = getattr(self, "fixtures", []) for fixture in fixtures: self.ingestFixture(fixture) def tearDown(self): for pid in self.fedora_fixtures_ingested: try: self.repo.purge_object(pid) except RequestFailed as rf: logger.warn("Error purging test object %s in tear down: %s" % (pid, rf)) def getNextPid(self): pidspace = getattr(self, "pidspace", None) return self.repo.get_next_pid(namespace=pidspace) def loadFixtureData(self, fname): data = load_fixture_data(fname) # if pidspace is specified, get a new pid from fedora and set it as the pid in the xml if hasattr(self, "pidspace"): xml = xmlmap.load_xmlobject_from_string(data, _MinimalFoxml) xml.pid = self.getNextPid() return xml.serialize() else: return data def ingestFixture(self, fname): object = self.loadFixtureData(fname) pid = self.repo.ingest(object) if pid: # we'd like this always to be true. if ingest fails we should # throw an exception. that probably hasn't been thoroughly # tested yet, though, so we'll check it until it has been. self.append_test_pid(pid) def append_test_pid(self, pid): self.fedora_fixtures_ingested.append(pid)
def purge_item(item_id): repo = Repository() pid = 'pitt:%s' % (item_id,) objs = repo.find_objects(pid__contains=pid) for o in objs: repo.purge_object(o.pid) print '%s purged' % (o.pid,)
def remove_test_objects(self): # remove any leftover test object before or after running tests # NOTE: This method expects to be called only when FEDORA_PIDSPACE has been # switched to a test pidspace # use test fedora credentials if they are set repo = Repository(root=getattr(settings, 'FEDORA_TEST_ROOT', None), username=getattr(settings, 'FEDORA_TEST_USER', None), password=getattr(settings, 'FEDORA_TEST_PASSWORD', None)) test_objects = repo.find_objects(pid__contains='%s:*' % settings.FEDORA_PIDSPACE) count = 0 for obj in test_objects: # if objects are unexpectedly not being cleaned up, pid/label may help # to isolate which test is creating the leftover objects try: repo.purge_object(obj.pid, "removing test object") # NOTE: not displaying label because we may not have permission to access it logger.info('Purged test object %s' % obj.pid) count += 1 except RequestFailed: logger.warn('Error purging test object %s' % obj.pid) if count: print >> sys.stderr, "Removed %s test object(s) with pidspace %s" \ % (count, settings.FEDORA_PIDSPACE)
class ThesisBase(unittest.TestCase): """Base class for testing the functionality of the ETD Django application.""" def __init__(self, *args, **kwargs): unittest.TestCase.__init__(self, *args, **kwargs) self.fedora_fixtures_ingested = [] self.pidspace = FEDORA_PIDSPACE def setUp(self): """Creates a base class instance of an `eulfedora` Repository for testing the basic functionality of the ingesting a thesis object into a Fedora Repository.""" self.repo = Repository() # self.repo = Repository(FEDORA_ROOT,FEDORA_USER,FEDORA_PASSWORD) self.repo.risearch.RISEARCH_FLUSH_ON_QUERY = True def tearDown(self): """Removes test objects from the repository""" for pid in self.fedora_fixtures_ingested: try: self.repo.purge_object(pid) except RequestFailed as rf: logger.warn('Error purging test object %s in tear down:%s' %\ (pid,rf))
class PdfToTextTest(unittest.TestCase): fixture_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'fixtures') pdf_filepath = os.path.join(fixture_dir, 'test.pdf') pdf_text = 'This is a short PDF document to use for testing.' def setUp(self): self.repo = Repository(settings.FEDORA_ROOT, settings.FEDORA_USER, settings.FEDORA_PASSWORD) with open(self.pdf_filepath) as pdf: self.pdfobj = self.repo.get_object(type=TestPdfObject) self.pdfobj.label = 'eulindexer test pdf object' self.pdfobj.pdf.content = pdf self.pdfobj.save() def tearDown(self): self.repo.purge_object(self.pdfobj.pid) def test_file(self): # extract text from a pdf from a file on the local filesystem text = pdf_to_text(open(self.pdf_filepath, 'rb')) self.assertEqual(self.pdf_text, text) def test_object_datastream(self): # extract text from a pdf datastream in fedora pdfobj = self.repo.get_object(self.pdfobj.pid, type=TestPdfObject) text = pdf_to_text(pdfobj.pdf.content) self.assertEqual(self.pdf_text, text)
def remove_test_objects(self): # remove any leftover test object before or after running tests # NOTE: This method expects to be called only when FEDORA_PIDSPACE has been # switched to a test pidspace # use test fedora credentials if they are set repo = Repository(root=getattr(settings, 'FEDORA_TEST_ROOT', None), username=getattr(settings, 'FEDORA_TEST_USER', None), password=getattr(settings, 'FEDORA_TEST_PASSWORD', None)) test_objects = repo.find_objects(pid__contains='%s:*' % settings.FEDORA_PIDSPACE) count = 0 for obj in test_objects: # if objects are unexpectedly not being cleaned up, pid/label may help # to isolate which test is creating the leftover objects try: repo.purge_object(obj.pid, "removing test object") # NOTE: not displaying label because we may not have permission to access it logger.info('Purged test object %s' % obj.pid) count += 1 except RequestFailed: logger.warn('Error purging test object %s' % obj.pid) if count: print "Removed %s test object(s) with pidspace %s" % (count, settings.FEDORA_PIDSPACE)
class ModelUtilsTest(TestCase): # tests for utility methods declared in file.models repo_admin = None def setUp(self): # instantiate repo_admin the first time we run, after the test settings are in place if self.repo_admin is None: self.repo_admin = Repository(username=getattr(settings, 'FEDORA_TEST_USER', None), password=getattr(settings, 'FEDORA_TEST_PASSWORD', None)) self.pids = [] def tearDown(self): for pid in self.pids: self.repo_admin.purge_object(pid) def test_object_type_from_mimetype(self): self.assertEqual(ImageObject, object_type_from_mimetype('image/jpeg')) self.assertEqual(ImageObject, object_type_from_mimetype('image/gif')) self.assertEqual(FileObject, object_type_from_mimetype('image/unsupported-img')) self.assertEqual(FileObject, object_type_from_mimetype('text/plain')) def test_init_by_cmodel(self): # create file and image objects to test initialization fileobj = self.repo_admin.get_object(type=FileObject) fileobj.save() imgobj = self.repo_admin.get_object(type=ImageObject) imgobj.save() self.pids.extend([fileobj.pid, imgobj.pid]) # init a new object from file pid - should be a file object initobj = init_by_cmodel(fileobj.pid) self.assert_(isinstance(initobj, FileObject)) # since ImageObject extends FileObject, confirm that we didn't get the wrong thing self.assert_(not isinstance(initobj, ImageObject)) # image pid should be returned as an ImageObject initobj = init_by_cmodel(imgobj.pid) self.assert_(isinstance(initobj, ImageObject))
def test_index_details(self): repo = Repository() for pid in self.pids: repo.purge_object(pid) # Test with no settings set. # - should be denied response = index_config(self.request) self.assertEqual(403, response.status_code, 'index data should be forbidden if no IPs are configured to access') # NOTE: these aren't meaningful errors anyway, and settings # are now being pulled from somewhere so they fail # self.assertRaises(AttributeError, index_config, self.request) # Test with only the allowed SOLR url set. # self.assertRaises(AttributeError, index_config, self.request) # Test with this IP not allowed to hit the service. #settings.EUL_INDEXER_ALLOWED_IPS = ['0.13.23.134'] with override_settings(EUL_INDEXER_ALLOWED_IPS=['0.13.23.134']): response = index_config(self.request) expected, got = 403, response.status_code self.assertEqual(expected, got, 'Expected %s but returned %s for indexdata/index_details view' \ % (expected, got)) expected, got = 'text/html', response['Content-Type'] self.assertEqual(expected, got, 'Expected %s but returned %s for mimetype on indexdata/index_details view' \ % (expected, got)) # Test with this IP allowed to hit the view. with override_settings(EUL_INDEXER_ALLOWED_IPS=['0.13.23.134', self.request_ip]): response = index_config(self.request) expected, got = 200, response.status_code self.assertEqual(expected, got, 'Expected %s but returned %s for indexdata/index_details view' \ % (expected, got)) expected, got = 'application/json', response['Content-Type'] self.assertEqual(expected, got, 'Expected %s but returned %s for mimetype on indexdata/index_details view' \ % (expected, got)) # load json content so we can inspect the result content = json.loads(response.content) self.assertEqual(TEST_SOLR_URL, content['SOLR_URL']) self.assert_(SimpleObject.CONTENT_MODELS in content['CONTENT_MODELS']) self.assert_(LessSimpleDigitalObject.CONTENT_MODELS in content['CONTENT_MODELS']) self.assert_(ContentModel.CONTENT_MODELS not in content['CONTENT_MODELS'], 'Fedora system content models should not be included in indexed cmodels by default') # Test with the "ANY" setting for allowed IPs with override_settings(EUL_INDEXER_ALLOWED_IPS='ANY'): response = index_config(self.request) expected, got = 200, response.status_code self.assertEqual(expected, got, 'Expected %s but returned %s for indexdata/index_details view' \ % (expected, got)) # Test with 'EUL_INDEXER_CONTENT_MODELS' setting configured to override autodetect. with override_settings(EUL_INDEXER_ALLOWED_IPS='ANY', EUL_INDEXER_CONTENT_MODELS=[ ['content-model_1', 'content-model_2'], ['content-model_3']]): response = index_config(self.request) expected, got = 200, response.status_code self.assertEqual(expected, got, 'Expected %s but returned %s for indexdata/index_details view' \ % (expected, got)) expected, got = 'application/json', response['Content-Type'] self.assertEqual(expected, got, 'Expected %s but returned %s for mimetype on indexdata/index_details view' \ % (expected, got)) # load json content so we can inspect the result content = json.loads(response.content) self.assertEqual(settings.EUL_INDEXER_CONTENT_MODELS, content['CONTENT_MODELS'])
class FedoraTestCase(unittest.TestCase): def __init__(self, *args, **kwargs): unittest.TestCase.__init__(self, *args, **kwargs) self.fedora_fixtures_ingested = [] self.pidspace = FEDORA_PIDSPACE self.repo = Repository(FEDORA_ROOT, FEDORA_USER, FEDORA_PASSWORD) # fixture cleanup happens in tearDown, which doesn't always run # if a test fails - clean up stale test objects from a previous run here stale_objects = list(self.repo.find_objects(pid__contains='%s:*' % self.pidspace)) if stale_objects: logger.info('Removing %d stale test object(s) in pidspace %s' \ % (len(stale_objects), self.pidspace)) for obj in stale_objects: try: self.repo.purge_object(obj.pid) except RequestFailed as rf: logger.warn('Error purging stale test object %s (TestCase init): %s' % \ (obj.pid, rf)) def setUp(self): # NOTE: queries require RI flush=True or test objects will not show up in RI self.repo.risearch.RISEARCH_FLUSH_ON_QUERY = True self.opener = self.repo.opener self.api = ApiFacade(self.opener) fixtures = getattr(self, 'fixtures', []) for fixture in fixtures: self.ingestFixture(fixture) def tearDown(self): for pid in self.fedora_fixtures_ingested: try: self.repo.purge_object(pid) except RequestFailed as rf: logger.warn('Error purging test object %s in tear down: %s' % \ (pid, rf)) def getNextPid(self): pidspace = getattr(self, 'pidspace', None) return self.repo.get_next_pid(namespace=pidspace) def loadFixtureData(self, fname): data = load_fixture_data(fname) # if pidspace is specified, get a new pid from fedora and set it as the pid in the xml if hasattr(self, 'pidspace'): xml = xmlmap.load_xmlobject_from_string(data, _MinimalFoxml) xml.pid = self.getNextPid() return xml.serialize() else: return data def ingestFixture(self, fname): object = self.loadFixtureData(fname) pid = self.repo.ingest(object) if pid: # we'd like this always to be true. if ingest fails we should # throw an exception. that probably hasn't been thoroughly # tested yet, though, so we'll check it until it has been. self.append_pid(pid) # note: renamed from append_test_pid so that nosetests doesn't # autodetect and attempt to run as a unit test. def append_pid(self, pid): self.fedora_fixtures_ingested.append(pid)
class CollectionViewsTest(TestCase): fixtures = ['users'] # repository with default access (no credentials) repo = Repository() # repository with test credentials for loading & removing test objects # DON'T instantiate this at load time, since Fedora Test settings are not yet switched repo_admin = None new_coll_url = reverse('collection:new') def setUp(self): # instantiate repo_admin the first time we run, after the test settings are in place if self.repo_admin is None: # repository with test credentials for loading & removing test objects self.repo_admin = Repository( username=getattr(settings, 'FEDORA_TEST_USER', None), password=getattr(settings, 'FEDORA_TEST_PASSWORD', None)) self.client = Client() # create test collection object for testing view/edit functionality self.obj = self.repo_admin.get_object(type=CollectionObject) self.obj.label = 'Genrepo test collection' self.obj.dc.content.title = 'my test title' self.obj.dc.content.description = 'this collection contains test content' self.obj.save() self.edit_coll_url = reverse('collection:edit', kwargs={'pid': self.obj.pid}) self.view_coll_url = reverse('collection:view', kwargs={'pid': self.obj.pid}) self.list_coll_url = reverse('collection:list') self.pids = [self.obj.pid] def tearDown(self): # purge any objects created by individual tests for pid in self.pids: self.repo_admin.purge_object(pid) def test_get_create_form(self): 'Test displaying the form (HTTP GET) for creating a collection object (%s)' % __name__ # not logged in - should redirect to login page response = self.client.get(self.new_coll_url) code = response.status_code expected = 302 self.assertEqual( code, expected, 'Expected %s but returned %s for %s as AnonymousUser' % (expected, code, self.new_coll_url)) # logged in as user without required permissions - should 403 self.client.login(**NONADMIN_CREDENTIALS) response = self.client.get(self.new_coll_url) code = response.status_code expected = 403 self.assertEqual( code, expected, 'Expected %s but returned %s for %s as logged in non-repo editor' % (expected, code, self.new_coll_url)) # log in as repository editor for all other tests # NOTE: using admin view so user credentials will be used to access fedora self.client.post(settings.LOGIN_URL, ADMIN_CREDENTIALS) # on GET, form should be displayed response = self.client.get(self.new_coll_url) expected, code = 200, response.status_code self.assertEqual( code, expected, 'Expected %s but returned %s for %s' % (expected, code, self.new_coll_url)) self.assert_( isinstance(response.context['form'], CollectionDCEditForm), "CollectionDCEditForm should be set in response context") self.assertContains(response, 'Create a new collection') def test_create_invalid(self): 'Test submitting invalid data to create a collection object' # log in as repository editor self.client.post(settings.LOGIN_URL, ADMIN_CREDENTIALS) # test submitting incomplete/invalid data - should redisplay form with errors # title is required test_data = {'title': '', 'description': 'a new test collection'} response = self.client.post(self.new_coll_url, test_data) self.assert_( isinstance(response.context['form'], CollectionDCEditForm), "CollectionDCEditForm is set in response context when form is POSTed without title" ) self.assertContains( response, 'This field is required', 1, msg_prefix='error message for 1 missing required fields') def test_create_valid(self): 'Test submitting valid data to create collection object' # log in as repository editor self.client.post(settings.LOGIN_URL, ADMIN_CREDENTIALS) # POST and create new object, verify in fedora test_data = { 'title': 'genrepo test collection', 'description': 'my second test collection' } response = self.client.post(self.new_coll_url, test_data, follow=True) # on success, should redirect with a message about the object messages = [str(msg) for msg in response.context['messages']] self.assert_( 'Successfully created new collection' in messages[0], 'successful collection creation message displayed to user') # inspect newly created object and in fedora # - pull the pid out of the success message pidmatch = re.search('<b>(.*)</b>', messages[0]) pid = pidmatch.group(1) # first parenthesized subgroup # append to list of pids to be cleaned up after the test self.pids.append(pid) # inspect object - use repo_admin so we can access it new_obj = self.repo_admin.get_object(pid, type=CollectionObject) self.assertTrue( new_obj.has_requisite_content_models, 'new object was created with the expected content models for a CollectionObject' ) self.assertEqual(test_data['title'], new_obj.dc.content.title, "posted title should be set in dc:title; expected '%s', got '%s'" % \ (test_data['title'], new_obj.dc.content.title)) self.assertEqual(test_data['description'], new_obj.dc.content.description, "posted description should be set in dc:description; expected '%s', got '%s'" % \ (test_data['description'], new_obj.dc.content.description)) self.assertEqual(test_data['title'], new_obj.label, "posted title should be set in object label; expected '%s', got '%s'" % \ (test_data['title'], new_obj.label)) # confirm that current site user appears in fedora audit trail xml, uri = new_obj.api.getObjectXML(pid) self.assert_('<audit:responsibility>%s</audit:responsibility>' % \ ADMIN_CREDENTIALS['username'] in xml) def test_create_save_errors(self): 'Test fedora error handling when saving a collection object' # simulate fedora errors with mock objects # log in as repository editor self.client.post(settings.LOGIN_URL, ADMIN_CREDENTIALS) testobj = Mock(name='MockDigitalObject') testobj.dc.content = DublinCore() # Create a RequestFailed exception to simulate Fedora error # - eulcore.fedora wrap around an httplib response err_resp = Mock() err_resp.status = 500 err_resp.read.return_value = 'error message' # generate Fedora error on object save testobj.save.side_effect = RequestFailed(err_resp) # valid form data to post test_data = {'title': 'foo', 'description': 'bar'} # 500 error / request failed # patch the repository class to return the mock object instead of a real one with patch.object(Repository, 'get_object', new=Mock(return_value=testobj)): response = self.client.post(self.new_coll_url, test_data, follow=True) expected, code = 500, response.status_code self.assertEqual( code, expected, 'Expected %s but returned %s for %s (Fedora 500 error)' % (expected, code, self.new_coll_url)) messages = [str(msg) for msg in response.context['messages']] self.assert_( 'error communicating with the repository' in messages[0]) # update the mock error to generate a permission denied error err_resp.status = 401 err_resp.read.return_value = 'denied' # generate Fedora error on object save testobj.save.side_effect = PermissionDenied(err_resp) # 401 error - permission denied with patch.object(Repository, 'get_object', new=Mock(return_value=testobj)): response = self.client.post(self.new_coll_url, test_data, follow=True) expected, code = 401, response.status_code self.assertEqual( code, expected, 'Expected %s but returned %s for %s (Fedora 401 error)' % (expected, code, self.new_coll_url)) messages = [str(msg) for msg in response.context['messages']] self.assert_("You don't have permission to create a collection" in messages[0]) def test_get_edit_form(self): # not logged in - should redirect to login page response = self.client.get(self.edit_coll_url) code = response.status_code expected = 302 self.assertEqual( code, expected, 'Expected %s but returned %s for %s as AnonymousUser' % (expected, code, self.edit_coll_url)) # logged in as user without required permissions - should 403 self.client.post(settings.LOGIN_URL, NONADMIN_CREDENTIALS) response = self.client.get(self.edit_coll_url) code = response.status_code expected = 403 self.assertEqual( code, expected, 'Expected %s but returned %s for %s as logged in non-repo editor' % (expected, code, self.edit_coll_url)) # log in as repository editor self.client.post(settings.LOGIN_URL, ADMIN_CREDENTIALS) # on GET, form should be displayed # add oai set values to check form fields are correctly pre-populated self.obj.oai_set = 'test:edit' self.obj.oai_setlabel = 'the edit subset in the test master set' self.obj.save() response = self.client.get(self.edit_coll_url) expected, code = 200, response.status_code self.assertEqual( code, expected, 'Expected %s but returned %s for %s' % (expected, code, self.edit_coll_url)) self.assert_( isinstance(response.context['form'], CollectionDCEditForm), "CollectionDCEditForm should be set in response context") self.assertEqual(response.context['form'].instance, self.obj.dc.content) self.assertContains(response, 'Update %s' % self.obj.label) # oai set values should be set self.assertEqual(self.obj.oai_set, response.context['form']['oai_set'].value()) self.assertEqual(self.obj.oai_setlabel, response.context['form']['oai_set_name'].value()) def test_valid_edit_form(self): # log in as repository editor self.client.post(settings.LOGIN_URL, ADMIN_CREDENTIALS) # POST data and update object, verify in fedora update_data = { 'title': 'new title', 'description': 'new description too', 'oai_set': 'foo:bar', 'oai_set_name': 'all of bar in foo', } response = self.client.post(self.edit_coll_url, update_data, follow=True) # on success, should redirect with a message about the object messages = [str(msg) for msg in response.context['messages']] self.assert_('Successfully updated collection' in messages[0], 'successful collection update message displayed to user') # get a fresh copy of the object to compare updated_obj = self.repo_admin.get_object(self.obj.pid, type=CollectionObject) self.assertEqual(update_data['title'], updated_obj.dc.content.title, "posted title should be set in dc:title; expected '%s', got '%s'" % \ (update_data['title'], updated_obj.dc.content.title)) self.assertEqual(update_data['description'], updated_obj.dc.content.description, "posted description should be set in dc:description; expected '%s', got '%s'" % \ (update_data['description'], updated_obj.dc.content.description)) self.assertEqual(update_data['title'], updated_obj.label, "posted title should be set in object label; expected '%s', got '%s'" % \ (update_data['title'], updated_obj.label)) self.assertEqual( update_data['oai_set'], updated_obj.oai_set, 'posted OAI set should be set as OAI setSpec in rels-ext') self.assertEqual( update_data['oai_set_name'], updated_obj.oai_setlabel, 'posted OAI set should be set as OAI setName in rels-ext') update_data.update({'oai_set': '', 'oai_set_name': ''}) response = self.client.post(self.edit_coll_url, update_data, follow=True) # on success, should redirect with a message about the object messages = [str(msg) for msg in response.context['messages']] self.assert_('Successfully updated collection' in messages[0]) # get a fresh copy of the object to check oai set values were cleared updated_obj = self.repo_admin.get_object(self.obj.pid, type=CollectionObject) self.assertEqual( None, updated_obj.oai_set, 'OAI setSpec should not be set when no value was posted on the form' ) self.assertEqual( None, updated_obj.oai_setlabel, 'OAI setName should not be set when no value was posted on the form' ) def test_view(self): # test viewing an existing collection object # not logged in - public access content only for now, should # be accessible to anyone response = self.client.get(self.view_coll_url) code = response.status_code expected = 200 self.assertEqual( code, expected, 'Expected %s but returned %s for %s as AnonymousUser' % (expected, code, self.view_coll_url)) self.assertNotContains( response, reverse('collection:edit', kwargs={'pid': self.obj.pid}), msg_prefix= 'collection view should not include edit link for non repo editor') # logged in as user without required permissions - should still be accessible # NOTE: using admin view so user credentials will be used to access fedora self.client.post(settings.LOGIN_URL, ADMIN_CREDENTIALS) response = self.client.get(self.view_coll_url) code = response.status_code expected = 200 self.assertEqual( code, expected, 'Expected %s but returned %s for %s as logged in non-repo editor' % (expected, code, self.view_coll_url)) # check for object display self.assert_(isinstance(response.context['obj'], CollectionObject), 'collection object should be set in response context') self.assertEqual( self.obj.pid, response.context['obj'].pid, 'correct collection object should be set in response context') self.assertContains( response, self.obj.label, msg_prefix='response should include title of collection object') self.assertContains( response, self.obj.dc.content.description, msg_prefix= 'response should include description of collection object') self.assertContains( response, reverse('collection:edit', kwargs={'pid': self.obj.pid}), msg_prefix= 'collection view should include edit link for repo editor') # check for links to raw datastreams self.assertContains( response, reverse('collection:raw-ds', kwargs={ 'pid': self.obj.pid, 'dsid': 'DC' }), msg_prefix='metadata view should link to raw DC view') self.assertContains( response, reverse('collection:raw-ds', kwargs={ 'pid': self.obj.pid, 'dsid': 'RELS-EXT' }), msg_prefix='metadata view should link to raw RELS-EXT view') def test_view_nonexistent(self): # non-existent object should 404 view_coll_url = reverse('collection:view', kwargs={'pid': 'bogus:nonexistent-pid'}) response = self.client.get(view_coll_url) code = response.status_code expected = 404 self.assertEqual( code, expected, 'Expected %s but returned %s for %s (nonexistent object)' % (expected, code, view_coll_url)) def test_view_members(self): # collection view should include brief listing of items that belong to the collection # use mock objects to test collection member view testcoll = Mock(spec=CollectionObject, name='MockCollectionObject') testcoll.pid = 'coll:1' testcoll.label.return_value = 'mock collection' testcoll.exists = True file1 = Mock(spec=FileObject, name='MockDigitalObject') file1.pid = 'file:1' file1.label = 'One Fish' file2 = Mock(spec=FileObject, name='MockDigitalObject') file2.pid = 'file:2' file2.label = 'Two Fish' testcoll.members = (file1, file2) # django templates recognize Mock objects as callables; work around that # by setting the objects to return themselves when called testcoll.return_value = testcoll file1.return_value = file1 file2.return_value = file2 # patch the repository class to return the mock object instead of a real one with patch.object(Repository, 'get_object', new=Mock(return_value=testcoll)): response = self.client.get(self.view_coll_url) code = response.status_code expected = 200 self.assertEqual( code, expected, 'Expected %s but returned %s for %s as AnonymousUser' % (expected, code, self.view_coll_url)) # FIXME: works in django 1.2.5, not in django 1.3 # member items should be listed self.assertContains( response, file1.label, msg_prefix= 'collection view should include first member item label') self.assertContains( response, file2.label, msg_prefix= 'collection view should include second member item label') self.assertContains( response, reverse('file:view', kwargs={'pid': file1.pid}), msg_prefix= 'collection view should include link to view first member item' ) self.assertContains( response, reverse('file:view', kwargs={'pid': file2.pid}), msg_prefix= 'collection view should include link to view second member item' ) self.assertNotContains( response, reverse('file:edit', kwargs={'pid': file1.pid}), msg_prefix= 'collection view should include link to edit first member item (not repo editor)' ) self.assertNotContains( response, reverse('file:edit', kwargs={'pid': file2.pid}), msg_prefix= 'collection view should include link to edit second member item (not repo editor)' ) # log in as repo editor - should also see item edit links self.client.post(settings.LOGIN_URL, ADMIN_CREDENTIALS) with patch.object(Repository, 'get_object', new=Mock(return_value=testcoll)): response = self.client.get(self.view_coll_url) self.assertContains( response, reverse('file:edit', kwargs={'pid': file1.pid}), msg_prefix= 'collection view should include link to edit first member item (repo editor)' ) self.assertContains( response, reverse('file:edit', kwargs={'pid': file2.pid}), msg_prefix= 'collection view should include link to edit second member item (repo editor)' ) def test_list(self): # test listing collections # not logged in accessible to anyone response = self.client.get(self.list_coll_url) code = response.status_code expected = 200 self.assertEqual( code, expected, 'Expected %s but returned %s for %s as AnonymousUser' % (expected, code, self.view_coll_url)) #when not logged in no edit link should be present self.assertNotContains( response, reverse('collection:edit', kwargs={'pid': self.obj.pid}), msg_prefix= 'collection list should not include edit link for non repo editor') #label should be displayed self.assertContains( response, self.obj.label, msg_prefix='response should include title of collection object') #login to get edit link self.client.post(settings.LOGIN_URL, ADMIN_CREDENTIALS) response = self.client.get(self.view_coll_url) code = response.status_code expected = 200 self.assertEqual( code, expected, 'Expected %s but returned %s for %s as logged in non-repo editor' % (expected, code, self.view_coll_url)) #now edit link is available self.assertContains( response, reverse('collection:edit', kwargs={'pid': self.obj.pid}), msg_prefix= 'collection list should include edit link for repo editor') def test_raw_xml_datastreams(self): # check that raw datastream views are configured correctly dc_url = reverse('collection:raw-ds', kwargs={ 'pid': self.obj.pid, 'dsid': 'DC' }) relsext_url = reverse('collection:raw-ds', kwargs={ 'pid': self.obj.pid, 'dsid': 'RELS-EXT' }) response = self.client.get(dc_url) code = response.status_code expected = 200 self.assertEqual( code, expected, 'Expected %s but returned %s for GET %s (raw DC)' % (expected, code, dc_url)) self.assertEqual(response.content, self.obj.dc.content.serialize(pretty=True)) response = self.client.get(relsext_url) code = response.status_code expected = 200 self.assertEqual( code, expected, 'Expected %s but returned %s for GET %s (raw RELS-EXT)' % (expected, code, relsext_url)) # may not serialize exactly the same every time # simple check to make sure we're getting rdf that looks corretc self.assert_('<rdf:RDF' in response.content) self.assert_(self.obj.pid in response.content)
def test_index_details(self): repo = Repository() for pid in self.pids: repo.purge_object(pid) #Test with no settings set. self.assertRaises(AttributeError, index_config, self.request) # Test with only the allowed SOLR url set. self.assertRaises(AttributeError, index_config, self.request) # Test with this IP not allowed to hit the service. #settings.EUL_INDEXER_ALLOWED_IPS = ['0.13.23.134'] with override_settings(EUL_INDEXER_ALLOWED_IPS=['0.13.23.134']): response = index_config(self.request) expected, got = 403, response.status_code self.assertEqual(expected, got, 'Expected %s but returned %s for indexdata/index_details view' \ % (expected, got)) expected, got = 'text/html', response['Content-Type'] self.assertEqual(expected, got, 'Expected %s but returned %s for mimetype on indexdata/index_details view' \ % (expected, got)) # Test with this IP allowed to hit the view. with override_settings( EUL_INDEXER_ALLOWED_IPS=['0.13.23.134', self.request_ip]): response = index_config(self.request) expected, got = 200, response.status_code self.assertEqual(expected, got, 'Expected %s but returned %s for indexdata/index_details view' \ % (expected, got)) expected, got = 'application/json', response['Content-Type'] self.assertEqual(expected, got, 'Expected %s but returned %s for mimetype on indexdata/index_details view' \ % (expected, got)) # load json content so we can inspect the result content = simplejson.loads(response.content) self.assertEqual(TEST_SOLR_URL, content['SOLR_URL']) print '** list of cmodels is ', content['CONTENT_MODELS'] print '*** looking for ', SimpleObject.CONTENT_MODELS self.assert_( SimpleObject.CONTENT_MODELS in content['CONTENT_MODELS']) self.assert_(LessSimpleDigitalObject.CONTENT_MODELS in content['CONTENT_MODELS']) self.assert_( ContentModel.CONTENT_MODELS not in content['CONTENT_MODELS'], 'Fedora system content models should not be included in indexed cmodels by default' ) # Test with the "ANY" setting for allowed IPs with override_settings(INDEXER_ALLOWED_IPS='ANY'): response = index_config(self.request) expected, got = 200, response.status_code self.assertEqual(expected, got, 'Expected %s but returned %s for indexdata/index_details view' \ % (expected, got)) # Test with 'EUL_INDEXER_CONTENT_MODELS' setting configured to override autodetect. with override_settings(EUL_INDEXER_CONTENT_MODELS=[[ 'content-model_1', 'content-model_2' ], ['content-model_3']]): response = index_config(self.request) expected, got = 200, response.status_code self.assertEqual(expected, got, 'Expected %s but returned %s for indexdata/index_details view' \ % (expected, got)) expected, got = 'application/json', response['Content-Type'] self.assertEqual(expected, got, 'Expected %s but returned %s for mimetype on indexdata/index_details view' \ % (expected, got)) # load json content so we can inspect the result content = simplejson.loads(response.content) self.assertEqual(settings.EUL_INDEXER_CONTENT_MODELS, content['CONTENT_MODELS'])
class FileViewsTest(TestCase): fixtures = ['users'] # re-using collection users fixture & credentials # repository with test credentials for loading & removing test objects # DON'T instantiate this at load time, since Fedora Test settings are not yet switched repo_admin = None ingest_fname = os.path.join(settings.BASE_DIR, 'file', 'fixtures', 'hello.txt') ingest_md5sum = '746308829575e17c3331bbcb00c0898b' # md5sum of hello.txt image_fname = os.path.join(settings.BASE_DIR, 'file', 'fixtures', 'test.jpg') image_md5sum = 'ef7397e4bde82e558044458045bba96a' # md5sum of test.jpeg ingest_url = reverse('file:ingest') # required django form management metadata for formsets on DC edit form edit_mgmt_data = {} for field in ['creator', 'contributor', 'coverage', 'relation', 'subject']: edit_mgmt_data['%s_list-MAX_NUM_FORMS' % field] = '' edit_mgmt_data['%s_list-INITIAL_FORMS' % field] = 0 edit_mgmt_data['%s_list-TOTAL_FORMS' % field] = 0 def setUp(self): # instantiate repo_admin the first time we run, after the test settings are in place if self.repo_admin is None: self.repo_admin = Repository(username=getattr(settings, 'FEDORA_TEST_USER', None), password=getattr(settings, 'FEDORA_TEST_PASSWORD', None)) self.client = Client() # create a file object to edit with open(self.ingest_fname) as ingest_f: self.obj = self.repo_admin.get_object(type=FileObject) self.obj.dc.content.title = self.obj.label = 'Test file object' self.obj.dc.content.date = '2011' self.obj.master.content = ingest_f self.obj.master.label = 'hello-world.txt' self.obj.master.checksum = self.ingest_md5sum self.obj.save() self.edit_url = reverse('file:edit', kwargs={'pid': self.obj.pid}) self.download_url = reverse('file:download', kwargs={'pid': self.obj.pid}) self.view_url = reverse('file:view', kwargs={'pid': self.obj.pid}) # create a image object for testing with open(self.image_fname) as ingest_f: self.imgobj = self.repo_admin.get_object(type=FileObject) self.imgobj.dc.content.title = self.imgobj.label = 'Test file object' self.imgobj.master.content = ingest_f self.imgobj.master.label = 'test.jpg' self.imgobj.master.checksum = self.image_md5sum self.imgobj.save() self.pids = [self.obj.pid, self.imgobj.pid] def tearDown(self): for pid in self.pids: self.repo_admin.purge_object(pid) # ingest def test_get_ingest_form(self): # not logged in - should redirect to login page response = self.client.get(self.ingest_url) code = response.status_code expected = 302 self.assertEqual(code, expected, 'Expected %s but returned %s for %s as AnonymousUser' % (expected, code, self.ingest_url)) # logged in as user without required permissions - should 403 self.client.login(**NONADMIN_CREDENTIALS) response = self.client.get(self.ingest_url) code = response.status_code expected = 403 self.assertEqual(code, expected, 'Expected %s but returned %s for GET %s as logged in non-repo editor' % (expected, code, self.ingest_url)) # log in as repository editor self.client.post(settings.LOGIN_URL, ADMIN_CREDENTIALS) # on GET, form should be displayed response = self.client.get(self.ingest_url) self.assertEqual(response.status_code, 200) self.assertTrue(isinstance(response.context['form'], IngestForm)) # collection URI passed in via GET should be pre-selected collections = IngestForm().fields['collection'].choices collection_tuple = collections[1] # 0 is blank. 1 is the first non-blank one collection_uri = collection_tuple[0] response = self.client.get(self.ingest_url, {'collection': collection_uri}) self.assertEqual(collection_uri, response.context['form'].initial['collection'], 'collection URI specified in GET url parameter should be set as initial value') def test_incomplete_ingest_form(self): # not logged in - should redirect to login page response = self.client.post(self.ingest_url) code = response.status_code expected = 302 self.assertEqual(code, expected, 'Expected %s but returned %s for POST %s as AnonymousUser' % (expected, code, self.ingest_url)) # logged in as user without required permissions - should 403 self.client.login(**NONADMIN_CREDENTIALS) response = self.client.post(self.ingest_url) code = response.status_code expected = 403 self.assertEqual(code, expected, 'Expected %s but returned %s for POST %s as logged in non-repo editor' % (expected, code, self.ingest_url)) # log in as repository editor for normal behavior self.client.post(settings.LOGIN_URL, ADMIN_CREDENTIALS) # on POST with incomplete data, should be rejected response = self.client.post(self.ingest_url, { 'collection': 'info:fedora/example:42', }) self.assertTrue(isinstance(response.context['form'], IngestForm)) self.assertContains(response, 'This field is required') def test_correct_ingest_form(self): # log in as repository editor for normal behavior self.client.post(settings.LOGIN_URL, ADMIN_CREDENTIALS) # first find a valid collection collections = IngestForm().fields['collection'].choices collection_tuple = collections[1] # 0 is blank. 1 is the first non-blank one collection_uri = collection_tuple[0] # log in self.client.post(settings.LOGIN_URL, ADMIN_CREDENTIALS) # on POST, ingest object with open(self.ingest_fname) as ingest_f: response = self.client.post(self.ingest_url, { 'collection': collection_uri, 'file': ingest_f, }, follow=True) self.assertEqual(len(response.redirect_chain), 1) self.assertEqual(response.redirect_chain[0][1], 303) self.assertEqual(response.status_code, 200) messages = [ str(msg) for msg in response.context['messages'] ] self.assertTrue('Successfully ingested' in messages[0]) pid = re.search('<b>(.*)</b>', messages[0]).group(1) self.pids.append(pid) # inspect the ingested object new_obj = self.repo_admin.get_object(pid, type=FileObject) self.assertTrue(new_obj.has_requisite_content_models) statement = (new_obj.uriref, relsext.isMemberOfCollection, URIRef(collection_uri)) self.assertTrue(statement in new_obj.rels_ext.content, msg='RELS-EXT should have collection statement') self.assertEqual('hello.txt', new_obj.label, msg='filename should be set as preliminary object label') self.assertEqual('hello.txt', new_obj.dc.content.title, msg='filename should be set as preliminary dc:title') self.assertEqual('hello.txt', new_obj.master.label, msg='filename should be set as master datastream label') with open(self.ingest_fname) as ingest_f: self.assertEqual(new_obj.master.content.read(), ingest_f.read()) # confirm that current site user appears in fedora audit trail xml, uri = new_obj.api.getObjectXML(pid) self.assert_('<audit:responsibility>%s</audit:responsibility>' % \ ADMIN_CREDENTIALS['username'] in xml) # supported image file should be ingested as ImageObject with open(self.image_fname) as ingest_f: response = self.client.post(self.ingest_url, { 'collection': collection_uri, 'file': ingest_f, }, follow=True) self.assertEqual(response.status_code, 200) messages = [ str(msg) for msg in response.context['messages'] ] self.assertTrue('Successfully ingested' in messages[0]) pid = re.search('<b>(.*)</b>', messages[0]).group(1) self.pids.append(pid) # check that the object was ingested as an Image img_obj = self.repo_admin.get_object(pid, type=ImageObject) self.assertTrue(img_obj.has_requisite_content_models) # edit metadata def test_get_edit_form(self): # not logged in - should redirect to login page response = self.client.get(self.edit_url) code = response.status_code expected = 302 self.assertEqual(code, expected, 'Expected %s but returned %s for GET %s as AnonymousUser' % (expected, code, self.edit_url)) # logged in as user without required permissions - should 403 self.client.login(**NONADMIN_CREDENTIALS) response = self.client.get(self.edit_url) code = response.status_code expected = 403 self.assertEqual(code, expected, 'Expected %s but returned %s for GET %s as logged in non-repo editor' % (expected, code, self.edit_url)) # log in as repository editor self.client.post(settings.LOGIN_URL, ADMIN_CREDENTIALS) # on GET, form should be displayed with object data pre-populated response = self.client.get(self.edit_url) self.assertEqual(response.status_code, 200) self.assertTrue(isinstance(response.context['form'], DublinCoreEditForm)) self.assertContains(response, self.obj.label, msg_prefix='edit form should include object label') self.assertContains(response, self.obj.dc.content.date, msg_prefix='edit form should include DC content such as date') # enable_oai should be false self.assertFalse(response.context['form']['enable_oai'].value()) # master ds label should be set as filename self.assertEqual(self.obj.master.label, response.context['form']['file_name'].value(), 'master datastream label should be pre-populated as filename in the form') # enable_oai set based on presence of oai id self.obj.oai_id = 'oai:foo' self.obj.save() response = self.client.get(self.edit_url) # enable_oai should be true self.assertTrue(response.context['form']['enable_oai'].value()) def test_edit_invalid_form(self): self.client.post(settings.LOGIN_URL, ADMIN_CREDENTIALS) # POST invalid data (missing required title field) data = self.edit_mgmt_data.copy() data.update({'descrition': 'test'}) response = self.client.post(self.edit_url, data) self.assertTrue(isinstance(response.context['form'], DublinCoreEditForm)) self.assertContains(response, 'This field is required') def test_edit_valid_form(self): # not logged in - should redirect to login page response = self.client.post(self.edit_url) code = response.status_code expected = 302 self.assertEqual(code, expected, 'Expected %s but returned %s for POST %s as AnonymousUser' % (expected, code, self.edit_url)) # logged in as user without required permissions - should 403 self.client.login(**NONADMIN_CREDENTIALS) response = self.client.post(self.edit_url) code = response.status_code expected = 403 self.assertEqual(code, expected, 'Expected %s but returned %s for POST %s as logged in non-repo editor' % (expected, code, self.edit_url)) self.client.post(settings.LOGIN_URL, ADMIN_CREDENTIALS) # valid form new_data = self.edit_mgmt_data.copy() subjects = ['test', 'repositories'] new_data.update({'title': 'updated file object', 'description': 'test content', 'creator_list-TOTAL_FORMS': 1, 'creator_list-0-val': 'genrepo', 'subject_list-TOTAL_FORMS': 2, 'subject_list-0-val': subjects[0], 'subject_list-1-val': subjects[1], 'enable_oai': True, 'file_name': 'hello.txt' }) response = self.client.post(self.edit_url, new_data, follow=True) messages = [ str(msg) for msg in response.context['messages'] ] self.assertTrue('Successfully updated' in messages[0]) # inspect the updated object updated_obj = self.repo_admin.get_object(self.obj.pid, type=FileObject) self.assertEqual(new_data['title'], updated_obj.label, msg='posted title should be set as object label; expected %s, got %s' % \ (new_data['title'], updated_obj.label)) self.assertEqual(new_data['title'], updated_obj.dc.content.title, msg='posted title should be set as dc:title; expected %s, got %s' % \ (new_data['title'], updated_obj.dc.content.title)) self.assertEqual(new_data['description'], updated_obj.dc.content.description, msg='posted description should be set as dc:description; expected %s, got %s' % \ (new_data['description'], updated_obj.dc.content.description)) self.assertEqual(new_data['creator_list-0-val'], updated_obj.dc.content.creator, msg='posted creator should be set as dc:creator; expected %s, got %s' % \ (new_data['creator_list-0-val'], updated_obj.dc.content.creator)) self.assertEqual(2, len(updated_obj.dc.content.subject_list), msg='expected 2 subjects after posting 2 subject_list values, got %d' % \ len(updated_obj.dc.content.subject_list)) self.assertEqual(subjects, updated_obj.dc.content.subject_list) self.assertNotEqual(None, updated_obj.oai_id) self.assertEqual(new_data['file_name'], updated_obj.master.label, msg='posted file name should be set as master datastream label; expected %s, got %s' % \ (new_data['file_name'], updated_obj.master.label)) # remove oai item id new_data.update({'enable_oai': False}) response = self.client.post(self.edit_url, new_data, follow=True) messages = [ str(msg) for msg in response.context['messages'] ] self.assertTrue('Successfully updated' in messages[0]) # inspect the updated object updated_obj = self.repo_admin.get_object(self.obj.pid, type=FileObject) self.assertEqual(None, updated_obj.oai_id) def test_edit_save_errors(self): self.client.post(settings.LOGIN_URL, ADMIN_CREDENTIALS) data = self.edit_mgmt_data.copy() data.update({'title': 'foo', 'description': 'bar', 'creator': 'baz', 'file_name': 'foo.txt'}) # simulate fedora errors with mock objects testobj = Mock(spec=FileObject, name='MockDigitalObject') # django templates recognize this as a callable; set to return itself when called testobj.return_value = testobj # create a Mock object, but use a DublinCore instance for xmlobjectform to inspect testobj.dc.content = DublinCore() testobj.pid = 'pid:1' # required for url generation # Create a RequestFailed exception to simulate Fedora error # - eulcore.fedora wrap around an httplib response err_resp = Mock() err_resp.status = 500 err_resp.read.return_value = 'error message' # generate Fedora error on object save testobj.save.side_effect = RequestFailed(err_resp) # 500 error / request failed # patch the repository class to return the mock object instead of a real one #with patch.object(Repository, 'get_object', new=Mock(return_value=testobj)): with patch('genrepo.file.views.init_by_cmodel', new=Mock(return_value=testobj)): response = self.client.post(self.edit_url, data, follow=True) expected, code = 500, response.status_code self.assertEqual(code, expected, 'Expected %s but returned %s for %s (Fedora 500 error)' % (expected, code, self.edit_url)) messages = [ str(msg) for msg in response.context['messages'] ] self.assert_('error communicating with the repository' in messages[0]) # update the mock object to generate a permission denied error err_resp.status = 401 err_resp.read.return_value = 'denied' # generate Fedora error on object save testobj.save.side_effect = PermissionDenied(err_resp) # 401 error - permission denied #with patch.object(Repository, 'get_object', new=Mock(return_value=testobj)): with patch('genrepo.file.views.init_by_cmodel', new=Mock(return_value=testobj)): response = self.client.post(self.edit_url, data, follow=True) expected, code = 401, response.status_code self.assertEqual(code, expected, 'Expected %s but returned %s for %s (Fedora 401 error)' % (expected, code, self.edit_url)) messages = [ str(msg) for msg in response.context['messages'] ] self.assert_("You don't have permission to modify this object" in messages[0]) def test_download_master(self): response = self.client.get(self.download_url) code = response.status_code expected = 200 self.assertEqual(code, expected, 'Expected %s but returned %s for GET %s as AnonymousUser' % (expected, code, self.download_url)) expected = 'attachment; filename=%s' % self.obj.master.label self.assertEqual(response['Content-Disposition'], expected, "Expected '%s' but returned '%s' for %s content disposition" % \ (expected, response['Content-Disposition'], self.download_url)) with open(self.ingest_fname) as ingest_f: self.assertEqual(ingest_f.read(), response.content, 'download response content should be equivalent to file ingested as master datastream') # test image object (different datastraem) img_download_url = reverse('file:download', kwargs={'pid': self.imgobj.pid}) response = self.client.get(img_download_url) code = response.status_code expected = 200 self.assertEqual(code, expected, 'Expected %s but returned %s for GET %s as AnonymousUser' % (expected, code, img_download_url)) expected = 'attachment; filename=%s' % self.imgobj.master.label self.assertEqual(response['Content-Disposition'], expected, "Expected '%s' but returned '%s' for %s content disposition" % \ (expected, response['Content-Disposition'], img_download_url)) with open(self.image_fname) as ingest_f: self.assertEqual(ingest_f.read(), response.content, 'download response content should be equivalent to file ingested as master datastream') # errors not tested here because they should be handled by eulcore view def test_view_metadata_min(self): # view metadata - minimal fields present response = self.client.get(self.view_url) code = response.status_code expected = 200 self.assertEqual(code, expected, 'Expected %s but returned %s for GET %s as AnonymousUser' % (expected, code, self.view_url)) dc = self.obj.dc.content self.assertContains(response, dc.title) self.assertNotContains(response, 'Creator', msg_prefix='metadata view should not include creator when not set in dc') self.assertNotContains(response, 'Contributor', msg_prefix='metadata view should not include contributor when not set in dc') self.assertNotContains(response, 'Coverage:', msg_prefix='metadata view should not include coverage when not set in dc') self.assertNotContains(response, 'Language:', msg_prefix='metadata view should not include language when not set in dc') self.assertNotContains(response, 'Publisher:', msg_prefix='metadata view should not include publisher when not set in dc') self.assertNotContains(response, 'Source', msg_prefix='metadata view should not include source when not set in dc') self.assertNotContains(response, 'Type:', msg_prefix='metadata view should not include type when not set in dc') self.assertNotContains(response, 'Format:', msg_prefix='metadata view should not include format when not set in dc') self.assertContains(response, 'Date:') self.assertContains(response, dc.date) # check for links to raw datastreams self.assertContains(response, reverse('file:raw-ds', kwargs={'pid': self.obj.pid, 'dsid': 'DC'}), msg_prefix='metadata view should link to raw DC view') self.assertContains(response, reverse('file:raw-ds', kwargs={'pid': self.obj.pid, 'dsid': 'RELS-EXT'}), msg_prefix='metadata view should link to raw RELS-EXT view') def test_view_metadata_full(self): # update test object metadata to test template display with full fields self.obj.dc.content.description = 'Some explanatory text' self.obj.dc.content.creator_list = ['You', 'Me'] self.obj.dc.content.contributor_list = ['Them'] self.obj.dc.content.coverage_list = ['20th Century', 'Earth'] self.obj.dc.content.language = 'English' self.obj.dc.content.publisher = 'EUL' self.obj.dc.content.relation = 'Part of Collection Foo' self.obj.dc.content.source = 'the ether' self.obj.dc.content.subject_list = ['testing', 'generals', 'repositories'] self.obj.dc.content.type = 'Text' self.obj.dc.content.format = 'text/plain' self.obj.dc.content.identifier = 'foo1' self.obj.save('adding DC content to test metadata view') response = self.client.get(self.view_url) code = response.status_code expected = 200 self.assertEqual(code, expected, 'Expected %s but returned %s for GET %s as AnonymousUser' % (expected, code, self.view_url)) dc = self.obj.dc.content self.assertContains(response, dc.description) self.assertContains(response, 'Creators:') self.assertContains(response, dc.creator_list[0]) self.assertContains(response, dc.creator_list[1]) self.assertContains(response, 'Contributor:') self.assertContains(response, dc.contributor_list[0]) self.assertContains(response, 'Coverage:') self.assertContains(response, dc.coverage_list[0]) self.assertContains(response, dc.coverage_list[1]) self.assertContains(response, 'Language:') self.assertContains(response, dc.language) self.assertContains(response, 'Publisher:') self.assertContains(response, dc.publisher) self.assertContains(response, 'Source:') self.assertContains(response, dc.source) self.assertContains(response, 'Type:') self.assertContains(response, dc.type) self.assertContains(response, 'Format:') self.assertContains(response, dc.format) self.assertContains(response, 'Date:') self.assertContains(response, dc.date) def test_view_metadata_notfound(self): view_url = reverse('file:view', kwargs={'pid': 'bogus:123'}) response = self.client.get(view_url) code = response.status_code expected = 404 self.assertEqual(code, expected, 'Expected %s but returned %s for GET %s (invalid pid) as AnonymousUser' % (expected, code, view_url)) def test_raw_xml_datastreams(self): # check that raw datastream views are configured correctly dc_url = reverse('file:raw-ds', kwargs={'pid': self.obj.pid, 'dsid': 'DC'}) relsext_url = reverse('file:raw-ds', kwargs={'pid': self.obj.pid, 'dsid': 'RELS-EXT'}) response = self.client.get(dc_url) code = response.status_code expected = 200 self.assertEqual(code, expected, 'Expected %s but returned %s for GET %s (raw DC)' % (expected, code, dc_url)) self.assertEqual(response.content, self.obj.dc.content.serialize(pretty=True)) response = self.client.get(relsext_url) code = response.status_code expected = 200 self.assertEqual(code, expected, 'Expected %s but returned %s for GET %s (raw RELS-EXT)' % (expected, code, relsext_url)) # may not serialize exactly the same every time # simple check to make sure we're getting rdf that looks corretc self.assert_('<rdf:RDF' in response.content) self.assert_(self.obj.pid in response.content)
def tearDownModule(): global postcards repo = Repository() for p in postcards: repo.purge_object(p.pid)
pids_to_delete.append(etd.pid) pid_report.append(etd.related()) for r in etd.get_related_pids(): related = etd = repo.get_object(r, type=RelatedRecord) if related.check(): pids_to_delete.append(related.pid) else: print(related.pid + ' is related to multiple ETDs.') pp.pprint(pid_report) if not args['no_action']: print(str(len(pids_to_delete)) + ' will be purged.') raw_input("Press Enter to continue...") for bad_pid in pids_to_delete: # TODO add error handeling for a pid that might have already been deleted. ark = bad_pid.split(':')[1] # Important: we must deactive ark first. Otherwise we'll get a 404 on the uri. #try: # client.update_target(type="ark", noid=ark, active=False) #except requests.exceptions.HTTPError: # pass try: repo.purge_object(bad_pid) except eulfedora.util.RequestFailed: pass
def test_index_details(self): repo = Repository() for pid in self.pids: repo.purge_object(pid) # Test with no settings set. # - should be denied response = index_config(self.request) self.assertEqual( 403, response.status_code, 'index data should be forbidden if no IPs are configured to access' ) # NOTE: these aren't meaningful errors anyway, and settings # are now being pulled from somewhere so they fail # self.assertRaises(AttributeError, index_config, self.request) # Test with only the allowed SOLR url set. # self.assertRaises(AttributeError, index_config, self.request) # Test with this IP not allowed to hit the service. # settings.EUL_INDEXER_ALLOWED_IPS = ['0.13.23.134'] with override_settings(EUL_INDEXER_ALLOWED_IPS=['0.13.23.134']): response = index_config(self.request) expected, got = 403, response.status_code self.assertEqual(expected, got, 'Expected %s but returned %s for indexdata/index_details view' \ % (expected, got)) expected, got = 'text/html', response['Content-Type'] self.assertEqual(expected, got, 'Expected %s but returned %s for mimetype on indexdata/index_details view' \ % (expected, got)) # Test with this IP allowed to hit the view. with override_settings( EUL_INDEXER_ALLOWED_IPS=['0.13.23.134', self.request_ip]): response = index_config(self.request) expected, got = 200, response.status_code self.assertEqual(expected, got, 'Expected %s but returned %s for indexdata/index_details view' \ % (expected, got)) expected, got = 'application/json', response['Content-Type'] self.assertEqual(expected, got, 'Expected %s but returned %s for mimetype on indexdata/index_details view' \ % (expected, got)) # load json content so we can inspect the result content = json.loads(response.content.decode('utf-8')) self.assertEqual(TEST_SOLR_URL, content['SOLR_URL']) self.assert_( SimpleObject.CONTENT_MODELS in content['CONTENT_MODELS']) self.assert_(LessSimpleDigitalObject.CONTENT_MODELS in content['CONTENT_MODELS']) self.assert_( ContentModel.CONTENT_MODELS not in content['CONTENT_MODELS'], 'Fedora system content models should not be included in indexed cmodels by default' ) # Test with the "ANY" setting for allowed IPs with override_settings(EUL_INDEXER_ALLOWED_IPS='ANY'): response = index_config(self.request) expected, got = 200, response.status_code self.assertEqual(expected, got, 'Expected %s but returned %s for indexdata/index_details view' \ % (expected, got)) # Test with 'EUL_INDEXER_CONTENT_MODELS' setting configured to override autodetect. with override_settings(EUL_INDEXER_ALLOWED_IPS='ANY', EUL_INDEXER_CONTENT_MODELS=[[ 'content-model_1', 'content-model_2' ], ['content-model_3']]): response = index_config(self.request) expected, got = 200, response.status_code self.assertEqual(expected, got, 'Expected %s but returned %s for indexdata/index_details view' \ % (expected, got)) expected, got = 'application/json', response['Content-Type'] self.assertEqual(expected, got, 'Expected %s but returned %s for mimetype on indexdata/index_details view' \ % (expected, got)) # load json content so we can inspect the result content = json.loads(response.content.decode('utf-8')) self.assertEqual(settings.EUL_INDEXER_CONTENT_MODELS, content['CONTENT_MODELS'])
def handle(self, *args, **kwargs): verbosity = kwargs.get('verbosity', self.v_normal) repo = Repository() video_duplicates = repo.find_objects(type=Video, label="DELETE") for pid in video_duplicates: repo.purge_object(pid.pid)
class FedoraStorage(base.BaseStorage): """Fedora Commons repository storage.""" configform = ConfigForm defaults = dict( root=getattr(settings, "FEDORA_ROOT", ""), username=getattr(settings, "FEDORA_USER", ""), password=getattr(settings, "FEDORA_PASS", ""), namespace=getattr(settings, "FEDORA_PIDSPACE", ""), image_name=getattr(settings, "FEDORA_IMAGE_NAME", ""), transcript_name=getattr(settings, "FEDORA_TRANSCRIPT_NAME", "") ) def __init__(self, *args, **kwargs): super(FedoraStorage, self).__init__(*args, **kwargs) self.namespace = kwargs["namespace"] self.image_name = kwargs["image_name"] self.thumbnail_name = "THUMBNAIL" self.binary_name = "BINARY" self.script_name = "OCR_SCRIPT" self.transcript_name = kwargs["transcript_name"] self.repo = Repository( root=kwargs["root"], username=kwargs["username"], password=kwargs["password"]) self.model = type("Document", (DigitalObject,), { "default_pidspace": kwargs["namespace"], "FILE_CONTENT_MODEL": "info:fedora/genrepo:File-1.0", "CONTENT_MODELS": ["info:fedora/genrepo:File-1.0"], "image": FileDatastream(self.image_name, "Document image", defaults={ 'versionable': True, }), "binary": FileDatastream(self.binary_name, "Document image binary", defaults={ 'versionable': True, }), "thumbnail": FileDatastream(self.thumbnail_name, "Document image thumbnail", defaults={ 'versionable': True, }), "script": FileDatastream(self.script_name, "OCR Script", defaults={ "versionable": True, }), "transcript": FileDatastream(self.transcript_name, "Document transcript", defaults={ "versionable": True, }), "meta": FileDatastream("meta", "Document metadata", defaults={ "versionable": False, }), }) def read_metadata(self, doc): meta = doc._doc.meta.content if hasattr(meta, "read"): meta = meta.read() if not meta: return {} return dict([v.strip().split("=") for v in \ meta.split("\n") if re.match("^\w+=[^=]+$", v.strip())]) def write_metadata(self, doc, **kwargs): meta = self.read_metadata(doc) meta.update(kwargs) metacontent = [u"%s=%s" % (k, v) for k, v in meta.iteritems()] doc._doc.meta.content = "\n".join(metacontent) def attr_uri(self, doc, attr): """URI for image datastream.""" return "%sobjects/%s/datastreams/%s/content" % ( self.repo.fedora_root, urllib.quote(doc.pid), getattr(self, "%s_name" % attr) ) def document_label(self, doc): """Get the document label.""" return doc._doc.label def document_attr_empty(self, doc, attr): """Check if document attr is empty.""" return getattr(doc._doc, attr).info.size == 0 def document_attr_label(self, doc, attr): """Get label for an image type attribute.""" return getattr(doc._doc, attr).label def document_attr_mimetype(self, doc, attr): """Get mimetype for an image type attribute.""" return getattr(doc._doc, attr).mimetype def document_attr_content_handle(self, doc, attr): """Get content for an image type attribute.""" handle = getattr(doc._doc, attr).content return StringIO() if handle is None else handle def document_metadata(self, doc): """Get document metadata. This currently just exposes the DC stream attributes.""" return self.read_metadata(doc) def _set_document_ds_content(self, doc, dsattr, content): docattr = getattr(doc._doc, dsattr) #checksum = hashlib.md5(content.read()).hexdigest() #content.seek(0) #docattr.checksum = checksum #docattr.checksum_type = "MD5" docattr.content = content def set_document_attr_content(self, doc, attr, content): """Set image content.""" self._set_document_ds_content(doc, attr, content) def set_document_attr_mimetype(self, doc, attr, mimetype): """Set image mimetype.""" getattr(doc._doc, attr).mimetype = mimetype def set_document_attr_label(self, doc, attr, label): """Set image label.""" getattr(doc._doc, attr).label = label def set_document_label(self, doc, label): """Set document label.""" doc._doc.label = label def set_document_metadata(self, doc, **kwargs): """Set arbitrary document metadata.""" self.write_metadata(doc, kwargs) def save_document(self, doc): """Save document.""" doc._doc.save() def create_document(self, label): """Get a new document object""" dobj = self.repo.get_object(type=self.model) dobj.label = label dobj.meta.label = "Document Metadata" dobj.meta.mimetype = "text/plain" doc = FedoraDocument(dobj, self) return doc def get(self, pid): """Get an object by id.""" doc = self.repo.get_object(pid, type=self.model) if doc: return FedoraDocument(doc, self) def delete(self, doc, msg=None): """Delete an object.""" self.repo.purge_object(doc.pid, log_message=msg) def list(self, namespace=None): """List documents in the repository.""" ns = namespace if namespace is not None else self.namespace return [FedoraDocument(d, self) \ for d in self.repo.find_objects("%s:*" % ns, type=self.model)] def list_pids(self, namespace=None): """List of pids. This unforunately involves calling list(), so it not a quicker alternative.""" return [doc.pid for doc in self.list()]
def test_index_details(self): repo = Repository() for pid in self.pids: repo.purge_object(pid) #Test with no settings set. self.assertRaises(AttributeError, index_config, self.request) #Test with only the allowed SOLR url set. solr_url = 'http://localhost:5555' settings.SOLR_SERVER_URL = solr_url self.assertRaises(AttributeError, index_config, self.request) #Test with this IP not allowed to hit the service. settings.EUL_INDEXER_ALLOWED_IPS = ['0.13.23.134'] response = index_config(self.request) expected, got = 403, response.status_code self.assertEqual(expected, got, 'Expected %s but returned %s for indexdata/index_details view' \ % (expected, got)) expected, got = 'text/html', response['Content-Type'] self.assertEqual(expected, got, 'Expected %s but returned %s for mimetype on indexdata/index_details view' \ % (expected, got)) #Test with this IP allowed to hit the view. settings.EUL_INDEXER_ALLOWED_IPS = ['0.13.23.134', self.request_ip] response = index_config(self.request) expected, got = 200, response.status_code self.assertEqual(expected, got, 'Expected %s but returned %s for indexdata/index_details view' \ % (expected, got)) expected, got = 'application/json', response['Content-Type'] self.assertEqual(expected, got, 'Expected %s but returned %s for mimetype on indexdata/index_details view' \ % (expected, got)) # load json content so we can inspect the result content = simplejson.loads(response.content) self.assertEqual(solr_url, content['SOLR_URL']) self.assert_(SimpleDigitalObject.CONTENT_MODELS in content['CONTENT_MODELS']) self.assert_(LessSimpleDigitalObject.CONTENT_MODELS in content['CONTENT_MODELS']) self.assert_(ContentModel.CONTENT_MODELS not in content['CONTENT_MODELS'], 'Fedora system content models should not be included in indexed cmodels by default') #Test with the "ANY" setting for allowed IPs settings.INDEXER_ALLOWED_IPS = 'ANY' response = index_config(self.request) expected, got = 200, response.status_code self.assertEqual(expected, got, 'Expected %s but returned %s for indexdata/index_details view' \ % (expected, got)) #Test with 'EUL_INDEXER_CONTENT_MODELS' setting configured to override autodetect. settings.EUL_INDEXER_CONTENT_MODELS = [['content-model_1', 'content-model_2'], ['content-model_3']] response = index_config(self.request) expected, got = 200, response.status_code self.assertEqual(expected, got, 'Expected %s but returned %s for indexdata/index_details view' \ % (expected, got)) expected, got = 'application/json', response['Content-Type'] self.assertEqual(expected, got, 'Expected %s but returned %s for mimetype on indexdata/index_details view' \ % (expected, got)) # load json content so we can inspect the result content = simplejson.loads(response.content) self.assertEqual(settings.EUL_INDEXER_CONTENT_MODELS, content['CONTENT_MODELS'])