##// END OF EJS Templates
Improving tests and setting of Location header.
Brian E. Granger -
Show More
@@ -1,193 +1,195 b''
1 """Tornado handlers for the notebook.
1 """Tornado handlers for the notebook.
2
2
3 Authors:
3 Authors:
4
4
5 * Brian Granger
5 * Brian Granger
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2011 The IPython Development Team
9 # Copyright (C) 2008-2011 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 import logging
19 import logging
20 from tornado import web
20 from tornado import web
21
21
22 from zmq.utils import jsonapi
22 from zmq.utils import jsonapi
23
23
24 from IPython.utils.jsonutil import date_default
24 from IPython.utils.jsonutil import date_default
25 from IPython.html.utils import url_path_join
25
26
26 from ...base.handlers import IPythonHandler, json_errors
27 from ...base.handlers import IPythonHandler, json_errors
27 from ...base.zmqhandlers import AuthenticatedZMQStreamHandler
28 from ...base.zmqhandlers import AuthenticatedZMQStreamHandler
28
29
29 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
30 # Kernel handlers
31 # Kernel handlers
31 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
32
33
33
34
34 class MainKernelHandler(IPythonHandler):
35 class MainKernelHandler(IPythonHandler):
35
36
36 @web.authenticated
37 @web.authenticated
37 @json_errors
38 @json_errors
38 def get(self):
39 def get(self):
39 km = self.kernel_manager
40 km = self.kernel_manager
40 self.finish(jsonapi.dumps(km.list_kernels(self.ws_url)))
41 self.finish(jsonapi.dumps(km.list_kernels(self.ws_url)))
41
42
42 @web.authenticated
43 @web.authenticated
43 @json_errors
44 @json_errors
44 def post(self):
45 def post(self):
45 km = self.kernel_manager
46 km = self.kernel_manager
46 kernel_id = km.start_kernel()
47 kernel_id = km.start_kernel()
47 model = km.kernel_model(kernel_id, self.ws_url)
48 model = km.kernel_model(kernel_id, self.ws_url)
48 self.set_header('Location', '{0}api/kernels/{1}'.format(self.base_kernel_url, kernel_id))
49 location = url_path_join(self.base_kernel_url, 'api', 'kernels', kernel_id)
50 self.set_header('Location', location)
49 self.set_status(201)
51 self.set_status(201)
50 self.finish(jsonapi.dumps(model))
52 self.finish(jsonapi.dumps(model))
51
53
52
54
53 class KernelHandler(IPythonHandler):
55 class KernelHandler(IPythonHandler):
54
56
55 SUPPORTED_METHODS = ('DELETE', 'GET')
57 SUPPORTED_METHODS = ('DELETE', 'GET')
56
58
57 @web.authenticated
59 @web.authenticated
58 @json_errors
60 @json_errors
59 def get(self, kernel_id):
61 def get(self, kernel_id):
60 km = self.kernel_manager
62 km = self.kernel_manager
61 km._check_kernel_id(kernel_id)
63 km._check_kernel_id(kernel_id)
62 model = km.kernel_model(kernel_id, self.ws_url)
64 model = km.kernel_model(kernel_id, self.ws_url)
63 self.finish(jsonapi.dumps(model))
65 self.finish(jsonapi.dumps(model))
64
66
65 @web.authenticated
67 @web.authenticated
66 @json_errors
68 @json_errors
67 def delete(self, kernel_id):
69 def delete(self, kernel_id):
68 km = self.kernel_manager
70 km = self.kernel_manager
69 km.shutdown_kernel(kernel_id)
71 km.shutdown_kernel(kernel_id)
70 self.set_status(204)
72 self.set_status(204)
71 self.finish()
73 self.finish()
72
74
73
75
74 class KernelActionHandler(IPythonHandler):
76 class KernelActionHandler(IPythonHandler):
75
77
76 @web.authenticated
78 @web.authenticated
77 @json_errors
79 @json_errors
78 def post(self, kernel_id, action):
80 def post(self, kernel_id, action):
79 km = self.kernel_manager
81 km = self.kernel_manager
80 if action == 'interrupt':
82 if action == 'interrupt':
81 km.interrupt_kernel(kernel_id)
83 km.interrupt_kernel(kernel_id)
82 self.set_status(204)
84 self.set_status(204)
83 if action == 'restart':
85 if action == 'restart':
84 km.restart_kernel(kernel_id)
86 km.restart_kernel(kernel_id)
85 model = km.kernel_model(kernel_id, self.ws_url)
87 model = km.kernel_model(kernel_id, self.ws_url)
86 self.set_header('Location', '{0}api/kernels/{1}'.format(self.base_kernel_url, kernel_id))
88 self.set_header('Location', '{0}api/kernels/{1}'.format(self.base_kernel_url, kernel_id))
87 self.write(jsonapi.dumps(model))
89 self.write(jsonapi.dumps(model))
88 self.finish()
90 self.finish()
89
91
90
92
91 class ZMQChannelHandler(AuthenticatedZMQStreamHandler):
93 class ZMQChannelHandler(AuthenticatedZMQStreamHandler):
92
94
93 def create_stream(self):
95 def create_stream(self):
94 km = self.kernel_manager
96 km = self.kernel_manager
95 meth = getattr(km, 'connect_%s' % self.channel)
97 meth = getattr(km, 'connect_%s' % self.channel)
96 self.zmq_stream = meth(self.kernel_id, identity=self.session.bsession)
98 self.zmq_stream = meth(self.kernel_id, identity=self.session.bsession)
97
99
98 def initialize(self, *args, **kwargs):
100 def initialize(self, *args, **kwargs):
99 self.zmq_stream = None
101 self.zmq_stream = None
100
102
101 def on_first_message(self, msg):
103 def on_first_message(self, msg):
102 try:
104 try:
103 super(ZMQChannelHandler, self).on_first_message(msg)
105 super(ZMQChannelHandler, self).on_first_message(msg)
104 except web.HTTPError:
106 except web.HTTPError:
105 self.close()
107 self.close()
106 return
108 return
107 try:
109 try:
108 self.create_stream()
110 self.create_stream()
109 except web.HTTPError:
111 except web.HTTPError:
110 # WebSockets don't response to traditional error codes so we
112 # WebSockets don't response to traditional error codes so we
111 # close the connection.
113 # close the connection.
112 if not self.stream.closed():
114 if not self.stream.closed():
113 self.stream.close()
115 self.stream.close()
114 self.close()
116 self.close()
115 else:
117 else:
116 self.zmq_stream.on_recv(self._on_zmq_reply)
118 self.zmq_stream.on_recv(self._on_zmq_reply)
117
119
118 def on_message(self, msg):
120 def on_message(self, msg):
119 msg = jsonapi.loads(msg)
121 msg = jsonapi.loads(msg)
120 self.session.send(self.zmq_stream, msg)
122 self.session.send(self.zmq_stream, msg)
121
123
122 def on_close(self):
124 def on_close(self):
123 # This method can be called twice, once by self.kernel_died and once
125 # This method can be called twice, once by self.kernel_died and once
124 # from the WebSocket close event. If the WebSocket connection is
126 # from the WebSocket close event. If the WebSocket connection is
125 # closed before the ZMQ streams are setup, they could be None.
127 # closed before the ZMQ streams are setup, they could be None.
126 if self.zmq_stream is not None and not self.zmq_stream.closed():
128 if self.zmq_stream is not None and not self.zmq_stream.closed():
127 self.zmq_stream.on_recv(None)
129 self.zmq_stream.on_recv(None)
128 self.zmq_stream.close()
130 self.zmq_stream.close()
129
131
130
132
131 class IOPubHandler(ZMQChannelHandler):
133 class IOPubHandler(ZMQChannelHandler):
132 channel = 'iopub'
134 channel = 'iopub'
133
135
134 def create_stream(self):
136 def create_stream(self):
135 super(IOPubHandler, self).create_stream()
137 super(IOPubHandler, self).create_stream()
136 km = self.kernel_manager
138 km = self.kernel_manager
137 km.add_restart_callback(self.kernel_id, self.on_kernel_restarted)
139 km.add_restart_callback(self.kernel_id, self.on_kernel_restarted)
138 km.add_restart_callback(self.kernel_id, self.on_restart_failed, 'dead')
140 km.add_restart_callback(self.kernel_id, self.on_restart_failed, 'dead')
139
141
140 def on_close(self):
142 def on_close(self):
141 km = self.kernel_manager
143 km = self.kernel_manager
142 if self.kernel_id in km:
144 if self.kernel_id in km:
143 km.remove_restart_callback(
145 km.remove_restart_callback(
144 self.kernel_id, self.on_kernel_restarted,
146 self.kernel_id, self.on_kernel_restarted,
145 )
147 )
146 km.remove_restart_callback(
148 km.remove_restart_callback(
147 self.kernel_id, self.on_restart_failed, 'dead',
149 self.kernel_id, self.on_restart_failed, 'dead',
148 )
150 )
149 super(IOPubHandler, self).on_close()
151 super(IOPubHandler, self).on_close()
150
152
151 def _send_status_message(self, status):
153 def _send_status_message(self, status):
152 msg = self.session.msg("status",
154 msg = self.session.msg("status",
153 {'execution_state': status}
155 {'execution_state': status}
154 )
156 )
155 self.write_message(jsonapi.dumps(msg, default=date_default))
157 self.write_message(jsonapi.dumps(msg, default=date_default))
156
158
157 def on_kernel_restarted(self):
159 def on_kernel_restarted(self):
158 logging.warn("kernel %s restarted", self.kernel_id)
160 logging.warn("kernel %s restarted", self.kernel_id)
159 self._send_status_message('restarting')
161 self._send_status_message('restarting')
160
162
161 def on_restart_failed(self):
163 def on_restart_failed(self):
162 logging.error("kernel %s restarted failed!", self.kernel_id)
164 logging.error("kernel %s restarted failed!", self.kernel_id)
163 self._send_status_message('dead')
165 self._send_status_message('dead')
164
166
165 def on_message(self, msg):
167 def on_message(self, msg):
166 """IOPub messages make no sense"""
168 """IOPub messages make no sense"""
167 pass
169 pass
168
170
169
171
170 class ShellHandler(ZMQChannelHandler):
172 class ShellHandler(ZMQChannelHandler):
171 channel = 'shell'
173 channel = 'shell'
172
174
173
175
174 class StdinHandler(ZMQChannelHandler):
176 class StdinHandler(ZMQChannelHandler):
175 channel = 'stdin'
177 channel = 'stdin'
176
178
177
179
178 #-----------------------------------------------------------------------------
180 #-----------------------------------------------------------------------------
179 # URL to handler mappings
181 # URL to handler mappings
180 #-----------------------------------------------------------------------------
182 #-----------------------------------------------------------------------------
181
183
182
184
183 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
185 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
184 _kernel_action_regex = r"(?P<action>restart|interrupt)"
186 _kernel_action_regex = r"(?P<action>restart|interrupt)"
185
187
186 default_handlers = [
188 default_handlers = [
187 (r"/api/kernels", MainKernelHandler),
189 (r"/api/kernels", MainKernelHandler),
188 (r"/api/kernels/%s" % _kernel_id_regex, KernelHandler),
190 (r"/api/kernels/%s" % _kernel_id_regex, KernelHandler),
189 (r"/api/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
191 (r"/api/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
190 (r"/api/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
192 (r"/api/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
191 (r"/api/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
193 (r"/api/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
192 (r"/api/kernels/%s/stdin" % _kernel_id_regex, StdinHandler)
194 (r"/api/kernels/%s/stdin" % _kernel_id_regex, StdinHandler)
193 ]
195 ]
@@ -1,124 +1,127 b''
1 """Tornado handlers for the sessions web service.
1 """Tornado handlers for the sessions web service.
2
2
3 Authors:
3 Authors:
4
4
5 * Zach Sailer
5 * Zach Sailer
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2013 The IPython Development Team
9 # Copyright (C) 2013 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 import json
19 import json
20
20
21 from tornado import web
21 from tornado import web
22 from IPython.utils.jsonutil import date_default
22
23 from ...base.handlers import IPythonHandler, json_errors
23 from ...base.handlers import IPythonHandler, json_errors
24 from IPython.utils.jsonutil import date_default
25 from IPython.html.utils import url_path_join
24
26
25 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
26 # Session web service handlers
28 # Session web service handlers
27 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
28
30
29
31
30 class SessionRootHandler(IPythonHandler):
32 class SessionRootHandler(IPythonHandler):
31
33
32 @web.authenticated
34 @web.authenticated
33 @json_errors
35 @json_errors
34 def get(self):
36 def get(self):
35 # Return a list of running sessions
37 # Return a list of running sessions
36 sm = self.session_manager
38 sm = self.session_manager
37 sessions = sm.list_sessions()
39 sessions = sm.list_sessions()
38 self.finish(json.dumps(sessions, default=date_default))
40 self.finish(json.dumps(sessions, default=date_default))
39
41
40 @web.authenticated
42 @web.authenticated
41 @json_errors
43 @json_errors
42 def post(self):
44 def post(self):
43 # Creates a new session
45 # Creates a new session
44 #(unless a session already exists for the named nb)
46 #(unless a session already exists for the named nb)
45 sm = self.session_manager
47 sm = self.session_manager
46 nbm = self.notebook_manager
48 nbm = self.notebook_manager
47 km = self.kernel_manager
49 km = self.kernel_manager
48 model = self.get_json_body()
50 model = self.get_json_body()
49 if model is None:
51 if model is None:
50 raise web.HTTPError(400, "No JSON data provided")
52 raise web.HTTPError(400, "No JSON data provided")
51 try:
53 try:
52 name = model['notebook']['name']
54 name = model['notebook']['name']
53 except KeyError:
55 except KeyError:
54 raise web.HTTPError(400, "Missing field in JSON data: name")
56 raise web.HTTPError(400, "Missing field in JSON data: name")
55 try:
57 try:
56 path = model['notebook']['path']
58 path = model['notebook']['path']
57 except KeyError:
59 except KeyError:
58 raise web.HTTPError(400, "Missing field in JSON data: path")
60 raise web.HTTPError(400, "Missing field in JSON data: path")
59 # Check to see if session exists
61 # Check to see if session exists
60 if sm.session_exists(name=name, path=path):
62 if sm.session_exists(name=name, path=path):
61 model = sm.get_session(name=name, path=path)
63 model = sm.get_session(name=name, path=path)
62 else:
64 else:
63 kernel_id = km.start_kernel(cwd=nbm.notebook_dir)
65 kernel_id = km.start_kernel(cwd=nbm.notebook_dir)
64 model = sm.create_session(name=name, path=path, kernel_id=kernel_id, ws_url=self.ws_url)
66 model = sm.create_session(name=name, path=path, kernel_id=kernel_id, ws_url=self.ws_url)
65 self.set_header('Location', '{0}/api/sessions/{1}'.format(self.base_project_url, model['id']))
67 location = url_path_join(self.base_kernel_url, 'api', 'sessions', model['id'])
68 self.set_header('Location', location)
66 self.set_status(201)
69 self.set_status(201)
67 self.finish(json.dumps(model, default=date_default))
70 self.finish(json.dumps(model, default=date_default))
68
71
69 class SessionHandler(IPythonHandler):
72 class SessionHandler(IPythonHandler):
70
73
71 SUPPORTED_METHODS = ('GET', 'PATCH', 'DELETE')
74 SUPPORTED_METHODS = ('GET', 'PATCH', 'DELETE')
72
75
73 @web.authenticated
76 @web.authenticated
74 @json_errors
77 @json_errors
75 def get(self, session_id):
78 def get(self, session_id):
76 # Returns the JSON model for a single session
79 # Returns the JSON model for a single session
77 sm = self.session_manager
80 sm = self.session_manager
78 model = sm.get_session(session_id=session_id)
81 model = sm.get_session(session_id=session_id)
79 self.finish(json.dumps(model, default=date_default))
82 self.finish(json.dumps(model, default=date_default))
80
83
81 @web.authenticated
84 @web.authenticated
82 @json_errors
85 @json_errors
83 def patch(self, session_id):
86 def patch(self, session_id):
84 # Currently, this handler is strictly for renaming notebooks
87 # Currently, this handler is strictly for renaming notebooks
85 sm = self.session_manager
88 sm = self.session_manager
86 model = self.get_json_body()
89 model = self.get_json_body()
87 if model is None:
90 if model is None:
88 raise web.HTTPError(400, "No JSON data provided")
91 raise web.HTTPError(400, "No JSON data provided")
89 changes = {}
92 changes = {}
90 if 'notebook' in model:
93 if 'notebook' in model:
91 notebook = model['notebook']
94 notebook = model['notebook']
92 if 'name' in notebook:
95 if 'name' in notebook:
93 changes['name'] = notebook['name']
96 changes['name'] = notebook['name']
94 if 'path' in notebook:
97 if 'path' in notebook:
95 changes['path'] = notebook['path']
98 changes['path'] = notebook['path']
96
99
97 sm.update_session(session_id, **changes)
100 sm.update_session(session_id, **changes)
98 model = sm.get_session(session_id=session_id)
101 model = sm.get_session(session_id=session_id)
99 self.finish(json.dumps(model, default=date_default))
102 self.finish(json.dumps(model, default=date_default))
100
103
101 @web.authenticated
104 @web.authenticated
102 @json_errors
105 @json_errors
103 def delete(self, session_id):
106 def delete(self, session_id):
104 # Deletes the session with given session_id
107 # Deletes the session with given session_id
105 sm = self.session_manager
108 sm = self.session_manager
106 km = self.kernel_manager
109 km = self.kernel_manager
107 session = sm.get_session(session_id=session_id)
110 session = sm.get_session(session_id=session_id)
108 sm.delete_session(session_id)
111 sm.delete_session(session_id)
109 km.shutdown_kernel(session['kernel']['id'])
112 km.shutdown_kernel(session['kernel']['id'])
110 self.set_status(204)
113 self.set_status(204)
111 self.finish()
114 self.finish()
112
115
113
116
114 #-----------------------------------------------------------------------------
117 #-----------------------------------------------------------------------------
115 # URL to handler mappings
118 # URL to handler mappings
116 #-----------------------------------------------------------------------------
119 #-----------------------------------------------------------------------------
117
120
118 _session_id_regex = r"(?P<session_id>\w+-\w+-\w+-\w+-\w+)"
121 _session_id_regex = r"(?P<session_id>\w+-\w+-\w+-\w+-\w+)"
119
122
120 default_handlers = [
123 default_handlers = [
121 (r"/api/sessions/%s" % _session_id_regex, SessionHandler),
124 (r"/api/sessions/%s" % _session_id_regex, SessionHandler),
122 (r"/api/sessions", SessionRootHandler)
125 (r"/api/sessions", SessionRootHandler)
123 ]
126 ]
124
127
@@ -1,106 +1,107 b''
1 """Test the sessions web service API."""
1 """Test the sessions web service API."""
2
2
3 import io
3 import io
4 import os
4 import os
5 import json
5 import json
6 import requests
6 import requests
7 import shutil
7 import shutil
8
8
9 pjoin = os.path.join
9 pjoin = os.path.join
10
10
11 from IPython.html.utils import url_path_join
11 from IPython.html.utils import url_path_join
12 from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error
12 from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error
13 from IPython.nbformat.current import new_notebook, write
13 from IPython.nbformat.current import new_notebook, write
14
14
15 class SessionAPI(object):
15 class SessionAPI(object):
16 """Wrapper for notebook API calls."""
16 """Wrapper for notebook API calls."""
17 def __init__(self, base_url):
17 def __init__(self, base_url):
18 self.base_url = base_url
18 self.base_url = base_url
19
19
20 def _req(self, verb, path, body=None):
20 def _req(self, verb, path, body=None):
21 response = requests.request(verb,
21 response = requests.request(verb,
22 url_path_join(self.base_url, 'api/sessions', path), data=body)
22 url_path_join(self.base_url, 'api/sessions', path), data=body)
23
23
24 if 400 <= response.status_code < 600:
24 if 400 <= response.status_code < 600:
25 try:
25 try:
26 response.reason = response.json()['message']
26 response.reason = response.json()['message']
27 except:
27 except:
28 pass
28 pass
29 response.raise_for_status()
29 response.raise_for_status()
30
30
31 return response
31 return response
32
32
33 def list(self):
33 def list(self):
34 return self._req('GET', '')
34 return self._req('GET', '')
35
35
36 def get(self, id):
36 def get(self, id):
37 return self._req('GET', id)
37 return self._req('GET', id)
38
38
39 def create(self, name, path):
39 def create(self, name, path):
40 body = json.dumps({'notebook': {'name':name, 'path':path}})
40 body = json.dumps({'notebook': {'name':name, 'path':path}})
41 return self._req('POST', '', body)
41 return self._req('POST', '', body)
42
42
43 def modify(self, id, name, path):
43 def modify(self, id, name, path):
44 body = json.dumps({'notebook': {'name':name, 'path':path}})
44 body = json.dumps({'notebook': {'name':name, 'path':path}})
45 return self._req('PATCH', id, body)
45 return self._req('PATCH', id, body)
46
46
47 def delete(self, id):
47 def delete(self, id):
48 return self._req('DELETE', id)
48 return self._req('DELETE', id)
49
49
50 class SessionAPITest(NotebookTestBase):
50 class SessionAPITest(NotebookTestBase):
51 """Test the sessions web service API"""
51 """Test the sessions web service API"""
52 def setUp(self):
52 def setUp(self):
53 nbdir = self.notebook_dir.name
53 nbdir = self.notebook_dir.name
54 os.mkdir(pjoin(nbdir, 'foo'))
54 os.mkdir(pjoin(nbdir, 'foo'))
55
55
56 with io.open(pjoin(nbdir, 'foo', 'nb1.ipynb'), 'w') as f:
56 with io.open(pjoin(nbdir, 'foo', 'nb1.ipynb'), 'w') as f:
57 nb = new_notebook(name='nb1')
57 nb = new_notebook(name='nb1')
58 write(nb, f, format='ipynb')
58 write(nb, f, format='ipynb')
59
59
60 self.sess_api = SessionAPI(self.base_url())
60 self.sess_api = SessionAPI(self.base_url())
61
61
62 def tearDown(self):
62 def tearDown(self):
63 for session in self.sess_api.list().json():
63 for session in self.sess_api.list().json():
64 self.sess_api.delete(session['id'])
64 self.sess_api.delete(session['id'])
65 shutil.rmtree(pjoin(self.notebook_dir.name, 'foo'))
65 shutil.rmtree(pjoin(self.notebook_dir.name, 'foo'))
66
66
67 def test_create(self):
67 def test_create(self):
68 sessions = self.sess_api.list().json()
68 sessions = self.sess_api.list().json()
69 self.assertEqual(len(sessions), 0)
69 self.assertEqual(len(sessions), 0)
70
70
71 resp = self.sess_api.create('nb1.ipynb', 'foo')
71 resp = self.sess_api.create('nb1.ipynb', 'foo')
72 self.assertEqual(resp.status_code, 201)
72 self.assertEqual(resp.status_code, 201)
73 newsession = resp.json()
73 newsession = resp.json()
74 self.assertIn('id', newsession)
74 self.assertIn('id', newsession)
75 self.assertEqual(newsession['notebook']['name'], 'nb1.ipynb')
75 self.assertEqual(newsession['notebook']['name'], 'nb1.ipynb')
76 self.assertEqual(newsession['notebook']['path'], 'foo')
76 self.assertEqual(newsession['notebook']['path'], 'foo')
77 self.assertEqual(resp.headers['Location'], '/api/sessions/{0}'.format(newsession['id']))
77
78
78 sessions = self.sess_api.list().json()
79 sessions = self.sess_api.list().json()
79 self.assertEqual(sessions, [newsession])
80 self.assertEqual(sessions, [newsession])
80
81
81 # Retrieve it
82 # Retrieve it
82 sid = newsession['id']
83 sid = newsession['id']
83 got = self.sess_api.get(sid).json()
84 got = self.sess_api.get(sid).json()
84 self.assertEqual(got, newsession)
85 self.assertEqual(got, newsession)
85
86
86 def test_delete(self):
87 def test_delete(self):
87 newsession = self.sess_api.create('nb1.ipynb', 'foo').json()
88 newsession = self.sess_api.create('nb1.ipynb', 'foo').json()
88 sid = newsession['id']
89 sid = newsession['id']
89
90
90 resp = self.sess_api.delete(sid)
91 resp = self.sess_api.delete(sid)
91 self.assertEqual(resp.status_code, 204)
92 self.assertEqual(resp.status_code, 204)
92
93
93 sessions = self.sess_api.list().json()
94 sessions = self.sess_api.list().json()
94 self.assertEqual(sessions, [])
95 self.assertEqual(sessions, [])
95
96
96 with assert_http_error(404):
97 with assert_http_error(404):
97 self.sess_api.get(sid)
98 self.sess_api.get(sid)
98
99
99 def test_modify(self):
100 def test_modify(self):
100 newsession = self.sess_api.create('nb1.ipynb', 'foo').json()
101 newsession = self.sess_api.create('nb1.ipynb', 'foo').json()
101 sid = newsession['id']
102 sid = newsession['id']
102
103
103 changed = self.sess_api.modify(sid, 'nb2.ipynb', '').json()
104 changed = self.sess_api.modify(sid, 'nb2.ipynb', '').json()
104 self.assertEqual(changed['id'], sid)
105 self.assertEqual(changed['id'], sid)
105 self.assertEqual(changed['notebook']['name'], 'nb2.ipynb')
106 self.assertEqual(changed['notebook']['name'], 'nb2.ipynb')
106 self.assertEqual(changed['notebook']['path'], '')
107 self.assertEqual(changed['notebook']['path'], '')
General Comments 0
You need to be logged in to leave comments. Login now