def test_repeater_failed_sends(self): """ This tests records that fail to send three times """ def now(): return datetime.utcnow() repeat_records = RepeatRecord.all(domain=self.domain, due_before=now()) self.assertEqual(len(repeat_records), 2) for repeat_record in repeat_records: with patch( 'corehq.apps.repeaters.models.simple_post_with_cached_timeout', return_value=MockResponse(status_code=404)) as mock_post: repeat_record.fire() self.assertEqual(mock_post.call_count, 3) repeat_record.save() next_check_time = now() + timedelta(minutes=60) repeat_records = RepeatRecord.all( domain=self.domain, due_before=now() + timedelta(minutes=15), ) self.assertEqual(len(repeat_records), 0) repeat_records = RepeatRecord.all( domain=self.domain, due_before=next_check_time, ) self.assertEqual(len(repeat_records), 2)
def setUpClass(cls): cls.records = [ RepeatRecord(domain='a'), RepeatRecord(domain='b'), RepeatRecord(domain='c'), ] RepeatRecord.bulk_save(cls.records)
def test_repeater_successful_send(self): repeat_records = RepeatRecord.all(domain=self.domain, due_before=datetime.utcnow()) for repeat_record in repeat_records: with patch( 'corehq.apps.repeaters.models.simple_post', return_value=MockResponse(status_code=200, reason='No Reason')) as mock_post: repeat_record.fire() self.assertEqual(mock_post.call_count, 1) mock_post.assert_any_call( repeat_record.get_payload(), repeat_record.repeater.get_url(repeat_record), headers=repeat_record.repeater.get_headers(repeat_record), timeout=POST_TIMEOUT, auth=repeat_record.repeater.get_auth(), ) # The following is pretty fickle and depends on which of # - corehq.apps.repeaters.signals # - casexml.apps.case.signals # gets loaded first. # This is deterministic but easily affected by minor code changes repeat_records = RepeatRecord.all( domain=self.domain, due_before=datetime.utcnow(), ) for repeat_record in repeat_records: self.assertEqual(repeat_record.succeeded, True) self.assertEqual(repeat_record.next_check, None) self.assertEqual(len(self.repeat_records(self.domain)), 0) self.post_xml(self.update_xform_xml, self.domain) self.assertEqual(len(self.repeat_records(self.domain)), 2)
def test_repeater_failed_sends(self): """ This tests records that fail are requeued later """ def now(): return datetime.utcnow() repeat_records = RepeatRecord.all(domain=self.domain, due_before=now()) self.assertEqual(len(repeat_records), 2) for repeat_record in repeat_records: with patch( 'corehq.apps.repeaters.models.simple_post', return_value=MockResponse(status_code=404, reason='Not Found')) as mock_post: repeat_record.fire() self.assertEqual(mock_post.call_count, 1) next_check_time = now() + timedelta(minutes=60) repeat_records = RepeatRecord.all( domain=self.domain, due_before=now() + timedelta(minutes=15), ) self.assertEqual(len(repeat_records), 0) repeat_records = RepeatRecord.all( domain=self.domain, due_before=next_check_time, ) self.assertEqual(len(repeat_records), 2)
def test_repeat_record_created(self): ''' Tests that whenever an application with a repeater is saved that a repeat record is created. ''' application = Application(domain=self.domain) application.save() repeat_records = RepeatRecord.all(domain=self.domain, due_before=datetime.utcnow()) self.assertEqual(len(repeat_records), 0) app_structure_repeater = AppStructureRepeater(domain=self.domain, url=self.forwarding_url) app_structure_repeater.save() application.save() repeat_records = RepeatRecord.all(domain=self.domain, due_before=datetime.utcnow()) self.assertEqual(len(repeat_records), 1) for repeat_record in repeat_records: self.assertEqual(repeat_record.url, self.forwarding_url) self.assertEqual(repeat_record.get_payload(), application.get_id) repeat_record.delete() application.delete() app_structure_repeater.delete()
def test_repeat_record_status_check(self): self.assertEqual(len(RepeatRecord.all()), 2) # Do not trigger cancelled records for repeat_record in RepeatRecord.all(): repeat_record.cancelled = True repeat_record.save() with patch('corehq.apps.repeaters.models.simple_post') as mock_fire: check_repeaters() self.assertEqual(mock_fire.call_count, 0) # trigger force send records if not cancelled and tries not exhausted for repeat_record in RepeatRecord.all(): with patch('corehq.apps.repeaters.models.simple_post', return_value=MockResponse(status_code=200, reason='') ) as mock_fire: repeat_record.fire(force_send=True) self.assertEqual(mock_fire.call_count, 1) # all records should be in SUCCESS state after force try for repeat_record in RepeatRecord.all(): self.assertEqual(repeat_record.state, RECORD_SUCCESS_STATE) self.assertEqual(repeat_record.overall_tries, 1) # not trigger records succeeded triggered after cancellation with patch('corehq.apps.repeaters.models.simple_post') as mock_fire: check_repeaters() self.assertEqual(mock_fire.call_count, 0) for repeat_record in RepeatRecord.all(): self.assertEqual(repeat_record.state, RECORD_SUCCESS_STATE)
def test_get_all_repeat_records_by_domain_since(self): new_records = [ # FAIL RepeatRecord( domain=self.domain, repeater_id=self.repeater_id, last_checked=datetime(2017, 5, 24, 0, 0, 0), failure_reason='some error', ), # CANCELLED RepeatRecord( domain=self.domain, repeater_id=self.repeater_id, last_checked=datetime(2017, 5, 10, 0, 0, 0), cancelled=True, ), # CANCELLED RepeatRecord( domain=self.domain, repeater_id=self.repeater_id, last_checked=datetime(2017, 5, 24, 0, 0, 0), cancelled=True, ), ] RepeatRecord.bulk_save(new_records) self.addCleanup(RepeatRecord.bulk_delete, new_records) records = list( iter_repeat_records_by_domain(self.domain, state=RECORD_CANCELLED_STATE, since=datetime(2017, 5, 20))) self.assertEqual(len(records), 1) record, = records self.assertEqual(record.to_json(), new_records[-1].to_json())
def test_update_failure_next_check(self): now = datetime.utcnow() record = RepeatRecord(domain=self.domain, next_check=now) self.assertIsNone(record.last_checked) record.update_failure() self.assertTrue(record.last_checked > now) self.assertEqual(record.next_check, record.last_checked + MIN_RETRY_WAIT)
def test_update_failure_next_check(self): now = datetime.utcnow() record = RepeatRecord(domain=self.domain, next_check=now) self.assertIsNone(record.last_checked) record.set_next_try() self.assertTrue(record.last_checked > now) self.assertEqual(record.next_check, record.last_checked + MIN_RETRY_WAIT)
def test_update_failure_next_check(self): now = datetime.utcnow() record = RepeatRecord(domain=self.domain, next_check=now) self.assertIsNone(record.last_checked) attempt = record.make_set_next_try_attempt(None) record.add_attempt(attempt) self.assertTrue(record.last_checked > now) self.assertEqual(record.next_check, record.last_checked + MIN_RETRY_WAIT)
def testLocks(self): r = RepeatRecord(domain='test') r.save() r2 = RepeatRecord.get(r._id) self.assertTrue(r.acquire_lock(datetime.utcnow())) r3 = RepeatRecord.get(r._id) self.assertFalse(r2.acquire_lock(datetime.utcnow())) self.assertFalse(r3.acquire_lock(datetime.utcnow())) r.release_lock() r4 = RepeatRecord.get(r._id) self.assertTrue(r4.acquire_lock(datetime.utcnow()))
def test_skip_device_logs(self): devicelog_xml = XFORM_XML_TEMPLATE.format(DEVICE_LOG_XMLNS, USER_ID, '1234', '') self.post_xml(devicelog_xml, self.domain) repeat_records = RepeatRecord.all(domain=self.domain) for repeat_record in repeat_records: self.assertNotEqual(repeat_record.payload_id, '1234')
def tearDown(self): self.case_repeater.delete() self.form_repeater.delete() XFormInstance.get(instance_id).delete() repeat_records = RepeatRecord.all() for repeat_record in repeat_records: repeat_record.delete()
def check_repeaters(request, domain): if request.method == 'GET': return HttpResponse(PAGE % {'status': '', 'domain': domain}) elif request.method == 'POST': start = datetime.utcnow() repeat_records = RepeatRecord.all(domain, due_before=start, limit=100) process_repeater_list(repeat_records) return HttpResponse(PAGE % {'status': 'Done', 'domain': domain})
def post(self, request, domain): # Retriggers a repeat record record = RepeatRecord.get(request.POST.get('record_id')) record.fire(max_tries=1, force_send=True) return json_response({ 'success': record.succeeded, 'failure_reason': record.failure_reason, })
def post(self, request, domain): # Retriggers a repeat record record = RepeatRecord.get(request.POST.get('record_id')) record.fire(max_tries=1, force_send=True) record.save() return json_response({ 'success': record.succeeded, 'failure_reason': record.failure_reason, })
def setUpClass(cls): before = datetime.utcnow() - timedelta(minutes=5) failed = RepeatRecord( domain=cls.domain, failure_reason='Some python error', repeater_id=cls.repeater_id, next_check=before, ) success = RepeatRecord( domain=cls.domain, succeeded=True, repeater_id=cls.repeater_id, next_check=before, ) pending = RepeatRecord( domain=cls.domain, succeeded=False, repeater_id=cls.repeater_id, next_check=before, ) overdue = RepeatRecord( domain=cls.domain, succeeded=False, repeater_id=cls.repeater_id, next_check=before - timedelta(minutes=10), ) other_id = RepeatRecord( domain=cls.domain, succeeded=False, repeater_id=cls.other_id, next_check=before, ) cls.records = [ failed, success, pending, overdue, other_id, ] for record in cls.records: record.save()
def test_process_repeat_record_locking(self): self.assertEqual(len(RepeatRecord.all()), 2) with patch('corehq.apps.repeaters.tasks.process_repeat_record') as mock_process: check_repeaters() self.assertEqual(mock_process.delay.call_count, 2) with patch('corehq.apps.repeaters.tasks.process_repeat_record') as mock_process: check_repeaters() self.assertEqual(mock_process.delay.call_count, 0) records = RepeatRecord.all() # Saving should unlock them again by changing the rev for record in records: record.save() with patch('corehq.apps.repeaters.tasks.process_repeat_record') as mock_process: check_repeaters() self.assertEqual(mock_process.delay.call_count, 2)
def test_check_repeat_records(self): self.assertEqual(len(RepeatRecord.all()), 2) with patch('corehq.apps.repeaters.models.simple_post_with_cached_timeout') as mock_fire: check_repeaters() self.assertEqual(mock_fire.call_count, 2) with patch('corehq.apps.repeaters.models.simple_post_with_cached_timeout') as mock_fire: check_repeaters() self.assertEqual(mock_fire.call_count, 0)
def test_ignore_document(self): """ When get_payload raises IgnoreDocument, fire should call update_success """ repeat_records = RepeatRecord.all(domain=self.domain, ) for repeat_record_ in repeat_records: repeat_record_.fire() self.assertIsNone(repeat_record_.next_check) self.assertTrue(repeat_record_.succeeded)
def get_record_or_404(request, domain, record_id): try: record = RepeatRecord.get(record_id) except ResourceNotFound: raise Http404() if record.domain != domain: raise Http404() return record
def test_check_repeat_records(self): self.assertEqual(len(RepeatRecord.all()), 2) with patch('corehq.apps.repeaters.models.simple_post') as mock_fire: check_repeaters() self.assertEqual(mock_fire.call_count, 2) with patch('corehq.apps.repeaters.models.simple_post') as mock_fire: check_repeaters() self.assertEqual(mock_fire.call_count, 0)
def handle(self, *args, **options): if len(args) == 1: domain = args[0] else: raise CommandError('Usage: %s\n%s' % (self.args, self.help)) next_year = datetime.datetime.utcnow() + datetime.timedelta(days=365) records = RepeatRecord.all(domain=domain, due_before=next_year) for record in records: record.fire(post_fn=simple_post) record.save() print '{} {}'.format(record._id, 'successful' if record.succeeded else 'failed')
def handle(self, *args, **options): if len(args) == 1: domain = args[0] else: raise CommandError('Usage: %s\n%s' % (self.args, self.help)) next_year = datetime.datetime.utcnow() + datetime.timedelta(days=365) records = RepeatRecord.all(domain=domain, due_before=next_year) for record in records: record.fire(post_fn=simple_post) print('{} {}'.format( record._id, 'successful' if record.succeeded else 'failed'))
def test_ignore_document(self): """ When get_payload raises IgnoreDocument, fire should call update_success """ repeat_records = RepeatRecord.all( domain=self.domain, ) for repeat_record_ in repeat_records: repeat_record_.fire() self.assertIsNone(repeat_record_.next_check) self.assertTrue(repeat_record_.succeeded)
def test_repeater_successful_send(self): repeat_records = RepeatRecord.all(domain=self.domain, due_before=datetime.utcnow()) mocked_responses = [ MockResponse(status_code=404, reason='Not Found'), MockResponse(status_code=200, reason='No Reason') ] for repeat_record in repeat_records: with patch( 'corehq.apps.repeaters.models.simple_post_with_cached_timeout', side_effect=mocked_responses) as mock_post: repeat_record.fire() self.assertEqual(mock_post.call_count, 2) mock_post.assert_any_call( repeat_record.get_payload(), repeat_record.repeater.get_url(repeat_record), headers=repeat_record.repeater.get_headers(repeat_record), force_send=False, timeout=POST_TIMEOUT, ) repeat_record.save() # The following is pretty fickle and depends on which of # - corehq.apps.repeaters.signals # - casexml.apps.case.signals # gets loaded first. # This is deterministic but easily affected by minor code changes repeat_records = RepeatRecord.all( domain=self.domain, due_before=datetime.utcnow(), ) for repeat_record in repeat_records: self.assertEqual(repeat_record.succeeded, True) self.assertEqual(repeat_record.next_check, None) self.assertEqual(len(self.repeat_records(self.domain)), 0) self.post_xml(self.update_xform_xml, self.domain) self.assertEqual(len(self.repeat_records(self.domain)), 2)
def setUp(self): super(TestBetsResponseHandling, self).setUp() user = CommCareUser.create( self.domain, "*****@*****.**", "123", ) self.repeater = BETSUserRepeater( domain=self.domain, url='super-cool-url', ) self.repeater.save() self.repeat_record = RepeatRecord( repeater_id=self.repeater.get_id, payload_id=user.user_id, next_check=datetime.utcnow(), )
def loop(): # take LIMIT records off the top # the assumption is that they all get 'popped' in the for loop # the only exception I can see is if there's a problem with the # locking, a large number of locked tasks could pile up at the top, # so make a provision for that worst case number_locked = progress_report['number_locked'] repeat_records = RepeatRecord.all( due_before=start, limit=LIMIT + number_locked ) return process_repeater_list( repeat_records, start=start, cutoff=start + CHECK_REPEATERS_INTERVAL, progress_report=progress_report )
def test_ignore_document(self): """ When get_payload raises IgnoreDocument, fire should call update_success """ @RegisterGenerator(FormRepeater, 'new_format', 'XML') class NewFormGenerator(BasePayloadGenerator): def get_payload(self, repeat_record, payload_doc): raise IgnoreDocument repeat_records = RepeatRecord.all( domain=self.domain, ) for repeat_record_ in repeat_records: repeat_record_.fire() self.assertIsNone(repeat_record_.next_check) self.assertTrue(repeat_record_.succeeded)
def get(self, request, domain): record = RepeatRecord.get(request.GET.get('record_id')) content_type = record.repeater.get_payload_generator( record.repeater.format_or_default_format() ).content_type try: payload = record.get_payload() except XFormNotFound: return json_response({ 'error': u'Odd, could not find payload for: {}'.format(record.payload_id) }, status_code=404) if content_type == 'text/xml': payload = indent_xml(payload) return json_response({ 'payload': payload, 'content_type': content_type, })
def get(self, request, domain): record = RepeatRecord.get(request.GET.get('record_id')) content_type = record.repeater.get_payload_generator( record.repeater.format_or_default_format() ).content_type try: payload = record.get_payload() except XFormNotFound: return json_response({ 'error': u'Odd, could not find payload for: {}'.format(record.payload_id) }, status_code=404) if content_type == 'text/xml': payload = indent_xml(payload) elif content_type == 'application/json': payload = json.dumps(json.loads(payload), indent=4) return json_response({ 'payload': payload, 'content_type': content_type, })
def handle(self, domain, **options): next_year = datetime.datetime.utcnow() + datetime.timedelta(days=365) records = RepeatRecord.all(domain=domain, due_before=next_year) for record in records: record.fire(post_fn=simple_post) print('{} {}'.format(record._id, 'successful' if record.succeeded else 'failed'))
def test_repeater(self): # this test should probably be divided into more units CommCareCase.get(case_id) def now(): return datetime.utcnow() repeat_records = RepeatRecord.all(domain=self.domain, due_before=now()) self.assertEqual(len(repeat_records), 2) self.clear_log() records_by_repeater_id = {} for repeat_record in repeat_records: repeat_record.fire(post_fn=self.make_post_fn([404, 404, 404])) repeat_record.save() records_by_repeater_id[repeat_record.repeater_id] = repeat_record for (url, status, data, headers) in self.log: self.assertEqual(status, 404) self.clear_log() next_check_time = now() + timedelta(minutes=60) repeat_records = RepeatRecord.all( domain=self.domain, due_before=now() + timedelta(minutes=15), ) self.assertEqual(len(repeat_records), 0) repeat_records = RepeatRecord.all( domain=self.domain, due_before=next_check_time + timedelta(seconds=2), ) self.assertEqual(len(repeat_records), 2) for repeat_record in repeat_records: self.assertLess(abs(next_check_time - repeat_record.next_check), timedelta(seconds=3)) repeat_record.fire(post_fn=self.make_post_fn([404, 200])) repeat_record.save() self.assertEqual(len(self.log), 4) # The following is pretty fickle and depends on which of # - corehq.apps.repeaters.signals # - casexml.apps.case.signals # gets loaded first. # This is deterministic but easily affected by minor code changes # check case stuff rec = records_by_repeater_id[self.case_repeater.get_id] self.assertEqual(self.log[1][:2], (self.case_repeater.get_url(rec), 200)) self.assertIn('server-modified-on', self.log[1][3]) check_xml_line_by_line(self, self.log[1][2], case_block) # check form stuff rec = records_by_repeater_id[self.form_repeater.get_id] self.assertEqual(self.log[3][:3], (self.form_repeater.get_url(rec), 200, xform_xml)) self.assertIn('received-on', self.log[3][3]) repeat_records = RepeatRecord.all( domain=self.domain, due_before=next_check_time, ) for repeat_record in repeat_records: self.assertEqual(repeat_record.succeeded, True) self.assertEqual(repeat_record.next_check, None) self.assertEqual(len(self.repeat_records(self.domain)), 0) self.post_xml(update_xform_xml, self.domain) self.assertEqual(len(self.repeat_records(self.domain)), 2)
def tearDownClass(cls): RepeatRecord.bulk_delete(cls.records)
def tearDown(self): self.repeater.delete() repeat_records = RepeatRecord.all() for repeat_record in repeat_records: repeat_record.delete()
def test_skip_device_logs(self): devicelog_xml = xform_xml_template.format(DEVICE_LOG_XMLNS, '1234', '') self.post_xml(devicelog_xml, self.domain) repeat_records = RepeatRecord.all(domain=self.domain) for repeat_record in repeat_records: self.assertNotEqual(repeat_record.payload_id, '1234')
def repeat_records(self): return RepeatRecord.all(domain=self.domain, due_before=datetime.utcnow())
def repeat_records(cls, domain_name): return RepeatRecord.all(domain=domain_name, due_before=datetime.utcnow())