Merge lp:~frankban/charms/precise/juju-gui/guiserver-auth-fixes into lp:~juju-gui/charms/precise/juju-gui/trunk

Proposed by Francesco Banconi
Status: Merged
Merged at revision: 86
Proposed branch: lp:~frankban/charms/precise/juju-gui/guiserver-auth-fixes
Merge into: lp:~juju-gui/charms/precise/juju-gui/trunk
Diff against target: 409 lines (+82/-50)
7 files modified
server/guiserver/apps.py (+8/-5)
server/guiserver/auth.py (+11/-5)
server/guiserver/handlers.py (+7/-3)
server/guiserver/tests/test_apps.py (+0/-7)
server/guiserver/tests/test_auth.py (+17/-2)
server/guiserver/tests/test_handlers.py (+37/-27)
server/guiserver/tests/test_manage.py (+2/-1)
To merge this branch: bzr merge lp:~frankban/charms/precise/juju-gui/guiserver-auth-fixes
Reviewer Review Type Date Requested Status
charmers Pending
Review via email: mp+179014@code.launchpad.net

Description of the change

Authentication fixes.

Fixed an error in the guiserver authentication
when the request id is set to 0.

Moved the auth_backend from the app to the
handler: it is only relevant in the WebSocketHandler
context.

Some clean up in the tests.

https://codereview.appspot.com/12612043/

To post a comment you must log in.
Revision history for this message
Francesco Banconi (frankban) wrote :

Reviewers: mp+179014_code.launchpad.net,

Message:
Please take a look.

Description:
Authentication fixes.

Fixed an error in the guiserver authentication
when the request id is set to 0.

Moved the auth_backend from the app to the
handler: it is only relevant in the WebSocketHandler
context.

Some clean up in the tests.

https://code.launchpad.net/~frankban/charms/precise/juju-gui/guiserver-auth-fixes/+merge/179014

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/12612043/

Affected files:
   A [revision details]
   M server/guiserver/apps.py
   M server/guiserver/auth.py
   M server/guiserver/handlers.py
   M server/guiserver/tests/test_apps.py
   M server/guiserver/tests/test_auth.py
   M server/guiserver/tests/test_handlers.py
   M server/guiserver/tests/test_manage.py

Revision history for this message
Benjamin Saller (bcsaller) wrote :

LGTM

The whole 'is not None' things gets us all from time to time.

Thanks

https://codereview.appspot.com/12612043/

Revision history for this message
Madison Scott-Clary (makyo) wrote :

Did not QA, but LGTM, thanks!

https://codereview.appspot.com/12612043/

Revision history for this message
Francesco Banconi (frankban) wrote :

*** Submitted:

Authentication fixes.

Fixed an error in the guiserver authentication
when the request id is set to 0.

Moved the auth_backend from the app to the
handler: it is only relevant in the WebSocketHandler
context.

Some clean up in the tests.

R=benjamin.saller, matthew.scott
CC=
https://codereview.appspot.com/12612043

https://codereview.appspot.com/12612043/

Revision history for this message
Francesco Banconi (frankban) wrote :

Thank you both for the reviews!

