Пример #1
0
    def test_cant_own_case(self):
        interface = FormProcessorInterface()
        _, _, [case] = interface.submit_form_locally(ALICE_XML, ALICE_DOMAIN)
        response, form, cases = interface.submit_form_locally(EVE_XML, EVE_DOMAIN)

        self.assertIn('IllegalCaseId', response.content)
        self.assertFalse(hasattr(case, 'plan_to_buy_gun'))

        _, _, [case] = interface.submit_form_locally(ALICE_UPDATE_XML, ALICE_DOMAIN)
        self.assertEqual(case.plan_to_buy_gun, 'no')
    def testOutOfOrderSubmissions(self):
        dir = os.path.join(os.path.dirname(__file__), "data", "ordering")
        interface = FormProcessorInterface()
        for fname in ('update_oo.xml', 'create_oo.xml'):
            with open(os.path.join(dir, fname), "rb") as f:
                xml_data = f.read()
            interface.submit_form_locally(xml_data)

        case = interface.case_model.get('30bc51f6-3247-4966-b4ae-994f572e85fe')
        self.assertEqual('from the update form', case.pupdate)
        self.assertEqual('from the create form', case.pcreate)
        self.assertEqual('overridden by the update form', case.pboth)
class MultiCaseTest(TestCase):
    def setUp(self):
        self.domain = "gigglyfoo"
        self.interface = FormProcessorInterface()
        delete_all_xforms()
        delete_all_cases()

    def testParallel(self):
        file_path = os.path.join(os.path.dirname(__file__), "data", "multicase", "parallel_cases.xml")
        with open(file_path, "rb") as f:
            xml_data = f.read()

        _, form, cases = self.interface.submit_form_locally(xml_data, domain=self.domain)
        self.assertEqual(4, len(cases))
        self._check_ids(form, cases)

    def testMixed(self):
        file_path = os.path.join(os.path.dirname(__file__), "data", "multicase", "mixed_cases.xml")
        with open(file_path, "rb") as f:
            xml_data = f.read()
        _, form, cases = self.interface.submit_form_locally(xml_data, domain=self.domain)
        self.assertEqual(4, len(cases))
        self._check_ids(form, cases)

    def testCasesInRepeats(self):
        file_path = os.path.join(os.path.dirname(__file__), "data", "multicase", "case_in_repeats.xml")
        with open(file_path, "rb") as f:
            xml_data = f.read()
        _, form, cases = self.interface.submit_form_locally(xml_data, domain=self.domain)
        self.assertEqual(3, len(cases))
        self._check_ids(form, cases)

    def _check_ids(self, form, cases):
        for case in cases:
            ids = self.interface.case_model.get_case_xform_ids(case.case_id)
            self.assertEqual(1, len(ids))
            self.assertEqual(form._id, ids[0])
Пример #4
0
class DuplicateFormTest(TestCase, TestFileMixin):
    ID = '7H46J37FGH3'
    file_path = ('data',)
    root = os.path.dirname(__file__)

    def setUp(self):
        self.interface = FormProcessorInterface()

    def tearDown(self):
        FormProcessorTestUtils.delete_all_xforms()

    @run_with_all_backends
    def test_basic_duplicate(self):
        xml_data = self.get_xml('duplicate')
        xform = self.interface.post_xform(xml_data)
        self.assertEqual(self.ID, xform.form_id)
        self.assertTrue(xform.is_normal)
        self.assertEqual("test-domain", xform.domain)

        xform = self.interface.post_xform(xml_data, domain='test-domain')
        self.assertNotEqual(self.ID, xform.form_id)
        self.assertTrue(xform.is_duplicate)
        self.assertTrue(self.ID in xform.problem)

    @run_with_all_backends
    def test_wrong_doc_type(self):
        domain = 'test-domain'
        instance = self.get_xml('duplicate')

        # Post an xform with an alternate doc_type
        xform1 = self.interface.post_xform(
            instance,
            domain=domain,
        )

        # Change the doc_type of the form by archiving it
        self.interface.xform_model.archive(xform1)
        xform1 = self.interface.xform_model.get(xform1.form_id)
        self.assertTrue(xform1.is_archived)

        # Post an xform with that has different doc_type but same id
        _, xform2, _ = self.interface.submit_form_locally(
            instance,
            domain=domain,
        )

        self.assertNotEqual(xform1.form_id, xform2.form_id)

    @run_with_all_backends
    def test_wrong_domain(self):
        domain = 'test-domain'
        instance = self.get_xml('duplicate')

        _, xform1, _ = self.interface.submit_form_locally(
            instance,
            domain='wrong-domain',
        )
        _, xform2, _ = self.interface.submit_form_locally(
            instance,
            domain=domain,
        )
        self.assertNotEqual(xform1.form_id, xform2.form_id)
