class TestAnswerStore(unittest.TestCase): # pylint: disable=too-many-public-methods def setUp(self): self.store = AnswerStore() def tearDown(self): self.store.clear() def test_adds_answer(self): answer = Answer( answer_id='4', answer_instance=1, group_instance=1, value=25, ) self.store.add_or_update(answer) self.assertEqual(len(self.store), 1) def test_raises_error_on_invalid(self): with self.assertRaises(TypeError) as ite: self.store.add_or_update({ 'answer_id': '4', 'answer_instance': 1, 'group_instance': 1, 'value': 25, }) self.assertIn('Method only supports Answer argument type', str(ite.exception)) def test_add_inserts_instances(self): answer_1 = Answer( answer_id='4', answer_instance=1, group_instance=1, value=25, ) self.store.add_or_update(answer_1) answer_1.answer_instance = 2 self.store.add_or_update(answer_1) answer_1.answer_instance = 3 self.store.add_or_update(answer_1) self.assertEqual(len(self.store), 3) def test_updates_answer(self): answer_1 = Answer( answer_id='4', answer_instance=1, group_instance=1, value=25, ) answer_2 = Answer( answer_id='4', answer_instance=1, group_instance=1, value=65, ) self.store.add_or_update(answer_1) self.store.add_or_update(answer_2) self.assertEqual(len(self.store), 1) store_match = self.store.filter( answer_ids=['4'], answer_instance=1, group_instance=1, ) self.assertEqual(store_match, AnswerStore([answer_2.__dict__])) def test_filters_answers(self): answer_1 = Answer( answer_id='2', answer_instance=1, group_instance=1, value=25, ) answer_2 = Answer( answer_id='5', answer_instance=1, group_instance=1, value=65, ) self.store.add_or_update(answer_1) self.store.add_or_update(answer_2) filtered = self.store.filter(answer_ids=['5']) self.assertEqual(len(filtered), 1) def test_filters_answers_with_limit(self): for i in range(1, 50): self.store.add_or_update(Answer( answer_id='2', answer_instance=i, group_instance=1, value=25, )) filtered = self.store.filter(answer_ids=['2'], limit=True) self.assertEqual(len(filtered), 25) def test_escaped(self): self.store.add_or_update(Answer( answer_id='1', answer_instance=0, group_instance=1, value=25, )) self.store.add_or_update(Answer( answer_id='2', answer_instance=0, group_instance=1, value="'Twenty Five'", )) escaped = self.store.escaped() self.assertEqual(len(escaped), 2) self.assertEqual(escaped.filter(answer_ids=['1']).values()[0], 25) self.assertEqual(escaped.filter(answer_ids=['2']).values()[0], ''Twenty Five'') # answers in the store have not been escaped self.assertEqual(self.store.filter(answer_ids=['1']).values()[0], 25) self.assertEqual(self.store.filter(answer_ids=['2']).values()[0], "'Twenty Five'") def test_filter_answers_does_not_escapes_values(self): self.store.add_or_update(Answer( answer_id='1', answer_instance=0, group_instance=1, value=25, )) self.store.add_or_update(Answer( answer_id='2', answer_instance=0, group_instance=1, value="'Twenty Five'", )) filtered = self.store.filter(['1', '2']) self.assertEqual(len(filtered), 2) self.assertEqual(filtered.filter(answer_ids=['1']).values()[0], 25) self.assertEqual(filtered.filter(answer_ids=['2']).values()[0], "'Twenty Five'") def test_filter_chaining_escaped(self): self.store.add_or_update(Answer( answer_id='1', answer_instance=0, group_instance=1, value=25, )) self.store.add_or_update(Answer( answer_id='2', answer_instance=0, group_instance=1, value="'Twenty Five'", )) escaped = self.store.filter(answer_ids=['2']).escaped() self.assertEqual(len(escaped), 1) self.assertEqual(escaped.values()[0], ''Twenty Five'') # answers in the store have not been escaped self.assertEqual(self.store.filter(answer_ids=['1']).values()[0], 25) self.assertEqual(self.store.filter(answer_ids=['2']).values()[0], "'Twenty Five'") values = self.store.filter(answer_ids=['2']).escaped().values() self.assertEqual(len(values), 1) self.assertEqual(values[0], ''Twenty Five'') def test_filter_chaining_count(self): self.store.add_or_update(Answer( answer_id='1', answer_instance=0, group_instance=1, value=25, )) self.store.add_or_update(Answer( answer_id='2', answer_instance=0, group_instance=1, value="'Twenty Five'", )) self.assertEqual(self.store.count(), 2) self.assertEqual(self.store.filter(answer_ids=['2']).count(), 1) self.assertEqual(self.store.filter(answer_ids=['1', '2']).count(), 2) def tests_upgrade_reformats_date(self): questionnaire = { 'survey_id': '021', 'data_version': '0.0.2', 'sections': [{ 'id': 'secetion1', 'groups': [{ 'id': 'group1', 'blocks': [{ 'id': 'block1', 'questions': [{ 'id': 'question1', 'answers': [ { 'id': 'answer1', 'type': 'Date' } ] }] }] }] }] } answers = [ { 'answer_id': 'answer1', 'answer_instance': 0, 'group_instance': 0, 'value': '25/12/2017' } ] self.store = AnswerStore(existing_answers=answers) upgrade_to_1_update_date_formats(self.store, QuestionnaireSchema(questionnaire)) self.assertEqual(self.store.values()[0], '2017-12-25') def tests_upgrade_reformats_month_year_date(self): questionnaire = { 'survey_id': '021', 'data_version': '0.0.2', 'sections': [{ 'id': 'section1', 'groups': [{ 'id': 'group1', 'blocks': [{ 'id': 'block1', 'questions': [{ 'id': 'question1', 'answers': [ { 'id': 'answer1', 'type': 'MonthYearDate' } ] }] }] }] }] } answers = [ { 'answer_id': 'answer1', 'answer_instance': 0, 'group_instance': 0, 'value': '12/2017' } ] self.store = AnswerStore(existing_answers=answers) upgrade_to_1_update_date_formats(self.store, QuestionnaireSchema(questionnaire)) self.assertEqual(self.store.values()[0], '2017-12') def tests_upgrade_when_answer_no_longer_in_schema_does_not_reformat(self): questionnaire = { 'survey_id': '021', 'data_version': '0.0.2', 'sections': [{ 'id': 'section1', 'groups': [{ 'id': 'group1', 'blocks': [{ 'id': 'block1', 'questions': [{ 'id': 'question1', 'answers': [ ] }] }] }] }] } answers = [ { 'answer_id': 'answer1', 'answer_instance': 0, 'group_instance': 0, 'value': '12/2017' } ] self.store = AnswerStore(existing_answers=answers) upgrade_to_1_update_date_formats(self.store, QuestionnaireSchema(questionnaire)) self.assertEqual(self.store.values()[0], '12/2017') def tests_upgrade_when_block_no_longer_in_schema_does_not_reformat(self): questionnaire = { 'survey_id': '021', 'data_version': '0.0.2', 'sections': [{ 'id': 'section1', 'groups': [{ 'id': 'group1', 'blocks': [] }] }] } answers = [ { 'answer_id': 'answer1', 'answer_instance': 0, 'group_instance': 0, 'value': '12/2017' } ] self.store = AnswerStore(existing_answers=answers) upgrade_to_1_update_date_formats(self.store, QuestionnaireSchema(questionnaire)) self.assertEqual(self.store.values()[0], '12/2017') def test_upgrade_add_group_instance_id(self): survey = { 'survey_id': '021', 'data_version': '0.0.2', 'sections': [{ 'id': 'section1', 'groups': [{ 'id': 'group1', 'blocks': [{ 'id': 'block1', 'type': 'Question', 'questions': [{ 'id': 'question1', 'answers': [ { 'id': 'answer1', 'type': 'TextArea' } ] }] }] }, { 'id': 'group-2', 'blocks': [{ 'id': 'block-2', 'type': 'Question' }], 'routing_rules':[{ 'repeat': { 'type': 'group', 'group_ids': ['group1'] } }] }] }] } existing_answers = [ { 'answer_id': 'answer1', 'answer_instance': 0, 'group_instance': 0, 'value': '12/2017' }, { 'answer_id': 'answer1', 'answer_instance': 1, 'group_instance': 0, 'value': '12/2017' }, { 'answer_id': 'answer1', 'answer_instance': 0, 'group_instance': 1, 'value': '12/2017' }, ] answer_store = AnswerStore(existing_answers) upgrade_to_2_add_group_instance_id(answer_store, QuestionnaireSchema(survey)) filtered = iter(answer_store.filter(answer_ids=['answer1'])) first_group_instance_id = next(filtered)['group_instance_id'] self.assertEqual(first_group_instance_id, next(filtered)['group_instance_id']) self.assertNotEqual(first_group_instance_id, next(filtered)['group_instance_id']) def test_upgrade_multiple_versions(self): answer_store = AnswerStore() upgrade_0 = MagicMock() upgrade_0.__name__ = 'upgrade_0' upgrade_1 = MagicMock() upgrade_1.__name__ = 'upgrade_1' upgrade_2 = MagicMock() upgrade_2.__name__ = 'upgrade_2' UPGRADE_TRANSFORMS = { 1: upgrade_0, 2: upgrade_1, 3: upgrade_2 } schema = MagicMock() with patch('app.data_model.answer_store.UPGRADE_TRANSFORMS', UPGRADE_TRANSFORMS): answer_store.upgrade(0, schema) upgrade_0.assert_called_once_with(answer_store, schema) upgrade_1.assert_called_once_with(answer_store, schema) upgrade_2.assert_called_once_with(answer_store, schema) def test_upgrade_multiple_versions_skipping_already_run(self): answer_store = AnswerStore() upgrade_0 = MagicMock() upgrade_0.__name__ = 'upgrade_0' upgrade_1 = MagicMock() upgrade_1.__name__ = 'upgrade_1' upgrade_2 = MagicMock() upgrade_2.__name__ = 'upgrade_2' UPGRADE_TRANSFORMS = { 1: upgrade_0, 2: upgrade_1, 3: upgrade_2 } schema = MagicMock() with patch('app.data_model.answer_store.UPGRADE_TRANSFORMS', UPGRADE_TRANSFORMS): answer_store.upgrade(1, schema) upgrade_0.assert_not_called() upgrade_1.assert_called_once_with(answer_store, schema) upgrade_2.assert_called_once_with(answer_store, schema) def test_upgrade_skipped_version(self): """ Ensure skipping an answer store version does not affect the upgrade path. """ answer_store = AnswerStore() upgrade_0 = MagicMock() upgrade_0.__name__ = 'upgrade_0' upgrade_2 = MagicMock() upgrade_2.__name__ = 'upgrade_2' upgrade_3 = MagicMock() upgrade_3.__name__ = 'upgrade_3' UPGRADE_TRANSFORMS = { 1: upgrade_0, 3: upgrade_2, 4: upgrade_3 } schema = MagicMock() with patch('app.data_model.answer_store.UPGRADE_TRANSFORMS', UPGRADE_TRANSFORMS): answer_store.upgrade(0, schema) upgrade_0.assert_called_once_with(answer_store, schema) upgrade_2.assert_called_once_with(answer_store, schema) upgrade_3.assert_called_once_with(answer_store, schema) def test_remove_all_answers(self): answer_1 = Answer( answer_id='answer1', value=10, ) answer_2 = Answer( answer_id='answer2', value=20, ) self.store.add_or_update(answer_1) self.store.add_or_update(answer_2) self.store.clear() self.assertEqual(len(self.store), 0) def test_remove_answer(self): answer_1 = Answer( answer_id='answer1', value=10, ) answer_2 = Answer( answer_id='answer2', value=20, ) self.store.add_or_update(answer_1) self.store.add_or_update(answer_2) self.store.remove_answer(vars(answer_1)) self.assertEqual(len(self.store), 1)
class TestAnswerStore(unittest.TestCase): # pylint: disable=too-many-public-methods def setUp(self): self.store = AnswerStore() def tearDown(self): self.store.clear() def test_adds_answer(self): answer = Answer( block_id="3", answer_id="4", answer_instance=1, group_id="5", group_instance=1, value=25, ) self.store.add(answer) self.assertEqual(self.store.count(answer), 1) def test_raises_error_on_invalid(self): with self.assertRaises(TypeError) as ite: self.store.add({ "block_id": "3", "answer_id": "4", "answer_instance": 1, "group_id": "5", "group_instance": 1, "value": 25, }) self.assertIn("Method only supports Answer argument type", str(ite.exception)) def test_raises_error_on_add_existing(self): answer_1 = Answer( block_id="3", answer_id="4", answer_instance=1, group_id="5", group_instance=1, value=25, ) self.store.add(answer_1) with self.assertRaises(ValueError) as ite: self.store.add(answer_1) self.assertIn("Answer instance already exists in store", str(ite.exception)) def test_raises_error_on_update_nonexisting(self): answer_1 = Answer( block_id="3", answer_id="4", answer_instance=1, group_id="5", group_instance=1, value=25, ) with self.assertRaises(ValueError) as ite: self.store.update(answer_1) self.assertIn("Answer instance does not exist in store", str(ite.exception)) def test_raises_error_on_get_nonexisting(self): answer_1 = Answer( block_id="3", answer_id="4", answer_instance=1, group_id="5", group_instance=1, value=25, ) with self.assertRaises(ValueError) as ite: self.store.get(answer_1) self.assertIn("Answer instance does not exist in store", str(ite.exception)) def test_gets_answer(self): answer_1 = Answer( block_id="3", answer_id="4", answer_instance=1, group_id="5", group_instance=1, value=25, ) answer_2 = Answer( block_id="4", answer_id="5", group_id="6", group_instance=1, value=56, ) self.store.add(answer_1) self.store.add(answer_2) self.assertEqual(self.store.get(answer_1), 25) self.assertEqual(self.store.get(answer_2), 56) def test_adds_multidict_answer(self): answer_1 = Answer( block_id="3", answer_id="4", answer_instance=1, group_id="5", group_instance=1, value=[23, 45, 67], ) self.store.add(answer_1) value = self.store.get(answer_1) self.assertEqual(value, [23, 45, 67]) def test_add_inserts_instances(self): answer_1 = Answer( block_id="3", answer_id="4", answer_instance=1, group_id="5", group_instance=1, value=25, ) self.store.add(answer_1) answer_1.answer_instance = 2 self.store.add(answer_1) answer_1.answer_instance = 3 self.store.add(answer_1) self.assertEqual(len(self.store.answers), 3) def test_updates_answer(self): answer_1 = Answer( block_id="3", answer_id="4", answer_instance=1, group_id="5", group_instance=1, value=25, ) answer_2 = Answer( block_id="3", answer_id="4", answer_instance=1, group_id="5", group_instance=1, value=65, ) self.store.add(answer_1) self.store.update(answer_2) self.assertEqual(self.store.count(answer_2), 1) store_match = self.store.filter( block_id="3", answer_id="4", answer_instance=1, group_id="5", group_instance=1, ) self.assertEqual(store_match, [answer_2.__dict__]) def test_filters_answers(self): answer_1 = Answer( block_id="1", answer_id="2", answer_instance=1, group_id="5", group_instance=1, value=25, ) answer_2 = Answer( block_id="1", answer_id="5", answer_instance=1, group_id="6", group_instance=1, value=65, ) self.store.add(answer_1) self.store.add(answer_2) filtered = self.store.filter(block_id="1") self.assertEqual(len(filtered), 2) filtered = self.store.filter(answer_id="5") self.assertEqual(len(filtered), 1) filtered = self.store.filter(group_id="6") self.assertEqual(len(filtered), 1) def test_filters_answers_by_location(self): answer_1 = Answer( block_id="1", answer_id="2", answer_instance=1, group_id="5", group_instance=1, value=25, ) answer_2 = Answer( block_id="1", answer_id="5", answer_instance=1, group_id="6", group_instance=1, value=65, ) self.store.add(answer_1) self.store.add(answer_2) location = Location("6", 1, "1") filtered = self.store.filter(location=location) self.assertEqual(len(filtered), 1) def test_maps_answers(self): answer_1 = Answer( block_id="1", answer_id="2", answer_instance=1, group_id="5", group_instance=1, value=25, ) answer_2 = Answer( block_id="1", answer_id="5", answer_instance=1, group_id="6", group_instance=1, value=65, ) self.store.add(answer_1) self.store.add(answer_2) expected_answers = {"2_1": 25, "5_1": 65} self.assertEqual(self.store.map(), expected_answers) def test_maps_and_filters_answers(self): answer_1 = Answer( block_id="1", answer_id="2", answer_instance=1, group_id="5", group_instance=1, value=25, ) answer_2 = Answer( block_id="1", answer_id="5", answer_instance=1, group_id="6", group_instance=1, value=65, ) self.store.add(answer_1) self.store.add(answer_2) expected_answers = {"5_1": 65} self.assertEqual(self.store.map(answer_id="5"), expected_answers) def test_returns_ordered_map(self): answer = Answer( block_id="1", answer_id="2", group_id="5", group_instance=1, value=25, ) for i in range(0, 100): answer.answer_instance = i self.store.add(answer) last_instance = -1 self.assertEqual(len(self.store.answers), 100) mapped = self.store.map() for key, _ in mapped.items(): pos = key.find('_') instance = 0 if pos == -1 else int(key[pos + 1:]) self.assertGreater(instance, last_instance) last_instance = instance def test_remove_answer(self): answer_1 = Answer( group_id="1", block_id="1", answer_id="2", answer_instance=1, value=25, ) answer_2 = Answer( group_id="1", block_id="1", answer_id="5", answer_instance=1, value=65, ) self.store.add(answer_1) self.store.add(answer_2) expected_answers = { "2_1": 25, "5_1": 65, } self.assertEqual(self.store.map(), expected_answers) self.store.remove_answer(answer_2) expected_answers = { "2_1": 25, } self.assertEqual(self.store.map(), expected_answers) def test_remove_answer_that_does_not_exist(self): answer_1 = Answer( group_id="1", block_id="1", answer_id="1", answer_instance=1, value=25, ) answer_2 = Answer( group_id="1", block_id="1", answer_id="2", answer_instance=1, value=65, ) answer_3 = Answer( group_id="1", block_id="1", answer_id="3", answer_instance=1, value=65, ) self.store.add(answer_1) self.store.add(answer_2) expected_answers = { "1_1": 25, "2_1": 65, } self.assertEqual(self.store.map(), expected_answers) self.store.remove_answer(answer_3) self.assertEqual(self.store.map(), expected_answers) def test_remove_first_answer(self): answer_1 = Answer( group_id="1", block_id="1", answer_id="2", answer_instance=1, value=25, ) answer_2 = Answer( group_id="1", block_id="1", answer_id="5", answer_instance=1, value=65, ) self.store.add(answer_1) self.store.add(answer_2) self.store.remove_answer(answer_1) expected_answers = { "5_1": 65, } self.assertEqual(self.store.map(), expected_answers) def test_remove_single_answer_by_group_id(self): answer_1 = Answer( group_id="group1", block_id="block1", answer_id="answer1", value=10, ) answer_2 = Answer( group_id="group2", block_id="block1", answer_id="answer2", value=20, ) answer_3 = Answer( group_id="group3", block_id="block1", answer_id="answer3", value=30, ) self.store.add(answer_1) self.store.add(answer_2) self.store.add(answer_3) self.store.remove(group_id="group1") expected_answers = {"answer2": 20, "answer3": 30} self.assertEqual(self.store.map(), expected_answers) def test_remove_multiple_answers_by_group_id(self): answer_1 = Answer( group_id="group1", block_id="block1", answer_id="answer1", value=10, ) answer_2 = Answer( group_id="group2", block_id="block1", answer_id="answer2", value=20, ) answer_3 = Answer( group_id="group2", block_id="block1", answer_id="answer3", value=30, ) self.store.add(answer_1) self.store.add(answer_2) self.store.add(answer_3) self.store.remove(group_id="group2") expected_answers = { "answer1": 10, } self.assertEqual(self.store.map(), expected_answers) def test_remove_single_answer_by_block_id(self): answer_1 = Answer( group_id="group1", block_id="block1", answer_id="answer1", value=10, ) answer_2 = Answer( group_id="group1", block_id="block2", answer_id="answer2", value=20, ) answer_3 = Answer( group_id="group1", block_id="block3", answer_id="answer3", value=30, ) self.store.add(answer_1) self.store.add(answer_2) self.store.add(answer_3) self.store.remove(block_id="block1") expected_answers = {"answer2": 20, "answer3": 30} self.assertEqual(self.store.map(), expected_answers) def test_remove_multiple_answers_by_block_id(self): answer_1 = Answer( group_id="group1", block_id="block1", answer_id="answer1", value=10, ) answer_2 = Answer( group_id="group1", block_id="block2", answer_id="answer2", value=20, ) answer_3 = Answer( group_id="group1", block_id="block2", answer_id="answer3", value=30, ) self.store.add(answer_1) self.store.add(answer_2) self.store.add(answer_3) self.store.remove(block_id="block2") expected_answers = { "answer1": 10, } self.assertEqual(self.store.map(), expected_answers) def test_remove_multiple_answers_by_location(self): answer_1 = Answer( group_id="group1", group_instance=0, block_id="block1", answer_id="answer1", value=10, ) answer_2 = Answer( group_id="group1", group_instance=0, block_id="block1", answer_id="answer2", value=20, ) answer_3 = Answer( group_id="group1", group_instance=0, block_id="block2", answer_id="answer3", value=30, ) self.store.add(answer_1) self.store.add(answer_2) self.store.add(answer_3) location = Location("group1", 0, "block1") self.store.remove(location=location) expected_answers = { "answer3": 30, } self.assertEqual(self.store.map(), expected_answers) def test_remove_answers_by_group_id_that_does_not_exist(self): answer_1 = Answer( group_id="group1", block_id="block1", answer_id="answer1", value=10, ) answer_2 = Answer( group_id="group1", block_id="block2", answer_id="answer2", value=20, ) self.store.add(answer_1) self.store.add(answer_2) self.store.remove(group_id="group2") expected_answers = {"answer1": 10, "answer2": 20} self.assertEqual(self.store.map(), expected_answers) def test_remove_answers_by_block_id_that_does_not_exist(self): answer_1 = Answer( group_id="group1", block_id="block1", answer_id="answer1", value=10, ) answer_2 = Answer( group_id="group1", block_id="block1", answer_id="answer2", value=20, ) self.store.add(answer_1) self.store.add(answer_2) self.store.remove(block_id="block2") expected_answers = {"answer1": 10, "answer2": 20} self.assertEqual(self.store.map(), expected_answers) def test_remove_all_answers(self): answer_1 = Answer( group_id="group1", block_id="block1", answer_id="answer1", value=10, ) answer_2 = Answer( group_id="group1", block_id="block1", answer_id="answer2", value=20, ) self.store.add(answer_1) self.store.add(answer_2) self.store.remove() self.assertEqual(self.store.map(), {})