def test_rserve_skips_existing(self): program = Program.create( name="The Engagement Project", label="ep19", preview_url='foo.com', ) week = util.datelike_to_iso_string(datetime.date.today()) org = Organization.create(name="Organization", captain_id="User_cap", program_id=program.uid) org_to_skip = Organization.create(name="Organization", captain_id="User_cap", program_id=program.uid) Organization.put_multi([org, org_to_skip]) team = Team.create(name="Team", captain_id="User_cap", program_id=program.uid) team_to_skip = Team.create(name="Team", captain_id="User_cap", program_id=program.uid) Team.put_multi([team, team_to_skip]) cl = Classroom.create(name="Classroom", team_id=team.uid, code="foo", contact_id="User_contact") cl_to_skip = Classroom.create(name="Classroom", team_id=team.uid, code="foo", contact_id="User_contact") Classroom.put_multi([cl, cl_to_skip]) Report.put_multi([ Report.create(parent_id=org_to_skip.uid, filename="foo", issue_date=week), Report.create(parent_id=team_to_skip.uid, filename="foo", issue_date=week), Report.create(parent_id=cl_to_skip.uid, filename="foo", issue_date=week), ]) # Skips all the parents who have reports already this week. orgs, teams, classes = cron_rserve.get_report_parents( program, week, False) self.assertEqual(len(orgs), 1) self.assertEqual(len(teams), 1) self.assertEqual(len(classes), 1) # ...unless you force it, then they're all there. orgs, teams, classes = cron_rserve.get_report_parents( program, week, True) self.assertEqual(len(orgs), 2) self.assertEqual(len(teams), 2) self.assertEqual(len(classes), 2)
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_remove_fellow_from_organization_success(self): """Admins can remove each other.""" org = Organization.create(name='foo', program_id=self.program.uid) org.put() user = User.create(name='Admin', email='*****@*****.**', user_type='user', owned_organizations=['Organization_foo']) req = User.create(name='Valid Requestor', email='*****@*****.**', user_type='user', owned_organizations=['Organization_foo']) user.put() req.put() # Successful removal. response = self.testapp.put_json( '/api/users/{}'.format(user.uid), {'owned_organizations': []}, headers=self.login_headers(req), ) self.assertEqual(json.loads(response.body)['owned_organizations'], []) # Changed in the db. fetched_user = User.get_by_id(user.uid) self.assertEqual(fetched_user.owned_organizations, []) self.assertEqual(user.user_type, fetched_user.user_type)
def test_remove_from_organization_forbidden(self): """Normally you can't modify someone else's membership.""" org = Organization.create(name='foo', program_id=self.program.uid) org.put() user = User.create(name='Admin', email='*****@*****.**', user_type='user', owned_organizations=['Organization_foo']) req = User.create(name='Invalid Requestor', email='*****@*****.**', user_type='user') user.put() req.put() response = self.testapp.put_json( '/api/users/{}'.format(user.uid), {'owned_organizations': []}, headers=self.login_headers(req), status=403, ) # Not changed in the db. fetched_user = User.get_by_id(user.uid) self.assertEqual(user.user_type, fetched_user.user_type) self.assertEqual(user.owned_organizations, fetched_user.owned_organizations)
def test_dashboard_forbidden(self): org = Organization.create( name='Org Foo', program_id=self.program.uid, ) org.put() bad_admin = User.create( name='Bad Admin', email='*****@*****.**', ) bad_admin.put() # 404 self.testapp.get( '/api/organization_dashboards/{}'.format('Organization_dne'), headers=self.login_headers(bad_admin), status=404, ) # 403 self.testapp.get( '/api/organization_dashboards/{}'.format(org.uid), headers=self.login_headers(bad_admin), status=403, )
def test_super_admin_changes_organization_task(self): """Org admins get notifications about activity on their org tasks.""" org = Organization.create(name='Foo Org') admin1 = User.create(email='*****@*****.**', user_type='user', owned_organizations=[org.uid]) admin2 = User.create(email='*****@*****.**', user_type='user', owned_organizations=[org.uid]) admin3 = User.create(email='*****@*****.**', user_type='user') admin1.put() admin2.put() admin3.put() task = org.tasklist.tasks[0] sup = User.create(email='*****@*****.**', user_type='super_admin', name="Susan Super") notifier.changed_organization_task(sup, org, task) # Each related org admin should get a notification. self.assertEqual(len(admin1.notifications()), 1) self.assertEqual(len(admin2.notifications()), 1) self.assertEqual(len(admin3.notifications()), 0)
def create_org_tasks(self, user, cohort_date=datetime.datetime.today()): org = Organization.create(name='Org Foo') org.put() org.tasklist.open(user) return (org, Task.get(ancestor=org, order='ordinal'))
def test_create_project(self): """All program owners notified about new projects.""" prog1 = User.create(email='*****@*****.**', user_type='program_admin', owned_programs=['demo-program']) prog2 = User.create(email='*****@*****.**', user_type='super_admin', owned_programs=['demo-program']) prog3 = User.create(email='*****@*****.**', user_type='program_admin', owned_programs=[]) prog1.put() prog2.put() prog3.put() org = Organization.create(name='Foo Org') org.put() admin = User.create(email='*****@*****.**', user_type='user', name='Addi Admin') project = Project.create(program_label='demo-program', organization_id=org.uid) notifier.created_project(admin, project) # Each super admin should get a notification. self.assertEqual(len(prog1.notifications()), 1) self.assertEqual(len(prog2.notifications()), 1) self.assertEqual(len(prog3.notifications()), 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 create_for_permission(self): org = Organization.create( name="Foo Org", program_id=self.ep_program.uid) org.put() team = Team.create(name="Foo Team", program_id=self.ep_program.uid, captain_id="User_cap", organization_ids=[org.uid]) team.put() network = Network.create( name="Foo Network", program_id=self.ep_program.uid, association_ids=[org.uid] ) meta_network = Network.create( name="Foo Network", program_id=self.ep_program.uid, association_ids=[network.uid] ) Network.put_multi([network, meta_network]) net_admin = User.create(email="*****@*****.**", owned_networks=[network.uid]) meta_admin = User.create(email="*****@*****.**", owned_networks=[meta_network.uid]) User.put_multi([net_admin, meta_admin]) return (meta_admin, net_admin, meta_network, network, org, team)
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_user_changes_project_task(self): """All program admins are notified.""" prog1 = User.create(email='*****@*****.**', user_type='program_admin', owned_programs=['demo-program']) prog2 = User.create(email='*****@*****.**', user_type='super_admin', owned_programs=['demo-program']) prog3 = User.create(email='*****@*****.**', user_type='program_admin', owned_programs=[]) prog1.put() prog2.put() prog3.put() org = Organization.create(name='Foo Org') org.put() admin = User.create(email='*****@*****.**', user_type='user', name='Addi Admin') project = Project.create(program_label='demo-program', organization_id=org.uid) task = project.tasklist.tasks[0] notifier.changed_project_task(admin, project, task) # Each related program admin should get a notification. self.assertEqual(len(prog1.notifications()), 1) self.assertEqual(len(prog2.notifications()), 1) self.assertEqual(len(prog3.notifications()), 0)
def test_user_changes_project_task_with_account_manager(self): """Account manager notified.""" acct_mgr = User.create(email='*****@*****.**', user_type='program_admin', owned_programs=['demo-program']) other_prog = User.create(email='*****@*****.**', user_type='program_admin', owned_programs=['demo-program']) acct_mgr.put() other_prog.put() org = Organization.create(name='Foo Org') org.put() admin = User.create(email='*****@*****.**', user_type='user', name='Addi Admin') project = Project.create(program_label='demo-program', organization_id=org.uid, account_manager_id=acct_mgr.uid) task = project.tasklist.tasks[0] notifier.changed_project_task(admin, project, task) # Each account manager gets a notification, other related program # admins don't. self.assertEqual(len(acct_mgr.notifications()), 1) self.assertEqual(len(other_prog.notifications()), 0)
def test_redundant(self): """Multiple undismissed updates about the same task are ignored.""" org = Organization.create(name='Foo Org') admin1 = User.create(email='*****@*****.**', user_type='user', owned_organizations=[org.uid]) admin1.put() task = org.tasklist.tasks[0] sup = User.create(email='*****@*****.**', user_type='super_admin', name="Susan Super") # Second notification should not be saved. notifier.changed_organization_task(sup, org, task) notifier.changed_organization_task(sup, org, task) notes = admin1.notifications() self.assertEqual(len(notes), 1) # Two will arrive if one is dismissed. notes[0].dismissed = True notes[0].put() notifier.changed_organization_task(sup, org, task) self.assertEqual(len(admin1.notifications()), 2)
def test_program_admin_changes_project_task_with_liaison(self): """Liaison notified.""" org = Organization.create(name='Foo Org') admin1 = User.create(email='*****@*****.**', user_type='user', owned_organizations=[org.uid]) admin2 = User.create(email='*****@*****.**', user_type='user', owned_organizations=[org.uid]) admin1.put() admin2.put() task = org.tasklist.tasks[0] prog = User.create(email='*****@*****.**', user_type='program_admin', name='Petrarch Prog') project = Project.create(program_label='demo-program', organization_id=org.uid, liaison_id=admin1.uid) notifier.changed_project_task(prog, project, task) # Each related org admin should get a notification. self.assertEqual(len(admin1.notifications()), 1) self.assertEqual(len(admin2.notifications()), 0)
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_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_add_org_not_found(self): captain = User.create(name='captain', email='*****@*****.**') team = Team.create(name='Team Foo', captain_id=captain.uid, program_id=self.demo_program.uid) org = Organization.create(name='Org Bar', program_id='Program_other') captain.owned_teams = [team.uid] team.put() org.put() captain.put() # Forbidden b/c org doesn't exist response = self.testapp.post_json( '/api/teams/{}/organizations'.format(team.uid), {'organization_code': 'dne'}, headers=self.login_headers(captain), status=400, ) # Forbidden b/c org isn't on the same program. response = self.testapp.post_json( '/api/teams/{}/organizations'.format(team.uid), {'organization_code': org.code}, headers=self.login_headers(captain), status=400, )
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_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_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_organization_name(self): """Updating a project should update organization name""" organization = Organization.create(name='Foo College') organization.put() project = Project.create(program_label='demo-program', organization_id=organization.uid) project.put() self.assertEqual(project.to_client_dict()['organization_name'], organization.name)
def test_post_empty_report(self): """RServe posts "empty" reports to note why it hasn't produced a visible report. Test that the API accepts these for each kind of parent. """ rserve_user = User.create( id='rserve', email='*****@*****.**', user_type='super_admin', ) rserve_user.put() org = Organization.create(name='Organization', captain_id='User_cap', program_id=self.program.uid) org.put() team = Team.create( name='Team Foo', captain_id='User_cap', organization_ids=[org.uid], program_id=self.program.uid, ) team.put() classroom = Classroom.create(name='Class foo', team_id=team.uid, code='trout viper', contact_id='User_contact') classroom.put() url = '/api/reports' report_date = datetime.date.today().strftime('%Y-%m-%d') self.testapp.post_json( url, dict(self.empty_report_params(report_date, org.uid), organization_id=org.uid), headers=self.login_headers(rserve_user), ) self.testapp.post_json( url, dict(self.empty_report_params(report_date, team.uid), team_id=team.uid), headers=self.login_headers(rserve_user), ) self.testapp.post_json( url, dict(self.empty_report_params(report_date, classroom.uid), team_id=team.uid, classroom_id=classroom.uid), headers=self.login_headers(rserve_user), ) reports = Report.get() self.assertEqual(len(reports), 3) self.assertEqual(all(r.template == 'empty' for r in reports), True)
def test_checkpoint_task_ids(self): """Checkpoints should track task ids created within them.""" org = Organization.create(name='Foo Org') for i, c in enumerate(org.tasklist.checkpoints): num_tasks = len(organization_tasks.tasklist_template[i]['tasks']) task_ids = json.loads(c.task_ids) self.assertEqual(len(task_ids), num_tasks) self.assertEqual(DatastoreModel.get_kind(task_ids[0]), 'Task')
def create_for_search(self): # Test that users can be matched on either name or email. admin_foo = User.create(name="Admin Foo", email="*****@*****.**") user_foo1 = User.create(name="User Foo", email="*****@*****.**") user_foo2 = User.create(name="Generic Name", email="*****@*****.**") user_bar = User.create(name="User Bar", email="*****@*****.**") org_foo = Organization.create(name="Org Foo", program_id=self.ep.uid) org_bar = Organization.create(name="Org Bar", program_id=self.ep.uid) team_foo = Team.create(name="Team Foo", program_id=self.ep.uid, captain_id=user_foo1.uid) team_bar = Team.create(name="Team Bar", program_id=self.ep.uid, captain_id=user_bar.uid) cl_foo = Classroom.create( name="Class Foo", code="foo", team_id=team_foo.uid, contact_id=user_foo2.uid, ) cl_bar = Classroom.create( name="Class Bar", code="bar", team_id=team_bar.uid, contact_id="User_contact", ) # Test that users can be matched on either name or email. admin_foo.owned_organizations = [org_foo.uid] user_foo1.owned_teams = [team_foo.uid] user_foo2.owned_teams = [team_foo.uid] user_bar.owned_teams = [team_bar.uid] Organization.put_multi([org_foo, org_bar]) Team.put_multi([team_foo, team_bar]) Classroom.put_multi([cl_foo, cl_bar]) User.put_multi([admin_foo, user_foo1, user_foo2, user_bar]) return (org_foo, org_bar, team_foo, team_bar, cl_foo, cl_bar, admin_foo, user_foo1, user_foo2, user_bar)
def test_get_all_for_other_organization(self): """You can't list users from someone else's organization.""" org = Organization.create(name='foo', program_id=self.program.uid) org.put() user = User.create(name='foo', email='*****@*****.**') user.put() response = self.testapp.get('/api/organizations/{}/users'.format( org.uid), headers=self.login_headers(user), status=403)
def test_create(self): o = Organization.create() self.assertNotEqual(len(o.tasklist.tasks), 0) self.assertNotEqual(len(o.tasklist.checkpoints), 0) for t in o.tasklist.tasks: self.assertIsInstance(t, Task) for c in o.tasklist.checkpoints: self.assertIsInstance(c, Checkpoint) self.assertEqual(DatastoreModel.get_kind(c.uid), 'Checkpoint')
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_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_program_access(self): """Should be able to look up program config through project.""" organization = Organization.create(name='Foo College') organization.put() project = Project.create(program_label='demo-program', organization_id=organization.uid) project.put() p_dict = project.to_client_dict() self.assertEqual(p_dict['program_name'], 'Demo Program') self.assertIsInstance(p_dict['program_description'], basestring)