##// END OF EJS Templates
Added notebooks API tests.
Zachary Sailer -
Show More
@@ -0,0 +1,32 b''
1 """Test the sessions service API."""
2
3
4 import os
5 import sys
6 import json
7 import urllib
8
9 import requests
10
11 from IPython.html.tests.launchnotebook import NotebookTestBase
12
13 '''
14 class SessionAPITest(NotebookTestBase):
15 """Test the sessions web service API"""
16
17 def base_url(self):
18 return super(SessionAPITest,self).base_url() + 'api/sessions'
19
20 def test_no_sessions(self):
21 """Make sure there are no sessions running at the start"""
22 url = self.base_url()
23 r = requests.get(url)
24 self.assertEqual(r.json(), [])
25
26 def test_start_session(self):
27 url = self.base_url()
28 param = urllib.urlencode({'notebook_path': 'test.ipynb'})
29 r = requests.post(url, params=param)
30 print r
31 #self.assertNotEqual(r.json(), [])
32 ''' No newline at end of file
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,124 b''
1 """Test the all of the services API."""
2
3
4 import os
5 import sys
6 import json
7 import urllib
8 from zmq.utils import jsonapi
9
10 import requests
11
12 from IPython.html.tests.launchnotebook import NotebookTestBase
13
14 class APITest(NotebookTestBase):
15 """Test the kernels web service API"""
16
17 def base_url(self):
18 return super(APITest,self).base_url()
19
20 def notebooks_url(self):
21 return self.base_url() + 'api/notebooks'
22
23 def kernels_url(self):
24 return self.base_url() + 'api/kernels'
25
26 def sessions_url(self):
27 return self.base_url() + 'api/sessions'
28
29 def contents_url(self):
30 return self.contents_url() + 'api/contents'
31
32 def mknb(self, name='', path='/'):
33 url = self.notebooks_url() + path
34 return url, requests.post(url)
35
36 def delnb(self, name, path='/'):
37 url = self.notebooks_url() + path + name
38 r = requests.delete(url)
39 return r.status_code
40
41 def test_no_notebooks(self):
42 url = self.notebooks_url()
43 r = requests.get(url)
44 self.assertEqual(r.json(), [])
45
46 def test_root_notebook_handler(self):
47 # POST a notebook and test the dict thats returned.
48 url, nb = self.mknb()
49 data = nb.json()
50 assert isinstance(data, dict)
51 assert data.has_key("name")
52 assert data.has_key("path")
53 self.assertEqual(data['name'], u'Untitled0.ipynb')
54 self.assertEqual(data['path'], u'/')
55
56 # GET list of notebooks in directory.
57 r = requests.get(url)
58 assert isinstance(r.json(), list)
59 assert isinstance(r.json()[0], dict)
60
61 # GET with a notebook name.
62 url = self.notebooks_url() + '/Untitled0.ipynb'
63 r = requests.get(url)
64 assert isinstance(data, dict)
65 self.assertEqual(r.json(), data)
66
67 # PATCH (rename) request.
68 new_name = {'name':'test.ipynb'}
69 r = requests.patch(url, data=jsonapi.dumps(new_name))
70 data = r.json()
71 assert isinstance(data, dict)
72
73 # make sure the patch worked.
74 new_url = self.notebooks_url() + '/test.ipynb'
75 r = requests.get(new_url)
76 assert isinstance(r.json(), dict)
77 self.assertEqual(r.json(), data)
78
79 # GET bad (old) notebook name.
80 r = requests.get(url)
81 self.assertEqual(r.status_code, 404)
82
83 # POST notebooks to folders one and two levels down.
84 os.makedirs(os.path.join(self.notebook_dir.name, 'foo'))
85 os.makedirs(os.path.join(self.notebook_dir.name, 'foo','bar'))
86 url, nb = self.mknb(path='/foo/')
87 url2, nb2 = self.mknb(path='/foo/bar/')
88 data = nb.json()
89 data2 = nb2.json()
90 assert isinstance(data, dict)
91 assert isinstance(data2, dict)
92 assert data.has_key("name")
93 assert data.has_key("path")
94 self.assertEqual(data['name'], u'Untitled0.ipynb')
95 self.assertEqual(data['path'], u'/foo/')
96 assert data2.has_key("name")
97 assert data2.has_key("path")
98 self.assertEqual(data2['name'], u'Untitled0.ipynb')
99 self.assertEqual(data2['path'], u'/foo/bar/')
100
101 # GET request on notebooks one and two levels down.
102 r = requests.get(url+'Untitled0.ipynb')
103 r2 = requests.get(url2+'Untitled0.ipynb')
104 assert isinstance(r.json(), dict)
105 self.assertEqual(r.json(), data)
106 assert isinstance(r2.json(), dict)
107 self.assertEqual(r2.json(), data2)
108
109 # PATCH notebooks that are one and two levels down.
110 new_name = {'name': 'testfoo.ipynb'}
111 r = requests.patch(url+'Untitled0.ipynb', data=jsonapi.dumps(new_name))
112 r = requests.get(url+'testfoo.ipynb')
113 data = r.json()
114 assert isinstance(data, dict)
115 assert data.has_key('name')
116 self.assertEqual(data['name'], 'testfoo.ipynb')
117 r = requests.get(url+'Untitled0.ipynb')
118 self.assertEqual(r.status_code, 404)
119
120 # DELETE notebooks
121 r = self.delnb('testfoo.ipynb', '/foo/')
122 r2 = self.delnb('Untitled0.ipynb', '/foo/bar/')
123 self.assertEqual(r, 204)
124 self.assertEqual(r2, 204) No newline at end of file
@@ -109,7 +109,9 b' class TestContentManager(TestCase):'
109 # and subdirectory.
109 # and subdirectory.
110 contents = cm.list_contents('/')
110 contents = cm.list_contents('/')
111 contents1 = cm.list_contents('fold1')
111 contents1 = cm.list_contents('fold1')
112 self.assertEqual(type(contents), type(list()))
112 assert isinstance(contents, list)
113 self.assertEqual(type(contents[0]), type(dict()))
113 assert isinstance(contents[0], dict)
114 assert contents[0].has_key('path')
115 assert contents[0].has_key('path')
114 self.assertEqual(contents[0]['path'], '/')
116 self.assertEqual(contents[0]['path'], '/')
115 self.assertEqual(contents1[0]['path'], 'fold1')
117 self.assertEqual(contents1[0]['path'], 'fold1')
@@ -94,17 +94,16 b' class FileNotebookManager(NotebookManager):'
94 def change_notebook(self, data, notebook_name, notebook_path='/'):
94 def change_notebook(self, data, notebook_name, notebook_path='/'):
95 """Changes notebook"""
95 """Changes notebook"""
96 changes = data.keys()
96 changes = data.keys()
97 response = 200
98 for change in changes:
97 for change in changes:
99 full_path = self.get_os_path(notebook_name, notebook_path)
98 full_path = self.get_os_path(notebook_name, notebook_path)
100 if change == "name":
99 if change == "name":
101 new_path = self.get_os_path(data['name'], notebook_path)
100 new_path = self.get_os_path(data['name'], notebook_path)
102 if not os.path.isfile(new_path):
101 try:
103 os.rename(full_path,
102 os.rename(full_path,
104 self.get_os_path(data['name'], notebook_path))
103 self.get_os_path(data['name'], notebook_path))
105 notebook_name = data['name']
104 notebook_name = data['name']
106 else:
105 except OSError as e:
107 response = 409
106 raise web.HTTPError(409, u'Notebook name already exists.')
108 if change == "path":
107 if change == "path":
109 new_path = self.get_os_path(data['name'], data['path'])
108 new_path = self.get_os_path(data['name'], data['path'])
110 stutil.move(full_path, new_path)
109 stutil.move(full_path, new_path)
@@ -112,7 +111,7 b' class FileNotebookManager(NotebookManager):'
112 if change == "content":
111 if change == "content":
113 self.save_notebook(data, notebook_name, notebook_path)
112 self.save_notebook(data, notebook_name, notebook_path)
114 model = self.notebook_model(notebook_name, notebook_path)
113 model = self.notebook_model(notebook_name, notebook_path)
115 return model, response
114 return model
116
115
117 def notebook_exists(self, name, path):
116 def notebook_exists(self, name, path):
118 """Returns a True if the notebook exists. Else, returns False.
117 """Returns a True if the notebook exists. Else, returns False.
@@ -131,31 +130,6 b' class FileNotebookManager(NotebookManager):'
131 path = self.get_os_path(name, path)
130 path = self.get_os_path(name, path)
132 return os.path.isfile(path)
131 return os.path.isfile(path)
133
132
134 def get_os_path(self, fname, path='/'):
135 """Given a notebook name and a server URL path, return its file system
136 path.
137
138 Parameters
139 ----------
140 fname : string
141 The name of a notebook file with the .ipynb extension
142 path : string
143 The relative URL path (with '/' as separator) to the named
144 notebook.
145
146 Returns
147 -------
148 path : string
149 A file system path that combines notebook_dir (location where
150 server started), the relative path, and the filename with the
151 current operating system's url.
152 """
153 parts = path.split('/')
154 parts = [p for p in parts if p != ''] # remove duplicate splits
155 parts += [fname]
156 path = os.path.join(self.notebook_dir, *parts)
157 return path
158
159 def read_notebook_object_from_path(self, path):
133 def read_notebook_object_from_path(self, path):
160 """read a notebook object from a path"""
134 """read a notebook object from a path"""
161 info = os.stat(path)
135 info = os.stat(path)
@@ -36,7 +36,7 b' class NotebookRootHandler(IPythonHandler):'
36 """get returns a list of notebooks from the location
36 """get returns a list of notebooks from the location
37 where the server was started."""
37 where the server was started."""
38 nbm = self.notebook_manager
38 nbm = self.notebook_manager
39 notebooks = nbm.list_notebooks("")
39 notebooks = nbm.list_notebooks("/")
40 self.finish(jsonapi.dumps(notebooks))
40 self.finish(jsonapi.dumps(notebooks))
41
41
42 @web.authenticated
42 @web.authenticated
@@ -53,18 +53,9 b' class NotebookRootHandler(IPythonHandler):'
53 fname = nbm.new_notebook(notebook_path='/')
53 fname = nbm.new_notebook(notebook_path='/')
54 self.set_header('Location', nbm.notebook_dir + fname)
54 self.set_header('Location', nbm.notebook_dir + fname)
55 model = nbm.notebook_model(fname)
55 model = nbm.notebook_model(fname)
56 self.set_header('Location', '{0}api/notebooks/{1}'.format(self.base_project_url, notebook_name))
56 self.set_header('Location', '{0}api/notebooks/{1}'.format(self.base_project_url, fname))
57 self.finish(jsonapi.dumps(model))
57 self.finish(jsonapi.dumps(model))
58
58
59
60 class NotebookRootRedirect(IPythonHandler):
61
62 @web.authenticated
63 def get(self):
64 """get redirects to not include trailing backslash"""
65 self.redirect("/api/notebooks")
66
67
68 class NotebookHandler(IPythonHandler):
59 class NotebookHandler(IPythonHandler):
69
60
70 SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH', 'POST','DELETE')
61 SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH', 'POST','DELETE')
@@ -86,7 +77,7 b' class NotebookHandler(IPythonHandler):'
86 # get and return notebook representation
77 # get and return notebook representation
87 format = self.get_argument('format', default='json')
78 format = self.get_argument('format', default='json')
88 download = self.get_argument('download', default='False')
79 download = self.get_argument('download', default='False')
89 model = nbm.notebook_model(name,path)
80 model = nbm.notebook_model(name, path)
90 last_mod, representation, name = nbm.get_notebook(name, path, format)
81 last_mod, representation, name = nbm.get_notebook(name, path, format)
91 self.set_header('Last-Modified', last_mod)
82 self.set_header('Last-Modified', last_mod)
92
83
@@ -109,8 +100,7 b' class NotebookHandler(IPythonHandler):'
109 nbm = self.notebook_manager
100 nbm = self.notebook_manager
110 name, path = nbm.named_notebook_path(notebook_path)
101 name, path = nbm.named_notebook_path(notebook_path)
111 data = jsonapi.loads(self.request.body)
102 data = jsonapi.loads(self.request.body)
112 model, response = nbm.change_notebook(data, name, path)
103 model = nbm.change_notebook(data, name, path)
113 self.set_status(response)
114 self.finish(jsonapi.dumps(model))
104 self.finish(jsonapi.dumps(model))
115
105
116 @web.authenticated
106 @web.authenticated
@@ -217,7 +207,7 b' default_handlers = ['
217 ModifyNotebookCheckpointsHandler),
207 ModifyNotebookCheckpointsHandler),
218 (r"api/notebooks/%s/" % _notebook_path_regex, NotebookHandler),
208 (r"api/notebooks/%s/" % _notebook_path_regex, NotebookHandler),
219 (r"api/notebooks/%s" % _notebook_path_regex, NotebookHandler),
209 (r"api/notebooks/%s" % _notebook_path_regex, NotebookHandler),
220 (r"api/notebooks/", NotebookRootRedirect),
210 (r"api/notebooks/", NotebookRootHandler),
221 (r"api/notebooks", NotebookRootHandler),
211 (r"api/notebooks", NotebookRootHandler),
222 ]
212 ]
223
213
@@ -71,6 +71,32 b' class NotebookManager(LoggingConfigurable):'
71 name = None
71 name = None
72 path = "/".join(names) + '/'
72 path = "/".join(names) + '/'
73 return name, path
73 return name, path
74
75 def get_os_path(self, fname=None, path='/'):
76 """Given a notebook name and a server URL path, return its file system
77 path.
78
79 Parameters
80 ----------
81 fname : string
82 The name of a notebook file with the .ipynb extension
83 path : string
84 The relative URL path (with '/' as separator) to the named
85 notebook.
86
87 Returns
88 -------
89 path : string
90 A file system path that combines notebook_dir (location where
91 server started), the relative path, and the filename with the
92 current operating system's url.
93 """
94 parts = path.split('/')
95 parts = [p for p in parts if p != ''] # remove duplicate splits
96 if fname is not None:
97 parts += [fname]
98 path = os.path.join(self.notebook_dir, *parts)
99 return path
74
100
75 def url_encode(self, path):
101 def url_encode(self, path):
76 """Returns the path with all special characters URL encoded"""
102 """Returns the path with all special characters URL encoded"""
@@ -135,7 +161,7 b' class NotebookManager(LoggingConfigurable):'
135 model = {"name": notebook_name,
161 model = {"name": notebook_name,
136 "path": notebook_path,
162 "path": notebook_path,
137 "last_modified (UTC)": last_modified.ctime()}
163 "last_modified (UTC)": last_modified.ctime()}
138 if content == True:
164 if content is True:
139 model['content'] = contents
165 model['content'] = contents
140 return model
166 return model
141
167
@@ -196,7 +222,7 b' class NotebookManager(LoggingConfigurable):'
196 nb.metadata.name = name
222 nb.metadata.name = name
197 self.write_notebook_object(nb, name, notebook_path, new_name)
223 self.write_notebook_object(nb, name, notebook_path, new_name)
198
224
199 def write_notebook_object(self, nb, notebook_name=None, notebook_path='/', new_name=None):
225 def write_notebook_object(self, nb, notebook_name='/', notebook_path='/', new_name=None):
200 """Write a notebook object and return its notebook_name.
226 """Write a notebook object and return its notebook_name.
201
227
202 If notebook_name is None, this method should create a new notebook_name.
228 If notebook_name is None, this method should create a new notebook_name.
@@ -28,6 +28,7 b' class NotebookTestBase(TestCase):'
28 '--ipython-dir=%s' % self.ipython_dir.name,
28 '--ipython-dir=%s' % self.ipython_dir.name,
29 '--notebook-dir=%s' % self.notebook_dir.name
29 '--notebook-dir=%s' % self.notebook_dir.name
30 ]
30 ]
31 #self.notebook = Popen(notebook_args)
31 self.notebook = Popen(notebook_args, stdout=PIPE, stderr=PIPE)
32 self.notebook = Popen(notebook_args, stdout=PIPE, stderr=PIPE)
32 time.sleep(3.0)
33 time.sleep(3.0)
33
34
General Comments 0
You need to be logged in to leave comments. Login now