test_user_api.py 11.4 KB
# -*- coding: utf8 -*-
# This file is part of PyBossa.
#
# Copyright (C) 2015 SciFabric LTD.
#
# PyBossa is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PyBossa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with PyBossa.  If not, see <http://www.gnu.org/licenses/>.
import json
from default import with_context
from nose.tools import assert_raises
from werkzeug.exceptions import MethodNotAllowed
from pybossa.api.user import UserAPI
from test_api import TestAPI

from factories import UserFactory



class TestUserAPI(TestAPI):

    @with_context
    def test_user_get(self):
        """Test API User GET"""
        expected_user = UserFactory.create()
        # Test GET all users
        res = self.app.get('/api/user')
        data = json.loads(res.data)
        user = data[0]
        assert len(data) == 1, data
        assert user['name'] == expected_user.name, data

        # The output should have a mime-type: application/json
        assert res.mimetype == 'application/json', res

        # Test GETting a specific user by ID
        res = self.app.get('/api/user/1')
        data = json.loads(res.data)
        user = data
        assert user['name'] == expected_user.name, data

        # Test a non-existant ID
        res = self.app.get('/api/user/3434209')
        err = json.loads(res.data)
        assert res.status_code == 404, err
        assert err['status'] == 'failed', err
        assert err['target'] == 'user', err
        assert err['exception_cls'] == 'NotFound', err
        assert err['action'] == 'GET', err


    @with_context
    def test_query_user(self):
        """Test API query for user endpoint works"""
        expected_user = UserFactory.create_batch(2)[0]
        # When querying with a valid existing field which is unique
        # It should return one correct result if exists
        res = self.app.get('/api/user?name=%s' % expected_user.name)
        data = json.loads(res.data)
        assert len(data) == 1, data
        assert data[0]['name'] == expected_user.name, data
        # And it should return no results if there are no matches
        res = self.app.get('/api/user?name=Godzilla')
        data = json.loads(res.data)
        assert len(data) == 0, data

        # When querying with a valid existing non-unique field
        res = self.app.get("/api/user?locale=en")
        data = json.loads(res.data)
        # It should return 3 results, as every registered user has locale=en by default
        assert len(data) == 2, data
        # And they should be the correct ones
        assert (data[0]['locale'] == data[1]['locale'] == 'en'
               and data[0] != data[1]), data

        # When querying with multiple valid fields
        res = self.app.get('/api/user?name=%s&locale=en' % expected_user.name)
        data = json.loads(res.data)
        # It should find and return one correct result
        assert len(data) == 1, data
        assert data[0]['name'] == expected_user.name, data
        assert data[0]['locale'] == 'en', data

        # When querying with non-valid fields -- Errors
        res = self.app.get('/api/user?something_invalid=whatever')
        err = json.loads(res.data)
        err_msg = "AttributeError exception should be raised"
        assert res.status_code == 415, err_msg
        assert err['action'] == 'GET', err_msg
        assert err['status'] == 'failed', err_msg
        assert err['exception_cls'] == 'AttributeError', err_msg


    @with_context
    def test_user_not_allowed_actions(self):
        """Test POST, PUT and DELETE actions are not allowed for user
        in the API"""

        user_api_instance = UserAPI()
        post_response = self.app.post('/api/user')
        assert post_response.status_code == 405, post_response.status_code
        assert_raises(MethodNotAllowed, user_api_instance.post)
        delete_response = self.app.delete('/api/user')
        assert delete_response.status_code == 405, delete_response.status_code
        assert_raises(MethodNotAllowed, user_api_instance.delete)
        put_response = self.app.put('/api/user')
        assert put_response.status_code == 405, put_response.status_code
        assert_raises(MethodNotAllowed, user_api_instance.put)


    @with_context
    def test_privacy_mode_user_get(self):
        """Test API user queries for privacy mode"""
        admin = UserFactory.create()
        user = UserFactory.create()
        # Add user with fullname 'Public user', privacy mode disabled
        user_with_privacy_disabled = UserFactory.create(email_addr='public@user.com',
                                    name='publicUser', fullname='Public user',
                                    privacy_mode=False)
        # Add user with fullname 'Private user', privacy mode enabled
        user_with_privacy_enabled = UserFactory.create(email_addr='private@user.com',
                                    name='privateUser', fullname='Private user',
                                    privacy_mode=True)

        # With no API-KEY
        # User with privacy disabled
        res = self.app.get('/api/user/3')
        data = json.loads(res.data)
        user_with_privacy_disabled = data
        # When checking a public field it should be returned
        assert user_with_privacy_disabled['locale'] == 'en', data
        # When checking a private field it should be returned too
        assert user_with_privacy_disabled['fullname'] == 'Public user', data
        # User with privacy enabled
        res = self.app.get('/api/user/4')
        data = json.loads(res.data)
        user_with_privacy_enabled = data
        # When checking a public field it should be returned
        assert user_with_privacy_enabled['locale'] == 'en', data
        # When checking a private field it should not be returned
        assert 'fullname' not in user_with_privacy_enabled, data
        # Users with privacy enabled and disabled, mixed together
        res = self.app.get('/api/user')
        data = json.loads(res.data)
        user_with_privacy_disabled = data[2]
        user_with_privacy_enabled = data[3]
        assert user_with_privacy_disabled['locale'] == 'en', data
        assert user_with_privacy_disabled['fullname'] == 'Public user', data
        assert user_with_privacy_enabled['locale'] == 'en', data
        assert 'fullname' not in user_with_privacy_enabled, data

        # With a non-admin API-KEY
        # User with privacy disabled
        res = self.app.get('/api/user/3?api_key=' + user.api_key)
        data = json.loads(res.data)
        user_with_privacy_disabled = data
        # When checking a public field it should be returned
        assert user_with_privacy_disabled['locale'] == 'en', data
        # When checking a private field it should be returned too
        assert user_with_privacy_disabled['fullname'] == 'Public user', data
        # User with privacy enabled
        res = self.app.get('/api/user/4?api_key=' + user.api_key)
        data = json.loads(res.data)
        user_with_privacy_enabled = data
        # When checking a public field it should be returned
        assert user_with_privacy_enabled['locale'] == 'en', data
        # When checking a private field it should not be returned
        assert 'fullname' not in user_with_privacy_enabled, data
        # Users with privacy enabled and disabled, mixed together
        res = self.app.get('/api/user?api_key=' + user.api_key)
        data = json.loads(res.data)
        user_with_privacy_disabled = data[2]
        user_with_privacy_enabled = data[3]
        assert user_with_privacy_disabled['locale'] == 'en', data
        assert user_with_privacy_disabled['fullname'] == 'Public user', data
        assert user_with_privacy_enabled['locale'] == 'en', data
        assert 'fullname' not in user_with_privacy_enabled, data

        # Admin API-KEY should be able to retrieve every field in user
        res = self.app.get('/api/user/3?api_key=' + admin.api_key)
        data = json.loads(res.data)
        user_with_privacy_disabled = data
        # When checking a public field it should be returned
        assert user_with_privacy_disabled['locale'] == 'en', data
        # When checking a private field it should be returned too
        assert user_with_privacy_disabled['fullname'] == 'Public user', data
        # User with privacy enabled
        res = self.app.get('/api/user/4?api_key=' + admin.api_key)
        data = json.loads(res.data)
        user_with_privacy_enabled = data
        # When checking a public field it should be returned
        assert user_with_privacy_enabled['locale'] == 'en', data
        # When checking a private field it should be returned too
        assert user_with_privacy_enabled['fullname'] == 'Private user', data
        # Users with privacy enabled and disabled, mixed together
        res = self.app.get('/api/user?api_key=' + admin.api_key)
        data = json.loads(res.data)
        user_with_privacy_disabled = data[2]
        user_with_privacy_enabled = data[3]
        assert user_with_privacy_disabled['locale'] == 'en', data
        assert user_with_privacy_disabled['fullname'] == 'Public user', data
        assert user_with_privacy_enabled['locale'] == 'en', data
        assert user_with_privacy_enabled['fullname'] == 'Private user', data


    @with_context
    def test_privacy_mode_user_queries(self):
        """Test API user queries for privacy mode with private fields in query
        """
        admin = UserFactory.create()
        user = UserFactory.create()
        # Add user with fullname 'Public user', privacy mode disabled
        user_with_privacy_disabled = UserFactory(email_addr='public@user.com',
                                    name='publicUser', fullname='User',
                                    privacy_mode=False)
        # Add user with fullname 'Private user', privacy mode enabled
        user_with_privacy_enabled = UserFactory(email_addr='private@user.com',
                                    name='privateUser', fullname='User',
                                    privacy_mode=True)

        # When querying with private fields
        query = 'api/user?fullname=User'
        # with no API-KEY, no user with privacy enabled should be returned,
        # even if it matches the query
        res = self.app.get(query)
        data = json.loads(res.data)
        assert len(data) == 1, data
        public_user = data[0]
        assert public_user['name'] == 'publicUser', public_user

        # with a non-admin API-KEY, the result should be the same
        res = self.app.get(query + '&api_key=' + user.api_key)
        data = json.loads(res.data)
        assert len(data) == 1, data
        public_user = data[0]
        assert public_user['name'] == 'publicUser', public_user

        # with an admin API-KEY, all the matching results should be returned
        res = self.app.get(query + '&api_key=' + admin.api_key)
        data = json.loads(res.data)
        assert len(data) == 2, data
        public_user = data[0]
        assert public_user['name'] == 'publicUser', public_user
        private_user = data[1]
        assert private_user['name'] == 'privateUser', private_user