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.xml', 'bugs_in_case_update_datatypes.xml' ]: file_path = os.path.join(os.path.dirname(__file__), "data", "bugs", filename) with open(file_path, "rb") as f: xml_data = f.read() xml_data = xml_data.format(**format_args) form = post_xform_to_couch(xml_data) # before the bug was fixed this call failed process_cases(form) case = CommCareCase.get(case_id) 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)
def testDuplicateCasePropertiesBug(self): """ How do we do when submitting multiple values for the same property in an update block """ file_path = os.path.join(os.path.dirname(__file__), "data", "bugs", "duplicate_case_properties.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) # before the bug was fixed this call failed process_cases(form) case = CommCareCase.get(form.xpath("form/case/@case_id")) # make sure the property is there, but empty self.assertEqual("", case.foo) file_path = os.path.join(os.path.dirname(__file__), "data", "bugs", "duplicate_case_properties_2.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) process_cases(form) case = CommCareCase.get(form.xpath("form/case/@case_id")) # make sure the property takes the last defined value self.assertEqual("2", case.bar)
def testUserRestoreWithCase(self): file_path = os.path.join(os.path.dirname(__file__), "data", "create_short.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) process_cases(form) expected_case_block = """ <case case_id="asdf" date_modified="2010-06-29T13:42:50Z" user_id="foo" xmlns="http://commcarehq.org/case/transaction/v2"> <create> <case_type>test_case_type</case_type> <case_name>test case name</case_name> <owner_id>foo</owner_id> </create> <update> <external_id>someexternal</external_id> </update> </case>""" restore_payload = generate_restore_payload( user=dummy_user(), items=True, version=V3 ) sync_log_id = SyncLog.view( "phone/sync_logs_by_user", include_docs=True, reduce=False, ).one().get_id check_xml_line_by_line( self, dummy_restore_xml(sync_log_id, expected_case_block, items=4), restore_payload )
def testMixed(self): self.assertEqual(0, len(CommCareCase.view("case/by_user", reduce=False).all())) 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 = post_xform_to_couch(xml_data) process_cases(form) self.assertEqual(4, len(CommCareCase.view("case/by_user", reduce=False).all()))
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 = post_xform_to_couch(xml_data, domain=self.domain) process_cases(form) cases = self._get_cases() self.assertEqual(3, len(cases)) self._check_ids(form, cases)
def _postWithSyncToken(self, filename, token_id): file_path = os.path.join(os.path.dirname(__file__), "data", filename) with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) # set last sync token on the form before saving form.last_sync_token = token_id process_cases(form) return form
def testStringFormatProblems(self): """ If two forms share an ID it's a conflict """ file_path = os.path.join(os.path.dirname(__file__), "data", "bugs", "string_formatting.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) # before the bug was fixed this call failed process_cases(form)
def testCasesInRepeats(self): self.assertEqual(0, len(CommCareCase.view("case/by_user", reduce=False).all())) 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 = post_xform_to_couch(xml_data) process_cases(form) cases = self._get_cases() self.assertEqual(3, len(cases)) self._check_ids(form, cases)
def testTopLevelExclusion(self): """ Entire forms tagged as device logs should be excluded """ file_path = os.path.join(os.path.dirname(__file__), "data", "exclusion", "device_report.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) process_cases(form) self.assertEqual(0, get_total_case_count())
def testLotsOfSubcases(self): """ How do we do when submitting a form with multiple blocks for the same case? """ file_path = os.path.join(os.path.dirname(__file__), "data", "bugs", "lots_of_subcases.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) # before the bug was fixed this call failed process_cases(form) self.assertEqual(11, get_total_case_count())
def testStringFormatProblems(self): """ If two forms share an ID it's a conflict """ self.assertEqual(0, len(CommCareCase.view("case/by_user", reduce=False).all())) file_path = os.path.join(os.path.dirname(__file__), "data", "bugs", "string_formatting.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) # before the bug was fixed this call failed process_cases(form)
def testNestedExclusion(self): """ Blocks inside forms tagged as device logs should be excluded """ file_path = os.path.join(os.path.dirname(__file__), "data", "exclusion", "nested_device_report.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) process_cases(form) self.assertEqual(1, get_total_case_count()) case = CommCareCase.get("case_in_form") self.assertEqual("form case", case.name)
def testParseClose(self): self.testParseCreate() file_path = os.path.join(os.path.dirname(__file__), "data", "v2", "basic_close.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) process_cases(form) case = CommCareCase.get("foo-case-id") self.assertTrue(case.closed) self.assertEqual("bar-user-id", case.closed_by)
def testLotsOfSubcases(self): """ How do we do when submitting a form with multiple blocks for the same case? """ self.assertEqual(0, len(CommCareCase.view("case/by_user", reduce=False).all())) file_path = os.path.join(os.path.dirname(__file__), "data", "bugs", "lots_of_subcases.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) # before the bug was fixed this call failed process_cases(form) self.assertEqual(11, len(CommCareCase.view("case/by_user", reduce=False).all()))
def testConflictingIds(self): """ If two forms share an ID it's a conflict """ file_path = os.path.join(os.path.dirname(__file__), "data", "bugs", "id_conflicts.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) try: process_cases(form) self.fail("Previous statement should have raised an exception") except Exception: pass
def testEmptyCaseId(self): """ How do we do when submitting an empty case id? """ file_path = os.path.join(os.path.dirname(__file__), "data", "bugs", "empty_id.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) try: process_cases(form) self.fail("Empty Id should crash") except: pass
def testConflictingIds(self): """ If two forms share an ID it's a conflict """ self.assertEqual(0, len(CommCareCase.view("case/by_user", reduce=False).all())) file_path = os.path.join(os.path.dirname(__file__), "data", "bugs", "id_conflicts.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) try: process_cases(form) self.fail("Previous statement should have raised an exception") except Exception: pass
def testEmptyCaseId(self): """ How do we do when submitting an empty case id? """ self.assertEqual(0, len(CommCareCase.view("case/by_user", reduce=False).all())) file_path = os.path.join(os.path.dirname(__file__), "data", "bugs", "empty_id.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) try: process_cases(form) self.fail("Empty Id should crash") except: pass
def testParseNamedNamespace(self): file_path = os.path.join(os.path.dirname(__file__), "data", "v2", "named_namespace.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) process_cases(form) case = CommCareCase.get("14cc2770-2d1c-49c2-b252-22d6ecce385a") self.assertFalse(case.closed) self.assertEqual("d5ce3a980b5b69e793445ec0e3b2138e", case.user_id) self.assertEqual(datetime(2011, 12, 27), case.modified_on) self.assertEqual("cc_bihar_pregnancy", case.type) self.assertEqual("TEST", case.name) self.assertEqual(2, len(case.actions))
def testParseWithIndices(self): self.testParseCreate() user_id = "bar-user-id" for prereq in ["some_referenced_id", "some_other_referenced_id"]: post_case_blocks([ CaseBlock( create=True, case_id=prereq, user_id=user_id, version=V2 ).as_xml() ]) file_path = os.path.join(os.path.dirname(__file__), "data", "v2", "index_update.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) process_cases(form) case = CommCareCase.get("foo-case-id") self.assertEqual(2, len(case.indices)) self.assertTrue(case.has_index("foo_ref")) self.assertTrue(case.has_index("baz_ref")) self.assertEqual("bar", case.get_index("foo_ref").referenced_type) self.assertEqual("some_referenced_id", case.get_index("foo_ref").referenced_id) self.assertEqual("bop", case.get_index("baz_ref").referenced_type) self.assertEqual("some_other_referenced_id", case.get_index("baz_ref").referenced_id) # check the action self.assertEqual(2, len(case.actions)) [_, index_action] = case.actions self.assertEqual(const.CASE_ACTION_INDEX, index_action.action_type) self.assertEqual(2, len(index_action.indices)) # quick test for ota restore v2response = xml.get_case_xml(case, [const.CASE_ACTION_CREATE, const.CASE_ACTION_UPDATE], V2) expected_v2_response = """ <case case_id="foo-case-id" date_modified="2011-12-07T13:42:50.000000Z" user_id="bar-user-id" xmlns="http://commcarehq.org/case/transaction/v2"> <create> <case_type>v2_case_type</case_type> <case_name>test case name</case_name> <owner_id>bar-user-id</owner_id> </create> <index> <baz_ref case_type="bop">some_other_referenced_id</baz_ref> <foo_ref case_type="bar">some_referenced_id</foo_ref> </index> </case>""" check_xml_line_by_line(self, expected_v2_response, v2response)
def testParseNoop(self): self.testParseCreate() file_path = os.path.join(os.path.dirname(__file__), "data", "v2", "basic_noop.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) process_cases(form) case = CommCareCase.get("foo-case-id") self.assertFalse(case.closed) self.assertEqual("bar-user-id", case.user_id) self.assertEqual(datetime(2011, 12, 7, 13, 44, 50), case.modified_on) self.assertEqual(2, len(case.actions)) self.assertEqual("bar-user-id", case.actions[1].user_id) self.assertEqual(2, len(case.xform_ids))
def bootstrap_case_from_xml(test_class, filename, case_id_override=None, domain=None): starttime = utcnow_sans_milliseconds() file_path = os.path.join(os.path.dirname(__file__), "data", filename) with open(file_path, "rb") as f: xml_data = f.read() doc, uid, case_id = _replace_ids_and_post( xml_data, case_id_override=case_id_override, ) if domain: doc.domain = domain process_cases(doc) case = CommCareCase.get(case_id) test_class.assertLessEqual(starttime, case.server_modified_on) test_class.assertGreaterEqual(datetime.utcnow(), case.server_modified_on) test_class.assertEqual(case_id, case.case_id) return case
def testOutOfOrderSubmissions(self): dir = os.path.join(os.path.dirname(__file__), "data", "ordering") forms = [] for fname in ('create_oo.xml', 'update_oo.xml'): with open(os.path.join(dir, fname), "rb") as f: xml_data = f.read() forms.append(post_xform_to_couch(xml_data)) [create, update] = forms # process out of order process_cases(update) process_cases(create) case = CommCareCase.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)
def reprocess_form_cases(form, config=None, case_db=None): """ For a given form, reprocess all case elements inside it. This operation should be a no-op if the form was sucessfully processed, but should correctly inject the update into the case history if the form was NOT successfully processed. """ from casexml.apps.case.xform import process_cases, process_cases_with_casedb if case_db: process_cases_with_casedb([form], case_db, config=config) else: process_cases(form, config) # mark cleaned up now that we've reprocessed it if form.doc_type != 'XFormInstance': form = XFormInstance.get(form._id) form.doc_type = 'XFormInstance' form.save()
def testMultipleCaseBlocks(self): """ How do we do when submitting a form with multiple blocks for the same case? """ file_path = os.path.join(os.path.dirname(__file__), "data", "bugs", "multiple_case_blocks.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) # before the bug was fixed this call failed process_cases(form) case = CommCareCase.get(form.xpath("form/comunidad/case/@case_id")) self.assertEqual('1630005', case.community_code) self.assertEqual('SantaMariaCahabon', case.district_name) self.assertEqual('TAMERLO', case.community_name) ids = case.get_xform_ids_from_couch() self.assertEqual(1, len(ids)) self.assertEqual(form._id, ids[0])
def testParseUpdate(self): self.testParseCreate() file_path = os.path.join(os.path.dirname(__file__), "data", "v2", "basic_update.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) process_cases(form) case = CommCareCase.get("foo-case-id") self.assertFalse(case.closed) self.assertEqual("bar-user-id", case.user_id) self.assertEqual(datetime(2011, 12, 7, 13, 42, 50), case.modified_on) self.assertEqual("updated_v2_case_type", case.type) self.assertEqual("updated case name", case.name) self.assertEqual("something dynamic", case.dynamic) self.assertEqual(2, len(case.actions)) self.assertEqual("bar-user-id", case.actions[1].user_id)
def testParseCreate(self): file_path = os.path.join(os.path.dirname(__file__), "data", "v2", "basic_create.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) process_cases(form) case = CommCareCase.get("foo-case-id") self.assertFalse(case.closed) self.assertEqual("bar-user-id", case.user_id) self.assertEqual("bar-user-id", case.opened_by) self.assertEqual(datetime(2011, 12, 6, 13, 42, 50), case.modified_on) self.assertEqual("v2_case_type", case.type) self.assertEqual("test case name", case.name) self.assertEqual(1, len(case.actions)) [action] = case.actions self.assertEqual("http://openrosa.org/case/test/create", action.xform_xmlns) self.assertEqual("v2 create", action.xform_name) self.assertEqual("bar-user-id", case.actions[0].user_id)
def testRestoreAttributes(self): file_path = os.path.join(os.path.dirname(__file__), "data", "attributes.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data) process_cases(form) [newcase] = CommCareCase.view("case/by_user", reduce=False, include_docs=True).all() self.assertTrue(isinstance(newcase.adate, dict)) self.assertEqual(date(2012,02,01), newcase.adate["#text"]) self.assertEqual("i am an attribute", newcase.adate["@someattr"]) self.assertTrue(isinstance(newcase.dateattr, dict)) self.assertEqual("this shouldn't break", newcase.dateattr["#text"]) self.assertEqual(date(2012,01,01), newcase.dateattr["@somedate"]) self.assertTrue(isinstance(newcase.stringattr, dict)) self.assertEqual("neither should this", newcase.stringattr["#text"]) self.assertEqual("i am a string", newcase.stringattr["@somestring"]) restore_payload = generate_restore_payload(dummy_user()) # ghetto self.assertTrue('<dateattr somedate="2012-01-01">' in restore_payload) self.assertTrue('<stringattr somestring="i am a string">' in restore_payload)
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.xml', 'bugs_in_case_update_datatypes.xml']: file_path = os.path.join(os.path.dirname(__file__), "data", "bugs", filename) with open(file_path, "rb") as f: xml_data = f.read() xml_data = xml_data.format(**format_args) form = post_xform_to_couch(xml_data) # before the bug was fixed this call failed process_cases(form) case = CommCareCase.get(case_id) 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)
def testUserRestoreWithCase(self): file_path = os.path.join(os.path.dirname(__file__), "data", "create_short.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data, domain=self.domain) process_cases(form) expected_case_block = """ <case case_id="asdf" date_modified="2010-06-29T13:42:50.000000Z" user_id="foo" xmlns="http://commcarehq.org/case/transaction/v2"> <create> <case_type>test_case_type</case_type> <case_name>test case name</case_name> <owner_id>foo</owner_id> </create> <update> <external_id>someexternal</external_id> </update> </case>""" restore_payload = generate_restore_payload( project=Domain(name=self.domain), user=dummy_user(), items=True, version=V3 ) sync_log_id = SyncLog.view( "phone/sync_logs_by_user", include_docs=True, reduce=False, ).one().get_id check_xml_line_by_line( self, dummy_restore_xml(sync_log_id, expected_case_block, items=4), restore_payload )
def testCantPwnCase(self): form = post_xform_to_couch(ALICE_XML) form.domain = ALICE_DOMAIN (case,) = process_cases(form) case_id = case.case_id form = post_xform_to_couch(EVE_XML) form.domain = EVE_DOMAIN with self.assertRaises(IllegalCaseId): process_cases(form) self.assertFalse(hasattr(CommCareCase.get(case_id), 'plan_to_buy_gun')) form = post_xform_to_couch(ALICE_UPDATE_XML) form.domain = ALICE_DOMAIN process_cases(form) self.assertEqual(CommCareCase.get(case_id).plan_to_buy_gun, 'no')
def testRestoreAttributes(self): file_path = os.path.join(os.path.dirname(__file__), "data", "attributes.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data, domain=self.project.name) [newcase] = process_cases(form) self.assertTrue(isinstance(newcase.adate, dict)) self.assertEqual(date(2012,02,01), newcase.adate["#text"]) self.assertEqual("i am an attribute", newcase.adate["@someattr"]) self.assertTrue(isinstance(newcase.dateattr, dict)) self.assertEqual("this shouldn't break", newcase.dateattr["#text"]) self.assertEqual(date(2012,01,01), newcase.dateattr["@somedate"]) self.assertTrue(isinstance(newcase.stringattr, dict)) self.assertEqual("neither should this", newcase.stringattr["#text"]) self.assertEqual("i am a string", newcase.stringattr["@somestring"]) restore_payload = generate_restore_payload(self.project, dummy_user()) # ghetto self.assertTrue('<dateattr somedate="2012-01-01">' in restore_payload) self.assertTrue('<stringattr somestring="i am a string">' in restore_payload)
def _test_sync_token(self, items): """ Tests sync token / sync mode support """ file_path = os.path.join(os.path.dirname(__file__), "data", "create_short.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data, domain=self.project.name) process_cases(form) time.sleep(1) restore_payload = generate_restore_payload(self.project, dummy_user(), items=items) sync_log_id = SyncLog.view( "phone/sync_logs_by_user", include_docs=True, reduce=False ).one().get_id expected_restore_payload = dummy_restore_xml( sync_log_id, const.CREATE_SHORT, items=4 if items else None, ) check_xml_line_by_line(self, expected_restore_payload, restore_payload) time.sleep(1) sync_restore_payload = generate_restore_payload( project=self.project, user=dummy_user(), restore_id=sync_log_id, items=items, ) all_sync_logs = SyncLog.view( "phone/sync_logs_by_user", include_docs=True, reduce=False, ).all() [latest_log] = [log for log in all_sync_logs if log.get_id != sync_log_id] # should no longer have a case block in the restore XML check_xml_line_by_line( self, dummy_restore_xml(latest_log.get_id, items=3 if items else None), sync_restore_payload, ) # apply an update time.sleep(1) file_path = os.path.join(os.path.dirname(__file__), "data", "update_short.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data, domain=self.project.name) process_cases(form) time.sleep(1) sync_restore_payload = generate_restore_payload( self.project, user=dummy_user(), restore_id=latest_log.get_id, items=items, ) all_sync_logs = SyncLog.view( "phone/sync_logs_by_user", include_docs=True, reduce=False, ).all() [even_latest_log] = [log for log in all_sync_logs if log.get_id != sync_log_id and log.get_id != latest_log.get_id] # case block should come back expected_sync_restore_payload = dummy_restore_xml( even_latest_log.get_id, const.UPDATE_SHORT, items=4 if items else None, ) check_xml_line_by_line(self, expected_sync_restore_payload, sync_restore_payload)
def testUserRestoreWithCase(self): file_path = os.path.join(os.path.dirname(__file__), "data", "create_short.xml") with open(file_path, "rb") as f: xml_data = f.read() form = post_xform_to_couch(xml_data, domain=self.project.name) # implicit length assertion [newcase] = process_cases(form) user = dummy_user() self.assertEqual(1, len(list( BatchedCaseSyncOperation(RestoreState(self.project, user, RestoreParams())).get_all_case_updates() ))) expected_case_block = """ <case> <case_id>asdf</case_id> <date_modified>2010-06-29T13:42:50.000000Z</date_modified> <create> <case_type_id>test_case_type</case_type_id> <user_id>foo</user_id> <case_name>test case name</case_name> <external_id>someexternal</external_id> </create> </case>""" check_xml_line_by_line(self, expected_case_block, xml.get_case_xml(newcase, [case_const.CASE_ACTION_CREATE, case_const.CASE_ACTION_UPDATE])) # check v2 expected_v2_case_block = """ <case case_id="asdf" date_modified="2010-06-29T13:42:50.000000Z" user_id="foo" xmlns="http://commcarehq.org/case/transaction/v2" > <create> <case_type>test_case_type</case_type> <case_name>test case name</case_name> <owner_id>foo</owner_id> </create> <update> <external_id>someexternal</external_id> </update> </case>""" check_xml_line_by_line( self, expected_v2_case_block, xml.get_case_xml( newcase, [case_const.CASE_ACTION_CREATE, case_const.CASE_ACTION_UPDATE], version="2.0", ), ) restore_payload = generate_restore_payload( project=self.project, user=dummy_user(), items=True, ) sync_log_id = SyncLog.view( "phone/sync_logs_by_user", include_docs=True, reduce=False, ).one().get_id check_xml_line_by_line( self, dummy_restore_xml(sync_log_id, expected_case_block, items=4), restore_payload )