Пример #5
0
class EditFormTest(TestCase, TestFileMixin):
    ID = '7H46J37FGH3'
    domain = 'test-form-edits'

    file_path = ('data', 'deprecation')
    root = os.path.dirname(__file__)

    def setUp(self):
        self.interface = FormProcessorInterface(TEST_DOMAIN_NAME)

    def tearDown(self):
        FormProcessorTestUtils.delete_all_xforms()
        UnfinishedSubmissionStub.objects.all().delete()

    @run_with_all_backends
    def test_basic_edit(self):
        original_xml = self.get_xml('original')
        edit_xml = self.get_xml('edit')
        yesterday = datetime.utcnow() - timedelta(days=1)

        def process(form):
            form.domain = self.domain
            form.received_on = yesterday

        xform = self.interface.post_xform(original_xml, process=process)
        self.assertEqual(self.ID, xform.form_id)
        self.assertTrue(xform.is_normal)
        self.assertEqual("", xform.form_data['vitals']['height'])
        self.assertEqual("other", xform.form_data['assessment']['categories'])

        xform = self.interface.post_xform(edit_xml, domain=self.domain)
        self.assertEqual(self.ID, xform.form_id)
        self.assertTrue(xform.is_normal)
        self.assertEqual("100", xform.form_data['vitals']['height'])
        self.assertEqual("Edited Baby!", xform.form_data['assessment']['categories'])

        deprecated_xform = self.interface.xform_model.get(xform.deprecated_form_id)

        self.assertEqual(self.ID, deprecated_xform.orig_id)
        self.assertNotEqual(self.ID, deprecated_xform.form_id)
        self.assertTrue(deprecated_xform.is_deprecated)
        self.assertEqual("", deprecated_xform.form_data['vitals']['height'])
        self.assertEqual("other", deprecated_xform.form_data['assessment']['categories'])

        self.assertEqual(xform.received_on, deprecated_xform.received_on)
        self.assertEqual(xform.deprecated_form_id, deprecated_xform.form_id)
        self.assertTrue(xform.edited_on > deprecated_xform.received_on)

        self.assertEqual(
            deprecated_xform.get_xml(),
            original_xml
        )
        self.assertEqual(xform.get_xml(), edit_xml)

    @run_with_all_backends
    def test_broken_save(self):
        """
        Test that if the second form submission terminates unexpectedly
        and the main form isn't saved, then there are no side effects
        such as the original having been marked as deprecated.
        """

        original_xml = self.get_xml('original')
        edit_xml = self.get_xml('edit')

        _, xform, _ = self.interface.submit_form_locally(original_xml, self.domain)
        self.assertEqual(self.ID, xform.form_id)
        self.assertTrue(xform.is_normal)
        self.assertEqual(self.domain, xform.domain)

        self.assertEqual(
            UnfinishedSubmissionStub.objects.filter(xform_id=self.ID).count(),
            0
        )

        with patch.object(self.interface.processor, 'bulk_save', side_effect=RequestFailed):
            with self.assertRaises(RequestFailed):
                self.interface.submit_form_locally(edit_xml, self.domain)

        # it didn't go through, so make sure there are no edits still
        self.assertIsNone(getattr(xform, 'deprecated_form_id', None))

        xform = self.interface.xform_model.get(self.ID)
        self.assertIsNotNone(xform)
        self.assertEqual(
            UnfinishedSubmissionStub.objects.filter(xform_id=self.ID,
                                                    saved=False).count(),
            1
        )
        self.assertEqual(
            UnfinishedSubmissionStub.objects.filter(xform_id=self.ID).count(),
            1
        )

    def test_case_management(self):
        form_id = uuid.uuid4().hex
        case_id = uuid.uuid4().hex
        owner_id = uuid.uuid4().hex
        case_block = CaseBlock(
            create=True,
            case_id=case_id,
            case_type='person',
            owner_id=owner_id,
            update={
                'property': 'original value'
            }
        ).as_string()
        submit_case_blocks(case_block, domain=self.domain, form_id=form_id)

        # validate some assumptions
        case = self.interface.case_model.get(case_id)
        self.assertEqual(case.type, 'person')
        self.assertEqual(case.property, 'original value')
        self.assertEqual([form_id], case.xform_ids)
        self.assertEqual(2, len(case.actions))
        for a in case.actions:
            self.assertEqual(form_id, a.xform_id)

        # submit a new form with a different case update
        case_block = CaseBlock(
            create=True,
            case_id=case_id,
            case_type='newtype',
            owner_id=owner_id,
            update={
                'property': 'edited value'
            }
        ).as_string()
        submit_case_blocks(case_block, domain=self.domain, form_id=form_id)

        case = self.interface.case_model.get(case_id)
        self.assertEqual(case.type, 'newtype')
        self.assertEqual(case.property, 'edited value')
        self.assertEqual([form_id], case.xform_ids)
        self.assertEqual(2, len(case.actions))
        for a in case.actions:
            self.assertEqual(form_id, a.xform_id)

    @run_with_all_backends
    def test_second_edit_fails(self):
        form_id = uuid.uuid4().hex
        case_id = uuid.uuid4().hex
        case_block = CaseBlock(
            create=True,
            case_id=case_id,
            case_type='person',
        ).as_string()
        submit_case_blocks(case_block, domain=self.domain, form_id=form_id)

        # submit an edit form with a bad case update (for example a bad ID)
        case_block = CaseBlock(
            create=True,
            case_id='',
            case_type='person',
        ).as_string()
        submit_case_blocks(case_block, domain=self.domain, form_id=form_id)

        xform = self.interface.xform_model.get(form_id)
        self.assertTrue(xform.is_error)

        deprecated_xform = self.interface.xform_model.get(xform.deprecated_form_id)
        self.assertTrue(deprecated_xform.is_deprecated)

    def test_case_management_ordering(self):
        case_id = uuid.uuid4().hex
        owner_id = uuid.uuid4().hex

        # create a case
        case_block = CaseBlock(
            create=True,
            case_id=case_id,
            case_type='person',
            owner_id=owner_id,
        ).as_string()
        create_form_id = submit_case_blocks(case_block, domain=self.domain)

        # validate that worked
        case = self.interface.case_model.get(case_id)
        self.assertEqual([create_form_id], case.xform_ids)
        self.assertEqual([create_form_id], [a.xform_id for a in case.actions])
        for a in case.actions:
            self.assertEqual(create_form_id, a.xform_id)

        edit_date = datetime.utcnow()
        # set some property value
        case_block = CaseBlock(
            create=False,
            case_id=case_id,
            date_modified=edit_date,
            update={
                'property': 'first value',
            }
        ).as_string()
        edit_form_id = submit_case_blocks(case_block, domain=self.domain)

        # validate that worked
        case = self.interface.case_model.get(case_id)
        self.assertEqual(case.property, 'first value')
        self.assertEqual([create_form_id, edit_form_id], case.xform_ids)
        self.assertEqual([create_form_id, edit_form_id], [a.xform_id for a in case.actions])

        # submit a second (new) form updating the value
        case_block = CaseBlock(
            create=False,
            case_id=case_id,
            update={
                'property': 'final value',
            }
        ).as_string()
        second_edit_form_id = submit_case_blocks(case_block, domain=self.domain)

        # validate that worked
        case = self.interface.case_model.get(case_id)
        self.assertEqual(case.property, 'final value')
        self.assertEqual([create_form_id, edit_form_id, second_edit_form_id], case.xform_ids)
        self.assertEqual([create_form_id, edit_form_id, second_edit_form_id], [a.xform_id for a in
            case.actions])

        # deprecate the middle edit
        case_block = CaseBlock(
            create=False,
            case_id=case_id,
            date_modified=edit_date,  # need to use the previous edit date for action sort comparisons
            update={
                'property': 'edited value',
                'added_property': 'added value',
            }
        ).as_string()
        submit_case_blocks(case_block, domain=self.domain, form_id=edit_form_id)

        # ensure that the middle edit stays in the right place and is applied
        # before the final one
        case = self.interface.case_model.get(case_id)
        self.assertEqual(case.property, 'final value')
        self.assertEqual(case.added_property, 'added value')
        self.assertEqual([create_form_id, edit_form_id, second_edit_form_id], case.xform_ids)
        self.assertEqual([create_form_id, edit_form_id, second_edit_form_id], [a.xform_id for a in
            case.actions])
