def _call_runcrons(self, mock_logs=(), expected_call_count=0, expected_logs=[], additional_context_managers=[], **cron_kwargs): mock_call = mock.Mock() @cron_job(**cron_kwargs) def mock_cron_job(): mock_call() created_logs = [] for delta, is_success, ran_at_time in mock_logs: created_logs.append(CronJobLog.objects.create( code=mock_cron_job.code, start_time=(utc_now() + delta), end_time=(utc_now() + delta), is_success=is_success, ran_at_time=ran_at_time, )) with mock.patch(u'poleno.cron.tests.mock_cron_job', mock_cron_job): with self.settings(CRON_CLASSES=(u'poleno.cron.tests.mock_cron_job',)): with contextlib.nested(*additional_context_managers): # ``runcrons`` command runs ``logging.debug()`` that somehow spoils stderr. with mock.patch(u'django_cron.logging'): call_command(u'runcrons') self.assertEqual(mock_call.call_count, expected_call_count) found_logs = [] for log in CronJobLog.objects.order_by(u'pk'): if log in created_logs: continue self.assertEqual(log.code, mock_cron_job.code) self.assertAlmostEqual(log.start_time, utc_now(), delta=datetime.timedelta(seconds=10)) self.assertAlmostEqual(log.end_time, utc_now(), delta=datetime.timedelta(seconds=10)) found_logs.append((log.is_success, log.ran_at_time)) self.assertEqual(found_logs, expected_logs)
def mail(): # Get inbound mail path = getattr(settings, u'EMAIL_INBOUND_TRANSPORT', None) if path: klass = import_by_path(path) with klass() as transport: messages = transport.get_messages() while True: try: with transaction.atomic(): message = next(messages) nop() # To let tests raise testing exception here. cron_logger.info(u'Received email: %s' % repr(message)) except StopIteration: break except Exception: cron_logger.error(u'Receiving emails failed:\n%s' % traceback.format_exc()) break # Process inbound mail; At most 10 messages in one batch messages = (Message.objects.inbound().not_processed().order_by_pk(). prefetch_related(Message.prefetch_recipients()))[:10] for message in messages: try: with transaction.atomic(): message.processed = utc_now() message.save(update_fields=[u'processed']) message_received.send(sender=None, message=message) nop() # To let tests raise testing exception here. cron_logger.info(u'Processed received email: %s' % repr(message)) except Exception: cron_logger.error(u'Processing received email failed: %s\n%s' % (repr(message), traceback.format_exc())) # Send outbound mail; At most 10 messages in one batch path = getattr(settings, u'EMAIL_OUTBOUND_TRANSPORT', None) if path: messages = (Message.objects.outbound().not_processed().order_by_pk( ).prefetch_related(Message.prefetch_recipients()).prefetch_related( Message.prefetch_attachments()))[:10] if messages: klass = import_by_path(path) with klass() as transport: for message in messages: try: with transaction.atomic(): transport.send_message(message) message.processed = utc_now() message.save(update_fields=[u'processed']) message_sent.send(sender=None, message=message) nop() # To let tests raise testing exception here. cron_logger.info(u'Sent email: %s' % repr(message)) except Exception: cron_logger.error( u'Seding email failed: %s\n%s' % (repr(message), traceback.format_exc()))
def test_logs_newer_than_one_week_are_kept(self): old_log = CronJobLog.objects.create( code=u'mock_code', start_time=(utc_now() - datetime.timedelta(days=6)), end_time=(utc_now() - datetime.timedelta(days=6)), is_success=True, ran_at_time=None, ) clear_old_cronlogs().do() self.assertTrue(CronJobLog.objects.filter(pk=old_log.pk).exists())
def deferred(user): invitation = self._get_invitation(request) if invitation: invitation.accepted = utc_now() invitation.invitee = user invitation.save() request.session[u'invitations_invitation_key'] = None
def datachecks(superficial, autofix): u""" Checks that every ``Attachment`` instance has its file working, and there are not any orphaned attachment files. """ # This check is a bit slow. We skip it if running from cron or the user asked for # superficial tests only. if superficial: return attachments = Attachment.objects.all() attachment_names = {a.file.name for a in attachments} for attachment in attachments: try: try: attachment.file.open(u'rb') finally: attachment.file.close() except IOError: yield datacheck.Error(u'{} is missing its file: "{}".', attachment, attachment.file.name) field = Attachment._meta.get_field(u'file') if not field.storage.exists(field.upload_to): return for file_name in field.storage.listdir(field.upload_to)[1]: attachment_name = u'{}/{}'.format(field.upload_to, file_name) modified_time = utc_datetime_from_local(field.storage.modified_time(attachment_name)) timedelta = utc_now() - modified_time if timedelta > datetime.timedelta(days=5) and attachment_name not in attachment_names: yield datacheck.Info(squeeze(u""" There is no Attachment instance for file: "{}". The file is {} days old, so you can probably remove it. """), attachment_name, timedelta.days)
def test_upload(self): response = self.client.post( u'/upload/', {u'files': ContentFile(u'uploaded', name=u'filename')}) self.assertIs(type(response), JsonResponse) self.assertEqual(response.status_code, 200) self.assertEqual( json.loads(response.content), { u'files': [ { u'url': u'/download/1/', u'pk': 1, u'name': u'filename', u'size': 8 }, ] }) obj = Attachment.objects.get(pk=1) self.assertEqual(obj.generic_object, self.user) self.assertEqual(obj.name, u'filename') self.assertEqual(obj.content_type, u'application/octet-stream') self.assertAlmostEqual(obj.created, utc_now(), delta=datetime.timedelta(seconds=10)) self.assertEqual(obj.size, 8) self.assertEqual(obj.content, u'uploaded')
def datachecks(superficial, autofix): u""" Checks that every ``Attachment`` instance has its file working, and there are not any orphaned attachment files. """ # This check is a bit slow. We skip it if running from cron or the user asked for # superficial tests only. if superficial: return attachments = Attachment.objects.all() attachment_names = {a.file.name for a in attachments} for attachment in attachments: try: try: attachment.file.open(u'rb') finally: attachment.file.close() except IOError: yield datacheck.Error(u'%r is missing its file: "%s".', attachment, attachment.file.name) field = Attachment._meta.get_field(u'file') if not field.storage.exists(field.upload_to): return for file_name in field.storage.listdir(field.upload_to)[1]: attachment_name = u'%s/%s' % (field.upload_to, file_name) timedelta = utc_now() - utc_datetime_from_local( field.storage.modified_time(attachment_name)) if timedelta > datetime.timedelta( days=5) and attachment_name not in attachment_names: yield datacheck.Info( u'There is no Attachment instance for file: "%s". The file is %d days old, so you can probably remove it.', attachment_name, timedelta.days)
def test_processed_and_not_processed_query_methods(self): obj1 = self._create_message(omit=[u'processed']) obj2 = self._create_message(omit=[u'processed']) obj3 = self._create_message(processed=utc_now()) result = Message.objects.processed() self.assertItemsEqual(result, [obj3]) result = Message.objects.not_processed() self.assertItemsEqual(result, [obj1, obj2])
def send_applicant_deadline_reminder(self, action): self._send_notification(u'inforequests/mails/applicant_deadline_reminder', u'#a{}'.format(action.pk), { u'action': action, }) action.last_deadline_reminder = utc_now() action.save(update_fields=[u'last_deadline_reminder'])
def save(self, *args, **kwargs): if self.pk is None: # Creating a new object self.file.name = random_string(10) if self.created is None: self.created = utc_now() self.size = self.file.size super(Attachment, self).save(*args, **kwargs)
def _call_runcrons(self, mock_logs=(), expected_call_count=0, expected_logs=[], additional_context_managers=[], **cron_kwargs): mock_call = mock.Mock() @cron_job(**cron_kwargs) def mock_cron_job(): mock_call() created_logs = [] for delta, is_success, ran_at_time in mock_logs: created_logs.append( CronJobLog.objects.create( code=mock_cron_job.code, start_time=(utc_now() + delta), end_time=(utc_now() + delta), is_success=is_success, ran_at_time=ran_at_time, )) with mock.patch(u'poleno.cron.tests.mock_cron_job', mock_cron_job): with self.settings( CRON_CLASSES=(u'poleno.cron.tests.mock_cron_job', )): with contextlib.nested(*additional_context_managers): # ``runcrons`` command runs ``logging.debug()`` that somehow spoils stderr. with mock.patch(u'django_cron.logging'): call_command(u'runcrons') self.assertEqual(mock_call.call_count, expected_call_count) found_logs = [] for log in CronJobLog.objects.order_by(u'pk'): if log in created_logs: continue self.assertEqual(log.code, mock_cron_job.code) self.assertAlmostEqual(log.start_time, utc_now(), delta=datetime.timedelta(seconds=10)) self.assertAlmostEqual(log.end_time, utc_now(), delta=datetime.timedelta(seconds=10)) found_logs.append((log.is_success, log.ran_at_time)) self.assertEqual(found_logs, expected_logs)
def test_clearlogs(self): u""" Checks that ``cronserver`` clears all cron logs if called with ``--clearlogs`` option. """ CronJobLog.objects.create( code=u'mock_code', start_time=utc_now(), end_time=utc_now(), is_success=True, ran_at_time=None, ) self.assertEqual(CronJobLog.objects.count(), 1) with mock.patch(u'poleno.cron.management.commands.cronserver.time.sleep', side_effect=KeyboardInterrupt): with mock.patch(u'poleno.cron.management.commands.cronserver.call_command'): self._call_cronserver(clearlogs=True) self.assertEqual(CronJobLog.objects.count(), 0)
def save(self, *args, **kwargs): if self.pk is None: # Creating a new object self.key = random_string(64, chars=(string.ascii_lowercase + string.digits)) if self.created is None: self.created = utc_now() if self.valid_to is None: self.valid_to = self.created + datetime.timedelta(days=app_settings.DEFAULT_VALIDITY) super(Invitation, self).save(*args, **kwargs)
def test_clear_logs_from_future(self): past_log = CronJobLog.objects.create( code=u'mock_code', start_time=(utc_now() + datetime.timedelta(hours=-1)), end_time=(utc_now() + datetime.timedelta(hours=-1)), is_success=True, ran_at_time=None, ) future_log = CronJobLog.objects.create( code=u'mock_code', start_time=(utc_now() + datetime.timedelta(hours=+1)), end_time=(utc_now() + datetime.timedelta(hours=+1)), is_success=True, ran_at_time=None, ) self._call_runcrons_timewarp_aware() self.assertTrue(CronJobLog.objects.filter(pk=past_log.pk).exists()) self.assertFalse(CronJobLog.objects.filter(pk=future_log.pk).exists())
def send_applicant_deadline_reminder(self, action): self._send_notification( u'inforequests/mails/applicant_deadline_reminder', u'#a{}'.format(action.pk), { u'action': action, }) action.last_deadline_reminder = utc_now() action.save(update_fields=[u'last_deadline_reminder'])
def send_obligee_deadline_reminder(self, action): self._send_notification( u'inforequests/mails/obligee_deadline_reminder', u'#action-%s' % action.pk, { u'action': action, }) action.last_deadline_reminder = utc_now() action.save(update_fields=[u'last_deadline_reminder'])
def save(self, *args, **kwargs): if self.pk is None: # Creating a new object self.key = random_string(64, chars=(string.ascii_lowercase + string.digits)) if self.created is None: self.created = utc_now() if self.valid_to is None: delta = datetime.timedelta(days=app_settings.DEFAULT_VALIDITY) self.valid_to = self.created + delta super(Invitation, self).save(*args, **kwargs)
def side_effect(): past_log = CronJobLog.objects.create( code=u'mock_code', start_time=(utc_now() + datetime.timedelta(hours=-1)), end_time=(utc_now() + datetime.timedelta(hours=-1)), is_success=True, ran_at_time=None, ) future_log = CronJobLog.objects.create( code=u'mock_code', start_time=(utc_now() + datetime.timedelta(hours=+1)), end_time=(utc_now() + datetime.timedelta(hours=+1)), is_success=True, ran_at_time=None, ) self.assertItemsEqual(CronJobLog.objects.all(), [past_log, future_log]) yield None self.assertItemsEqual(CronJobLog.objects.all(), [past_log]) yield KeyboardInterrupt
def test_cleancronlogs(self): u""" Checks that ``cleancronlogs`` cleans cron logs. """ CronJobLog.objects.create( code=u'mock_code', start_time=utc_now(), end_time=utc_now(), is_success=True, ran_at_time=datetime.time(10, 0), ) CronJobLog.objects.create( code=u'mock_code2', start_time=utc_now(), end_time=utc_now(), is_success=False, ran_at_time=None, ) call_command(u'cleancronlogs') self.assertEqual(CronJobLog.objects.count(), 0)
def _create_message(self, **kwargs): return self._call_with_defaults(Message.objects.create, kwargs, { u'type': Message.TYPES.OUTBOUND, u'processed': utc_now(), u'from_name': u'Default Testing From Name', u'from_mail': u'*****@*****.**', u'received_for': u'*****@*****.**', u'subject': u'Default Testing Subject', u'text': u'Default Testing Text Content', u'html': u'<p>Default Testing HTML Content</p>', })
def _create_message(self, **kwargs): return self._call_with_defaults( Message.objects.create, kwargs, { u'type': Message.TYPES.OUTBOUND, u'processed': utc_now(), u'from_name': u'Default Testing From Name', u'from_mail': u'*****@*****.**', u'received_for': u'*****@*****.**', u'subject': u'Default Testing Subject', u'text': u'Default Testing Text Content', u'html': u'<p>Default Testing HTML Content</p>', })
def create(email, invitor, validity=None, send_email=True): invitation = Invitation(email=email, invitor=invitor) if validity is not None: invitation.created = utc_now() invitation.valid_to = invitation.created + datetime.timedelta(days=validity) if send_email: @after_saved(invitation) def deferred(invitation): invitation.send_by_email() return invitation
def test_outbound_transport(self): u""" Checks that the registered transport is used to send the queued message, the sent message is marked as processed and ``message_sent`` signal is emmited for it. """ msg = self._create_message(type=Message.TYPES.OUTBOUND, processed=None) method, receiver = mock.Mock(), mock.Mock() self._run_mail_cron_job(outbound=True, send_message_method=method, message_sent_receiver=receiver) msg = Message.objects.get(pk=msg.pk) self.assertAlmostEqual(msg.processed, utc_now(), delta=datetime.timedelta(seconds=10)) self.assertItemsEqual(method.mock_calls, [mock.call(msg)]) self.assertItemsEqual(receiver.mock_calls, [mock.call(message=msg, sender=None, signal=message_sent)])
def test_outbound_transport_sends_at_most_10_messages_in_one_batch(self): msgs = [self._create_message(type=Message.TYPES.OUTBOUND, processed=None) for i in range(20)] method, receiver = mock.Mock(), mock.Mock() self._run_mail_cron_job(outbound=True, send_message_method=method, message_sent_receiver=receiver) # We expect first 10 messages (sorted by their ``pk``) to be sent. msgs = Message.objects.filter(pk__in=sorted(m.pk for m in msgs)[:10]) self.assertEqual(len(msgs), 10) for msg in msgs: self.assertAlmostEqual(msg.processed, utc_now(), delta=datetime.timedelta(seconds=10)) self.assertItemsEqual(method.mock_calls, [mock.call(m) for m in msgs]) self.assertItemsEqual(receiver.mock_calls, [mock.call(message=m, sender=None, signal=message_sent) for m in msgs])
def test_clearlogs(self): u""" Checks that ``cronserver`` clears all cron logs if called with ``--clearlogs`` option. """ CronJobLog.objects.create( code=u'mock_code', start_time=utc_now(), end_time=utc_now(), is_success=True, ran_at_time=None, ) self.assertEqual(CronJobLog.objects.count(), 1) with mock.patch( u'poleno.cron.management.commands.cronserver.time.sleep', side_effect=KeyboardInterrupt): with mock.patch( u'poleno.cron.management.commands.cronserver.call_command' ): self._call_cronserver(clearlogs=True) self.assertEqual(CronJobLog.objects.count(), 0)
def test_upload_file_uploads_file_and_attaches_it_to_session(self): self._login_user(self.user1) data = {u'files': ContentFile(u'Content', name=u'filename.txt')} url = reverse(u'inforequests:upload_attachment') with created_instances(Attachment.objects) as attachment_set: response = self.client.post(url, data, HTTP_X_REQUESTED_WITH=u'XMLHttpRequest') attachment = attachment_set.get() self.assertEqual(attachment.generic_object, self._get_session()) self.assertEqual(attachment.name, u'filename.txt') self.assertEqual(attachment.content_type, u'text/plain') self.assertAlmostEqual(attachment.created, utc_now(), delta=datetime.timedelta(seconds=10)) self.assertEqual(attachment.size, 7) self.assertEqual(attachment.content, u'Content')
def test_upload(self): response = self.client.post(u'/upload/', {u'files': ContentFile(u'uploaded', name=u'filename')}) self.assertIs(type(response), JsonResponse) self.assertEqual(response.status_code, 200) self.assertEqual(json.loads(response.content), {u'files': [ {u'url': u'/download/1/', u'pk': 1, u'name': u'filename', u'size': 8}, ]}) obj = Attachment.objects.get(pk=1) self.assertEqual(obj.generic_object, self.user) self.assertEqual(obj.name, u'filename') self.assertEqual(obj.content_type, u'application/octet-stream') self.assertAlmostEqual(obj.created, utc_now(), delta=datetime.timedelta(seconds=10)) self.assertEqual(obj.size, 8) self.assertEqual(obj.content, u'uploaded')
def handle_noargs(self, **options): interval = options[u'interval'] clearlogs = options[u'clearlogs'] try: if clearlogs: CronJobLog.objects.all().delete() while True: # If we are timewarping, we may encounter cron logs from future. We must remove # them, otherwise django_cron won't run any jobs with logs from furure. CronJobLog.objects.filter(end_time__gt=utc_now()).delete() call_command(u'runcrons') time.sleep(interval) except KeyboardInterrupt: pass
def test_upload_file_uploads_file_and_attaches_it_to_session(self): self._login_user(self.user1) data = {u'files': ContentFile(u'Content', name=u'filename.txt')} url = reverse(u'inforequests:upload_attachment') with created_instances(Attachment.objects) as attachment_set: response = self.client.post( url, data, HTTP_X_REQUESTED_WITH=u'XMLHttpRequest') attachment = attachment_set.get() self.assertEqual(attachment.generic_object, self._get_session()) self.assertEqual(attachment.name, u'filename.txt') self.assertEqual(attachment.content_type, u'text/plain') self.assertAlmostEqual(attachment.created, utc_now(), delta=datetime.timedelta(seconds=10)) self.assertEqual(attachment.size, 7) self.assertEqual(attachment.content, u'Content')
def test_inbound_transport_processes_at_most_10_messages_in_one_batch(self): msgs = [] def method(transport): for i in range(20): msg = self._create_message(type=Message.TYPES.INBOUND, processed=None) msgs.append(msg) yield msg receiver = mock.Mock() self._run_mail_cron_job(inbound=True, get_messages_method=method, message_received_receiver=receiver) # We expect only first 10 messages (sorted by their ``pk``) to be processed. processed = Message.objects.filter(pk__in=sorted(m.pk for m in msgs)[:10]) remaining = Message.objects.filter(pk__in=sorted(m.pk for m in msgs)[10:]) self.assertEqual(len(processed), 10) self.assertEqual(len(remaining), 10) for msg in processed: self.assertAlmostEqual(msg.processed, utc_now(), delta=datetime.timedelta(seconds=10)) for msg in remaining: self.assertIsNone(msg.processed)
def handle(self, *args, **options): CronJobLog.objects.filter(end_time__gt=utc_now()).delete() super(Command, self).handle(*args, **options)
def delete_old_drafts(): threshold = utc_now() - datetime.timedelta(days=15) WizardDraft.objects.filter(modified__lt=threshold).delete()
def expired(self): return self.filter(accepted__isnull=True).filter(valid_to__lt=utc_now())
def test_mail_processed_date_ignoring_mail_date_header(self): mail = self._create_mail(headers={u'Date': u'Sat, 25 Oct 2014 22:32:52 -0000'}) transport = self._run_mail_cron_job(mails=[mail]) msg = Message.objects.first() self.assertAlmostEqual(msg.processed, utc_now(), delta=datetime.timedelta(seconds=10))
def test_created_field_with_default_value_if_omitted(self): obj = self._create_instance(_omit=[u'created']) self.assertAlmostEqual(obj.created, utc_now(), delta=datetime.timedelta(seconds=10))
def clear_old_cronlogs(): threshold = utc_now() - timedelta(days=7) CronJobLog.objects.filter(start_time__lt=threshold).delete() cron_logger.info(u'Cleared old cron logs.')
def is_expired(self): return not self.is_accepted and self.valid_to < utc_now()
def mail(): # Get inbound mail path = getattr(settings, u'EMAIL_INBOUND_TRANSPORT', None) if path: klass = import_by_path(path) with klass() as transport: messages = transport.get_messages() while True: try: with transaction.atomic(): message = next(messages) nop() # To let tests raise testing exception here. cron_logger.info(u'Received email: {}'.format(message)) except StopIteration: break except Exception: trace = unicode(traceback.format_exc(), u'utf-8') cron_logger.error(u'Receiving emails failed:\n{}'.format(trace)) break # Process inbound mail; At most 10 messages in one batch messages = (Message.objects .inbound() .not_processed() .order_by_pk() .prefetch_related(Message.prefetch_recipients()) )[:10] for message in messages: try: with transaction.atomic(): message.processed = utc_now() message.save(update_fields=[u'processed']) message_received.send(sender=None, message=message) nop() # To let tests raise testing exception here. cron_logger.info(u'Processed received email: {}'.format(message)) except Exception: trace = unicode(traceback.format_exc(), u'utf-8') cron_logger.error(u'Processing received email failed: {}\n{}'.format(message, trace)) # Send outbound mail; At most 10 messages in one batch path = getattr(settings, u'EMAIL_OUTBOUND_TRANSPORT', None) if path: messages = (Message.objects .outbound() .not_processed() .order_by_pk() .prefetch_related(Message.prefetch_recipients()) .prefetch_related(Message.prefetch_attachments()) )[:10] if messages: klass = import_by_path(path) with klass() as transport: for message in messages: try: with transaction.atomic(): transport.send_message(message) message.processed = utc_now() message.save(update_fields=[u'processed']) message_sent.send(sender=None, message=message) nop() # To let tests raise testing exception here. cron_logger.info(u'Sent email: {}'.format(message)) except Exception: trace = unicode(traceback.format_exc(), u'utf-8') cron_logger.error(u'Seding email failed: {}\n{}'.format(message, trace))
def send_undecided_email_reminder(self): self._send_notification(u'inforequests/mails/undecided_email_reminder', u'', { }) self.last_undecided_email_reminder = utc_now() self.save(update_fields=[u'last_undecided_email_reminder'])
def send_undecided_email_reminder(self): self._send_notification(u'inforequests/mails/undecided_email_reminder', u'', {}) self.last_undecided_email_reminder = utc_now() self.save(update_fields=[u'last_undecided_email_reminder'])
def test_utc_now(self): with mock.patch(u'django.utils.timezone.datetime', test_datetime(2014, 9, 10, 2, 20)): # in UTC value = utc_now() self._check_dt(value, u'2014-09-10 02:20:00.000000 UTC +0000')
def pending(self): return self.filter(accepted__isnull=True).filter(valid_to__gte=utc_now())