예제 #1
0
def zapier_subscription_pre_save(sender, instance, *args, **kwargs):
    """
    Creates a repeater object corresponding to the type of trigger (form or case)
    """
    if instance.pk:
        return

    if instance.event_name == EventTypes.NEW_FORM:
        repeater = FormRepeater(
            domain=instance.domain,
            url=instance.url,
            format='form_json',
            include_app_id_param=False,
            white_listed_form_xmlns=[instance.form_xmlns]
        )

    elif instance.event_name in CASE_TYPE_REPEATER_CLASS_MAP:
        repeater = CASE_TYPE_REPEATER_CLASS_MAP[instance.event_name](
            domain=instance.domain,
            url=instance.url,
            format='case_json',
            white_listed_case_types=[instance.case_type],
        )
    else:
        raise ImmediateHttpResponse(
            HttpBadRequest('The passed event type is not valid.')
        )

    repeater.save()
    instance.repeater_id = repeater.get_id
예제 #2
0
 def setUp(self):
     super(RepeaterTest, self).setUp()
     self.domain = "test-domain"
     create_domain(self.domain)
     self.case_repeater = CaseRepeater(
         domain=self.domain,
         url='case-repeater-url',
     )
     self.case_repeater.save()
     self.form_repeater = FormRepeater(
         domain=self.domain,
         url='form-repeater-url',
     )
     self.form_repeater.save()
     self.log = []
     self.post_xml(self.xform_xml, self.domain)
예제 #3
0
 def setUpClass(cls):
     super(FormRepeaterTest, cls).setUpClass()
     cls.repeater = FormRepeater(
         domain=cls.domain,
         url="form-repeater-url",
     )
     cls.repeater.save()
예제 #4
0
class IgnoreDocumentTest(BaseRepeaterTest):
    domain = 'ignore-doc'

    @classmethod
    def setUpClass(cls):
        super(IgnoreDocumentTest, cls).setUpClass()

        class NewFormGenerator(BasePayloadGenerator):
            format_name = 'new_format'
            format_label = 'XML'

            def get_payload(self, repeat_record, payload_doc):
                raise IgnoreDocument

        RegisterGenerator.get_collection(FormRepeater).add_new_format(
            NewFormGenerator)

    def setUp(self):
        super().setUp()
        self.connx = ConnectionSettings.objects.create(
            domain=self.domain,
            url='form-repeater-url',
        )
        self.repeater = FormRepeater(domain=self.domain,
                                     connection_settings_id=self.connx.id,
                                     format='new_format')
        self.repeater.save()

    def tearDown(self):
        self.repeater.delete()
        self.connx.delete()
        FormProcessorTestUtils.delete_all_cases_forms_ledgers(self.domain)
        delete_all_repeat_records()

    @run_with_all_backends
    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)
예제 #5
0
class IgnoreDocumentTest(BaseRepeaterTest):

    @classmethod
    def setUpClass(cls):
        super(IgnoreDocumentTest, cls).setUpClass()

        class NewFormGenerator(BasePayloadGenerator):
            format_name = 'new_format'
            format_label = 'XML'

            def get_payload(self, repeat_record, payload_doc):
                raise IgnoreDocument

        RegisterGenerator.get_collection(FormRepeater).add_new_format(NewFormGenerator)

    def setUp(self):
        super(IgnoreDocumentTest, self).setUp()
        self.domain = "test-domain"
        create_domain(self.domain)

        self.repeater = FormRepeater(
            domain=self.domain,
            url='form-repeater-url',
            format='new_format'
        )
        self.repeater.save()

    def tearDown(self):
        self.repeater.delete()
        delete_all_repeat_records()
        super(IgnoreDocumentTest, self).tearDown()

    @run_with_all_backends
    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)