Пример #6
0
class TestFormArchiving(TestCase, TestFileMixin):
    file_path = ('data', 'sample_xforms')
    root = os.path.dirname(__file__)

    def setUp(self):
        self.interface = FormProcessorInterface('test-domain')

    def tearDown(self):
        FormProcessorTestUtils.delete_all_xforms()
        FormProcessorTestUtils.delete_all_cases()

    @run_with_all_backends
    def testArchive(self):
        xml_data = self.get_xml('basic')
        response, xform, cases = self.interface.submit_form_locally(
            xml_data,
            'test-domain',
        )

        self.assertTrue(xform.is_normal)
        self.assertEqual(0, len(xform.history))

        lower_bound = datetime.utcnow() - timedelta(seconds=1)
        xform.archive(user='******')
        upper_bound = datetime.utcnow() + timedelta(seconds=1)

        xform = self.interface.xform_model.get(xform.form_id)
        self.assertTrue(xform.is_archived)

        [archival] = xform.history
        self.assertTrue(lower_bound <= archival.date <= upper_bound)
        self.assertEqual('archive', archival.operation)
        self.assertEqual('mr. librarian', archival.user)

        lower_bound = datetime.utcnow() - timedelta(seconds=1)
        xform.unarchive(user='******')
        upper_bound = datetime.utcnow() + timedelta(seconds=1)

        xform = self.interface.xform_model.get(xform.form_id)
        self.assertTrue(xform.is_normal)

        [archival, restoration] = xform.history
        self.assertTrue(lower_bound <= restoration.date <= upper_bound)
        self.assertEqual('unarchive', restoration.operation)
        self.assertEqual('mr. researcher', restoration.user)

    def testSignal(self):
        global archive_counter, restore_counter
        archive_counter = 0
        restore_counter = 0

        def count_archive(**kwargs):
            global archive_counter
            archive_counter += 1

        def count_unarchive(**kwargs):
            global restore_counter
            restore_counter += 1

        xform_archived.connect(count_archive)
        xform_unarchived.connect(count_unarchive)

        xml_data = self.get_xml('basic')
        response, xform, cases = self.interface.submit_form_locally(
            xml_data,
            'test-domain',
        )

        self.assertEqual(0, archive_counter)
        self.assertEqual(0, restore_counter)

        xform.archive()
        self.assertEqual(1, archive_counter)
        self.assertEqual(0, restore_counter)

        xform.unarchive()
        self.assertEqual(1, archive_counter)
        self.assertEqual(1, restore_counter)
