##// END OF EJS Templates
test upload of v2 notebooks
MinRK -
Show More
@@ -1,295 +1,312 b''
1 1 # coding: utf-8
2 2 """Test the notebooks webservice API."""
3 3
4 4 import io
5 5 import json
6 6 import os
7 7 import shutil
8 8 from unicodedata import normalize
9 9
10 10 pjoin = os.path.join
11 11
12 12 import requests
13 13
14 14 from IPython.html.utils import url_path_join, url_escape
15 15 from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error
16 from IPython.nbformat import current
16 17 from IPython.nbformat.current import (new_notebook, write, read, new_worksheet,
17 18 new_heading_cell, to_notebook_json)
19 from IPython.nbformat import v2
18 20 from IPython.utils import py3compat
19 21 from IPython.utils.data import uniq_stable
20 22
21 23
22 24 class NBAPI(object):
23 25 """Wrapper for notebook API calls."""
24 26 def __init__(self, base_url):
25 27 self.base_url = base_url
26 28
27 29 def _req(self, verb, path, body=None):
28 30 response = requests.request(verb,
29 31 url_path_join(self.base_url, 'api/notebooks', path),
30 32 data=body,
31 33 )
32 34 response.raise_for_status()
33 35 return response
34 36
35 37 def list(self, path='/'):
36 38 return self._req('GET', path)
37 39
38 40 def read(self, name, path='/'):
39 41 return self._req('GET', url_path_join(path, name))
40 42
41 43 def create_untitled(self, path='/'):
42 44 return self._req('POST', path)
43 45
44 46 def upload_untitled(self, body, path='/'):
45 47 return self._req('POST', path, body)
46 48
47 49 def copy_untitled(self, copy_from, path='/'):
48 50 body = json.dumps({'copy_from':copy_from})
49 51 return self._req('POST', path, body)
50 52
51 53 def create(self, name, path='/'):
52 54 return self._req('PUT', url_path_join(path, name))
53 55
54 56 def upload(self, name, body, path='/'):
55 57 return self._req('PUT', url_path_join(path, name), body)
56 58
57 59 def copy(self, copy_from, copy_to, path='/'):
58 60 body = json.dumps({'copy_from':copy_from})
59 61 return self._req('PUT', url_path_join(path, copy_to), body)
60 62
61 63 def save(self, name, body, path='/'):
62 64 return self._req('PUT', url_path_join(path, name), body)
63 65
64 66 def delete(self, name, path='/'):
65 67 return self._req('DELETE', url_path_join(path, name))
66 68
67 69 def rename(self, name, path, new_name):
68 70 body = json.dumps({'name': new_name})
69 71 return self._req('PATCH', url_path_join(path, name), body)
70 72
71 73 def get_checkpoints(self, name, path):
72 74 return self._req('GET', url_path_join(path, name, 'checkpoints'))
73 75
74 76 def new_checkpoint(self, name, path):
75 77 return self._req('POST', url_path_join(path, name, 'checkpoints'))
76 78
77 79 def restore_checkpoint(self, name, path, checkpoint_id):
78 80 return self._req('POST', url_path_join(path, name, 'checkpoints', checkpoint_id))
79 81
80 82 def delete_checkpoint(self, name, path, checkpoint_id):
81 83 return self._req('DELETE', url_path_join(path, name, 'checkpoints', checkpoint_id))
82 84
83 85 class APITest(NotebookTestBase):
84 86 """Test the kernels web service API"""
85 87 dirs_nbs = [('', 'inroot'),
86 88 ('Directory with spaces in', 'inspace'),
87 89 (u'unicodΓ©', 'innonascii'),
88 90 ('foo', 'a'),
89 91 ('foo', 'b'),
90 92 ('foo', 'name with spaces'),
91 93 ('foo', u'unicodΓ©'),
92 94 ('foo/bar', 'baz'),
93 95 (u'Γ₯ b', u'Γ§ d')
94 96 ]
95 97
96 98 dirs = uniq_stable([d for (d,n) in dirs_nbs])
97 99 del dirs[0] # remove ''
98 100
99 101 def setUp(self):
100 102 nbdir = self.notebook_dir.name
101 103
102 104 for d in self.dirs:
103 105 if not os.path.isdir(pjoin(nbdir, d)):
104 106 os.mkdir(pjoin(nbdir, d))
105 107
106 108 for d, name in self.dirs_nbs:
107 109 with io.open(pjoin(nbdir, d, '%s.ipynb' % name), 'w') as f:
108 110 nb = new_notebook(name=name)
109 111 write(nb, f, format='ipynb')
110 112
111 113 self.nb_api = NBAPI(self.base_url())
112 114
113 115 def tearDown(self):
114 116 nbdir = self.notebook_dir.name
115 117
116 118 for dname in ['foo', 'Directory with spaces in', u'unicodΓ©', u'Γ₯ b']:
117 119 shutil.rmtree(pjoin(nbdir, dname), ignore_errors=True)
118 120
119 121 if os.path.isfile(pjoin(nbdir, 'inroot.ipynb')):
120 122 os.unlink(pjoin(nbdir, 'inroot.ipynb'))
121 123
122 124 def test_list_notebooks(self):
123 125 nbs = self.nb_api.list().json()
124 126 self.assertEqual(len(nbs), 1)
125 127 self.assertEqual(nbs[0]['name'], 'inroot.ipynb')
126 128
127 129 nbs = self.nb_api.list('/Directory with spaces in/').json()
128 130 self.assertEqual(len(nbs), 1)
129 131 self.assertEqual(nbs[0]['name'], 'inspace.ipynb')
130 132
131 133 nbs = self.nb_api.list(u'/unicodΓ©/').json()
132 134 self.assertEqual(len(nbs), 1)
133 135 self.assertEqual(nbs[0]['name'], 'innonascii.ipynb')
134 136 self.assertEqual(nbs[0]['path'], u'unicodΓ©')
135 137
136 138 nbs = self.nb_api.list('/foo/bar/').json()
137 139 self.assertEqual(len(nbs), 1)
138 140 self.assertEqual(nbs[0]['name'], 'baz.ipynb')
139 141 self.assertEqual(nbs[0]['path'], 'foo/bar')
140 142
141 143 nbs = self.nb_api.list('foo').json()
142 144 self.assertEqual(len(nbs), 4)
143 145 nbnames = { normalize('NFC', n['name']) for n in nbs }
144 146 expected = [ u'a.ipynb', u'b.ipynb', u'name with spaces.ipynb', u'unicodΓ©.ipynb']
145 147 expected = { normalize('NFC', name) for name in expected }
146 148 self.assertEqual(nbnames, expected)
147 149
148 150 def test_list_nonexistant_dir(self):
149 151 with assert_http_error(404):
150 152 self.nb_api.list('nonexistant')
151 153
152 154 def test_get_contents(self):
153 155 for d, name in self.dirs_nbs:
154 156 nb = self.nb_api.read('%s.ipynb' % name, d+'/').json()
155 157 self.assertEqual(nb['name'], u'%s.ipynb' % name)
156 158 self.assertIn('content', nb)
157 159 self.assertIn('metadata', nb['content'])
158 160 self.assertIsInstance(nb['content']['metadata'], dict)
159 161
160 162 # Name that doesn't exist - should be a 404
161 163 with assert_http_error(404):
162 164 self.nb_api.read('q.ipynb', 'foo')
163 165
164 166 def _check_nb_created(self, resp, name, path):
165 167 self.assertEqual(resp.status_code, 201)
166 168 location_header = py3compat.str_to_unicode(resp.headers['Location'])
167 169 self.assertEqual(location_header, url_escape(url_path_join(u'/api/notebooks', path, name)))
168 170 self.assertEqual(resp.json()['name'], name)
169 171 assert os.path.isfile(pjoin(self.notebook_dir.name, path, name))
170 172
171 173 def test_create_untitled(self):
172 174 resp = self.nb_api.create_untitled(path=u'Γ₯ b')
173 175 self._check_nb_created(resp, 'Untitled0.ipynb', u'Γ₯ b')
174 176
175 177 # Second time
176 178 resp = self.nb_api.create_untitled(path=u'Γ₯ b')
177 179 self._check_nb_created(resp, 'Untitled1.ipynb', u'Γ₯ b')
178 180
179 181 # And two directories down
180 182 resp = self.nb_api.create_untitled(path='foo/bar')
181 183 self._check_nb_created(resp, 'Untitled0.ipynb', pjoin('foo', 'bar'))
182 184
183 185 def test_upload_untitled(self):
184 186 nb = new_notebook(name='Upload test')
185 187 nbmodel = {'content': nb}
186 188 resp = self.nb_api.upload_untitled(path=u'Γ₯ b',
187 189 body=json.dumps(nbmodel))
188 190 self._check_nb_created(resp, 'Untitled0.ipynb', u'Γ₯ b')
189 191
190 192 def test_upload(self):
191 193 nb = new_notebook(name=u'ignored')
192 194 nbmodel = {'content': nb}
193 195 resp = self.nb_api.upload(u'Upload tΓ©st.ipynb', path=u'Γ₯ b',
194 196 body=json.dumps(nbmodel))
195 197 self._check_nb_created(resp, u'Upload tΓ©st.ipynb', u'Γ₯ b')
196 198
199 def test_upload_v2(self):
200 nb = v2.new_notebook()
201 ws = v2.new_worksheet()
202 nb.worksheets.append(ws)
203 ws.cells.append(v2.new_code_cell(input='print("hi")'))
204 nbmodel = {'content': nb}
205 resp = self.nb_api.upload(u'Upload tΓ©st.ipynb', path=u'Γ₯ b',
206 body=json.dumps(nbmodel))
207 self._check_nb_created(resp, u'Upload tΓ©st.ipynb', u'Γ₯ b')
208 resp = self.nb_api.read(u'Upload tΓ©st.ipynb', u'Γ₯ b')
209 data = resp.json()
210 self.assertEqual(data['content']['nbformat'], current.nbformat)
211 self.assertEqual(data['content']['orig_nbformat'], 2)
212
197 213 def test_copy_untitled(self):
198 214 resp = self.nb_api.copy_untitled(u'Γ§ d.ipynb', path=u'Γ₯ b')
199 215 self._check_nb_created(resp, u'Γ§ d-Copy0.ipynb', u'Γ₯ b')
200 216
201 217 def test_copy(self):
202 218 resp = self.nb_api.copy(u'Γ§ d.ipynb', u'cΓΈpy.ipynb', path=u'Γ₯ b')
203 219 self._check_nb_created(resp, u'cΓΈpy.ipynb', u'Γ₯ b')
204 220
205 221 def test_delete(self):
206 222 for d, name in self.dirs_nbs:
207 223 resp = self.nb_api.delete('%s.ipynb' % name, d)
208 224 self.assertEqual(resp.status_code, 204)
209 225
210 226 for d in self.dirs + ['/']:
211 227 nbs = self.nb_api.list(d).json()
212 228 self.assertEqual(len(nbs), 0)
213 229
214 230 def test_rename(self):
215 231 resp = self.nb_api.rename('a.ipynb', 'foo', 'z.ipynb')
216 232 self.assertEqual(resp.headers['Location'].split('/')[-1], 'z.ipynb')
217 233 self.assertEqual(resp.json()['name'], 'z.ipynb')
218 234 assert os.path.isfile(pjoin(self.notebook_dir.name, 'foo', 'z.ipynb'))
219 235
220 236 nbs = self.nb_api.list('foo').json()
221 237 nbnames = set(n['name'] for n in nbs)
222 238 self.assertIn('z.ipynb', nbnames)
223 239 self.assertNotIn('a.ipynb', nbnames)
224 240
225 241 def test_save(self):
226 242 resp = self.nb_api.read('a.ipynb', 'foo')
227 243 nbcontent = json.loads(resp.text)['content']
228 244 nb = to_notebook_json(nbcontent)
229 245 ws = new_worksheet()
230 246 nb.worksheets = [ws]
231 247 ws.cells.append(new_heading_cell(u'Created by test Β³'))
232 248
233 249 nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb}
234 250 resp = self.nb_api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
235 251
236 252 nbfile = pjoin(self.notebook_dir.name, 'foo', 'a.ipynb')
237 253 with io.open(nbfile, 'r', encoding='utf-8') as f:
238 254 newnb = read(f, format='ipynb')
239 255 self.assertEqual(newnb.worksheets[0].cells[0].source,
240 256 u'Created by test Β³')
241 257 nbcontent = self.nb_api.read('a.ipynb', 'foo').json()['content']
242 258 newnb = to_notebook_json(nbcontent)
243 259 self.assertEqual(newnb.worksheets[0].cells[0].source,
244 260 u'Created by test Β³')
245 261
246 262 # Save and rename
247 263 nbmodel= {'name': 'a2.ipynb', 'path':'foo/bar', 'content': nb}
248 264 resp = self.nb_api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
249 265 saved = resp.json()
250 266 self.assertEqual(saved['name'], 'a2.ipynb')
251 267 self.assertEqual(saved['path'], 'foo/bar')
252 268 assert os.path.isfile(pjoin(self.notebook_dir.name,'foo','bar','a2.ipynb'))
253 269 assert not os.path.isfile(pjoin(self.notebook_dir.name, 'foo', 'a.ipynb'))
254 270 with assert_http_error(404):
255 271 self.nb_api.read('a.ipynb', 'foo')
256 272
257 273 def test_checkpoints(self):
258 274 resp = self.nb_api.read('a.ipynb', 'foo')
259 275 r = self.nb_api.new_checkpoint('a.ipynb', 'foo')
260 276 self.assertEqual(r.status_code, 201)
261 277 cp1 = r.json()
262 278 self.assertEqual(set(cp1), {'id', 'last_modified'})
263 279 self.assertEqual(r.headers['Location'].split('/')[-1], cp1['id'])
264 280
265 281 # Modify it
266 282 nbcontent = json.loads(resp.text)['content']
267 283 nb = to_notebook_json(nbcontent)
268 284 ws = new_worksheet()
269 285 nb.worksheets = [ws]
270 286 hcell = new_heading_cell('Created by test')
271 287 ws.cells.append(hcell)
272 288 # Save
273 289 nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb}
274 290 resp = self.nb_api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
275 291
276 292 # List checkpoints
277 293 cps = self.nb_api.get_checkpoints('a.ipynb', 'foo').json()
278 294 self.assertEqual(cps, [cp1])
279 295
280 296 nbcontent = self.nb_api.read('a.ipynb', 'foo').json()['content']
281 297 nb = to_notebook_json(nbcontent)
282 298 self.assertEqual(nb.worksheets[0].cells[0].source, 'Created by test')
283 299
284 300 # Restore cp1
285 301 r = self.nb_api.restore_checkpoint('a.ipynb', 'foo', cp1['id'])
286 302 self.assertEqual(r.status_code, 204)
287 303 nbcontent = self.nb_api.read('a.ipynb', 'foo').json()['content']
288 304 nb = to_notebook_json(nbcontent)
289 305 self.assertEqual(nb.worksheets, [])
290 306
291 307 # Delete cp1
292 308 r = self.nb_api.delete_checkpoint('a.ipynb', 'foo', cp1['id'])
293 309 self.assertEqual(r.status_code, 204)
294 310 cps = self.nb_api.get_checkpoints('a.ipynb', 'foo').json()
295 311 self.assertEqual(cps, [])
312
General Comments 0
You need to be logged in to leave comments. Login now