def test_create(self): son = { 'title': 'Foo report', 'description': "Foo bar baz", 'text': "SELECT * FROM organisation", } with base.mock_user('super_admin'): response_son = self.fetch( "/custom_query.json", method='POST', body=json_encode(son), expected=200, decode=True) for k in son: self.assertIn(k, response_son) self.assertEqual(son[k], response_son[k]) qid = response_son['id'] with base.mock_user('super_admin'): response_son = self.fetch( "/custom_query/%s.json" % qid, method='GET', decode=True) self.assertEqual(qid, response_son['id']) for k in son: self.assertIn(k, response_son) self.assertEqual(son[k], response_son[k])
def test_get_statistics(self): with model.session_scope() as session: program = session.query(model.Program).one() organisation = (session.query( model.Organisation).filter_by(name='Utility').one()) survey = (session.query( model.Survey).filter_by(title='Survey 2').one()) self.program_id = str(program.id) self.organisation_id = str(organisation.id) self.survey_id = str(survey.id) with base.mock_user('consultant'): self.fetch("/report/sub/stats/program" "/%s/survey/%s.json?approval=reviewed" % (self.program_id, self.survey_id), method='GET', expected=200, decode=False) with base.mock_user('authority'): self.fetch("/report/sub/stats/program" "/%s/survey/%s.json?approval=reviewed" % (self.program_id, self.survey_id), method='GET', expected=200, decode=False) # Before purchase survey with base.mock_user('clerk'): self.fetch("/report/sub/stats/program" "/%s/survey/%s.json?approval=reviewed" % (self.program_id, self.survey_id), method='GET', expected=403, decode=False) # After purchase survey with base.mock_user('admin'): self.purchase_program() with base.mock_user('clerk'): self.fetch("/report/sub/stats/program" "/%s/survey/%s.json?approval=reviewed" % (self.program_id, self.survey_id), method='GET', expected=200, decode=False)
def test_modify_user(self): with model.session_scope() as session: # TODO: Refactor this to make it reusable. user = (session.query( model.AppUser).filter(model.AppUser.email == 'clerk').one()) to_son = ToSon( r'/id$', r'/title$', r'/name$', r'/surveygroups$', r'/organisation$', r'/[0-9]+$', # Exclude password r'!/password$', ) user_son = to_son(user) users = [('consultant', 403, "are not the owner"), ('authority', 403, "are not the owner"), ('author', 403, "are not the owner"), ('clerk', 200, 'OK'), ('org_admin', 200, 'OK'), ('admin', 200, 'OK')] for user_email, code, reason in users: post_data = user_son.copy() post_data['organisation'] = post_data['organisation'].copy() with base.mock_user(user_email): response = self.fetch("/user/%s.json" % user_son['id'], method='PUT', body=json_encode(post_data)) self.assertIn(reason, response.reason, msg=user_email) self.assertEqual(code, response.code, msg=user_email) users = [ ('clerk', 403, "can't add that user"), ('org_admin', 200, 'OK'), ] for user_email, code, reason in users: post_data = user_son.copy() post_data['organisation'] = post_data['organisation'].copy() post_data['deleted'] = True with base.mock_user(user_email): response = self.fetch("/user/%s.json" % user_son['id'], method='PUT', body=json_encode(post_data)) self.assertIn(reason, response.reason, msg=user_email) self.assertEqual(code, response.code, msg=user_email)
def modify_org(self, user_email, org_name, code, reason): with model.session_scope() as session: org = session.query(model.Organisation).\ filter(func.lower(model.Organisation.name) == func.lower(org_name)).one() to_son = ToSon( r'/id$', r'/name$', r'/title$', r'/locations.*$', r'/meta.*$', r'/surveygroups$', r'/[0-9]+$', ) to_son.exclude( r'/locations/[0-9]+/id$', r'/locations/[0-9]+/organisation.*$', r'/meta/id$', r'/meta/organisation.*$', ) org_son = to_son(org) with base.mock_user(user_email): post_data = org_son.copy() response = self.fetch("/organisation/%s.json" % org_son['id'], method='PUT', body=json_encode(post_data), expected=code) self.assertIn(reason, response.reason, "%s operating on %s" % (user_email, org_name))
def test_list_org(self): with base.mock_user('authority'): response = self.fetch("/organisation.json", method='GET', expected=200) orgs_son = json_decode(response.body) self.assertIsInstance(orgs_son, list) self.assertEqual(len(orgs_son), 2) del orgs_son[0]['id'] del orgs_son[1]['id'] expected = [{ 'name': 'Primary', 'deleted': False, 'locations': [{ 'description': 'Nowhere', }], 'meta': { 'assetTypes': ['water wholesale'], } }, { 'name': 'Utility', 'deleted': False, 'locations': [{ 'description': 'Somewhere', }], 'meta': { 'assetTypes': ['water local'], } }] self.assertEqual(orgs_son, expected)
def modify_org_in_user(self, user_email, old_org_name, new_org_name, code, reason): with model.session_scope() as session: org = (session.query(model.Organisation).filter( func.lower(model.Organisation.name) == func.lower( new_org_name)).one()) to_son = ToSon( r'/id$', r'/name$', r'/surveygroups$', r'/[0-9]+$', ) org_son = to_son(org) user = (session.query(model.AppUser).filter( func.lower(model.AppUser.email) == func.lower( user_email)).one()) user_son = to_son(user) user_son['organisation'] = org_son with base.mock_user(user_email): response = self.fetch("/user/%s.json" % user_son['id'], method='PUT', body=json_encode(user_son), expected=code) self.assertIn(reason, response.reason, msg=user_email)
def test_list_org(self): with base.mock_user('admin'): response = self.fetch("/user.json?pageSize=2&page=0", method='GET', expected=200) orgs_son = json_decode(response.body) self.assertIsInstance(orgs_son, list) self.assertEqual(len(orgs_son), 2) del orgs_son[0]['id'] del orgs_son[0]['organisation']['id'] del orgs_son[1]['id'] del orgs_son[1]['organisation']['id'] expected = [{ 'name': 'Admin', 'deleted': False, 'organisation': { 'name': 'Primary', 'deleted': False, } }, { 'name': 'Author', 'deleted': False, 'organisation': { 'name': 'Primary', 'deleted': False, } }] self.assertEqual(orgs_son, expected)
def test_create_org(self): users = [('clerk', 403, "can't add that organisation"), ('org_admin', 403, "can't add that organisation"), ('consultant', 403, "can't add that organisation"), ('authority', 403, "can't add that organisation"), ('author', 403, "can't add that organisation"), ('admin', 200, 'OK')] with model.session_scope() as session: surveygroups_son = self.get_groups_son(session, 'apple') for i, (user, code, reason) in enumerate(users): post_data = { "name": "Foo %d" % i, "url": "http://foo%d.com" % i, 'locations': [{ 'description': 'Foo', 'region': 'Foo', }], 'meta': { 'numberOfCustomers': 0, }, 'surveygroups': surveygroups_son, } with base.mock_user(user): response = self.fetch("/organisation.json", method='POST', body=json_encode(post_data), expected=code) self.assertIn(reason, response.reason, "%s" % (user))
def create_user(self, prefix, i, user_email, org_name, role, code, reason, surveygroup_names, custom_data=None): with model.session_scope() as session: surveygroups_son = self.get_groups_son(session, *surveygroup_names) post_data = { 'email': 'user%s%s' % (prefix, i), 'name': 'foo', 'password': '******', 'role': role, 'organisation': { 'id': str(self.org_id(org_name)) }, 'surveygroups': surveygroups_son, } if custom_data: post_data.update(custom_data) with base.mock_user(user_email), \ mock.patch( 'crud.user.test_password', lambda x: (1.0, 0.1, {})): response = self.fetch("/user.json", method='POST', body=json_encode(post_data), expected=code) self.assertIn(reason, response.reason, msg=user_email)
def test_password_strength(self): response = self.fetch("/password.json", method='POST', body=json_encode({'password': '******'}), expected=403) with base.mock_user('clerk'): response = self.fetch("/password.json", method='POST', body=json_encode({'password': '******'}), expected=200) son = json_decode(response.body) self.assertLess(son['strength'], 0.5) self.assertIn('charmix', son['improvements']) self.assertIn('length', son['improvements']) with base.mock_user('clerk'): response = self.fetch("/password.json", method='POST', body=json_encode({'password': '******'}), expected=200) son = json_decode(response.body) self.assertNotIn('charmix', son['improvements']) self.assertIn('length', son['improvements']) with base.mock_user('clerk'): response = self.fetch( "/password.json", method='POST', body=json_encode({'password': '******'}), expected=200) son = json_decode(response.body) self.assertIn('charmix', son['improvements']) self.assertNotIn('length', son['improvements']) with base.mock_user('clerk'): response = self.fetch("/password.json", method='POST', body=json_encode( {'password': '******'}), expected=200) son = json_decode(response.body) self.assertGreater(son['strength'], 0.9) self.assertNotIn('length', son['improvements']) self.assertNotIn('charmix', son['improvements'])
def test_create(self): with model.session_scope() as session: program = session.query(model.Program).one() organisation = (session.query( model.Organisation).filter_by(name='Utility').one()) survey = (session.query( model.Survey).filter_by(title='Survey 2').one()) program_id = str(program.id) organisation_id = str(organisation.id) survey_id = str(survey.id) # Try to create a submission against a suvery that hasn't been # purchased with base.mock_user('org_admin'): submission_son = {'title': "Submission"} submission_son = self.fetch( "/submission.json?organisationId=%s&programId=%s&surveyId=%s" % (organisation_id, program_id, survey_id), method='POST', body=json_encode(submission_son), expected=403, decode=False) # Grant access with base.mock_user('admin'): self.fetch("/organisation/%s/survey/%s.json?programId=%s" % (organisation_id, survey_id, program_id), method='PUT', body='', expected=200) # Retry with base.mock_user('org_admin'): submission_son = { 'title': "Submission", 'created': datetime.datetime(2012, 1, 1).timestamp(), } submission_son = self.fetch( "/submission.json?organisationId=%s&programId=%s&surveyId=%s" % (organisation_id, program_id, survey_id), method='POST', body=json_encode(submission_son), expected=200, decode=True)
def test_submission_exporter(self): with base.mock_user('admin'): self.purchase_program() with base.mock_user('clerk'): self.add_submission() with model.session_scope() as session: submission = (session.query(model.Submission).filter( model.Submission.program_id == self.program_id, model.Submission.organisation_id == self.organisation_id, model.Submission.survey_id == self.survey_id).first()) submission_id = submission.id with base.mock_user('author'): self.fetch("/report/sub/export/%s/tabular.xlsx" % submission_id, method='GET', expected=403, decode=False) self.fetch("/report/sub/export/%s/nested.xlsx" % submission_id, method='GET', expected=403, decode=False) with base.mock_user('consultant'): self.fetch("/report/sub/export/%s/tabular.xlsx" % submission_id, method='GET', expected=200, decode=False) self.fetch("/report/sub/export/%s/nested.xlsx" % submission_id, method='GET', expected=200, decode=False) with base.mock_user('clerk'): self.fetch("/report/sub/export/%s/tabular.xlsx" % submission_id, method='GET', expected=200, decode=False) self.fetch("/report/sub/export/%s/nested.xlsx" % submission_id, method='GET', expected=200, decode=False)
def test_structure_exporter_with_purchase(self): with base.mock_user('admin'): self.fetch("/report/prog/export/%s/survey/%s/nested.xlsx" % (self.program_id, self.survey_id), method='GET', expected=200) self.fetch("/report/prog/export/%s/survey/%s/tabular.xlsx" % (self.program_id, self.survey_id), method='GET', expected=200)
def test_recalculate(self): aid = self.create_submission() with model.session_scope() as session: submission = session.query(model.Submission).get(aid) sid = submission.program_id process_id = submission.survey.qnodes[0].children[0].id function_2_id = submission.survey.qnodes[1].id # Move a process (qnode) to a different function with base.mock_user('author'): url = "/qnode/{}.json?programId={}".format(process_id, sid) qnode_son = self.fetch(url, method='GET', expected=200, decode=True) qnode_son = self.fetch(url + "&parentId={}".format(function_2_id), method='PUT', expected=200, decode=True, body=json_encode(qnode_son)) # Check that rnode score is out of date with model.session_scope() as session: submission = session.query(model.Submission).get(aid) functions = list(submission.rnodes) self.assertAlmostEqual(functions[0].score, 3 + 6 + 11 + 13) self.assertAlmostEqual(functions[1].score, 0) self.assertAlmostEqual(functions[0].qnode.total_weight, 11 + 13) self.assertAlmostEqual(functions[1].qnode.total_weight, 3 + 6) # Run recalculation script config = utils.get_config("recalculate.yaml") messages = None def send(config, msg): messages.append(msg) messages = [] with mock.patch('recalculate.send', send): recalculate.process_once(config) self.assertEqual(len(messages), 0) # Check that rnode score is no longer out of date with model.session_scope() as session: submission = session.query(model.Submission).get(aid) functions = list(submission.rnodes) self.assertAlmostEqual(functions[0].score, 11 + 13) self.assertAlmostEqual(functions[1].score, 3 + 6) self.assertAlmostEqual(functions[0].qnode.total_weight, 11 + 13) self.assertAlmostEqual(functions[1].qnode.total_weight, 3 + 6)
def test_impersonate_expired(self): ''' Ensure superusers can't keep impersonating after jurisdiction changes. ''' with model.session_scope() as session: admin = (session.query( model.AppUser).filter(model.AppUser.email == 'admin').first()) clerk = (session.query( model.AppUser).filter(model.AppUser.email == 'clerk').first()) with base.mock_user(user_email='clerk', super_email='admin'): # Wrong group; fails self.set_groups(clerk, 'banana') session.commit() response = self.fetch("/program.json".format(), method='GET', follow_redirects=False, expected=403) self.assertIn("not a memeber of that survey group", response.reason) # Right group; works self.set_groups(clerk, 'apple') session.commit() response = self.fetch("/program.json".format(), method='GET', follow_redirects=False, expected=200) self.assertIn("OK", response.reason) # Impersonated user disabled; fails clerk.deleted = True admin.deleted = False session.commit() response = self.fetch("/program.json".format(), method='GET', follow_redirects=False, expected=403) self.assertIn("account has been disabled", response.reason) # Superuser (true user) disabled; fails clerk.deleted = False admin.deleted = True session.commit() response = self.fetch("/program.json".format(), method='GET', follow_redirects=False, expected=403) self.assertIn("account has been disabled", response.reason)
def test_recalculate_failure(self): aid = self.create_submission() with model.session_scope() as session: submission = session.query(model.Submission).get(aid) sid = submission.program_id process_id = submission.survey.qnodes[0].children[0].id function_2_id = submission.survey.qnodes[1].id # Move a process (qnode) to a different function with base.mock_user('author'): url = "/qnode/{}.json?programId={}".format(process_id, sid) qnode_son = self.fetch(url, method='GET', expected=200, decode=True) qnode_son = self.fetch(url + "&parentId={}".format(function_2_id), method='PUT', expected=200, decode=True, body=json_encode(qnode_son)) # Run recalculation script config = utils.get_config("recalculate.yaml") messages = None def send(config, msg, to): messages.append(msg) messages = [] with mock.patch('recalculate.send', send), \ mock.patch('response_type.ResponseType.validate', side_effect=ResponseTypeError): count, n_errors = recalculate.process_once(config) self.assertEqual(n_errors, 1) messages = [] with mock.patch('recalculate.send', send), \ mock.patch('recalculate.process_once', side_effect=ExpectedError), \ self.assertRaises(ExpectedError), \ mock.patch('recalculate.time.sleep', side_effect=UnexpectedError): recalculate.process_loop() self.assertEqual(len(messages), 1)
def test_impersonate(self): users = [ ('clerk', 'author', 403, "rank is too low"), ('org_admin', 'clerk', 403, "rank is too low"), ('author', 'clerk', 403, "rank is too low"), ('consultant', 'clerk', 403, "rank is too low"), ('authority', 'clerk', 403, "rank is too low"), ('author', 'clerk', 403, "rank is too low"), ('clerk', 'clerk_b', 403, "rank is too low"), ('super_admin', 'clerk', 200, 'OK'), ('super_admin', 'org_admin', 200, 'OK'), ('super_admin', 'author', 200, 'OK'), ('super_admin', 'consultant', 200, 'OK'), ('super_admin', 'authority', 200, 'OK'), ('super_admin', 'author', 200, 'OK'), ('super_admin', 'admin', 200, 'OK'), ('super_admin', 'clerk_b', 200, 'OK'), ('admin', 'clerk', 200, 'OK'), ('admin', 'org_admin', 200, 'OK'), ('admin', 'consultant', 200, 'OK'), ('admin', 'authority', 200, 'OK'), ('admin', 'author', 200, 'OK'), ('admin', 'admin', 200, 'OK'), ('admin', 'super_admin', 403, 'rank is too low'), ('admin', 'clerk_b', 403, 'not a memeber of that survey group'), ] for super_email, user_email, code, reason in users: with model.session_scope() as session: user = (session.query(model.AppUser).filter( model.AppUser.email == user_email).first()) user_id = user.id with base.mock_user(user_email, super_email): response = self.fetch("/impersonate/{}".format(user_id), method='PUT', body='') self.assertIn( reason, response.reason, "{} failed to impersonate {}".format( super_email, user_email)) self.assertEqual(code, response.code)
def test_set_password(self): with model.session_scope() as session: surveygroups_son = self.get_groups_son(session, 'apple') post_data = { 'email': 'passwordTest', 'name': 'ptest', 'password': '******', 'role': 'clerk', 'organisation': { 'id': str(self.org_id('utility')) }, 'surveygroups': surveygroups_son, } with base.mock_user('admin'): response = self.fetch("/user.json", method='POST', body=json_encode(post_data), expected=403) self.assertIn('not strong enough', response.reason)
def test_authenticated_root(self): with base.mock_user('admin'): response = self.fetch("/", expected=200) self.assertIn("Sign out", response.body.decode('utf8'))
def test_extern(self): '''Check that variables can depend on each other''' with model.session_scope() as session: user = (session.query( model.AppUser).filter_by(email='clerk').one()) survey = (session.query( model.Survey).filter_by(title='Survey 1').one()) program = survey.program # Add a response type that has an extenal variable rt = next(rt for rt in TEST_RESPONSE_TYPES if rt['id'] == 'external-var') ext_response_type = model.ResponseType(program=program, name=rt['name'], parts=rt['parts'], formula=rt['formula']) session.add(ext_response_type) # Attach response type to a measure target_qm = survey.qnodes[0].children[0].qnode_measures[1] target_qm.measure.response_type = ext_response_type self.assertEqual(1, len(ext_response_type.measures)) # Bind variable to link measures source_qm = survey.qnodes[0].children[0].qnode_measures[0] session.add( model.MeasureVariable(program=program, survey=survey, source_qnode_measure=source_qm, source_field='_score', target_qnode_measure=target_qm, target_field='ext')) submission = model.Submission(program=program, organisation=user.organisation, survey=survey, title="Intermeasure Variables Test", approval='draft') session.add(submission) session.flush() submission_id = str(submission.id) mid_111 = str( survey.qnodes[0].children[0].qnode_measures[0].measure_id) mid_112 = str( survey.qnodes[0].children[0].qnode_measures[1].measure_id) # Put dependant response with errors. Check that the error refers to # missing dependency. with base.mock_user('clerk'): response_son = { 'notRelevant': False, 'responseParts': [], 'comment': "Incomplete dependant response", 'approval': 'draft', } response_son = self.fetch("/submission/%s/response/%s.json" % (submission_id, mid_112), method='PUT', body=response_son, expected=200, decode=True) with model.session_scope() as session: response = (session.query(model.Response).get( (submission_id, mid_112))) self.assertIn('depends on', response.error) self.assertIn('measure has an error', response.parent.error) self.assertIn('sub-category has an error', response.parent.parent.error) self.assertIn('category has an error', response.submission.error) # Put dependency, and check that error of dependant has changed. with base.mock_user('clerk'): response_son = { 'notRelevant': False, 'responseParts': [{ 'note': 'Yes', 'index': 1 }], 'comment': "Dependency", 'approval': 'draft', } response_son = self.fetch("/submission/%s/response/%s.json" % (submission_id, mid_111), method='PUT', body=response_son, expected=200, decode=True) with model.session_scope() as session: response = (session.query(model.Response).get( (submission_id, mid_111))) self.assertIs(response.error, None) # Parent still has an error due to sibling self.assertIn('measure has an error', response.parent.error) response = (session.query(model.Response).get( (submission_id, mid_112))) # Error has changed: dependency has been provided, but response # is still incomplete self.assertIn('undefined variable', response.error) # Fix dependant response and check that errors are resolved. with base.mock_user('clerk'): response_son = self.fetch("/submission/%s/response/%s.json" % (submission_id, mid_112), method='GET', expected=200, decode=True) response_son['response_parts'] = [{'value': 1}] response_son['comment'] = "Complete dependant response" response_son = self.fetch("/submission/%s/response/%s.json" % (submission_id, mid_112), method='PUT', body=response_son, expected=200, decode=True) with model.session_scope() as session: response = (session.query(model.Response).get( (submission_id, mid_112))) # Error has been resolved. self.assertIs(response.error, None) self.assertIs(response.error, response.parent.error) self.assertIs(response.error, response.parent.parent.error) self.assertIs(response.error, response.submission.error)
def test_timeline(self): # Delete a qnode; this should subscribe to the program and add an event # to the timeline with base.mock_user('author'): program_sons = self.fetch("/program.json", method='GET', expected=200, decode=True) sid = program_sons[0]['id'] survey_sons = self.fetch( "/survey.json?programId=%s&term=Survey%%201" % sid, method='GET', expected=200, decode=True) hid = survey_sons[0]['id'] url = ("/qnode.json?programId=%s&surveyId=%s&root=&deleted=false" % (sid, hid)) qnode_sons = self.fetch(url, method='GET', expected=200, decode=True) self.assertTrue(all(q['deleted'] == False for q in qnode_sons)) qid1 = qnode_sons[0]['id'] qid2 = qnode_sons[1]['id'] a_son = self.fetch("/activity.json?period=604800", method='GET', expected=200, decode=True) self.assertEqual(len(a_son['actions']), 0) sub_son = self.fetch("/subscription/qnode/{},{}.json".format( qid1, sid), method='GET', expected=200, decode=True) ss = [sub['subscribed'] for sub in sub_son] self.assertTrue(all(s is None for s in ss)) self.fetch("/qnode/{}.json?programId={}".format(qid1, sid), method='DELETE', expected=200) sub_son = self.fetch("/subscription/qnode/{},{}.json".format( qid1, sid), method='GET', expected=200, decode=True) ss = [sub['subscribed'] for sub in sub_son] self.assertTrue(any(s is not None for s in ss)) self.assertTrue(any(s is None for s in ss)) a_son = self.fetch("/activity.json?period=604800", method='GET', expected=200, decode=True) self.assertEqual(len(a_son['actions']), 1) config = utils.get_config("notification.yaml") messages = None def send(config, msg, to): messages[to] = msg # Send message to everyone in 'apple' group with base.mock_user('admin'), model.session_scope() as session: self.fetch("/activity.json", method='POST', expected=200, decode=True, body=json_encode({ 'to': 'all', 'sticky': True, 'message': "Foo", 'surveygroups': self.get_groups_son(session, 'apple'), })) messages = {} with mock.patch('notifications.send', send): n_sent = notifications.process_once(config) self.assertEqual(n_sent, 6) self.assertEqual(len(messages), 6) # Check message URLs with model.session_scope() as session: app_base_url = app_config.get_setting(session, 'app_base_url') author_checked = False for to, m in messages.items(): self.assertIn("\nAdmin said:\nFoo\n", str(m)) # Assumes base URL is used in the notification email, this could # be optional self.assertIn(app_base_url, str(m)) if to == 'author': self.assertIn( '\nFunction 1\nAuthor deleted this survey category\n', str(m)) log.info("Notification email: %s", str(m)) author_checked = True self.assertTrue(author_checked) time.sleep(0.1) # Delete another qnode with base.mock_user('author'): self.fetch("/qnode/{}.json?programId={}".format(qid2, sid), method='DELETE', expected=200) # Run again, and make sure no nofications send (because not enough time # has elapsed since the last email) messages = {} with mock.patch('notifications.send', send): n_sent = notifications.process_once(config) self.assertEqual(n_sent, 0) self.assertEqual(len(messages), 0) time.sleep(0.1) sa_func_now = sa.func.now def next_week(): return sa_func_now() + datetime.timedelta(days=7) # Run again, pretending to be in the future, and check that another # notification is sent messages = {} with mock.patch('notifications.send', send), \ mock.patch('notifications.func.now', next_week): n_sent = notifications.process_once(config) self.assertEqual(n_sent, 1) self.assertEqual(len(messages), 1) author_checked = False for to, m in messages.items(): if to == 'author': self.assertIn( '\nFunction 2\nAuthor deleted this survey category\n', str(m)) log.info("Notification email: %s", str(m)) author_checked = True self.assertTrue(author_checked)
def test_duplicate(self): # Respond to a survey with model.session_scope() as session: user = (session.query( model.AppUser).filter_by(email='clerk').one()) survey_1 = (session.query( model.Survey).filter_by(title='Survey 1').one()) survey_2 = (session.query( model.Survey).filter_by(title='Survey 2').one()) submission = self.create_submission(survey_1, user) organisation_id = str(user.organisation.id) first_submission_id = str(submission.id) survey_1_id = str(survey_1.id) survey_2_id = str(survey_2.id) # Duplicate program with base.mock_user('author'): program_sons = self.fetch("/program.json", method='GET', expected=200, decode=True) self.assertEqual(len(program_sons), 1) original_program_id = program_sons[0]['id'] program_son = self.fetch("/program/%s.json" % original_program_id, method='GET', expected=200, decode=True) program_son['title'] = "Duplicate program" program_son = self.fetch("/program.json?duplicateId=%s" % original_program_id, method='POST', body=json_encode(program_son), expected=200, decode=True) new_program_id = program_son['id'] # Open (purchase) both new surveys for organisation with base.mock_user('admin'): self.fetch("/organisation/%s/survey/%s.json?programId=%s" % (organisation_id, survey_1_id, new_program_id), method='PUT', body='', expected=200) self.fetch("/organisation/%s/survey/%s.json?programId=%s" % (organisation_id, survey_2_id, new_program_id), method='PUT', body='', expected=200) # Duplicate submission, once for each survey, in the new program with base.mock_user('org_admin'): submission_son = { 'title': "Second submission", 'created': datetime.datetime(2013, 1, 1).timestamp(), } submission_son = self.fetch( "/submission.json?organisationId=%s&programId=%s&" "surveyId=%s&duplicateId=%s" % (organisation_id, new_program_id, survey_1_id, first_submission_id), method='POST', body=json_encode(submission_son), expected=200, decode=True) second_submission_id = submission_son['id'] submission_son = { 'title': "Third submission", 'created': datetime.datetime(2013, 1, 1).timestamp(), } submission_son = self.fetch( "/submission.json?organisationId=%s&programId=%s&" "surveyId=%s&duplicateId=%s" % (organisation_id, new_program_id, survey_2_id, first_submission_id), method='POST', body=json_encode(submission_son), expected=200, decode=True) third_submission_id = submission_son['id'] self.assertNotEqual(first_submission_id, second_submission_id) self.assertNotEqual(first_submission_id, third_submission_id) self.assertNotEqual(second_submission_id, third_submission_id) # Check contents with model.session_scope() as session: submission_1 = (session.query( model.Submission).get(first_submission_id)) submission_2 = (session.query( model.Submission).get(second_submission_id)) submission_3 = (session.query( model.Submission).get(third_submission_id)) self.assertEqual(submission_1.survey_id, submission_2.survey_id) self.assertNotEqual(submission_1.survey_id, submission_3.survey_id) # Submission 1 has responses against six measures. Two are # descendants of deleted qnodes. # Submission 2 uses the same survey as the source submission, # so it should have the same number of responses (four, because # the deleted ones are not copied). # Submission 3 uses a different survey with only one common # measure, so it should have a different number of responses (two). self.assertEqual(len(submission_1.responses), 6) self.assertEqual(len(submission_2.responses), 4) self.assertEqual(len(submission_3.responses), 2) self.assertEqual(session.query(model.Response).count(), 12) # Make sure the number of rnodes matches the number of qnodes in # the survey (no leftovers). self.assertEqual( session.query(model.ResponseNode).filter_by( submission_id=submission_1.id).count(), 4) self.assertEqual( session.query(model.ResponseNode).filter_by( submission_id=submission_2.id).count(), 4) self.assertEqual( session.query(model.ResponseNode).filter_by( submission_id=submission_3.id).count(), 3) # Check scores. Submissions 1 and 2 should have the same score: # 100 + 200 = 300 (due to weighting of the measures). Submission 3 # should have just 200. self.assertEqual([r.score for r in submission_1.ordered_responses], [3, 6, 11, 13]) self.assertEqual(list(submission_1.rnodes)[0].score, 33) self.assertEqual(list(submission_1.rnodes)[1].score, 0) self.assertEqual([r.score for r in submission_2.ordered_responses], [3, 6, 11, 13]) self.assertEqual(list(submission_2.rnodes)[0].score, 33) self.assertEqual(list(submission_2.rnodes)[1].score, 0) self.assertEqual([r.score for r in submission_3.ordered_responses], [6, 11]) self.assertEqual(list(submission_3.rnodes)[0].score, 17) self.assertEqual(list(submission_3.rnodes)[1].score, 0) # When a submission is duplicated, all of its responses are set to # 'draft'. self.assertEqual(submission_1.approval, 'final') self.assertTrue( all(r.approval == 'final' for r in submission_1.responses)) self.assertEqual(list(submission_1.rnodes)[0].n_draft, 4) self.assertEqual(list(submission_1.rnodes)[1].n_draft, 0) self.assertEqual(list(submission_1.rnodes)[0].n_final, 4) self.assertEqual(list(submission_1.rnodes)[1].n_final, 0) self.assertEqual(submission_2.approval, 'draft') self.assertTrue( all(r.approval == 'draft' for r in submission_2.responses)) self.assertEqual(list(submission_2.rnodes)[0].n_draft, 4) self.assertEqual(list(submission_2.rnodes)[1].n_draft, 0) self.assertEqual(list(submission_2.rnodes)[0].n_final, 0) self.assertEqual(list(submission_2.rnodes)[1].n_final, 0) self.assertEqual(submission_3.approval, 'draft') self.assertTrue( all(r.approval == 'draft' for r in submission_3.responses)) self.assertEqual(list(submission_3.rnodes)[0].n_draft, 2) self.assertEqual(list(submission_3.rnodes)[1].n_draft, 0) self.assertEqual(list(submission_3.rnodes)[0].n_final, 0) self.assertEqual(list(submission_3.rnodes)[1].n_final, 0) # Check attachment duplication self.assertNotEqual(str(submission_1.id), str(submission_2.id)) self.assertNotEqual(str(original_program_id), str(new_program_id)) for r1, r2 in zip(submission_1.ordered_responses, submission_2.ordered_responses): self.assertEqual(str(r1.submission_id), str(submission_1.id)) self.assertEqual(str(r2.submission_id), str(submission_2.id)) self.assertEqual(str(r1.program_id), str(original_program_id)) self.assertEqual(str(r2.program_id), str(new_program_id)) self.assertEqual(str(r1.survey_id), str(r2.survey_id)) self.assertEqual(str(r1.measure_id), str(r2.measure_id)) self.assertEqual(len(r1.attachments), 3) self.assertEqual(len(r2.attachments), 3) attachments_1 = sorted(r1.attachments, key=lambda a: a.file_name) attachments_2 = sorted(r2.attachments, key=lambda a: a.file_name) for a1, a2 in zip(attachments_1, attachments_2): self.assertNotEqual(str(a1.id), str(a2.id)) self.assertEqual(a1.file_name, a2.file_name) self.assertEqual(a1.url, a2.url) self.assertEqual(a1.blob, a2.blob)
def test_timeline_groups(self): config = utils.get_config("notification.yaml") messages = None def send(config, msg, to): messages[to] = msg def clear_timeline(): with model.session_scope() as session: for activity in session.query(model.Activity).all(): activity.surveygroups = set() session.delete(activity) for user in session.query(model.AppUser).all(): user.email_time = None # Send message to everyone in 'banana' group clear_timeline() with base.mock_user('super_admin'), model.session_scope() as session: self.fetch("/activity.json", method='POST', expected=200, decode=True, body=json_encode({ 'to': 'all', 'sticky': True, 'message': "Foo", 'surveygroups': self.get_groups_son(session, 'banana'), })) messages = {} with mock.patch('notifications.send', send): n_sent = notifications.process_once(config) self.assertEqual(n_sent, 1) self.assertEqual(len(messages), 1) # Send message to everyone clear_timeline() with base.mock_user('super_admin'), model.session_scope() as session: self.fetch("/activity.json", method='POST', expected=200, decode=True, body=json_encode({ 'to': 'all', 'sticky': True, 'message': "Foo", 'surveygroups': self.get_groups_son(session, 'apple', 'banana'), })) messages = {} with mock.patch('notifications.send', send): n_sent = notifications.process_once(config) self.assertEqual(n_sent, 7) self.assertEqual(len(messages), 7)