def testModelSaveHooks(self): """ This tests the general correctness of the model save hooks """ def preSave(event): count["pre"] += 1 def createdSave(event): count["created"] += 1 def postSave(event): count["post"] += 1 count = collections.defaultdict(int) with events.bound("model.user.save.created", "test", createdSave): user = self.model("user").createUser( login="******", password="******", firstName="A", lastName="A", email="*****@*****.**" ) self.assertEqual(count["created"], 1) count = collections.defaultdict(int) with events.bound("model.user.save", "test", preSave), events.bound( "model.user.save.after", "test", postSave ): user = self.model("user").save(user, triggerEvents=False) self.assertEqual(count["pre"], 0) self.assertEqual(count["created"], 0) self.assertEqual(count["post"], 0) count = collections.defaultdict(int) self.model("user").save(user) self.assertEqual(count["pre"], 1) self.assertEqual(count["created"], 0) self.assertEqual(count["post"], 1)
def testModelSaveHooks(self): """ This tests the general correctness of the model save hooks """ def preSave(event): count['pre'] += 1 def createdSave(event): count['created'] += 1 def postSave(event): count['post'] += 1 count = collections.defaultdict(int) with events.bound('model.user.save.created', 'test', createdSave): user = User().createUser(login='******', password='******', firstName='A', lastName='A', email='*****@*****.**') self.assertEqual(count['created'], 1) count = collections.defaultdict(int) with events.bound('model.user.save', 'test', preSave), \ events.bound('model.user.save.after', 'test', postSave): user = User().save(user, triggerEvents=False) self.assertEqual(count['pre'], 0) self.assertEqual(count['created'], 0) self.assertEqual(count['post'], 0) count = collections.defaultdict(int) User().save(user) self.assertEqual(count['pre'], 1) self.assertEqual(count['created'], 0) self.assertEqual(count['post'], 1)
def testModelSaveHooks(self): """ This tests the general correctness of the model save hooks """ def preSave(event): count['pre'] += 1 def createdSave(event): count['created'] += 1 def postSave(event): count['post'] += 1 count = collections.defaultdict(int) with events.bound('model.user.save.created', 'test', createdSave): user = User().createUser( login='******', password='******', firstName='A', lastName='A', email='*****@*****.**') self.assertEqual(count['created'], 1) count = collections.defaultdict(int) with events.bound('model.user.save', 'test', preSave), \ events.bound('model.user.save.after', 'test', postSave): user = User().save(user, triggerEvents=False) self.assertEqual(count['pre'], 0) self.assertEqual(count['created'], 0) self.assertEqual(count['post'], 0) count = collections.defaultdict(int) User().save(user) self.assertEqual(count['pre'], 1) self.assertEqual(count['created'], 0) self.assertEqual(count['post'], 1)
def testValidateCustomStrStatus(self): job = self.jobModel.createJob(title='test', type='x', user=self.users[0]) def validateStatus(event): states = ['a', 'b', 'c'] if event.info in states: event.preventDefault().addResponse(True) def validTransitions(event): if event.info['status'] == 'a': event.preventDefault().addResponse([JobStatus.INACTIVE]) with self.assertRaises(ValidationException): self.jobModel.updateJob(job, status='a') with events.bound('jobs.status.validate', 'test', validateStatus), \ events.bound('jobs.status.validTransitions', 'test', validTransitions): self.jobModel.updateJob(job, status='a') self.assertEqual(job['status'], 'a') with self.assertRaises(ValidationException), \ events.bound('jobs.status.validate', 'test', validateStatus): self.jobModel.updateJob(job, status='foo')
def testValidateCustomStrStatus(self): jobModel = self.model('job', 'jobs') job = jobModel.createJob(title='test', type='x', user=self.users[0]) def validateStatus(event): states = ['a', 'b', 'c'] if event.info in states: event.preventDefault().addResponse(True) def validTransitions(event): if event.info['status'] == 'a': event.preventDefault().addResponse([JobStatus.INACTIVE]) with self.assertRaises(ValidationException): jobModel.updateJob(job, status='a') with events.bound('jobs.status.validate', 'test', validateStatus), \ events.bound('jobs.status.validTransitions', 'test', validTransitions): jobModel.updateJob(job, status='a') self.assertEqual(job['status'], 'a') with self.assertRaises(ValidationException), \ events.bound('jobs.status.validate', 'test', validateStatus): jobModel.updateJob(job, status='foo')
def testDeleteFolder(self): cbInfo = {} # Hook into model deletion with kwargs event to test it def cb(event): cbInfo["kwargs"] = event.info["kwargs"] cbInfo["doc"] = event.info["document"] with events.bound("model.folder.remove_with_kwargs", "test", cb): # Requesting with no path should fail resp = self.request(path="/folder", method="DELETE", user=self.admin) self.assertStatus(resp, 400) # Grab one of the user's top level folders folders = self.model("folder").childFolders( parent=self.admin, parentType="user", user=self.admin, limit=1, sort=[("name", SortDir.DESCENDING)] ) folderResp = six.next(folders) # Add a subfolder and an item to that folder subfolder = self.model("folder").createFolder(folderResp, "sub", parentType="folder", creator=self.admin) item = self.model("item").createItem("item", creator=self.admin, folder=subfolder) self.assertTrue("_id" in subfolder) self.assertTrue("_id" in item) # Delete the folder resp = self.request( path="/folder/%s" % folderResp["_id"], method="DELETE", user=self.admin, params={"progress": "true"} ) self.assertStatusOk(resp) # Make sure the folder, its subfolder, and its item were all deleted folder = self.model("folder").load(folderResp["_id"], force=True) subfolder = self.model("folder").load(subfolder["_id"], force=True) item = self.model("item").load(item["_id"]) self.assertEqual(folder, None) self.assertEqual(subfolder, None) self.assertEqual(item, None) # Make sure progress record exists and that it is set to expire soon notifs = list(self.model("notification").get(self.admin)) self.assertEqual(len(notifs), 1) self.assertEqual(notifs[0]["type"], "progress") self.assertEqual(notifs[0]["data"]["state"], ProgressState.SUCCESS) self.assertEqual(notifs[0]["data"]["title"], "Deleting folder Public") self.assertEqual(notifs[0]["data"]["message"], "Done") self.assertEqual(notifs[0]["data"]["total"], 3) self.assertEqual(notifs[0]["data"]["current"], 3) self.assertTrue(notifs[0]["expires"] < datetime.datetime.utcnow() + datetime.timedelta(minutes=1)) # Make sure our event handler was called with expected args self.assertTrue("kwargs" in cbInfo) self.assertTrue("doc" in cbInfo) self.assertTrue("progress" in cbInfo["kwargs"]) self.assertEqual(cbInfo["doc"]["_id"], folderResp["_id"])
def testSynchronousEvents(self): name, failname = '_test.event', '_test.failure' handlerName = '_test.handler' with events.bound(name, handlerName, self._increment), \ events.bound(failname, handlerName, self._raiseException): # Make sure our exception propagates out of the handler try: events.trigger(failname) self.assertTrue(False) except Exception as e: self.assertEqual(e.args[0], 'Failure condition') # Bind an event to increment the counter self.assertEqual(self.ctr, 0) event = events.trigger(name, {'amount': 2}) self.assertEqual(self.ctr, 2) self.assertTrue(event.propagate) self.assertFalse(event.defaultPrevented) self.assertEqual(event.responses, []) # The event should still be bound here if another handler unbinds events.unbind(name, 'not the handler name') events.trigger(name, {'amount': 2}) self.assertEqual(self.ctr, 4) # Actually unbind the event, by going out of scope of "bound" events.trigger(name, {'amount': 2}) self.assertEqual(self.ctr, 4) # Bind an event that prevents the default action and passes a response with events.bound(name, handlerName, self._eatEvent), \ events.bound(name, 'other handler name', self._shouldNotBeCalled): event = events.trigger(name) self.assertTrue(event.defaultPrevented) self.assertFalse(event.propagate) self.assertEqual(event.responses, [{'foo': 'bar'}]) # Test that the context manager unbinds after an unhandled exception try: with events.bound(failname, handlerName, self._raiseException): events.trigger(failname) except Exception: # The event should should be unbound at this point events.trigger(failname)
def _testUploadReference(self): eventList = [] def processEvent(event): eventList.append(event.info) with events.bound('model.file.finalizeUpload.after', 'lib_test', processEvent): path = os.path.join(self.libTestDir, 'sub0', 'f') size = os.path.getsize(path) with open(path) as fh: self.client.uploadFile( self.publicFolder['_id'], fh, name='test1', size=size, parentType='folder', reference='test1_reference') self.assertEqual(len(eventList), 1) self.assertEqual(eventList[0]['upload']['reference'], 'test1_reference') self.client.uploadFileToItem(str(eventList[0]['file']['itemId']), path, reference='test2_reference') self.assertEqual(len(eventList), 2) self.assertEqual(eventList[1]['upload']['reference'], 'test2_reference') self.assertNotEqual(eventList[0]['file']['_id'], eventList[1]['file']['_id']) with open(path, 'ab') as fh: fh.write(b'test') size = os.path.getsize(path) self.client.uploadFileToItem(str(eventList[0]['file']['itemId']), path, reference='test3_reference') self.assertEqual(len(eventList), 3) self.assertEqual(eventList[2]['upload']['reference'], 'test3_reference') self.assertNotEqual(eventList[0]['file']['_id'], eventList[2]['file']['_id']) self.assertEqual(eventList[1]['file']['_id'], eventList[2]['file']['_id']) item = self.client.createItem(self.publicFolder['_id'], 'a second item') # Test explicit MIME type setting file = self.client.uploadFileToItem(item['_id'], path, mimeType='image/jpeg') self.assertEqual(file['mimeType'], 'image/jpeg') # Test guessing of MIME type testPath = os.path.join(self.libTestDir, 'out.txt') with open(testPath, 'w') as fh: fh.write('test') file = self.client.uploadFileToItem(item['_id'], testPath) self.assertEqual(file['mimeType'], 'text/plain') # Test uploading to a folder self.client.uploadFileToFolder( str(self.publicFolder['_id']), path, reference='test4_reference') self.assertEqual(len(eventList), 6) self.assertEqual(eventList[-1]['upload']['reference'], 'test4_reference') self.assertNotEqual(eventList[2]['file']['_id'], eventList[-1]['file']['_id'])
def testUploadReference(self): eventList = [] def processEvent(event): eventList.append(event.info) with events.bound('model.file.finalizeUpload.after', 'lib_test', processEvent): path = os.path.join(self.libTestDir, 'sub0', 'f') size = os.path.getsize(path) with open(path) as fh: self.client.uploadFile( self.publicFolder['_id'], fh, name='test1', size=size, parentType='folder', reference='test1_reference') self.assertEqual(len(eventList), 1) self.assertEqual(eventList[0]['upload']['reference'], 'test1_reference') self.client.uploadFileToItem(str(eventList[0]['file']['itemId']), path, reference='test2_reference') self.assertEqual(len(eventList), 2) self.assertEqual(eventList[1]['upload']['reference'], 'test2_reference') self.assertNotEqual(eventList[0]['file']['_id'], eventList[1]['file']['_id']) with open(path, 'ab') as fh: fh.write(b'test') self.client.uploadFileToItem(str(eventList[0]['file']['itemId']), path, reference='test3_reference') self.assertEqual(len(eventList), 3) self.assertEqual(eventList[2]['upload']['reference'], 'test3_reference') self.assertNotEqual(eventList[0]['file']['_id'], eventList[2]['file']['_id']) self.assertEqual(eventList[1]['file']['_id'], eventList[2]['file']['_id']) item = self.client.createItem(self.publicFolder['_id'], 'a second item') # Test explicit MIME type setting file = self.client.uploadFileToItem(item['_id'], path, mimeType='image/jpeg') self.assertEqual(file['mimeType'], 'image/jpeg') # Test guessing of MIME type testPath = os.path.join(self.libTestDir, 'out.txt') with open(testPath, 'w') as fh: fh.write('test') file = self.client.uploadFileToItem(item['_id'], testPath) self.assertEqual(file['mimeType'], 'text/plain') # Test uploading to a folder self.client.uploadFileToFolder( str(self.publicFolder['_id']), path, reference='test4_reference') self.assertEqual(len(eventList), 6) self.assertEqual(eventList[-1]['upload']['reference'], 'test4_reference') self.assertNotEqual(eventList[2]['file']['_id'], eventList[-1]['file']['_id'])
def testSynchronousEvents(eventsHelper): name, failname = '_test.event', '_test.failure' handlerName = '_test.handler' with events.bound(name, handlerName, eventsHelper._increment), \ events.bound(failname, handlerName, eventsHelper._raiseException): # Make sure our exception propagates out of the handler with pytest.raises(Exception, match='^Failure condition$'): events.trigger(failname) # Bind an event to increment the counter assert eventsHelper.ctr == 0 event = events.trigger(name, {'amount': 2}) assert eventsHelper.ctr == 2 assert event.propagate assert not event.defaultPrevented assert event.responses == [] # The event should still be bound here if another handler unbinds events.unbind(name, 'not the handler name') events.trigger(name, {'amount': 2}) assert eventsHelper.ctr == 4 # Actually unbind the event, by going out of scope of "bound" events.trigger(name, {'amount': 2}) assert eventsHelper.ctr == 4 # Bind an event that prevents the default action and passes a response with events.bound(name, handlerName, eventsHelper._eatEvent), \ events.bound(name, 'other handler name', eventsHelper._shouldNotBeCalled): event = events.trigger(name) assert event.defaultPrevented assert not event.propagate assert event.responses == [{'foo': 'bar'}] # Test that the context manager unbinds after an unhandled exception try: with events.bound(failname, handlerName, eventsHelper._raiseException): events.trigger(failname) except Exception: # The event should should be unbound at this point events.trigger(failname)
def testValidateCustomStatus(self): job = self.jobModel.createJob(title='test', type='x', user=self.users[0]) def validateStatus(event): if event.info == 1234: event.preventDefault().addResponse(True) def validTransitions(event): if event.info['status'] == 1234: event.preventDefault().addResponse([JobStatus.INACTIVE]) with self.assertRaises(ValidationException): self.jobModel.updateJob(job, status=1234) # Should fail with events.bound('jobs.status.validate', 'test', validateStatus), \ events.bound('jobs.status.validTransitions', 'test', validTransitions): self.jobModel.updateJob(job, status=1234) # Should work with self.assertRaises(ValidationException): self.jobModel.updateJob(job, status=4321) # Should fail
def testForegroundDaemon(eventsHelper): assert isinstance(events.daemon, events.ForegroundEventsDaemon) # Should still be able to call start events.daemon.start() def callback(event): eventsHelper.ctr += 1 eventsHelper.responses = event.responses with events.bound('_test.event', '_test.handler', eventsHelper._raiseException): with pytest.raises(Exception, match='^Failure condition$'): events.daemon.trigger('_test.event', None, callback) with events.bound('_test.event', '_test.handler', eventsHelper._incrementWithResponse): events.daemon.trigger('_test.event', {'amount': 2}, callback) assert eventsHelper.ctr == 3 assert eventsHelper.responses == ['foo'] events.daemon.stop()
def testJobSaveEventModification(self): def customSave(event): kwargs = json_util.loads(event.info['kwargs']) kwargs['key2'] = 'newvalue' event.info['kwargs'] = json_util.dumps(kwargs) job = self.jobModel.createJob(title='A job', type='t', user=self.users[1], public=True) job['kwargs'] = {'key1': 'value1', 'key2': 'value2'} with events.bound('model.job.save', 'test', customSave): job = self.jobModel.save(job) self.assertEqual(job['kwargs']['key2'], 'newvalue')
def testForegroundDaemon(self): self.assertIsInstance(events.daemon, events.ForegroundEventsDaemon) # Should still be able to call start events.daemon.start() def callback(event): self.ctr += 1 self.responses = event.responses with events.bound('_test.event', '_test.handler', self._raiseException): with six.assertRaisesRegex(self, Exception, 'Failure condition'): events.daemon.trigger('_test.event', None, callback) with events.bound('_test.event', '_test.handler', self._incrementWithResponse): events.daemon.trigger('_test.event', {'amount': 2}, callback) self.assertEqual(self.ctr, 3) self.assertEqual(self.responses, ['foo']) events.daemon.stop()
def testAsyncEvents(eventsHelper): name, failname = '_test.event', '_test.failure' handlerName = '_test.handler' def callback(event): eventsHelper.ctr += 1 eventsHelper.responses = event.responses with events.bound(failname, handlerName, eventsHelper._raiseException), \ events.bound(name, handlerName, eventsHelper._incrementWithResponse): # Make sure an async handler that fails does not break the event # loop and that its callback is not triggered. assert events.daemon.eventQueue.qsize() == 0 events.daemon.trigger(failname, handlerName, callback) # Triggering the event before the daemon starts should do nothing assert events.daemon.eventQueue.qsize() == 1 events.daemon.trigger(name, {'amount': 2}, callback) assert events.daemon.eventQueue.qsize() == 2 assert eventsHelper.ctr == 0 # Now run the asynchronous event handler, which should eventually # cause our counter to be incremented. events.daemon.start() # Ensure that all of our events have been started within a # reasonable amount of time. Also check the results in the loop, # since the qsize only indicates if all events were started, not # finished. startTime = time.time() while True: if events.daemon.eventQueue.qsize() == 0: if eventsHelper.ctr == 3: break if time.time() - startTime > 15: break time.sleep(0.1) assert events.daemon.eventQueue.qsize() == 0 assert eventsHelper.ctr == 3 assert eventsHelper.responses == ['foo'] events.daemon.stop()
def testAsyncEvents(self): name, failname = '_test.event', '_test.failure' handlerName = '_test.handler' def callback(event): self.ctr += 1 self.responses = event.responses with events.bound(failname, handlerName, self._raiseException), \ events.bound(name, handlerName, self._incrementWithResponse): # Make sure an async handler that fails does not break the event # loop and that its callback is not triggered. self.assertEqual(events.daemon.eventQueue.qsize(), 0) events.daemon.trigger(failname, handlerName, callback) # Triggering the event before the daemon starts should do nothing self.assertEqual(events.daemon.eventQueue.qsize(), 1) events.daemon.trigger(name, {'amount': 2}, callback) self.assertEqual(events.daemon.eventQueue.qsize(), 2) self.assertEqual(self.ctr, 0) # Now run the asynchronous event handler, which should eventually # cause our counter to be incremented. events.daemon.start() # Ensure that all of our events have been started within a # reasonable amount of time. Also check the results in the loop, # since the qsize only indicates if all events were started, not # finished. startTime = time.time() while True: if events.daemon.eventQueue.qsize() == 0: if self.ctr == 3: break if time.time() - startTime > 15: break time.sleep(0.1) self.assertEqual(events.daemon.eventQueue.qsize(), 0) self.assertEqual(self.ctr, 3) self.assertEqual(self.responses, ['foo']) events.daemon.stop()
def testValidateCustomStatus(self): jobModel = self.model('job', 'jobs') job = jobModel.createJob(title='test', type='x', user=self.users[0]) def validateStatus(event): if event.info == 1234: event.preventDefault().addResponse(True) with self.assertRaises(ValidationException): jobModel.updateJob(job, status=1234) # Should fail with events.bound('jobs.status.validate', 'test', validateStatus): jobModel.updateJob(job, status=1234) # Should work with self.assertRaises(ValidationException): jobModel.updateJob(job, status=4321) # Should fail
def testDeleteFolder(self): cbInfo = {} # Hook into model deletion with kwargs event to test it def cb(event): cbInfo['kwargs'] = event.info['kwargs'] cbInfo['doc'] = event.info['document'] with events.bound('model.folder.remove_with_kwargs', 'test', cb): # Requesting with no path should fail resp = self.request(path='/folder', method='DELETE', user=self.admin) self.assertStatus(resp, 400) # Grab one of the user's top level folders folders = self.model('folder').childFolders( parent=self.admin, parentType='user', user=self.admin, limit=1, sort=[('name', SortDir.DESCENDING)]) folderResp = six.next(folders) # Add a subfolder and an item to that folder subfolder = self.model('folder').createFolder( folderResp, 'sub', parentType='folder', creator=self.admin) item = self.model('item').createItem( 'item', creator=self.admin, folder=subfolder) self.assertTrue('_id' in subfolder) self.assertTrue('_id' in item) # Delete the folder resp = self.request(path='/folder/%s' % folderResp['_id'], method='DELETE', user=self.admin, params={ 'progress': 'true' }) self.assertStatusOk(resp) # Make sure the folder, its subfolder, and its item were all deleted folder = self.model('folder').load(folderResp['_id'], force=True) subfolder = self.model('folder').load(subfolder['_id'], force=True) item = self.model('item').load(item['_id']) self.assertEqual(folder, None) self.assertEqual(subfolder, None) self.assertEqual(item, None) # Make sure progress record exists and that it is set to expire soon notifs = list(self.model('notification').get(self.admin)) self.assertEqual(len(notifs), 1) self.assertEqual(notifs[0]['type'], 'progress') self.assertEqual(notifs[0]['data']['state'], ProgressState.SUCCESS) self.assertEqual(notifs[0]['data']['title'], 'Deleting folder Public') self.assertEqual(notifs[0]['data']['message'], 'Done') self.assertEqual(notifs[0]['data']['total'], 3) self.assertEqual(notifs[0]['data']['current'], 3) self.assertTrue(notifs[0]['expires'] < datetime.datetime.utcnow() + datetime.timedelta(minutes=1)) # Make sure our event handler was called with expected args self.assertTrue('kwargs' in cbInfo) self.assertTrue('doc' in cbInfo) self.assertTrue('progress' in cbInfo['kwargs']) self.assertEqual(cbInfo['doc']['_id'], folderResp['_id'])
def testDeleteFolder(self): cbInfo = {} # Hook into model deletion with kwargs event to test it def cb(event): cbInfo['kwargs'] = event.info['kwargs'] cbInfo['doc'] = event.info['document'] with events.bound('model.folder.remove_with_kwargs', 'test', cb): # Requesting with no path should fail resp = self.request(path='/folder', method='DELETE', user=self.admin) self.assertStatus(resp, 400) # Grab one of the user's top level folders folders = self.model('folder').childFolders( parent=self.admin, parentType='user', user=self.admin, limit=1, sort=[('name', SortDir.DESCENDING)]) folderResp = six.next(folders) # Add a subfolder and an item to that folder subfolder = self.model('folder').createFolder(folderResp, 'sub', parentType='folder', creator=self.admin) item = self.model('item').createItem('item', creator=self.admin, folder=subfolder) self.assertTrue('_id' in subfolder) self.assertTrue('_id' in item) # Delete the folder resp = self.request(path='/folder/%s' % folderResp['_id'], method='DELETE', user=self.admin, params={'progress': 'true'}) self.assertStatusOk(resp) # Make sure the folder, its subfolder, and its item were all deleted folder = self.model('folder').load(folderResp['_id'], force=True) subfolder = self.model('folder').load(subfolder['_id'], force=True) item = self.model('item').load(item['_id']) self.assertEqual(folder, None) self.assertEqual(subfolder, None) self.assertEqual(item, None) # Make sure progress record exists and that it is set to expire soon notifs = list(self.model('notification').get(self.admin)) self.assertEqual(len(notifs), 1) self.assertEqual(notifs[0]['type'], 'progress') self.assertEqual(notifs[0]['data']['state'], ProgressState.SUCCESS) self.assertEqual(notifs[0]['data']['title'], 'Deleting folder Public') self.assertEqual(notifs[0]['data']['message'], 'Done') self.assertEqual(notifs[0]['data']['total'], 3) self.assertEqual(notifs[0]['data']['current'], 3) self.assertTrue(notifs[0]['expires'] < datetime.datetime.utcnow() + datetime.timedelta(minutes=1)) # Make sure our event handler was called with expected args self.assertTrue('kwargs' in cbInfo) self.assertTrue('doc' in cbInfo) self.assertTrue('progress' in cbInfo['kwargs']) self.assertEqual(cbInfo['doc']['_id'], folderResp['_id'])