예제 #6
0
class IgnoreDocumentTest(BaseRepeaterTest):

    @classmethod
    def setUpClass(cls):
        super(IgnoreDocumentTest, cls).setUpClass()

        class NewFormGenerator(BasePayloadGenerator):
            format_name = 'new_format'
            format_label = 'XML'

            def get_payload(self, repeat_record, payload_doc):
                raise IgnoreDocument

        RegisterGenerator.get_collection(FormRepeater).add_new_format(NewFormGenerator)

    def setUp(self):
        super(IgnoreDocumentTest, self).setUp()
        self.domain = "test-domain"
        create_domain(self.domain)

        self.repeater = FormRepeater(
            domain=self.domain,
            url='form-repeater-url',
            format='new_format'
        )
        self.repeater.save()

    def tearDown(self):
        self.repeater.delete()
        delete_all_repeat_records()
        super(IgnoreDocumentTest, self).tearDown()

    @run_with_all_backends
    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)
예제 #7
0
    def setUp(self):
        super(RepeaterTest, self).setUp()
        self.case_repeater = CaseRepeater(
            domain=self.domain,
            url='case-repeater-url',
        )
        self.case_repeater.save()
        self.form_repeater = FormRepeater(
            domain=self.domain,
            url='form-repeater-url',
        )
        self.form_repeater.save()
        self.log = []

        with patch('corehq.motech.repeaters.models.simple_post',
                   return_value=MockResponse(status_code=500, reason="Borked")) as mock_fire:
            self.post_xml(self.xform_xml, self.domain)
            self.initial_fire_call_count = mock_fire.call_count
예제 #8
0
 def test_unsubscribe_form(self):
     ZapierSubscription.objects.create(
         url=ZAPIER_URL,
         user_id=self.web_user.get_id,
         domain=TEST_DOMAIN,
         event_name=EventTypes.NEW_FORM,
         application_id=self.application.get_id,
         form_xmlns=FORM_XMLNS)
     self.assertNotEqual(len(FormRepeater.by_domain(TEST_DOMAIN)), 0)
     data = {"target_url": ZAPIER_URL}
     response = self.client.post(
         reverse(UnsubscribeView.urlname, kwargs={'domain': self.domain}),
         data=json.dumps(data),
         content_type='application/json; charset=utf-8',
         HTTP_AUTHORIZATION='ApiKey test:{}'.format(self.api_key))
     self.assertEqual(response.status_code, 200)
     self.assertEqual(ZapierSubscription.objects.all().count(), 0)
     self.assertEqual(len(FormRepeater.by_domain(TEST_DOMAIN)), 0)
예제 #9
0
 def setUpClass(cls):
     super().setUpClass()
     cls.repeater = FormRepeater(
         domain=cls.domain,
         url="form-repeater-url",
     )
     cls.repeatergenerator = FormRepeaterXMLPayloadGenerator(
         repeater=cls.repeater)
     cls.repeater.save()
예제 #10
0
    def setUp(self):
        super(RepeaterTest, self).setUp()
        self.domain = "test-domain"
        create_domain(self.domain)
        self.case_repeater = CaseRepeater(
            domain=self.domain,
            url='case-repeater-url',
        )
        self.case_repeater.save()
        self.form_repeater = FormRepeater(
            domain=self.domain,
            url='form-repeater-url',
        )
        self.form_repeater.save()
        self.log = []

        with patch('corehq.motech.repeaters.models.simple_post') as mock_fire:
            self.post_xml(self.xform_xml, self.domain)
            self.initial_fire_call_count = mock_fire.call_count
예제 #11
0
    def setUpClass(cls):
        super(FormRepeaterTest, cls).setUpClass()

        cls.domain_name = "test-domain"
        cls.domain = create_domain(cls.domain_name)
        cls.repeater = FormRepeater(
            domain=cls.domain_name,
            url="form-repeater-url",
        )
        cls.repeater.save()
