refstack/refstack/tests/unit/test_app.py

236 lines
8.9 KiB
Python

# Copyright (c) 2015 Mirantis, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Tests for API's utility"""
import json
import mock
from oslo_config import fixture as config_fixture
from oslotest import base
import pecan
import webob
from refstack.api import app
from refstack.common import validators
def get_response_kwargs(response_mock):
_, kwargs = response_mock.call_args
if kwargs['body']:
kwargs['body'] = json.loads(kwargs.get('body', ''))
return kwargs
class JSONErrorHookTestCase(base.BaseTestCase):
def setUp(self):
super(JSONErrorHookTestCase, self).setUp()
self.config_fixture = config_fixture.Config()
self.CONF = self.useFixture(self.config_fixture).conf
def _on_error(self, response, exc, expected_status_code, expected_body):
response.return_value = 'fake_value'
hook = app.JSONErrorHook()
result = hook.on_error(mock.Mock(), exc)
self.assertEqual(result, 'fake_value')
self.assertEqual(
dict(body=expected_body,
status=expected_status_code,
content_type='application/json'),
get_response_kwargs(response)
)
@mock.patch.object(webob, 'Response')
def test_on_error_with_webob_instance(self, response):
self.CONF.set_override('app_dev_mode', False, 'api')
exc = mock.Mock(spec=webob.exc.HTTPError,
status=418, status_int=418,
title='fake_title')
self._on_error(
response, exc, expected_status_code=exc.status,
expected_body={'code': exc.status_int, 'title': exc.title}
)
self.CONF.set_override('app_dev_mode', True, 'api')
self._on_error(
response, exc, expected_status_code=exc.status,
expected_body={'code': exc.status_int, 'title': exc.title,
'detail': str(exc)}
)
@mock.patch.object(webob, 'Response')
def test_on_error_with_validation_error(self, response):
self.CONF.set_override('app_dev_mode', False, 'api')
exc = mock.Mock(spec=validators.ValidationError,
title='No No No!')
self._on_error(
response, exc, expected_status_code=400,
expected_body={'code': 400, 'title': exc.title}
)
self.CONF.set_override('app_dev_mode', True, 'api')
self._on_error(
response, exc, expected_status_code=400,
expected_body={'code': 400, 'title': exc.title,
'detail': str(exc)}
)
@mock.patch.object(webob, 'Response')
def test_on_http_redirection(self, response):
self.CONF.set_override('app_dev_mode', False, 'api')
exc = mock.Mock(spec=webob.exc.HTTPRedirection)
hook = app.JSONErrorHook()
result = hook.on_error(mock.Mock(), exc)
self.assertEqual(result, None)
@mock.patch.object(webob, 'Response')
def test_on_error_with_other_exceptions(self, response):
self.CONF.set_override('app_dev_mode', False, 'api')
exc = mock.Mock(status=500)
self._on_error(
response, exc, expected_status_code=500,
expected_body={'code': 500, 'title': 'Internal Server Error'}
)
self.CONF.set_override('app_dev_mode', True, 'api')
self._on_error(
response, exc, expected_status_code=500,
expected_body={'code': 500, 'title': 'Internal Server Error',
'detail': str(exc)}
)
class CORSHookTestCase(base.BaseTestCase):
"""
Tests for the CORS hook used by the application.
"""
def setUp(self):
super(CORSHookTestCase, self).setUp()
self.config_fixture = config_fixture.Config()
self.CONF = self.useFixture(self.config_fixture).conf
def test_allowed_origin(self):
"""Test when the origin is in the list of allowed origins."""
self.CONF.set_override('allowed_cors_origins', 'test.com', 'api')
hook = app.CORSHook()
request = pecan.core.Request({})
request.headers = {'Origin': 'test.com'}
state = pecan.core.RoutingState(request, pecan.core.Response(), None)
hook.after(state)
self.assertIn('Access-Control-Allow-Origin', state.response.headers)
allow_origin = state.response.headers['Access-Control-Allow-Origin']
self.assertEqual('test.com', allow_origin)
self.assertIn('Access-Control-Allow-Methods', state.response.headers)
allow_methods = state.response.headers['Access-Control-Allow-Methods']
self.assertEqual('GET, OPTIONS, PUT, POST', allow_methods)
self.assertIn('Access-Control-Allow-Headers', state.response.headers)
allow_headers = state.response.headers['Access-Control-Allow-Headers']
self.assertEqual('origin, authorization, accept, content-type',
allow_headers)
def test_unallowed_origin(self):
"""Test when the origin is not in the list of allowed origins."""
hook = app.CORSHook()
request_headers = {'Origin': 'test.com'}
request = pecan.core.Request({})
request.headers = request_headers
state = pecan.core.RoutingState(request, pecan.core.Response(), None)
hook.after(state)
self.assertNotIn('Access-Control-Allow-Origin', state.response.headers)
self.assertNotIn('Access-Control-Allow-Methods',
state.response.headers)
self.assertNotIn('Access-Control-Allow-Headers',
state.response.headers)
def test_no_origin_header(self):
"""Test when there is no 'Origin' header in the request, in which case,
the request is not cross-origin and doesn't need the CORS headers."""
hook = app.CORSHook()
request = pecan.core.Request({})
state = pecan.core.RoutingState(request, pecan.core.Response(), None)
hook.after(state)
self.assertNotIn('Access-Control-Allow-Origin', state.response.headers)
self.assertNotIn('Access-Control-Allow-Methods',
state.response.headers)
self.assertNotIn('Access-Control-Allow-Headers',
state.response.headers)
class SetupAppTestCase(base.BaseTestCase):
def setUp(self):
super(SetupAppTestCase, self).setUp()
self.config_fixture = config_fixture.Config()
self.CONF = self.useFixture(self.config_fixture).conf
@mock.patch('pecan.hooks')
@mock.patch.object(app, 'JSONErrorHook')
@mock.patch.object(app, 'CORSHook')
@mock.patch('os.path.join')
@mock.patch('pecan.make_app')
@mock.patch('refstack.api.app.SessionMiddleware')
@mock.patch('refstack.api.utils.get_token', return_value='42')
def test_setup_app(self, get_token, session_middleware, make_app, os_join,
json_error_hook, cors_hook, pecan_hooks):
self.CONF.set_override('app_dev_mode',
True,
'api')
self.CONF.set_override('template_path',
'fake_template_path',
'api')
self.CONF.set_override('static_root',
'fake_static_root',
'api')
os_join.return_value = 'fake_project_root'
json_error_hook.return_value = 'json_error_hook'
cors_hook.return_value = 'cors_hook'
pecan_hooks.RequestViewerHook.return_value = 'request_viewer_hook'
pecan_config = mock.Mock()
pecan_config.app = {'root': 'fake_pecan_config'}
make_app.return_value = 'fake_app'
session_middleware.return_value = 'fake_app_with_middleware'
result = app.setup_app(pecan_config)
self.assertEqual(result, 'fake_app_with_middleware')
app_conf = dict(pecan_config.app)
make_app.assert_called_once_with(
app_conf.pop('root'),
debug=True,
static_root='fake_static_root',
template_path='fake_template_path',
hooks=['cors_hook', 'json_error_hook', 'request_viewer_hook']
)
session_middleware.assert_called_once_with(
'fake_app',
{'session.key': 'refstack',
'session.type': 'memory',
'session.timeout': 604800,
'session.validate_key': get_token.return_value}
)