https://codereview.appspot.com/12612043/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'server/guiserver/apps.py'
--- server/guiserver/apps.py 2013-07-25 11:33:00 +0000
+++ server/guiserver/apps.py 2013-08-07 16:40:41 +0000
@@ -36,12 +36,16 @@
36 # Set up static paths.36 # Set up static paths.
37 guiroot = options.guiroot37 guiroot = options.guiroot
38 static_path = os.path.join(guiroot, 'juju-ui')38 static_path = os.path.join(guiroot, 'juju-ui')
39 # Set up the authentication backend.
40 auth_backend = auth.get_backend(options.apiversion)
41 # Set up handlers.39 # Set up handlers.
40 websocket_handler_options = {
41 # The Juju API backend url.
42 'apiurl': options.apiurl,
43 # The backend to use for user authentication.
44 'auth_backend': auth.get_backend(options.apiversion),
45 }
42 server_handlers = [46 server_handlers = [
43 # Handle WebSocket connections.47 # Handle WebSocket connections.
44 (r'^/ws$', handlers.WebSocketHandler, {'apiurl': options.apiurl}),48 (r'^/ws$', handlers.WebSocketHandler, websocket_handler_options),
45 # Handle static files.49 # Handle static files.
46 (r'^/juju-ui/(.*)', web.StaticFileHandler, {'path': static_path}),50 (r'^/juju-ui/(.*)', web.StaticFileHandler, {'path': static_path}),
47 (r'^/(favicon\.ico)$', web.StaticFileHandler, {'path': guiroot}),51 (r'^/(favicon\.ico)$', web.StaticFileHandler, {'path': guiroot}),
@@ -56,8 +60,7 @@
56 # Any other path is served by index.html.60 # Any other path is served by index.html.
57 (r'^/(.*)', handlers.IndexHandler, {'path': guiroot}),61 (r'^/(.*)', handlers.IndexHandler, {'path': guiroot}),
58 )62 )
59 return web.Application(63 return web.Application(server_handlers, debug=options.debug)
60 server_handlers, debug=options.debug, auth_backend=auth_backend)
6164
6265
63def redirector():66def redirector():
6467
=== modified file 'server/guiserver/auth.py'
--- server/guiserver/auth.py 2013-07-25 15:42:46 +0000
+++ server/guiserver/auth.py 2013-08-07 16:40:41 +0000
@@ -21,10 +21,10 @@
21 - User: a simple data structure representing a logged in or anonymous user;21 - User: a simple data structure representing a logged in or anonymous user;
22 - authentication backends (GoBackend and PythonBackend): any object22 - authentication backends (GoBackend and PythonBackend): any object
23 implementing the following interface:23 implementing the following interface:
24 - get_request_id(data);24 - get_request_id(data) -> id or None;
25 - request_is_login(data);25 - request_is_login(data) -> bool;
26 - get_credentials(data);26 - get_credentials(data) -> (str, str);
27 - login_succeeded(data).27 - login_succeeded(data) -> bool.
28 The only purpose of auth backends is to provide the logic to parse28 The only purpose of auth backends is to provide the logic to parse
29 requests' data based on the API implementation currently in use. Backends29 requests' data based on the API implementation currently in use. Backends
30 don't know anything about the authentication process or the current user,30 don't know anything about the authentication process or the current user,
@@ -36,6 +36,8 @@
36 if the authentication succeeds.36 if the authentication succeeds.
37"""37"""
3838
39import logging
40
3941
40class User(object):42class User(object):
41 """The current WebSocket user."""43 """The current WebSocket user."""
@@ -57,6 +59,9 @@
57 username = self.username or 'anonymous'59 username = self.username or 'anonymous'
58 return '<User: {} ({})>'.format(username, status)60 return '<User: {} ({})>'.format(username, status)
5961
62 def __str__(self):
63 return self.username
64
6065
61class AuthMiddleware(object):66class AuthMiddleware(object):
62 """Handle user authentication.67 """Handle user authentication.
@@ -84,7 +89,7 @@
84 """89 """
85 backend = self._backend90 backend = self._backend
86 request_id = backend.get_request_id(data)91 request_id = backend.get_request_id(data)
87 if request_id and backend.request_is_login(data):92 if request_id is not None and backend.request_is_login(data):
88 self._request_id = request_id93 self._request_id = request_id
89 credentials = backend.get_credentials(data)94 credentials = backend.get_credentials(data)
90 self._user.username, self._user.password = credentials95 self._user.username, self._user.password = credentials
@@ -100,6 +105,7 @@
100 if request_id == self._request_id:105 if request_id == self._request_id:
101 logged_in = self._backend.login_succeeded(data)106 logged_in = self._backend.login_succeeded(data)
102 if logged_in:107 if logged_in:
108 logging.info('auth: user {} logged in'.format(self._user))
103 self._user.is_authenticated = True109 self._user.is_authenticated = True
104 else:110 else:
105 self._user.username = self._user.password = ''111 self._user.username = self._user.password = ''
106112
=== modified file 'server/guiserver/handlers.py'
--- server/guiserver/handlers.py 2013-07-24 16:22:21 +0000
+++ server/guiserver/handlers.py 2013-08-07 16:40:41 +0000
@@ -64,8 +64,13 @@
64 """64 """
6565
66 @gen.coroutine66 @gen.coroutine
67 def initialize(self, apiurl, io_loop=None):67 def initialize(self, apiurl, auth_backend, io_loop=None):
68 """Create a new WebSocket client and connect it to the Juju API."""68 """Initialize the WebSocket server.
69
70 Create a new WebSocket client and connect it to the Juju API.
71 Set up the authentication system.
72 Handle the queued messages.
73 """
69 if io_loop is None:74 if io_loop is None:
70 io_loop = IOLoop.current()75 io_loop = IOLoop.current()
71 self._io_loop = io_loop76 self._io_loop = io_loop
@@ -76,7 +81,6 @@
76 self._juju_message_queue = queue = deque()81 self._juju_message_queue = queue = deque()
77 # Set up the authentication infrastructure.82 # Set up the authentication infrastructure.
78 self.user = User()83 self.user = User()
79 auth_backend = self.application.settings['auth_backend']
80 self.auth = AuthMiddleware(self.user, auth_backend)84 self.auth = AuthMiddleware(self.user, auth_backend)
81 # Juju requires the Origin header to be included in the WebSocket85 # Juju requires the Origin header to be included in the WebSocket
82 # client handshake request. Propagate the client origin if present;86 # client handshake request. Propagate the client origin if present;
8387
=== modified file 'server/guiserver/tests/test_apps.py'
--- server/guiserver/tests/test_apps.py 2013-07-26 09:45:42 +0000
+++ server/guiserver/tests/test_apps.py 2013-08-07 16:40:41 +0000
@@ -22,7 +22,6 @@
2222
23from guiserver import (23from guiserver import (
24 apps,24 apps,
25 auth,
26 handlers,25 handlers,
27)26)
2827
@@ -90,12 +89,6 @@
90 spec = self.get_url_spec(app, r'^/test/(.*)$')89 spec = self.get_url_spec(app, r'^/test/(.*)$')
91 self.assertIsNone(spec)90 self.assertIsNone(spec)
9291
93 def test_auth_backend(self):
94 # The server settings include the currently used auth backend.
95 app = self.get_app(apiversion='go')
96 self.assertIn('auth_backend', app.settings)
97 self.assertIsInstance(app.settings['auth_backend'], auth.GoBackend)
98
9992
100class TestRedirector(AppsTestMixin, unittest.TestCase):93class TestRedirector(AppsTestMixin, unittest.TestCase):
10194
10295
=== modified file 'server/guiserver/tests/test_auth.py'
--- server/guiserver/tests/test_auth.py 2013-07-25 15:42:46 +0000
+++ server/guiserver/tests/test_auth.py 2013-08-07 16:40:41 +0000
@@ -18,6 +18,8 @@
1818
19import unittest19import unittest
2020
21from tornado.testing import LogTrapTestCase
22
21from guiserver import auth23from guiserver import auth
22from guiserver.tests import helpers24from guiserver.tests import helpers
2325
@@ -44,6 +46,11 @@
44 expected = '<User: anonymous (not authenticated)>'46 expected = '<User: anonymous (not authenticated)>'
45 self.assertEqual(expected, repr(user))47 self.assertEqual(expected, repr(user))
4648
49 def test_str(self):
50 # The string representation of an user is correctly generated.
51 user = auth.User(username='the-doctor')
52 self.assertEqual('the-doctor', str(user))
53
4754
48class AuthMiddlewareTestMixin(object):55class AuthMiddlewareTestMixin(object):
49 """Include tests for the AuthMiddleware.56 """Include tests for the AuthMiddleware.
@@ -118,15 +125,23 @@
118 self.assert_user('user2', 'passwd2', True)125 self.assert_user('user2', 'passwd2', True)
119 self.assertFalse(self.auth.in_progress())126 self.assertFalse(self.auth.in_progress())
120127
128 def test_request_id_is_zero(self):
129 # The authentication process starts if a login request is processed
130 # and the request id is zero.
131 request = self.make_login_request(request_id=0)
132 self.auth.process_request(request)
133 self.assertTrue(self.auth.in_progress())
134
121135
122class TestGoAuthMiddleware(136class TestGoAuthMiddleware(
123 helpers.GoAPITestMixin, AuthMiddlewareTestMixin, unittest.TestCase):137 helpers.GoAPITestMixin, AuthMiddlewareTestMixin,
138 LogTrapTestCase, unittest.TestCase):
124 pass139 pass
125140
126141
127class TestPythonAuthMiddleware(142class TestPythonAuthMiddleware(
128 helpers.PythonAPITestMixin, AuthMiddlewareTestMixin,143 helpers.PythonAPITestMixin, AuthMiddlewareTestMixin,
129 unittest.TestCase):144 LogTrapTestCase, unittest.TestCase):
130 pass145 pass
131146
132147
133148
=== modified file 'server/guiserver/tests/test_handlers.py'
--- server/guiserver/tests/test_handlers.py 2013-07-25 15:42:46 +0000
+++ server/guiserver/tests/test_handlers.py 2013-08-07 16:40:41 +0000
@@ -24,6 +24,7 @@
24import mock24import mock
25from tornado import (25from tornado import (
26 concurrent,26 concurrent,
27 gen,
27 web,28 web,
28)29)
29from tornado.testing import (30from tornado.testing import (
@@ -46,6 +47,7 @@
46class WebSocketHandlerTestMixin(object):47class WebSocketHandlerTestMixin(object):
47 """Base set up for all the WebSocketHandler test cases."""48 """Base set up for all the WebSocketHandler test cases."""
4849
50 auth_backend = auth.get_backend(manage.DEFAULT_API_VERSION)
49 hello_message = json.dumps({'hello': 'world'})51 hello_message = json.dumps({'hello': 'world'})
5052
51 def get_app(self):53 def get_app(self):
@@ -57,20 +59,21 @@
57 # ws-client -> ws-server -> ws-forwarding-client -> ws-echo-server59 # ws-client -> ws-server -> ws-forwarding-client -> ws-echo-server
58 # Messages arriving to the echo server are returned back to the client:60 # Messages arriving to the echo server are returned back to the client:
59 # ws-echo-server -> ws-forwarding-client -> ws-server -> ws-client61 # ws-echo-server -> ws-forwarding-client -> ws-server -> ws-client
60 self.echo_server_address = self.get_wss_url('/echo')62 self.apiurl = self.get_wss_url('/echo')
61 self.echo_server_closed_future = concurrent.Future()63 self.api_close_future = concurrent.Future()
62 echo_options = {64 echo_options = {
63 'close_future': self.echo_server_closed_future,65 'close_future': self.api_close_future,
64 'io_loop': self.io_loop,66 'io_loop': self.io_loop,
65 }67 }
66 ws_options = {68 ws_options = {
67 'apiurl': self.echo_server_address,69 'apiurl': self.apiurl,
70 'auth_backend': self.auth_backend,
68 'io_loop': self.io_loop,71 'io_loop': self.io_loop,
69 }72 }
70 return web.Application([73 return web.Application([
71 (r'/echo', helpers.EchoWebSocketHandler, echo_options),74 (r'/echo', helpers.EchoWebSocketHandler, echo_options),
72 (r'/ws', handlers.WebSocketHandler, ws_options),75 (r'/ws', handlers.WebSocketHandler, ws_options),
73 ], auth_backend=auth.get_backend(manage.DEFAULT_API_VERSION))76 ])
7477
75 def make_client(self):78 def make_client(self):
76 """Return a WebSocket client ready to be connected to the server."""79 """Return a WebSocket client ready to be connected to the server."""
@@ -90,6 +93,17 @@
90 handler.ws_connection = mock.Mock()93 handler.ws_connection = mock.Mock()
91 return handler94 return handler
9295
96 @gen.coroutine
97 def make_initialized_handler(
98 self, apiurl=None, headers=None, mock_protocol=False):
99 """Create and return an initialized WebSocketHandler instance."""
100 if apiurl is None:
101 apiurl = self.apiurl
102 handler = self.make_handler(
103 headers=headers, mock_protocol=mock_protocol)
104 yield handler.initialize(apiurl, self.auth_backend, self.io_loop)
105 raise gen.Return(handler)
106
93107
94class TestWebSocketHandlerConnection(108class TestWebSocketHandlerConnection(
95 WebSocketHandlerTestMixin, helpers.WSSTestMixin, LogTrapTestCase,109 WebSocketHandlerTestMixin, helpers.WSSTestMixin, LogTrapTestCase,
@@ -107,8 +121,7 @@
107 def test_initialization(self):121 def test_initialization(self):
108 # A WebSocket client is created and connected when the handler is122 # A WebSocket client is created and connected when the handler is
109 # initialized.123 # initialized.
110 handler = self.make_handler()124 handler = yield self.make_initialized_handler()
111 yield handler.initialize(self.echo_server_address, self.io_loop)
112 self.assertTrue(handler.connected)125 self.assertTrue(handler.connected)
113 self.assertTrue(handler.juju_connected)126 self.assertTrue(handler.juju_connected)
114 self.assertIsInstance(127 self.assertIsInstance(
@@ -120,38 +133,36 @@
120 def test_juju_connection_failure(self):133 def test_juju_connection_failure(self):
121 # If the connection to the Juju API server does not succeed, an134 # If the connection to the Juju API server does not succeed, an
122 # error is reported and the client is disconnected.135 # error is reported and the client is disconnected.
123 handler = self.make_handler()
124 expected_log = '.*unable to connect to the Juju API'136 expected_log = '.*unable to connect to the Juju API'
125 with ExpectLog('', expected_log, required=True):137 with ExpectLog('', expected_log, required=True):
126 yield handler.initialize(138 handler = yield self.make_initialized_handler(
127 'wss://127.0.0.1/does-not-exist', self.io_loop)139 apiurl='wss://127.0.0.1/no-such')
128 self.assertFalse(handler.connected)140 self.assertFalse(handler.connected)
129 self.assertFalse(handler.juju_connected)141 self.assertFalse(handler.juju_connected)
130142
131 @gen_test143 @gen_test
132 def test_juju_connection_propagated_request_headers(self):144 def test_juju_connection_propagated_request_headers(self):
133 # The Origin header is propagated to the client connection.145 # The Origin header is propagated to the client connection.
134 handler = self.make_handler(headers={'Origin': 'https://example.com'})146 expected = {'Origin': 'https://example.com'}
135 yield handler.initialize(self.echo_server_address, self.io_loop)147 handler = yield self.make_initialized_handler(headers=expected)
136 headers = handler.juju_connection.request.headers148 headers = handler.juju_connection.request.headers
137 self.assertIn('Origin', headers)149 self.assertIn('Origin', headers)
138 self.assertEqual('https://example.com', headers['Origin'])150 self.assertEqual(expected['Origin'], headers['Origin'])
139151
140 @gen_test152 @gen_test
141 def test_juju_connection_default_request_headers(self):153 def test_juju_connection_default_request_headers(self):
142 # The default Origin header is included in the client connection154 # The default Origin header is included in the client connection
143 # handshake if not found in the original request.155 # handshake if not found in the original request.
144 handler = self.make_handler()156 handler = yield self.make_initialized_handler()
145 yield handler.initialize(self.echo_server_address, self.io_loop)
146 headers = handler.juju_connection.request.headers157 headers = handler.juju_connection.request.headers
147 self.assertIn('Origin', headers)158 self.assertIn('Origin', headers)
148 self.assertEqual(self.get_url('/echo'), headers['Origin'])159 self.assertEqual(self.get_url('/echo'), headers['Origin'])
149160
161 @gen_test
150 def test_client_callback(self):162 def test_client_callback(self):
151 # The WebSocket client is created passing the proper callback.163 # The WebSocket client is created passing the proper callback.
152 handler = self.make_handler()
153 with self.mock_websocket_connect() as mock_websocket_connect:164 with self.mock_websocket_connect() as mock_websocket_connect:
154 handler.initialize(self.echo_server_address, self.io_loop)165 handler = yield self.make_initialized_handler()
155 self.assertEqual(1, mock_websocket_connect.call_count)166 self.assertEqual(1, mock_websocket_connect.call_count)
156 self.assertIn(167 self.assertIn(
157 handler.on_juju_message, mock_websocket_connect.call_args[0])168 handler.on_juju_message, mock_websocket_connect.call_args[0])
@@ -161,7 +172,7 @@
161 # The proxy connection is terminated when the client disconnects.172 # The proxy connection is terminated when the client disconnects.
162 client = yield self.make_client()173 client = yield self.make_client()
163 yield client.close()174 yield client.close()
164 yield self.echo_server_closed_future175 yield self.api_close_future
165176
166 @gen_test177 @gen_test
167 def test_connection_closed_by_server(self):178 def test_connection_closed_by_server(self):
@@ -171,7 +182,7 @@
171 expected_log = '.*Juju API unexpectedly disconnected'182 expected_log = '.*Juju API unexpectedly disconnected'
172 with ExpectLog('', expected_log, required=True):183 with ExpectLog('', expected_log, required=True):
173 # Fire the Future in order to force an echo server disconnection.184 # Fire the Future in order to force an echo server disconnection.
174 self.echo_server_closed_future.set_result(None)185 self.api_close_future.set_result(None)
175 message = yield client.read_message()186 message = yield client.read_message()
176 self.assertIsNone(message)187 self.assertIsNone(message)
177188
@@ -183,16 +194,15 @@
183 @mock.patch('guiserver.clients.WebSocketClientConnection')194 @mock.patch('guiserver.clients.WebSocketClientConnection')
184 def test_from_browser_to_juju(self, mock_juju_connection):195 def test_from_browser_to_juju(self, mock_juju_connection):
185 # A message from the browser is forwarded to the remote server.196 # A message from the browser is forwarded to the remote server.
186 handler = self.make_handler()197 handler = yield self.make_initialized_handler()
187 yield handler.initialize(self.echo_server_address, self.io_loop)
188 handler.on_message(self.hello_message)198 handler.on_message(self.hello_message)
189 mock_juju_connection.write_message.assert_called_once_with(199 mock_juju_connection.write_message.assert_called_once_with(
190 self.hello_message)200 self.hello_message)
191201
202 @gen_test
192 def test_from_juju_to_browser(self):203 def test_from_juju_to_browser(self):
193 # A message from the remote server is returned to the browser.204 # A message from the remote server is returned to the browser.
194 handler = self.make_handler()205 handler = yield self.make_initialized_handler()
195 handler.initialize(self.echo_server_address, self.io_loop)
196 with mock.patch('guiserver.handlers.WebSocketHandler.write_message'):206 with mock.patch('guiserver.handlers.WebSocketHandler.write_message'):
197 handler.on_juju_message(self.hello_message)207 handler.on_juju_message(self.hello_message)
198 handler.write_message.assert_called_once_with(self.hello_message)208 handler.write_message.assert_called_once_with(self.hello_message)
@@ -205,7 +215,7 @@
205 mock_path = 'guiserver.clients.WebSocketClientConnection.write_message'215 mock_path = 'guiserver.clients.WebSocketClientConnection.write_message'
206 with mock.patch(mock_path) as mock_write_message:216 with mock.patch(mock_path) as mock_write_message:
207 initialization = handler.initialize(217 initialization = handler.initialize(
208 self.echo_server_address, self.io_loop)218 self.apiurl, self.auth_backend, self.io_loop)
209 handler.on_message(self.hello_message)219 handler.on_message(self.hello_message)
210 self.assertFalse(mock_write_message.called)220 self.assertFalse(mock_write_message.called)
211 yield initialization221 yield initialization
@@ -246,7 +256,7 @@
246 def setUp(self):256 def setUp(self):
247 super(TestWebSocketHandlerAuthentication, self).setUp()257 super(TestWebSocketHandlerAuthentication, self).setUp()
248 self.handler = self.make_handler(mock_protocol=True)258 self.handler = self.make_handler(mock_protocol=True)
249 self.handler.initialize(self.echo_server_address, self.io_loop)259 self.handler.initialize(self.apiurl, self.auth_backend, self.io_loop)
250260
251 def send_login_request(self):261 def send_login_request(self):
252 """Create a login request and send it to the handler."""262 """Create a login request and send it to the handler."""
@@ -292,7 +302,7 @@
292 self.assertFalse(self.handler.auth.in_progress())302 self.assertFalse(self.handler.auth.in_progress())
293303
294304
295class TestIndexHandler(AsyncHTTPTestCase, LogTrapTestCase):305class TestIndexHandler(LogTrapTestCase, AsyncHTTPTestCase):
296306
297 def setUp(self):307 def setUp(self):
298 # Set up a static path with an index.html in it.308 # Set up a static path with an index.html in it.
@@ -328,7 +338,7 @@
328 self.ensure_index('/:flag:/activated/?my=query')338 self.ensure_index('/:flag:/activated/?my=query')
329339
330340
331class TestHttpsRedirectHandler(AsyncHTTPTestCase, LogTrapTestCase):341class TestHttpsRedirectHandler(LogTrapTestCase, AsyncHTTPTestCase):
332342
333 def get_app(self):343 def get_app(self):
334 return web.Application([(r'.*', handlers.HttpsRedirectHandler)])344 return web.Application([(r'.*', handlers.HttpsRedirectHandler)])
335345
=== modified file 'server/guiserver/tests/test_manage.py'
--- server/guiserver/tests/test_manage.py 2013-07-31 10:29:56 +0000
+++ server/guiserver/tests/test_manage.py 2013-08-07 16:40:41 +0000
@@ -21,6 +21,7 @@
21import unittest21import unittest
2222
23import mock23import mock
24from tornado.testing import LogTrapTestCase
2425
25from guiserver import manage26from guiserver import manage
2627
@@ -138,7 +139,7 @@
138 self.assertEqual(expected, manage._get_ssl_options())139 self.assertEqual(expected, manage._get_ssl_options())
139140
140141
141class TestRun(unittest.TestCase):142class TestRun(LogTrapTestCase, unittest.TestCase):
142143
143 def mock_and_run(self, **kwargs):144 def mock_and_run(self, **kwargs):
144 """Run the application after mocking the IO loop and the options/apps.145 """Run the application after mocking the IO loop and the options/apps.

Subscribers

People subscribed via source and target branches