예제 #12
0
class TestRepeatRecordsUpdateRepeater(TestCase):
    def setUp(self):
        self.rep = FormRepeater(
            domain=DOMAIN,
            url='https://server.example.com/api/',
        )
        self.rep.save()

    def tearDown(self):
        self.rep.delete()

    def test_success(self):
        one_minute = timedelta(minutes=1)
        rec = RepeatRecord(
            domain=DOMAIN,
            repeater_id=self.rep.get_id,
        )
        attempt = RepeatRecordAttempt(
            datetime=datetime.utcnow() - one_minute,
            success_response='w00t',
            succeeded=True,
        )
        rec.add_attempt(attempt)

        self.assertEqual(rec.repeater.last_success_at, attempt.datetime)
        self.assertEqual(rec.repeater.failure_streak, 0)

    def test_failure(self):
        one_minute = timedelta(minutes=1)
        rec = RepeatRecord(
            domain=DOMAIN,
            repeater_id=self.rep.get_id,
        )
        attempt = RepeatRecordAttempt(
            datetime=datetime.utcnow() - one_minute,
            failure_reason='oops',
            succeeded=False,
        )
        rec.add_attempt(attempt)

        self.assertIsNone(rec.repeater.last_success_at)
        self.assertEqual(rec.repeater.failure_streak, 1)
예제 #13
0
    def setUp(self):
        super(IgnoreDocumentTest, self).setUp()
        self.domain = "test-domain"
        create_domain(self.domain)

        self.repeater = FormRepeater(
            domain=self.domain,
            url='form-repeater-url',
            format='new_format'
        )
        self.repeater.save()
예제 #14
0
 def test_unsubscribe_form(self):
     ZapierSubscription.objects.create(
         url=ZAPIER_URL,
         user_id=self.web_user.get_id,
         domain=TEST_DOMAIN,
         event_name=EventTypes.NEW_FORM,
         application_id=self.application.get_id,
         form_xmlns=FORM_XMLNS
     )
     self.assertNotEqual(len(FormRepeater.by_domain(TEST_DOMAIN)), 0)
     data = {
         "target_url": ZAPIER_URL
     }
     response = self.client.post(reverse(UnsubscribeView.urlname, kwargs={'domain': self.domain}),
                                 data=json.dumps(data),
                                 content_type='application/json; charset=utf-8',
                                 HTTP_AUTHORIZATION='ApiKey test:{}'.format(self.api_key))
     self.assertEqual(response.status_code, 200)
     self.assertEqual(ZapierSubscription.objects.all().count(), 0)
     self.assertEqual(len(FormRepeater.by_domain(TEST_DOMAIN)), 0)
예제 #15
0
 def setUpClass(cls):
     super(FormRepeaterTest, cls).setUpClass()
     cls.connx = ConnectionSettings.objects.create(
         domain=cls.domain,
         url="form-repeater-url",
     )
     cls.repeater = FormRepeater(
         domain=cls.domain,
         connection_settings_id=cls.connx.id,
     )
     cls.repeater.save()
예제 #16
0
    def setUpClass(cls):
        super(FormPayloadGeneratorTest, cls).setUpClass()

        cls.domain_name = "test-domain"
        cls.domain = create_domain(cls.domain_name)
        cls.repeater = FormRepeater(
            domain=cls.domain_name,
            url="form-repeater-url",
        )
        cls.repeatergenerator = FormRepeaterXMLPayloadGenerator(
            repeater=cls.repeater)
        cls.repeater.save()
예제 #17
0
    def test_clone_repeaters(self):
        from corehq.motech.repeaters.models import Repeater
        from corehq.motech.repeaters.models import CaseRepeater
        from corehq.motech.repeaters.models import FormRepeater
        from custom.enikshay.integrations.nikshay.repeaters import NikshayRegisterPatientRepeater

        self.assertEqual(0, len(Repeater.by_domain(self.new_domain)))
        self.assertEqual(
            0, len(NikshayRegisterPatientRepeater.by_domain(self.new_domain)))

        case_repeater = CaseRepeater(
            domain=self.old_domain,
            url='case-repeater-url',
        )
        case_repeater.save()
        self.addCleanup(case_repeater.delete)
        form_repeater = FormRepeater(
            domain=self.old_domain,
            url='form-repeater-url',
        )
        form_repeater.save()
        self.addCleanup(form_repeater.delete)
        custom_repeater = NikshayRegisterPatientRepeater(
            domain=self.old_domain, url='99dots')
        custom_repeater.save()
        self.addCleanup(custom_repeater.delete)

        self.make_clone(include=['repeaters'])

        cloned_repeaters = Repeater.by_domain(self.new_domain)
        self.assertEqual(3, len(cloned_repeaters))
        self.assertEqual(
            {'CaseRepeater', 'FormRepeater', 'NikshayRegisterPatientRepeater'},
            {repeater.doc_type
             for repeater in cloned_repeaters})

        # test cache clearing
        cloned_niksay_repeaters = NikshayRegisterPatientRepeater.by_domain(
            self.new_domain)
        self.assertEqual(1, len(cloned_niksay_repeaters))
