def test_task_post_api_exceptions(self, inv_field, dup): """Get a list of tasks using a list of project_ids.""" [admin, subadminowner] = UserFactory.create_batch(2) make_admin(admin) make_subadmin(subadminowner) project = ProjectFactory.create(owner=subadminowner) admin_headers = dict(Authorization=admin.api_key) task_info = dict(field_1='one', field_2='two') gold_answers = dict(field_3='some ans') data = dict(project_id=project.id, info=task_info, gold_answers=gold_answers, n_answers=2) dup.return_value = True res = self.app.post('/api/task', data=json.dumps(data), headers=admin_headers) res_data = json.loads(res.data) assert json.loads( res_data['exception_msg'])['reason'] == 'DUPLICATE_TASK', res dup.return_value = False inv_field = True res = self.app.post('/api/task', data=json.dumps(data), headers=admin_headers) res_data = json.loads(res.data) assert res_data[ 'exception_msg'] == 'Missing or incorrect required fields: ', res
def test_unpublish_project(self): project = ProjectFactory.create( info=dict(published=True, task_presenter='task presenter')) make_subadmin(project.owner) task = TaskFactory.create(project=project, n_answers=1) TaskRunFactory.create(task=task) orig_taskruns = cached_projects.n_task_runs(project.id) #unpublish project url = '/project/%s/1/publish?api_key=%s' % (project.short_name, project.owner.api_key) res = self.app.get(url) assert res.status_code == 200, res.status_code assert 'Are you sure you want to publish this project?' in res.data, \ 'Confirmation message to publish project should be provided' assert 'Force remove taskruns and results' in res.data, \ 'Option to remove taskruns and results should be provided' resp = self.app.post('/project/%s/0/publish' % project.short_name, follow_redirects=True) assert res.status_code == 200, 'Failed to unpublish project' project = project_repo.get(project.id) assert not project.published, 'Project should not be published' #publish an unpublished project resp = self.app.post('/project/%s/1/publish' % project.short_name, follow_redirects=True, data={'force_reset': 'off'}) assert res.status_code == 200, 'Failed to publish project' project = project_repo.get(project.id) assert project.published, 'Project should be published' taskruns_when_published = cached_projects.n_task_runs(project.id) assert taskruns_when_published == orig_taskruns, 'Publish project should not have deleted taskruns'
def test_locked_sched_gold_task(self): """ Test gold tasks presented with locked scheduler """ [admin, owner, user] = UserFactory.create_batch(3) make_admin(admin) make_subadmin(owner) project = ProjectFactory.create(owner=owner) project.info['sched'] = Schedulers.locked project.set_gold_task_probability(1.0) project_repo.save(project) tasks = TaskFactory.create_batch(4, project=project, n_answers=1) gold_task = tasks[3] gold_task.calibration = 1; gold_task.gold_answers = dict(field_3='someans') # user #1 self.set_proj_passwd_cookie(project, user) res = self.app.get('api/project/{}/newtask?api_key={}' .format(project.id, user.api_key)) assert res.status_code == 200, res.status_code resp = json.loads(res.data) assert resp['id'] == gold_task.id, \ 'task presented to regular user under locked sched should be gold task' # submit answer for gold task task_run = dict(project_id=project.id, task_id=gold_task.id, info='hi there!') res = self.app.post('api/taskrun?api_key={}'.format(user.api_key), data=json.dumps(task_run)) assert res.status_code == 200, res.status_code # user #2 also gets gold_task even when redundancy was set to 1 res = self.app.get('api/project/{}/newtask?api_key={}' .format(project.id, owner.api_key)) assert res.status_code == 200, res.status_code resp = json.loads(res.data) assert resp['id'] == gold_task.id, \ 'task presented to owner under locked sched should be gold task' # after two task run submissions for gold task, state is unchanged to ongoing task_run = dict(project_id=project.id, task_id=gold_task.id, info='hi there!') res = self.app.post('api/taskrun?api_key={}'.format(owner.api_key), data=json.dumps(task_run)) assert res.status_code == 200, res.status_code res = self.app.get('api/task/{}?api_key={}' .format(gold_task.id, admin.api_key)) assert res.status_code == 200, res.status_code resp = json.loads(res.data) assert resp['id'] == gold_task.id and resp['state'] == 'ongoing', \ 'gold task state should be unchanged to ongoing' project.set_gold_task_probability(0.0) res = self.app.get('api/project/{}/newtask?api_key={}' .format(project.id, admin.api_key)) assert res.status_code == 200, res.status_code resp = json.loads(res.data) assert resp['id'] == tasks[0].id, \ 'task presented should not be gold task'
def test_published_disable_task_presenter_get(self): with patch.dict(self.flask_app.config, {'DISABLE_TASK_PRESENTER': True}): project = ProjectFactory.create(info=dict()) make_subadmin(project.owner) url = '/project/%s/publish?api_key=%s' % (project.short_name, project.owner.api_key) # With a task should return 200 as task presenter is disabled. TaskFactory.create(project=project) res = self.app.get(url) assert res.status_code == 200, res.status_code
def test_newtask_unpublish_project(self): """Test user obtains newtask with published projects only; 404 with unpublished projects""" project = ProjectFactory.create( info=dict(published=True, task_presenter='task presenter')) admin, subadmin_coowner, regular_coowner, user = UserFactory.create_batch( 4) make_admin(admin) make_subadmin(subadmin_coowner) tasks = TaskFactory.create_batch(10, project=project, n_answers=1) self.set_proj_passwd_cookie(project, user) res = self.app.get('api/project/{}/newtask?api_key={}'.format( project.id, user.api_key)) task = json.loads(res.data) assert res.status_code == 200 and task['id'] == tasks[ 0].id, 'User should have obtained new task' # submit answer for the task task_run = dict(project_id=project.id, task_id=task['id'], info='hi there!') res = self.app.post('api/taskrun?api_key={}'.format(user.api_key), data=json.dumps(task_run)) assert res.status_code == 200, res.status_code #unpublish project project.published = False project.owners_ids.append(regular_coowner.id) project.owners_ids.append(subadmin_coowner.id) project_repo.save(project) project = project_repo.get(project.id) assert not project.published, 'Project should not be published' # for unpublished project, obtaining newtask would succeed for admin res = self.app.get('api/project/{}/newtask?api_key={}'.format( project.id, admin.api_key)) assert res.status_code == 200, 'newtask to unpublished project should succeed for admin' # for unpublished project, obtaining newtask would succeed for subadmin coowner res = self.app.get('api/project/{}/newtask?api_key={}'.format( project.id, subadmin_coowner.api_key)) assert res.status_code == 200, 'newtask to unpublished project should succeed for admin' # for unpublished project, obtaining newtask would succeed for regular coowner res = self.app.get('api/project/{}/newtask?api_key={}'.format( project.id, regular_coowner.api_key)) assert res.status_code == 200, 'newtask to unpublished project should succeed for admin' # for unpublished project, obtaining newtask would fail for regular user res = self.app.get('api/project/{}/newtask?api_key={}'.format( project.id, user.api_key)) assert res.status_code == 404, 'newtask to unpublished project should return 404'
def test_transfer_auth_owner_get(self): """Test transfer ownership page is ok for owner.""" admin, owner, user = UserFactory.create_batch(3) make_subadmin(owner) project = ProjectFactory.create(owner=owner) url = '/project/%s/transferownership?api_key=%s' % (project.short_name, owner.api_key) res = self.app_get_json(url, follow_redirects=True) data = json.loads(res.data) assert data['form'], data assert data['form']['errors'] == {}, data assert data['form']['email_addr'] is None, data assert data['form']['csrf'] is not None, data
def test_publish_project(self): project = ProjectFactory.create( published=False, info=dict(task_presenter='task presenter')) make_subadmin(project.owner) task = TaskFactory.create(project=project, n_answers=1) TaskRunFactory.create(task=task) #unpublish project url = '/project/%s/publish?api_key=%s' % (project.short_name, project.owner.api_key) res = self.app.get(url) assert res.status_code == 200, res.status_code assert 'You are about to publish your project in' in res.data, \ 'You are about to publish your project in message should be provided'
def test_transfer_auth_owner_post_wrong_email(self): """Test transfer ownership page post is ok for wrong email.""" admin, owner, user = UserFactory.create_batch(3) make_subadmin(owner) project = ProjectFactory.create(owner=owner) url = '/project/%s/transferownership?api_key=%s' % (project.short_name, owner.api_key) assert project.owner_id == owner.id payload = dict(email_addr="*****@*****.**") res = self.app_post_json(url, data=payload, follow_redirects=True) data = json.loads(res.data) assert data['next'] is not None, data assert "project owner not found" in data['flash'], data err_msg = "The project owner id should be the same" assert project.owner_id == owner.id, err_msg
def test_task_delete(self): """Test API task delete""" admin = UserFactory.create() user = UserFactory.create() make_subadmin(user) non_owner = UserFactory.create() project = ProjectFactory.create(owner=user) task = TaskFactory.create(project=project) root_task = TaskFactory.create(project=project) ## anonymous res = self.app.delete('/api/task/%s' % task.id) error_msg = 'Anonymous should not be allowed to delete' print res.status assert_equal(res.status, '401 UNAUTHORIZED', error_msg) ### real user but not allowed as not owner! url = '/api/task/%s?api_key=%s' % (task.id, non_owner.api_key) res = self.app.delete(url) error_msg = 'Should not be able to update tasks of others' assert_equal(res.status, '403 FORBIDDEN', error_msg) #### real user # DELETE with not allowed args res = self.app.delete(url + "&foo=bar") err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'task', err assert err['action'] == 'DELETE', err assert err['exception_cls'] == 'AttributeError', err # DELETE returns 204 url = '/api/task/%s?api_key=%s' % (task.id, user.api_key) res = self.app.delete(url) assert_equal(res.status, '204 NO CONTENT', res.data) assert res.data == '', res.data #### root user url = '/api/task/%s?api_key=%s' % (root_task.id, admin.api_key) res = self.app.delete(url) assert_equal(res.status, '204 NO CONTENT', res.data) tasks = task_repo.filter_tasks_by(project_id=project.id) assert task not in tasks, tasks assert root_task not in tasks, tasks
def test_published_get(self): project = ProjectFactory.create(info=dict()) make_subadmin(project.owner) url = '/project/%s/publish?api_key=%s' % (project.short_name, project.owner.api_key) # Without tasks should return 403 res = self.app.get(url) assert res.status_code == 403, res.status_code # With a task should return 403 and no task presenter TaskFactory.create(project=project) res = self.app.get(url) assert res.status_code == 403, res.status_code project.info['task_presenter'] = 'task presenter' project_repo.update(project) res = self.app.get(url) assert res.status_code == 200, res.status_code
def test_task_update(self): """Test API task update""" admin = UserFactory.create() user = UserFactory.create() make_subadmin(user) non_owner = UserFactory.create() project = ProjectFactory.create(owner=user) task = TaskFactory.create(project=project) root_task = TaskFactory.create(project=project) data = {'n_answers': 1} datajson = json.dumps(data) root_data = {'n_answers': 4} root_datajson = json.dumps(root_data) ## anonymous res = self.app.put('/api/task/%s' % task.id, data=data) assert_equal(res.status, '401 UNAUTHORIZED', res.status) ### real user but not allowed as not owner! url = '/api/task/%s?api_key=%s' % (task.id, non_owner.api_key) res = self.app.put(url, data=datajson) assert_equal(res.status, '403 FORBIDDEN', res.status) ### real user url = '/api/task/%s?api_key=%s' % (task.id, user.api_key) res = self.app.put(url, data=datajson) out = json.loads(res.data) assert_equal(res.status, '200 OK', res.data) assert_equal(task.n_answers, data['n_answers']) assert_equal(task.state, 'ongoing') assert task.id == out['id'], out ### root res = self.app.put('/api/task/%s?api_key=%s' % (root_task.id, admin.api_key), data=root_datajson) assert_equal(res.status, '200 OK', res.data) assert_equal(root_task.n_answers, root_data['n_answers']) assert_equal(task.state, 'ongoing') # PUT with not JSON data res = self.app.put(url, data=data) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'task', err assert err['action'] == 'PUT', err assert err['exception_cls'] == 'ValueError', err # PUT with not allowed args res = self.app.put(url + "&foo=bar", data=json.dumps(data)) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'task', err assert err['action'] == 'PUT', err assert err['exception_cls'] == 'AttributeError', err # PUT with fake data data['wrongfield'] = 13 res = self.app.put(url, data=json.dumps(data)) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'task', err assert err['action'] == 'PUT', err assert err['exception_cls'] == 'TypeError', err
def test_task_post(self): """Test API Task creation""" admin = UserFactory.create() user = UserFactory.create() make_subadmin(user) non_owner = UserFactory.create() project = ProjectFactory.create(owner=user) data = dict(project_id=project.id, info='my task data') root_data = dict(project_id=project.id, info='my root task data') # anonymous user # no api-key res = self.app.post('/api/task', data=json.dumps(data)) error_msg = 'Should not be allowed to create' assert_equal(res.status, '401 UNAUTHORIZED', error_msg) ### real user but not allowed as not owner! res = self.app.post('/api/task?api_key=' + non_owner.api_key, data=json.dumps(data)) error_msg = 'Should not be able to post tasks for projects of others' assert_equal(res.status, '403 FORBIDDEN', error_msg) # now a real user res = self.app.post('/api/task?api_key=' + user.api_key, data=json.dumps(data)) assert res.data, res datajson = json.loads(res.data) out = task_repo.get_task(datajson['id']) assert out, out assert_equal(out.info, 'my task data'), out assert_equal(out.project_id, project.id) # now the root user res = self.app.post('/api/task?api_key=' + admin.api_key, data=json.dumps(root_data)) assert res.data, res datajson = json.loads(res.data) out = task_repo.get_task(datajson['id']) assert out, out assert_equal(out.info, 'my root task data'), out assert_equal(out.project_id, project.id) # POST with not JSON data url = '/api/task?api_key=%s' % user.api_key res = self.app.post(url, data=data) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'task', err assert err['action'] == 'POST', err assert err['exception_cls'] == 'ValueError', err # POST with not allowed args res = self.app.post(url + '&foo=bar', data=json.dumps(data)) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'task', err assert err['action'] == 'POST', err assert err['exception_cls'] == 'AttributeError', err # POST with fake data data['wrongfield'] = 13 data['info'] = 'Kicking around on a piece of ground in your home town' res = self.app.post(url, data=json.dumps(data)) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'task', err assert err['action'] == 'POST', err assert err['exception_cls'] == 'TypeError', err
def test_create_update_gold_answers(self): [admin, subadminowner, subadmin, reguser] = UserFactory.create_batch(4) make_admin(admin) make_subadmin(subadminowner) make_subadmin(subadmin) project = ProjectFactory.create(owner=subadminowner) admin_headers = dict(Authorization=admin.api_key) task_info = dict(field_1='one', field_2='two') gold_answers = dict(field_3='some ans', field_4='some ans 2') # POST gold_answers successful data = dict(project_id=project.id, info=task_info, gold_answers=gold_answers, n_answers=2) res = self.app.post('/api/task', data=json.dumps(data), headers=admin_headers) assert res.data, res jdata = json.loads(res.data) assert_equal(jdata['info'], task_info), jdata assert_equal(jdata['gold_answers'], gold_answers), jdata assert jdata[ 'calibration'] == 1, 'calibration should have been set with updating gold_answers' assert jdata[ 'exported'] == True, 'exported should be True with new gold task' # GET task by subadmin not owner user does not get gold answers, # whereas admin/subadmin gets gold answers subadminowner_headers = dict(Authorization=subadminowner.api_key) subadmin_headers = dict(Authorization=subadmin.api_key) reguser_headers = dict(Authorization=reguser.api_key) res = self.app.get('/api/task/1', headers=admin_headers) jdata = json.loads(res.data) assert_equal(jdata['gold_answers'], gold_answers), jdata res = self.app.get('/api/task/1', headers=subadminowner_headers) jdata = json.loads(res.data) assert_equal(jdata['gold_answers'], gold_answers), jdata res = self.app.get('/api/task/1', headers=subadmin_headers) jdata = json.loads(res.data) assert 'gold_answers' not in jdata, jdata assert 'calibration' not in jdata, jdata # regular users should not receive gold_answers and calibration info res = self.app.get('/api/task/1', headers=reguser_headers) jdata = json.loads(res.data) assert 'gold_answers' not in jdata, jdata assert 'calibration' not in jdata, jdata # PUT request updates gold answers updated_gold_answers = dict(field_3='some ans - updated', field_5='one more ans') data = dict(project_id=project.id, gold_answers=updated_gold_answers) res = self.app.put('/api/task/1', data=json.dumps(data), headers=subadminowner_headers) jdata = json.loads(res.data) gold_answers.update(updated_gold_answers) assert_equal(jdata['gold_answers'], updated_gold_answers), jdata # Beyond redundancy, submitting task runs to task with gold_answers # is permitted. such task run submissions should not mark task as complete task = task_repo.get_task(jdata['id']) n_taskruns = 8 taskruns = TaskRunFactory.create_batch(n_taskruns, project=project, task=task) assert task.state == 'ongoing', 'Gold task state should be ongoing beyond task submissions > task redundancy' assert len( taskruns ) == n_taskruns, 'For gold task, number of task runs submissions can be beyond task redundancy' assert task.exported == False, 'Gold tasks to be marked exported as False upon task run submission' task.exported = True taskruns = TaskRunFactory.create_batch(1, project=project, task=task) assert task.exported == False, 'Gold tasks to be marked exported as False upon task run submission' # reset gold answer data = dict(project_id=project.id, gold_answers={}) res = self.app.put('/api/task/1', data=json.dumps(data), headers=subadminowner_headers) jdata = json.loads(res.data) assert_equal(jdata['gold_answers'], {}), jdata assert jdata[ 'calibration'] == 0, 'Calibration should be reset upon gold_answers reset'
def test_blogpost_put_file(self): """Test API Blogpost file upload update.""" admin, owner, user = UserFactory.create_batch(3) make_subadmin(owner) project = ProjectFactory.create(owner=owner) project2 = ProjectFactory.create(owner=user) blogpost = BlogpostFactory.create(project=project) img = (io.BytesIO(b'test'), 'test_file.jpg') payload = dict(project_id=project.id, file=img) # As anon url = '/api/blogpost/%s' % blogpost.id res = self.app.put(url, data=payload, content_type="multipart/form-data") data = json.loads(res.data) assert res.status_code == 401, data assert data['status_code'] == 401, data # As a user img = (io.BytesIO(b'test'), 'test_file.jpg') payload = dict(project_id=project.id, file=img) url = '/api/blogpost/%s?api_key=%s' % (blogpost.id, user.api_key) res = self.app.put(url, data=payload, content_type="multipart/form-data") data = json.loads(res.data) assert res.status_code == 403, data assert data['status_code'] == 403, data # As owner img = (io.BytesIO(b'test'), 'test_file.jpg') payload = dict(project_id=project.id, file=img) url = '/api/blogpost/%s?api_key=%s' % (blogpost.id, project.owner.api_key) res = self.app.put(url, data=payload, content_type="multipart/form-data") data = json.loads(res.data) assert res.status_code == 200, data container = "user_%s" % owner.id assert data['info']['container'] == container, data assert data['info']['file_name'] == 'test_file.jpg', data assert 'test_file.jpg' in data['media_url'], data # As admin img = (io.BytesIO(b'test'), 'test_file.jpg') payload = dict(project_id=project.id, file=img) url = '/api/blogpost/%s?api_key=%s' % (blogpost.id, admin.api_key) res = self.app.put(url, data=payload, content_type="multipart/form-data") data = json.loads(res.data) assert res.status_code == 200, data container = "user_%s" % owner.id assert data['info']['container'] == container, data assert data['info']['file_name'] == 'test_file.jpg', data assert 'test_file.jpg' in data['media_url'], data # As owner wrong 404 project_id img = (io.BytesIO(b'test'), 'test_file.jpg') payload = dict(project_id=project.id, file=img) url = '/api/blogpost?api_key=%s' % owner.api_key payload['project_id'] = -1 res = self.app.post(url, data=payload, content_type="multipart/form-data") data = json.loads(res.data) assert res.status_code == 415, data # As owner using wrong project_id img = (io.BytesIO(b'test'), 'test_file.jpg') payload = dict(project_id=project.id, file=img) url = '/api/blogpost?api_key=%s' % owner.api_key payload['project_id'] = project2.id res = self.app.post(url, data=payload, content_type="multipart/form-data") data = json.loads(res.data) assert res.status_code == 403, data # As owner using wrong attribute img = (io.BytesIO(b'test'), 'test_file.jpg') payload = dict(project_id=project.id, wrong=img) url = '/api/blogpost?api_key=%s' % owner.api_key res = self.app.post(url, data=payload, content_type="multipart/form-data") data = json.loads(res.data) assert res.status_code == 415, data # As owner using reserved key img = (io.BytesIO(b'test'), 'test_file.jpg') payload = dict(project_id=project.id, file=img) url = '/api/blogpost?api_key=%s' % owner.api_key payload['project_id'] = project.id payload['id'] = 3 res = self.app.post(url, data=payload, content_type="multipart/form-data") data = json.loads(res.data) assert res.status_code == 400, data assert 'Reserved keys in payload' in data['exception_msg'], data
def test_result_update(self): """Test API result update""" admin = UserFactory.create() user = UserFactory.create() make_subadmin(user) non_owner = UserFactory.create() data = dict(info=dict(foo='bar')) datajson = json.dumps(data) result = self.create_result(owner=user) ## anonymous res = self.app.put('/api/result/%s' % result.id, data=datajson) assert_equal(res.status, '401 UNAUTHORIZED', res.status) ### real user but not allowed as not owner! url = '/api/result/%s?api_key=%s' % (result.id, non_owner.api_key) res = self.app.put(url, data=datajson) assert_equal(res.status, '403 FORBIDDEN', res.status) ### real user url = '/api/result/%s?api_key=%s' % (result.id, user.api_key) res = self.app.put(url, data=datajson) out = json.loads(res.data) assert_equal(res.status, '200 OK', res.data) assert_equal(result.info['foo'], data['info']['foo']) assert result.id == out['id'], out ### root data = dict(info=dict(foo='root')) datajson = json.dumps(data) res = self.app.put('/api/result/%s?api_key=%s' % (result.id, admin.api_key), data=datajson) assert_equal(res.status, '200 OK', res.status) assert_equal(result.info['foo'], data['info']['foo']) assert result.id == out['id'], out # PUT with not JSON data res = self.app.put(url, data=None) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'result', err assert err['action'] == 'PUT', err assert err['exception_cls'] == 'ValueError', err # PUT with not allowed args res = self.app.put(url + "&foo=bar", data=json.dumps(data)) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'result', err assert err['action'] == 'PUT', err assert err['exception_cls'] == 'AttributeError', err # PUT with fake data data['wrongfield'] = 13 res = self.app.put(url, data=json.dumps(data)) err = json.loads(res.data) assert res.status_code == 415, err assert err['status'] == 'failed', err assert err['target'] == 'result', err assert err['action'] == 'PUT', err assert err['exception_cls'] == 'TypeError', err