def test_update_checkpoint(self): """Can't change a checkpoint to an invalid status, based on tasks.""" # One checkpoint with one tasks. orig_config = organization_tasks.tasklist_template tasklist_tmpl = [ { 'name': "Checkpoint Foo", 'label': 'checkpoint label foo', 'tasks': [{ 'label': 'foo', 'non_admin_may_edit': True }, { 'label': 'bar', 'non_admin_may_edit': True }] }, ] organization_tasks.tasklist_template = tasklist_tmpl user = User.create(email='*****@*****.**', user_type='user') org = Organization.create(name='org', tasklist_template=tasklist_tmpl, liaison_id=user.uid) user.owned_organizations = [org.uid] org.put() user.put() # Make one task complete, the other stays incomplete by default. org.tasklist.tasks[0].status = 'complete' org.tasklist.tasks[0].put() # This is an unsaved, in-memory dict, and thus doesn't contain defaults # set in the table definition. ckpt = org.tasklist.checkpoints[0] # Fetch the checkpoint by id to pick up those defaults. ckpt = Checkpoint.get_by_id(ckpt.uid) # The derived checkpoint status is 'incomplete'. Setting it this way # should work. response = self.testapp.put_json( '/api/checkpoints/{}'.format(ckpt.uid), ckpt.to_client_dict(), headers=login_headers(user.uid), ) # Shouldn't be able to set the checkpoint to any other value. ckpt.status = 'foo' response = self.testapp.put_json( '/api/checkpoints/{}'.format(ckpt.uid), ckpt.to_client_dict(), headers=login_headers(user.uid), status=500, ) organization_tasks.tasklist_template = orig_config
def test_related_org_permissions(self): """Who besides the specified user can get related orgs?""" # Also creates 3 orgs, where user owns one and is assc with one. super_admin, program_admin, user = self.create_entities_to_get() # Besides the user specified in the URL, super admins can reach the # resource, but others can't. response = self.testapp.get('/api/users/{}/organizations'.format( user.uid), headers=login_headers(super_admin.uid), status=200) response = self.testapp.get('/api/users/{}/organizations'.format( user.uid), headers=login_headers(program_admin.uid), status=403)
def test_user_tasklist(self): """Get everything you need for a tasklist in one query.""" query, expected = self.tasklist_query_expected( *self.create_project_cohort()) project_cohort_id = expected['project_cohort']['uid'] org_id = expected['project_cohort']['organization']['uid'] user = User.create( email='*****@*****.**', user_type='user', owned_organizations=[org_id], ) user.put() # Now that there's an org-level user, expect another in the users list. # They'll always come second b/c the org's users are sorted by email. expected['project_cohort']['organization']['users'].append( user.to_client_dict()) response = self.testapp.get( '/api/tasklists/{}'.format(project_cohort_id), headers=login_headers(user.uid), ) self.assertEqual(response.body, json.dumps(expected))
def test_update_task(self): """Org admins can't change some tasks.""" org = Organization.create(name='org') org.put() user = User.create(email='*****@*****.**', user_type='user', owned_organizations=[org.uid]) user.put() orig_config = organization_tasks.tasklist_template organization_tasks.tasklist_template = [{ 'tasks': [{ 'label': 'task_foo', 'non_admin_may_edit': False, }] }] task = Task.create('task_foo', 1, 'checkpoint_foo', parent=org) task.put() # Org admin shouldn't be able to mark the task as complete, even though # they own the task. task.status = 'complete' response = self.testapp.put_json('/api/tasks/{}'.format(task.uid), task.to_client_dict(), headers=login_headers(user.uid), status=403) organization_tasks.tasklist_template = orig_config
def test_get_all_tasks(self): super_user = User.create(email='*****@*****.**', user_type='super_admin') super_user.put() org, tasks = self.create_org_tasks(super_user) query = ''' query GetAllTasks { tasks { label } } ''' expected = { # Should be ordered by ordinal 'tasks': [{ 'label': t.label } for t in tasks], } response = self.testapp.post_json( '/api/graphql', {'query': query}, headers=login_headers(super_user.uid), ) self.assertEqual(json.loads(response.body), expected)
def test_get_all_orgs(self): # Also creates 3 orgs, where user owns one and is assc with one. super_admin, program_admin, user = self.create_entities_to_get() # Super sees all response = self.testapp.get('/api/organizations', headers=login_headers(super_admin.uid)) self.assertEqual(len(json.loads(response.body)), 3) # Org and Program admins are forbidden. response = self.testapp.get('/api/organizations', headers=login_headers(user.uid), status=403) response = self.testapp.get('/api/organizations', headers=login_headers(program_admin.uid), status=403)
def test_get_all_checkpoints(self): checkpoints = self.create_checkpoints() user = User.create(email='*****@*****.**', user_type='super_admin') user.put() query = ''' query GetAllCheckpoints { checkpoints { label } } ''' expected = { # Should be ordered by ordinal 'checkpoints': [{'label': c.label} for c in checkpoints], } response = self.testapp.post_json( '/api/graphql', {'query': query}, headers=login_headers(user.uid), ) self.assertEqual(json.loads(response.body), expected)
def test_get_many_tasks(self): """Queries aren't limited to an arbitrarily small number.""" super_user = User.create(email='*****@*****.**', user_type='super_admin') super_user.put() num_orgs = 10 # Assumes only one org checkpoint. num_org_tasks = len(organization_tasks.tasklist_template[0]['tasks']) for x in range(num_orgs): self.create_org_tasks(super_user) query = ''' query GetAllTasks { tasks { label } } ''' response = self.testapp.post_json( '/api/graphql', {'query': query}, headers=login_headers(super_user.uid), ) response_dict = json.loads(response.body) self.assertEqual(len(response_dict['tasks']), num_org_tasks * num_orgs)
def test_create_project(self): """Creating a project through the api makes a TR for all org owners.""" user1 = User.create(email='*****@*****.**', user_type='user') user2 = User.create(email='*****@*****.**', user_type='user') org = Organization.create(name="Foo Org", liaison_id=user1.uid) user1.owned_organizations = [org.uid] user2.owned_organizations = [org.uid] user1.put() user2.put() org.put() # Bring org 2 into consistency, assuming they've been part of the org # for some time. Don't bring org 1 into consistency, to simulate # joining an org and creating a project within a short timespan, which # we expect. user2.key.get() response = self.testapp.post_json( '/api/projects', { 'organization_id': org.uid, 'program_label': 'demo-program', 'liaison_id': user1.uid }, headers=login_headers(user1.uid), ) project_dict = json.loads(response.body) trs1 = TaskReminder.get(ancestor=user1) trs2 = TaskReminder.get(ancestor=user2) self.assertEqual(len(trs1), 1) self.assertEqual(len(trs2), 1) self.assertEqual(trs1[0].context_id, project_dict['uid'])
def test_completion_csv_allowed(self): org_id = 'Organization_foo' pc = ProjectCohort.create( organization_id=org_id, program_label=self.program_label, cohort_label=self.cohort_label, ) pc.put() user = User.create( email='*****@*****.**', owned_organizations=[org_id], ) user.put() t = AuthToken.create(user.uid) t.put() self.testapp.get( '/api/project_cohorts/{}/completion/ids.csv'.format(pc.uid), params={'token': t.token}, headers=login_headers(user.uid), # Authenticated, one-time token valid, has permission: 200. status=200, )
def test_close_tasklist(self): """Should delete all associated task reminders.""" user1 = User.create(email='*****@*****.**', user_type='user') user2 = User.create(email='*****@*****.**', user_type='user') org = Organization.create(name="Foo Org", liaison_id=user1.uid) user1.owned_organizations = [org.uid] user2.owned_organizations = [org.uid] user1.put() user2.put() org.put() response = self.testapp.post_json( '/api/projects', { 'organization_id': org.uid, 'program_label': 'demo-program', 'liaison_id': user1.uid }, headers=login_headers(user1.uid), ) project_dict = json.loads(response.body) project = Project.get_by_id(project_dict['uid']) # Simulate time passing and the datastore reaching consistency. trs1 = TaskReminder.get(ancestor=user1) trs2 = TaskReminder.get(ancestor=user2) Tasklist(project).close() self.assertEqual(len(TaskReminder.get(ancestor=user1)), 0) self.assertEqual(len(TaskReminder.get(ancestor=user2)), 0)
def test_update_checkpoint_waiting(self): user = User.create(email='*****@*****.**', user_type='super_admin') user.put() org = Organization.create(name='Foo Org') org.put() # Work with the first checkpoint (generally there's only one anyway). c = org.tasklist.checkpoints[0] # Our org tasklist doesn't change much; break down its structure. tasks = [t for t in org.tasklist.tasks if t.checkpoint_id == c.uid] invite_task, liaison_task, approve_task = tasks # Marking the first two complete should change the checkpoint to # waiting, since the last is for super admins only. for task in (invite_task, liaison_task): task.status = 'complete' self.testapp.put_json( '/api/tasks/{}'.format(task.uid), task.to_client_dict(), headers=login_headers(user.uid), ) fetched_c = Checkpoint.get_by_id(c.uid) self.assertEqual(fetched_c.status, 'waiting')
def test_get_all_surveys(self): surveys = self.create_surveys() user = User.create(email='*****@*****.**', user_type='super_admin') user.put() query = ''' query GetAllSurveys { surveys { uid } } ''' expected = { # Should be ordered by ordinal 'surveys': [{ "uid": s.uid } for s in surveys], } response = self.testapp.post_json( '/api/graphql', {'query': query}, headers=login_headers(user.uid), ) self.assertEqual(response.body, json.dumps(expected))
def test_get_all_projects(self): user = User.create(email="*****@*****.**", user_type='super_admin') user.put() org = Organization.create(name="Org Foo") org.put() project1 = Project.create(organization_id=org.uid, program_label='demo-program') project2 = Project.create(organization_id=org.uid, program_label='demo-program') project1.put() project2.put() query = ''' query GetAllProjects { projects { uid } } ''' response = self.testapp.post_json( '/api/graphql', {'query': query}, headers=login_headers(user.uid), ) received = json.loads(response.body) # No particular order. self.assertIn({'uid': project1.uid}, received['projects']) self.assertIn({'uid': project2.uid}, received['projects'])
def test_get_single_project(self): user = User.create(email="*****@*****.**", user_type='super_admin') user.put() org = Organization.create(name="Org Foo") org.put() project = Project.create( organization_id=org.uid, program_label='demo-program', account_manager_id='User_001', liaison_id='User_002', priority=True, deidentification_method='total', loa_notes="Some stuff happened.", last_active=datetime.datetime.now(), ) project.put() query = ''' query GetSingleProject($uid: String!) { project(uid: $uid) { account_manager_id created deidentification_method deleted last_active liaison_id loa_notes modified organization_id organization_name organization_status priority program_description program_label program_name short_uid uid } } ''' response = self.testapp.post_json( '/api/graphql', # See http://graphql.org/learn/serving-over-http/#post-request { 'query': query, 'variables': { 'uid': project.uid }, }, headers=login_headers(user.uid), ) self.assertEqual( response.body, json.dumps({'project': project.to_client_dict()}), )
def test_get_assc_org(self): """You CAN get an org you're merely associated with.""" # Also creates 3 orgs, where user owns one and is assc with one. super_admin, program_admin, user = self.create_entities_to_get() assc_id = user.assc_organizations[0] response = self.testapp.get('/api/organizations/' + assc_id, headers=login_headers(user.uid))
def test_get_nonexistent_resource(self): user = User.create(email='*****@*****.**') user.put() for klass in self.get_all_models(): resource_name = util.camel_to_separated(klass.__name__) response = self.testapp.get('/api/{}/foo'.format(resource_name), headers=login_headers(user.uid), status=404)
def test_user_tasklist_not_found(self): user = User.create(email='*****@*****.**', user_type='user') user.put() self.testapp.get( '/api/tasklists/foo', headers=login_headers(user.uid), status=404, )
def test_join_org(self): """Joining an org makes task reminders for all contained tasklists.""" program_label = 'demo-program' cohort_label = 'demo-cohort' # Guarantee the dates will work by mocking the cohort config. cohort_config = { 'label': cohort_label, 'name': 'Demo Cohort', 'open_date': str(datetime.date.today()), 'close_date': str(datetime.date.today()), } Program.mock_program_config( program_label, {'cohorts': { cohort_label: cohort_config }}, ) program = Program.get_config(program_label) tasklist_template = program['surveys'][0]['survey_tasklist_template'] owner = User.create(email='*****@*****.**', user_type='user') org = Organization.create(name="Foo Org", liaison_id=owner.uid) owner.owned_organizations = [org.uid] project = Project.create(program_label=program_label, organization_id=org.uid) survey = Survey.create(tasklist_template, project_id=project.uid, organization_id=org.uid, program_label=program_label, ordinal=1, cohort_label=cohort_label) owner.put() org.put() project.put() survey.put() # The assumption here is the org and its contents are long-standing, # so force consistency with all. org.key.get() project.key.get() survey.key.get() joiner = User.create(email='*****@*****.**', user_type='user') joiner.put() self.testapp.post_json( '/api/users/{}/organizations'.format(joiner.uid), org.to_client_dict(), headers=login_headers(owner.uid), ) # One TaskReminder for each of: org tasklist, project tasklist, survey # tasklist. self.assertEqual(len(TaskReminder.get(ancestor=joiner)), 3) return (org, project, survey, owner, joiner)
def test_get_unrelated_org(self): """You can't get an org you're enitrely unrelated to.""" # Also creates 3 orgs, where user owns one and is assc with one. super_admin, program_admin, user = self.create_entities_to_get() unrelated_id = super_admin.owned_organizations[0] response = self.testapp.get('/api/organizations/' + unrelated_id, headers=login_headers(user.uid), status=403)
def test_register_page_as_user(self): user = User.create(email='*****@*****.**') user.put() headers = login_headers(user.uid) response = self.testapp.get('/register', headers=headers) # Should redirect to the dashboard. self.assertEqual(response.status_int, 302) self.assertRegexpMatches( response.headers.get('Location'), r'/dashboard$')
def test_completion_csv_invalid_token(self): user = User.create(email='*****@*****.**') user.put() self.testapp.get( '/api/project_cohorts/ProjectCohort_foo/completion/ids.csv', headers=login_headers(user.uid), # no auth token, request invalid status=400, )
def test_user_tasklist_forbidden(self): """Forbidden if you aren't on the org.""" liaison, org, project, pc, surveys = self.create_project_cohort() user = User.create(email='*****@*****.**', user_type='user') user.put() self.testapp.get( '/api/tasklists/{}'.format(pc.uid), headers=login_headers(user.uid), status=403, )
def test_get_single_user(self): user = User.create(email="*****@*****.**", user_type='super_admin', owned_organizations=['Organization_001']) user.hashed_password = User.hash_password('abcdefgh') user.put() query = ''' query GetSingleUser($uid: String!) { user(uid: $uid) { assc_organizations assc_projects created deleted email google_id hashed_password last_login modified name notification_option owned_data_requests owned_data_tables owned_organizations owned_programs owned_projects phone_number role short_uid uid user_type } } ''' response = self.testapp.post_json( '/api/graphql', # See http://graphql.org/learn/serving-over-http/#post-request { 'query': query, 'variables': { 'uid': user.uid }, }, headers=login_headers(user.uid), ) # Using the api gives the user a value for last_login. Refetch them to # get an accurate reference. user = user.key.get() self.assertEqual( response.body, json.dumps({'user': user.to_client_dict()}), )
def test_join_existing_project(self): project_dict, user = self.test_user_create() # Try to make another one, which should _succeed_, now that we support # multiple projects at an org. self.testapp.post_json( '/api/projects', {'organization_id': self.org_id, 'program_label': self.program_label}, headers=login_headers(user.uid), )
def test_related_query_forbidden(self): """Can't query for owners of an org you don't own.""" org = Organization.create() user = User.create(email="*****@*****.**") org.put() user.put() response = self.testapp.get('/api/organizations/{}/users'.format( org.uid), headers=login_headers(user.uid), status=403)
def test_add_self_to_organization(self): """Can't give org ownership if you're not an owner.""" org = Organization.create() joiner = User.create(email="*****@*****.**") org.put() joiner.put() response = self.testapp.put('/api/organizations/{}/users/{}'.format( org.uid, joiner.uid), headers=login_headers(joiner.uid), status=403)
def test_get_owned_org(self): """You can get an org you own.""" # Also creates 3 orgs, where user owns one and is assc with one. super_admin, program_admin, user = self.create_entities_to_get() owned_id = user.owned_organizations[0] response = self.testapp.get('/api/organizations/' + owned_id, headers=login_headers(user.uid)) org = json.loads(response.body) self.assertIs(type(org), dict) self.assertEquals(org['uid'], owned_id)
def test_leave_org(self): """Leaving an org deletes task reminders for all related task lists.""" org, project, survey, owner, joiner = self.test_join_org() self.testapp.delete( '/api/users/{}/organizations/{}'.format(joiner.uid, org.uid), headers=login_headers(owner.uid), ) self.assertEqual(len(TaskReminder.get(ancestor=joiner)), 0)
def test_super_tasklist_forbidden(self): """Forbidden if you're not a super.""" user = User.create(email='*****@*****.**', user_type='user') user.put() self.testapp.post_json( '/api/graphql', {}, headers=login_headers(user.uid), status=403, )