예제 #18
0
def zapier_subscription_post_delete(sender, instance, *args, **kwargs):
    """
    Deletes the repeater object when the corresponding zap is turned off
    """
    if instance.event_name == EventTypes.NEW_FORM:
        repeater = FormRepeater.get(instance.repeater_id)
    elif instance.event_name in CASE_TYPE_REPEATER_CLASS_MAP:
        repeater = CASE_TYPE_REPEATER_CLASS_MAP[instance.event_name].get(instance.repeater_id)
    else:
        raise ImmediateHttpResponse(
            HttpBadRequest('The passed event type is not valid.')
        )
    repeater.delete()
예제 #19
0
def zapier_subscription_post_delete(sender, instance, *args, **kwargs):
    """
    Deletes the repeater object when the corresponding zap is turned off
    """
    if instance.event_name == EventTypes.NEW_FORM:
        repeater = FormRepeater.get(instance.repeater_id)
    elif instance.event_name in CASE_TYPE_REPEATER_CLASS_MAP:
        repeater = CASE_TYPE_REPEATER_CLASS_MAP[instance.event_name].get(
            instance.repeater_id)
    else:
        raise ImmediateHttpResponse(
            HttpBadRequest('The passed event type is not valid.'))
    repeater.delete()
예제 #20
0
 def setUpClass(cls):
     super().setUpClass()
     cls.connx = ConnectionSettings.objects.create(
         domain=cls.domain,
         url="form-repeater-url",
     )
     cls.repeater = FormRepeater(
         domain=cls.domain,
         connection_settings_id=cls.connx.id,
     )
     cls.repeatergenerator = FormRepeaterXMLPayloadGenerator(
         repeater=cls.repeater)
     cls.repeater.save()
예제 #21
0
 def setUpClass(cls):
     super().setUpClass()
     cls.instance_id = str(uuid4())
     url = 'https://www.example.com/api/'
     conn = ConnectionSettings.objects.create(domain=DOMAIN,
                                              name=url,
                                              url=url)
     cls.repeater = FormRepeater(
         domain=DOMAIN,
         connection_settings_id=conn.id,
         include_app_id_param=False,
     )
     cls.repeater.save()
     cls.sql_repeater = SQLFormRepeater.objects.get(
         repeater_id=cls.repeater.get_id)
     cls.create_repeat_records()
예제 #22
0
 def setUp(self):
     super(RepeaterTest, self).setUp()
     self.domain = "test-domain"
     create_domain(self.domain)
     self.case_repeater = CaseRepeater(
         domain=self.domain,
         url='case-repeater-url',
     )
     self.case_repeater.save()
     self.form_repeater = FormRepeater(
         domain=self.domain,
         url='form-repeater-url',
     )
     self.form_repeater.save()
     self.log = []
     self.post_xml(self.xform_xml, self.domain)
예제 #23
0
    def setUpClass(cls):
        super().setUpClass()
        cls.domain_obj = create_domain(DOMAIN)
        cls.setup_subscription(DOMAIN, SoftwarePlanEdition.PRO)

        url = 'https://www.example.com/api/'
        conn = ConnectionSettings.objects.create(domain=DOMAIN, name=url, url=url)
        cls.repeater = FormRepeater(
            domain=DOMAIN,
            connection_settings_id=conn.id,
            include_app_id_param=False,
        )
        cls.repeater.save()
        cls.repeater_stub = RepeaterStub.objects.create(
            domain=DOMAIN,
            repeater_id=cls.repeater.get_id,
        )
        cls.instance_id = str(uuid4())
        post_xform(cls.instance_id)
