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])
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)
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])
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)
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)
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)