Пример #7
0
class BaseCaseMultimediaTest(TestCase, TestFileMixin):

    file_path = ('data', 'multimedia')
    root = os.path.dirname(__file__)

    def setUp(self):
        self.interface = FormProcessorInterface()
        FormProcessorTestUtils.delete_all_cases()
        FormProcessorTestUtils.delete_all_xforms()

    def _formatXForm(self, doc_id, raw_xml, attachment_block, date=None):
        if date is None:
            date = datetime.utcnow()
        final_xml = Template(raw_xml).render(Context({
            "attachments": attachment_block,
            "time_start": json_format_datetime(date - timedelta(minutes=4)),
            "time_end": json_format_datetime(date),
            "date_modified": json_format_datetime(date),
            "doc_id": doc_id
        }))
        return final_xml

    def _prepAttachments(self, new_attachments, removes=[]):
        """
        Returns:
            attachment_block - An XML representation of the attachment
            dict_attachments - A key-value dict where the key is the name and the value is a Stream of the
            attachment
        """
        attachment_block = ''.join([self._singleAttachBlock(x) for x in new_attachments] + [self._singleAttachRemoveBlock(x) for x in removes])
        dict_attachments = dict((MEDIA_FILES[attach_name], self._attachmentFileStream(attach_name)) for attach_name in new_attachments)
        return attachment_block, dict_attachments

    def _singleAttachBlock(self, key):
        return '<n0:%s src="%s" from="local"/>' % (key, MEDIA_FILES[key])

    def _singleAttachRemoveBlock(self, key):
        return '<n0:%s />' % key

    def _attachmentFileStream(self, key):
        attachment_path = MEDIA_FILES[key]
        attachment = open(attachment_path, 'rb')
        uf = UploadedFile(attachment, key)
        return uf

    def _calc_file_hash(self, key):
        with open(MEDIA_FILES[key], 'rb') as attach:
            return hashlib.md5(attach.read()).hexdigest()

    def _do_submit(self, xml_data, dict_attachments, sync_token=None, date=None):
        """
        RequestFactory submitter - simulates direct submission to server directly (no need to call process case after fact)
        """
        response, form, cases = self.interface.submit_form_locally(
            xml_data,
            TEST_DOMAIN_NAME,
            attachments=dict_attachments,
            last_sync_token=sync_token,
            received_on=date
        )
        attachments = form.attachments
        self.assertEqual(set(dict_attachments.keys()),
                         set(attachments.keys()))
        [case] = cases
        self.assertEqual(case.case_id, TEST_CASE_ID)

        return response, form, cases

    def _submit_and_verify(self, doc_id, xml_data, dict_attachments,
                           sync_token=None, date=None):
        response, form, cases = self._do_submit(xml_data, dict_attachments, sync_token, date=date)

        attachments = form.attachments
        self.assertEqual(len(dict_attachments), len(attachments))
        for k, vstream in dict_attachments.items():
            fileback = form.get_attachment(k)
            # rewind the pointer before comparing
            orig_attachment = vstream
            orig_attachment.seek(0)
            self.assertEqual(hashlib.md5(fileback).hexdigest(), hashlib.md5(orig_attachment.read()).hexdigest())
        return response, form, cases

    def _doCreateCaseWithMultimedia(self, attachments=['fruity_file']):
        xml_data = self.get_xml('multimedia_create')
        attachment_block, dict_attachments = self._prepAttachments(attachments)
        final_xml = self._formatXForm(CREATE_XFORM_ID, xml_data, attachment_block)
        return self._submit_and_verify(CREATE_XFORM_ID, final_xml, dict_attachments)

    def _doSubmitUpdateWithMultimedia(self, new_attachments=None, removes=None,
                                      sync_token=None, date=None):
        new_attachments = new_attachments if new_attachments is not None \
            else ['commcare_logo_file', 'dimagi_logo_file']
        removes = removes if removes is not None else ['fruity_file']
        attachment_block, dict_attachments = self._prepAttachments(new_attachments, removes=removes)
        raw_xform = self.get_xml('multimedia_update')
        doc_id = uuid.uuid4().hex
        final_xform = self._formatXForm(doc_id, raw_xform, attachment_block, date)
        return self._submit_and_verify(doc_id, final_xform, dict_attachments,
                                sync_token, date=date)