예제 #24
0
    def setUp(self):
        super(RepeaterTest, self).setUp()
        self.domain = "test-domain"
        create_domain(self.domain)
        self.case_repeater = CaseRepeater(
            domain=self.domain,
            url='case-repeater-url',
        )
        self.case_repeater.save()
        self.form_repeater = FormRepeater(
            domain=self.domain,
            url='form-repeater-url',
        )
        self.form_repeater.save()
        self.log = []

        with patch('corehq.motech.repeaters.models.simple_post') as mock_fire:
            self.post_xml(self.xform_xml, self.domain)
            self.initial_fire_call_count = mock_fire.call_count
예제 #25
0
class RepeaterTest(BaseRepeaterTest):
    domain = "repeater-test"

    def setUp(self):
        super(RepeaterTest, self).setUp()
        self.case_connx = ConnectionSettings.objects.create(
            domain=self.domain,
            url='case-repeater-url',
        )
        self.case_repeater = CaseRepeater(
            domain=self.domain,
            connection_settings_id=self.case_connx.id,
        )
        self.case_repeater.save()

        self.form_connx = ConnectionSettings.objects.create(
            domain=self.domain,
            url='form-repeater-url',
        )
        self.form_repeater = FormRepeater(
            domain=self.domain,
            connection_settings_id=self.form_connx.id,
        )
        self.form_repeater.save()
        self.log = []

        with patch('corehq.motech.repeaters.models.simple_post',
                   return_value=MockResponse(status_code=500,
                                             reason="Borked")) as mock_fire:
            self.post_xml(self.xform_xml, self.domain)
            self.initial_fire_call_count = mock_fire.call_count

    def tearDown(self):
        self.case_repeater.delete()
        self.case_connx.delete()
        self.form_repeater.delete()
        self.form_connx.delete()
        FormProcessorTestUtils.delete_all_cases_forms_ledgers(self.domain)
        delete_all_repeat_records()
        super(RepeaterTest, self).tearDown()

    def repeat_records(self):
        return super(RepeaterTest, self).repeat_records(self.domain)

    @run_with_all_backends
    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)
        for repeat_record in self.repeat_records():
            self.assertNotEqual(repeat_record.payload_id, '1234')

    @run_with_all_backends
    def test_skip_duplicates(self):
        """
        Ensure that submitting a duplicate form does not create extra RepeatRecords
        """
        self.assertEqual(len(self.repeat_records()), 2)
        # this form is already submitted during setUp so a second submission should be a duplicate
        form = self.post_xml(self.xform_xml, self.domain).xform
        self.assertTrue(form.is_duplicate)
        self.assertEqual(len(self.repeat_records()), 2)

    @run_with_all_backends
    def test_repeater_failed_sends(self):
        """
        This tests records that fail are requeued later
        """
        def now():
            return datetime.utcnow()

        repeat_records = self.repeat_records()
        self.assertEqual(len(repeat_records), 2)

        for repeat_record in repeat_records:
            with patch('corehq.motech.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)

        # Enqueued repeat records have next_check incremented by 48 hours
        next_check_time = now() + timedelta(minutes=60) + timedelta(hours=48)

        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)

    @run_with_all_backends
    def test_update_failure_next_check(self):
        now = datetime.utcnow()
        record = RepeatRecord(
            domain=self.domain,
            repeater_id=self.case_repeater.get_id,
            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)

    @run_with_all_backends
    def test_repeater_successful_send(self):

        repeat_records = self.repeat_records()

        for repeat_record in repeat_records:
            with patch('corehq.motech.repeaters.models.simple_post') as mock_post, \
                    patch.object(ConnectionSettings, 'get_auth_manager') as mock_manager:
                mock_post.return_value.status_code = 200
                mock_manager.return_value = 'MockAuthManager'
                repeat_record.fire()
                self.assertEqual(mock_post.call_count, 1)
                mock_post.assert_called_with(
                    self.domain,
                    repeat_record.repeater.get_url(repeat_record),
                    repeat_record.get_payload(),
                    headers=repeat_record.repeater.get_headers(repeat_record),
                    auth_manager='MockAuthManager',
                    verify=repeat_record.repeater.verify,
                    notify_addresses=[],
                    payload_id=repeat_record.payload_id,
                )

        # The following is pretty fickle and depends on which of
        #   - corehq.motech.repeaters.signals
        #   - casexml.apps.case.signals
        # gets loaded first.
        # This is deterministic but easily affected by minor code changes
        repeat_records = self.repeat_records()
        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()), 0)

        self.post_xml(self.update_xform_xml, self.domain)
        self.assertEqual(len(self.repeat_records()), 2)

    @run_with_all_backends
    def test_check_repeat_records(self):
        self.assertEqual(len(self.repeat_records()), 2)
        self.assertEqual(self.initial_fire_call_count, 2)

        with patch('corehq.motech.repeaters.models.simple_post') as mock_fire:
            check_repeaters()
            self.assertEqual(mock_fire.call_count, 0)

    @run_with_all_backends
    def test_repeat_record_status_check(self):
        self.assertEqual(len(self.repeat_records()), 2)

        # Do not trigger cancelled records
        for repeat_record in self.repeat_records():
            repeat_record.cancelled = True
            repeat_record.save()
        with patch('corehq.motech.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 self.repeat_records():
            with patch('corehq.motech.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 self.repeat_records():
            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.motech.repeaters.models.simple_post') as mock_fire:
            check_repeaters()
            self.assertEqual(mock_fire.call_count, 0)
            for repeat_record in self.repeat_records():
                self.assertEqual(repeat_record.state, RECORD_SUCCESS_STATE)

    @run_with_all_backends
    def test_retry_process_repeat_record_locking(self):
        self.assertEqual(len(self.repeat_records()), 2)

        with patch('corehq.motech.repeaters.tasks.retry_process_repeat_record'
                   ) as mock_process:
            check_repeaters()
            self.assertEqual(mock_process.delay.call_count, 0)

        for record in self.repeat_records():
            # Resetting next_check should allow them to be requeued
            record.next_check = datetime.utcnow()
            record.save()

        with patch('corehq.motech.repeaters.tasks.retry_process_repeat_record'
                   ) as mock_process:
            check_repeaters()
            self.assertEqual(mock_process.delay.call_count, 2)

    @run_with_all_backends
    def test_automatic_cancel_repeat_record(self):
        repeat_record = self.case_repeater.register(
            CaseAccessors(self.domain).get_case(CASE_ID))
        self.assertEqual(1, repeat_record.overall_tries)
        with patch('corehq.motech.repeaters.models.simple_post',
                   side_effect=Exception('Boom!')):
            for __ in range(repeat_record.max_possible_tries -
                            repeat_record.overall_tries):
                repeat_record.fire()
        self.assertEqual(True, repeat_record.cancelled)
        repeat_record.requeue()
        self.assertEqual(0, repeat_record.overall_tries)
        self.assertNotEqual(None, repeat_record.next_check)
예제 #26
0
class RepeaterTest(BaseRepeaterTest):

    def setUp(self):
        super(RepeaterTest, self).setUp()
        self.domain = "test-domain"
        create_domain(self.domain)
        self.case_repeater = CaseRepeater(
            domain=self.domain,
            url='case-repeater-url',
        )
        self.case_repeater.save()
        self.form_repeater = FormRepeater(
            domain=self.domain,
            url='form-repeater-url',
        )
        self.form_repeater.save()
        self.log = []

        with patch('corehq.motech.repeaters.models.simple_post') as mock_fire:
            self.post_xml(self.xform_xml, self.domain)
            self.initial_fire_call_count = mock_fire.call_count

    def tearDown(self):
        self.case_repeater.delete()
        self.form_repeater.delete()
        FormProcessorTestUtils.delete_all_cases_forms_ledgers(self.domain)
        delete_all_repeat_records()
        super(RepeaterTest, self).tearDown()

    def repeat_records(self):
        return super(RepeaterTest, self).repeat_records(self.domain)

    @run_with_all_backends
    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)
        for repeat_record in self.repeat_records():
            self.assertNotEqual(repeat_record.payload_id, '1234')

    @run_with_all_backends
    def test_skip_duplicates(self):
        """
        Ensure that submitting a duplicate form does not create extra RepeatRecords
        """
        self.assertEqual(len(self.repeat_records()), 2)
        # this form is already submitted during setUp so a second submission should be a duplicate
        form = self.post_xml(self.xform_xml, self.domain).xform
        self.assertTrue(form.is_duplicate)
        self.assertEqual(len(self.repeat_records()), 2)

    @run_with_all_backends
    def test_repeater_failed_sends(self):
        """
        This tests records that fail are requeued later
        """
        def now():
            return datetime.utcnow()

        repeat_records = self.repeat_records()
        self.assertEqual(len(repeat_records), 2)

        for repeat_record in repeat_records:
            with patch(
                    'corehq.motech.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)

        # Enqueued repeat records have next_check incremented by 48 hours
        next_check_time = now() + timedelta(minutes=60) + timedelta(hours=48)

        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)

    @run_with_all_backends
    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)

    @run_with_all_backends
    def test_repeater_successful_send(self):

        repeat_records = self.repeat_records()

        for repeat_record in repeat_records:
            with patch(
                    'corehq.motech.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(),
                    verify=repeat_record.repeater.verify,
                )

        # The following is pretty fickle and depends on which of
        #   - corehq.motech.repeaters.signals
        #   - casexml.apps.case.signals
        # gets loaded first.
        # This is deterministic but easily affected by minor code changes
        repeat_records = self.repeat_records()
        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()), 0)

        self.post_xml(self.update_xform_xml, self.domain)
        self.assertEqual(len(self.repeat_records()), 2)

    @run_with_all_backends
    def test_check_repeat_records(self):
        self.assertEqual(len(self.repeat_records()), 2)
        self.assertEqual(self.initial_fire_call_count, 2)

        with patch('corehq.motech.repeaters.models.simple_post') as mock_fire:
            check_repeaters()
            self.assertEqual(mock_fire.call_count, 0)

    @run_with_all_backends
    def test_repeat_record_status_check(self):
        self.assertEqual(len(self.repeat_records()), 2)

        # Do not trigger cancelled records
        for repeat_record in self.repeat_records():
            repeat_record.cancelled = True
            repeat_record.save()
        with patch('corehq.motech.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 self.repeat_records():
            with patch('corehq.motech.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 self.repeat_records():
                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.motech.repeaters.models.simple_post') as mock_fire:
            check_repeaters()
            self.assertEqual(mock_fire.call_count, 0)
            for repeat_record in self.repeat_records():
                self.assertEqual(repeat_record.state, RECORD_SUCCESS_STATE)

    @run_with_all_backends
    def test_process_repeat_record_locking(self):
        self.assertEqual(len(self.repeat_records()), 2)

        with patch('corehq.motech.repeaters.tasks.process_repeat_record') as mock_process:
            check_repeaters()
            self.assertEqual(mock_process.delay.call_count, 0)

        for record in self.repeat_records():
            # Resetting next_check should allow them to be requeued
            record.next_check = datetime.utcnow()
            record.save()

        with patch('corehq.motech.repeaters.tasks.process_repeat_record') as mock_process:
            check_repeaters()
            self.assertEqual(mock_process.delay.call_count, 2)

    @run_with_all_backends
    def test_automatic_cancel_repeat_record(self):
        repeat_record = self.case_repeater.register(CaseAccessors(self.domain).get_case(CASE_ID))
        self.assertEqual(1, repeat_record.overall_tries)
        with patch('corehq.motech.repeaters.models.simple_post', side_effect=Exception('Boom!')):
            for __ in range(repeat_record.max_possible_tries - repeat_record.overall_tries):
                repeat_record.fire()
        self.assertEqual(True, repeat_record.cancelled)
        repeat_record.requeue()
        self.assertEqual(0, repeat_record.overall_tries)
        self.assertNotEqual(None, repeat_record.next_check)
예제 #27
0
def cleanup_repeaters_for_domain(domain):
    for repeater in FormRepeater.by_domain(domain):
            repeater.delete()
    for case_repeater_class in CASE_TYPE_REPEATER_CLASS_MAP.values():
        for repeater in case_repeater_class.by_domain(domain):
            repeater.delete()
예제 #28
0
 def setUp(self):
     self.rep = FormRepeater(
         domain=DOMAIN,
         url='https://server.example.com/api/',
     )
     self.rep.save()
예제 #29
0
class RepeaterTest(BaseRepeaterTest):
    def setUp(self):
        super(RepeaterTest, self).setUp()
        self.domain = "test-domain"
        create_domain(self.domain)
        self.case_repeater = CaseRepeater(
            domain=self.domain,
            url='case-repeater-url',
        )
        self.case_repeater.save()
        self.form_repeater = FormRepeater(
            domain=self.domain,
            url='form-repeater-url',
        )
        self.form_repeater.save()
        self.log = []
        self.post_xml(self.xform_xml, self.domain)

    def tearDown(self):
        self.case_repeater.delete()
        self.form_repeater.delete()
        FormProcessorTestUtils.delete_all_cases_forms_ledgers(self.domain)
        delete_all_repeat_records()
        super(RepeaterTest, self).tearDown()

    @run_with_all_backends
    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')

    @run_with_all_backends
    def test_skip_duplicates(self):
        """
        Ensure that submitting a duplicate form does not create extra RepeatRecords
        """
        self.assertEqual(len(RepeatRecord.all()), 2)
        # this form is already submitted during setUp so a second submission should be a duplicate
        form = self.post_xml(self.xform_xml, self.domain).xform
        self.assertTrue(form.is_duplicate)
        self.assertEqual(len(RepeatRecord.all()), 2)

    @run_with_all_backends
    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.motech.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)

    @run_with_all_backends
    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)

    @run_with_all_backends
    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.motech.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(),
                    verify=repeat_record.repeater.verify,
                )

        # The following is pretty fickle and depends on which of
        #   - corehq.motech.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)

    @run_with_all_backends
    def test_check_repeat_records(self):
        self.assertEqual(len(RepeatRecord.all()), 2)

        with patch('corehq.motech.repeaters.models.simple_post') as mock_fire:
            check_repeaters()
            self.assertEqual(mock_fire.call_count, 2)

        with patch('corehq.motech.repeaters.models.simple_post') as mock_fire:
            check_repeaters()
            self.assertEqual(mock_fire.call_count, 0)

    @run_with_all_backends
    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.motech.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.motech.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.motech.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)

    @run_with_all_backends
    def test_process_repeat_record_locking(self):
        self.assertEqual(len(RepeatRecord.all()), 2)

        with patch('corehq.motech.repeaters.tasks.process_repeat_record'
                   ) as mock_process:
            check_repeaters()
            self.assertEqual(mock_process.delay.call_count, 2)

        with patch('corehq.motech.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.motech.repeaters.tasks.process_repeat_record'
                   ) as mock_process:
            check_repeaters()
            self.assertEqual(mock_process.delay.call_count, 2)

    @run_with_all_backends
    def test_automatic_cancel_repeat_record(self):
        repeat_record = self.case_repeater.register(
            CaseAccessors(self.domain).get_case(CASE_ID))
        repeat_record.overall_tries = 1
        with patch('corehq.motech.repeaters.models.simple_post',
                   side_effect=Exception('Boom!')):
            repeat_record.fire()
        self.assertEqual(2, repeat_record.overall_tries)
        with patch('corehq.motech.repeaters.models.simple_post',
                   side_effect=Exception('Boom!')):
            repeat_record.fire()
        self.assertEqual(True, repeat_record.cancelled)
        repeat_record.requeue()
        self.assertEqual(0, repeat_record.overall_tries)
        self.assertNotEqual(None, repeat_record.next_check)
예제 #30
0
def cleanup_repeaters_for_domain(domain):
    for repeater in FormRepeater.by_domain(domain):
        repeater.delete()
    for case_repeater_class in CASE_TYPE_REPEATER_CLASS_MAP.values():
        for repeater in case_repeater_class.by_domain(domain):
            repeater.delete()