"""
Contains unit tests for :mod:`api_server`
"""
import json
import unittest
import mock
from sqlalchemy import create_engine
import api_server
from database.schema import metadata
from database.models.users import User, Administrator
from database.sessions import ContextManagedSession
from freezegun import freeze_time
from datetime import datetime, timedelta
__author__ = 'Michal Kononenko'
test_engine = create_engine('sqlite:///')
[docs]class TestHelloWorld(unittest.TestCase):
"""
Tests :meth:`api_server.hello_world`
"""
def setUp(self):
api_server.database_session = ContextManagedSession(bind=test_engine)
self.app = api_server.app
self.client = self.app.test_client()
self.request_method = self.client.get
self.url = '/'
self.headers = {'content-type': 'application/json'}
[docs] def test_hello_world(self):
"""
Tests that a request to the server's root endpoint returns 200,
indicating that the server has set up and is running successfully.
"""
r = self.request_method(self.url, headers=self.headers)
self.assertEqual(r.status_code, 200)
[docs]class TestAPIServer(unittest.TestCase):
"""
Base class for unit tests in :mod:`api_server`
"""
@classmethod
[docs] def setUpClass(cls):
"""
Sets up basic parameters for testing in :mod:`api_server`.
"""
api_server.database_session = ContextManagedSession(bind=test_engine)
cls.app = api_server.app
cls.username = 'scott'
cls.password = 'tiger'
cls.email = 'scott@tiger.com'
cls.user = User(cls.username, cls.password, cls.email)
api_server.g = mock.MagicMock()
api_server.g.user = cls.user
cls.client = cls.app.test_client()
cls.headers = {'content-type': 'application/json'}
metadata.create_all(bind=test_engine)
@classmethod
[docs] def tearDownClass(cls):
"""
Tear down the tests
"""
metadata.drop_all(bind=test_engine)
@mock.patch('api_server.auth.verify_password_callback', return_value=True)
@freeze_time('2016-01-01')
[docs]class TestGetAuthToken(TestAPIServer):
"""
Tests :meth:`api_server.create_token`
"""
[docs] def setUp(self):
"""
Set up the tests by creating a mock token, and
assigning it as a return value to ``self.user``, which
is assigned to ``g.user``.
"""
self.url = 'api/v1/token'
self.token = 'mock_token'
self.token_expiry_date = datetime.utcnow()
self.request_method = self.client.post
self.user.generate_auth_token = mock.MagicMock(
return_value=(self.token, self.token_expiry_date)
)
def test_create_auth_token(self, mock_verify):
response = self.request_method(self.url, headers=self.headers)
self.assertEqual(response.status_code, 201)
json_dict = json.loads(response.data.decode('utf-8'))
self.assertEqual(self.token, json_dict['token'])
self.assertEqual(
self.token_expiry_date.isoformat(),
json_dict['expiration_date']
)
self.assertTrue(mock_verify.called)
def test_create_auth_token_with_expiration(self, mock_verify):
seconds_to_exp = 1200
url = '%s?expiration=%d' % (self.url, seconds_to_exp)
response = self.request_method(url, headers=self.headers)
self.assertEqual(response.status_code, 201)
json_dict = json.loads(response.data.decode('utf-8'))
self.assertEqual(self.token, json_dict['token'])
self.assertEqual(
mock.call(expiration=seconds_to_exp),
self.user.generate_auth_token.call_args
)
self.assertTrue(mock_verify.called)
@mock.patch('api_server.auth.verify_password_callback', return_value=True)
[docs]class TestRevokeToken(TestAPIServer):
"""
Tests :meth:`api_server.revoke_token`
"""
token = mock.MagicMock()
def setUp(self):
self.url = 'api/v1/token'
self.request_method = self.client.delete
self.admin = Administrator(self.username, self.password, self.email)
@mock.patch('sqlalchemy.orm.Query.first')
@mock.patch('api_server.auth.login_required', new=lambda t: t)
@mock.patch('database.User.current_token')
def test_revoke_token(self, mock_cur_token, mock_query, mock_auth):
mock_query.return_value = self.user
response = self.request_method(self.url, headers=self.headers)
self.assertEqual(response.status_code, 200)
self.assertTrue(mock_cur_token.first().revoke.called)
self.assertTrue(mock_auth.called)
@mock.patch('sqlalchemy.orm.Query.first')
@mock.patch('api_server.auth.login_required', new=lambda t: t)
@mock.patch('api_server.g')
@mock.patch('database.User.current_token')
def test_administrator_revoke(self, mock_cur_token, mock_g, mock_query,
mock_auth):
mock_g.user = self.admin
mock_query.return_value = self.admin
response = self.request_method(self.url, headers=self.headers)
self.assertEqual(response.status_code, 200)
self.assertTrue(mock_cur_token.first().revoke.called)
self.assertTrue(mock_auth.called)
@mock.patch('sqlalchemy.orm.Query.first', return_value=None)
@mock.patch('api_server.auth.login_required', new=lambda t: t)
@mock.patch('api_server.g')
@mock.patch('database.User.current_token')
def test_administrator_revoke_user_not_found(self, mock_cur_token, mock_g,
mock_query, mock_auth):
url = '%s?username=%s' % (self.url, self.username)
mock_g.user = self.admin
response = self.request_method(url, headers=self.headers)
self.assertEqual(response.status_code, 404)
self.assertFalse(mock_cur_token.first().revoke.called)
self.assertTrue(mock_auth.called)