Пример #8
0
class CaseBugTest(TestCase, TestFileMixin):
    """
    Tests bugs that come up in case processing
    """
    file_path = ('data', 'bugs')
    root = os.path.dirname(__file__)

    def setUp(self):
        self.interface = FormProcessorInterface()
        delete_all_cases()

    def test_conflicting_ids(self):
        """
        If a form and a case share an ID it's a conflict
        """
        xml_data = self.get_xml('id_conflicts')
        with self.assertRaises(BulkSaveError):
            self.interface.submit_form_locally(xml_data)

    def test_empty_case_id(self):
        """
        Ensure that form processor fails on empty id
        """
        xml_data = self.get_xml('empty_id')
        response, form, cases = self.interface.submit_form_locally(xml_data)
        self.assertIn('IllegalCaseId', response.content)

    def _testCornerCaseDatatypeBugs(self, value):

        def _test(custom_format_args):
            case_id = uuid.uuid4().hex
            format_args = {
                'case_id': case_id,
                'form_id': uuid.uuid4().hex,
                'user_id': uuid.uuid4().hex,
                'case_name': 'data corner cases',
                'case_type': 'datatype-check',
            }
            format_args.update(custom_format_args)
            for filename in ['bugs_in_case_create_datatypes', 'bugs_in_case_update_datatypes']:
                xml_data = self.get_xml(filename).format(**format_args)
                response, form, [case] = self.interface.submit_form_locally(xml_data)

                self.assertEqual(format_args['user_id'], case.user_id)
                self.assertEqual(format_args['case_name'], case.name)
                self.assertEqual(format_args['case_type'], case.type)

        _test({'case_name': value})
        _test({'case_type': value})
        _test({'user_id': value})

    def testDateInCasePropertyBug(self):
        """
        Submits a case name/case type/user_id that looks like a date
        """
        self._testCornerCaseDatatypeBugs('2011-11-16')

    def testIntegerInCasePropertyBug(self):
        """
        Submits a case name/case type/user_id that looks like a number
        """
        self._testCornerCaseDatatypeBugs('42')

    def testDecimalInCasePropertyBug(self):
        """
        Submits a case name/case type/user_id that looks like a decimal
        """
        self._testCornerCaseDatatypeBugs('4.06')

    def testDuplicateCasePropertiesBug(self):
        """
        Submit multiple values for the same property in an update block
        """
        xml_data = self.get_xml('duplicate_case_properties')
        response, form, [case] = self.interface.submit_form_locally(xml_data)
        self.assertEqual("", case.foo)

        xml_data = self.get_xml('duplicate_case_properties_2')
        response, form, [case] = self.interface.submit_form_locally(xml_data)
        self.assertEqual("2", case.bar)

    def testMultipleCaseBlocks(self):
        """
        How do we do when submitting a form with multiple blocks for the same case?
        """
        xml_data = self.get_xml('multiple_case_blocks')
        response, form, [case] = self.interface.submit_form_locally(xml_data)

        self.assertEqual('1630005', case.community_code)
        self.assertEqual('SantaMariaCahabon', case.district_name)
        self.assertEqual('TAMERLO', case.community_name)

        ids = case.xform_ids
        self.assertEqual(1, len(ids))
        self.assertEqual(form.form_id, ids[0])

    def testLotsOfSubcases(self):
        """
        How do we do when submitting a form with multiple blocks for the same case?
        """
        xml_data = self.get_xml('lots_of_subcases')
        response, form, cases = self.interface.submit_form_locally(xml_data)
        self.assertEqual(11, len(cases))

    def testSubmitToDeletedCase(self):
        # submitting to a deleted case should succeed and affect the case
        case_id = 'immagetdeleted'
        [xform, [case]] = self.interface.post_case_blocks([
            CaseBlock(create=True, case_id=case_id, user_id='whatever',
                update={'foo': 'bar'}).as_xml()
        ])
        case.soft_delete()

        self.assertEqual('bar', case.foo)
        self.assertTrue(case.is_deleted)

        [xform, [case]] = self.interface.post_case_blocks([
            CaseBlock(create=False, case_id=case_id, user_id='whatever',
                      update={'foo': 'not_bar'}).as_xml()
        ])
        self.assertEqual('not_bar', case.foo)
        self.assertTrue(case.is_deleted)