##// END OF EJS Templates
test creating a directory with PUT
MinRK -
Show More
@@ -1,449 +1,459 b''
1 # coding: utf-8
1 # coding: utf-8
2 """Test the contents webservice API."""
2 """Test the contents webservice API."""
3
3
4 import base64
4 import base64
5 import io
5 import io
6 import json
6 import json
7 import os
7 import os
8 import shutil
8 import shutil
9 from unicodedata import normalize
9 from unicodedata import normalize
10
10
11 pjoin = os.path.join
11 pjoin = os.path.join
12
12
13 import requests
13 import requests
14
14
15 from IPython.html.utils import url_path_join, url_escape
15 from IPython.html.utils import url_path_join, url_escape
16 from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error
16 from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error
17 from IPython.nbformat import current
17 from IPython.nbformat import current
18 from IPython.nbformat.current import (new_notebook, write, read, new_worksheet,
18 from IPython.nbformat.current import (new_notebook, write, read, new_worksheet,
19 new_heading_cell, to_notebook_json)
19 new_heading_cell, to_notebook_json)
20 from IPython.nbformat import v2
20 from IPython.nbformat import v2
21 from IPython.utils import py3compat
21 from IPython.utils import py3compat
22 from IPython.utils.data import uniq_stable
22 from IPython.utils.data import uniq_stable
23
23
24
24
25 # TODO: Remove this after we create the contents web service and directories are
25 # TODO: Remove this after we create the contents web service and directories are
26 # no longer listed by the notebook web service.
26 # no longer listed by the notebook web service.
27 def notebooks_only(dir_model):
27 def notebooks_only(dir_model):
28 return [nb for nb in dir_model['content'] if nb['type']=='notebook']
28 return [nb for nb in dir_model['content'] if nb['type']=='notebook']
29
29
30 def dirs_only(dir_model):
30 def dirs_only(dir_model):
31 return [x for x in dir_model['content'] if x['type']=='directory']
31 return [x for x in dir_model['content'] if x['type']=='directory']
32
32
33
33
34 class API(object):
34 class API(object):
35 """Wrapper for contents API calls."""
35 """Wrapper for contents API calls."""
36 def __init__(self, base_url):
36 def __init__(self, base_url):
37 self.base_url = base_url
37 self.base_url = base_url
38
38
39 def _req(self, verb, path, body=None):
39 def _req(self, verb, path, body=None):
40 response = requests.request(verb,
40 response = requests.request(verb,
41 url_path_join(self.base_url, 'api/contents', path),
41 url_path_join(self.base_url, 'api/contents', path),
42 data=body,
42 data=body,
43 )
43 )
44 response.raise_for_status()
44 response.raise_for_status()
45 return response
45 return response
46
46
47 def list(self, path='/'):
47 def list(self, path='/'):
48 return self._req('GET', path)
48 return self._req('GET', path)
49
49
50 def read(self, name, path='/'):
50 def read(self, name, path='/'):
51 return self._req('GET', url_path_join(path, name))
51 return self._req('GET', url_path_join(path, name))
52
52
53 def create_untitled(self, path='/', ext=None):
53 def create_untitled(self, path='/', ext=None):
54 body = None
54 body = None
55 if ext:
55 if ext:
56 body = json.dumps({'ext': ext})
56 body = json.dumps({'ext': ext})
57 return self._req('POST', path, body)
57 return self._req('POST', path, body)
58
58
59 def upload_untitled(self, body, path='/'):
59 def upload_untitled(self, body, path='/'):
60 return self._req('POST', path, body)
60 return self._req('POST', path, body)
61
61
62 def copy_untitled(self, copy_from, path='/'):
62 def copy_untitled(self, copy_from, path='/'):
63 body = json.dumps({'copy_from':copy_from})
63 body = json.dumps({'copy_from':copy_from})
64 return self._req('POST', path, body)
64 return self._req('POST', path, body)
65
65
66 def create(self, name, path='/'):
66 def create(self, name, path='/'):
67 return self._req('PUT', url_path_join(path, name))
67 return self._req('PUT', url_path_join(path, name))
68
68
69 def upload(self, name, body, path='/'):
69 def upload(self, name, body, path='/'):
70 return self._req('PUT', url_path_join(path, name), body)
70 return self._req('PUT', url_path_join(path, name), body)
71
71
72 def copy(self, copy_from, copy_to, path='/'):
72 def copy(self, copy_from, copy_to, path='/'):
73 body = json.dumps({'copy_from':copy_from})
73 body = json.dumps({'copy_from':copy_from})
74 return self._req('PUT', url_path_join(path, copy_to), body)
74 return self._req('PUT', url_path_join(path, copy_to), body)
75
75
76 def save(self, name, body, path='/'):
76 def save(self, name, body, path='/'):
77 return self._req('PUT', url_path_join(path, name), body)
77 return self._req('PUT', url_path_join(path, name), body)
78
78
79 def delete(self, name, path='/'):
79 def delete(self, name, path='/'):
80 return self._req('DELETE', url_path_join(path, name))
80 return self._req('DELETE', url_path_join(path, name))
81
81
82 def rename(self, name, path, new_name):
82 def rename(self, name, path, new_name):
83 body = json.dumps({'name': new_name})
83 body = json.dumps({'name': new_name})
84 return self._req('PATCH', url_path_join(path, name), body)
84 return self._req('PATCH', url_path_join(path, name), body)
85
85
86 def get_checkpoints(self, name, path):
86 def get_checkpoints(self, name, path):
87 return self._req('GET', url_path_join(path, name, 'checkpoints'))
87 return self._req('GET', url_path_join(path, name, 'checkpoints'))
88
88
89 def new_checkpoint(self, name, path):
89 def new_checkpoint(self, name, path):
90 return self._req('POST', url_path_join(path, name, 'checkpoints'))
90 return self._req('POST', url_path_join(path, name, 'checkpoints'))
91
91
92 def restore_checkpoint(self, name, path, checkpoint_id):
92 def restore_checkpoint(self, name, path, checkpoint_id):
93 return self._req('POST', url_path_join(path, name, 'checkpoints', checkpoint_id))
93 return self._req('POST', url_path_join(path, name, 'checkpoints', checkpoint_id))
94
94
95 def delete_checkpoint(self, name, path, checkpoint_id):
95 def delete_checkpoint(self, name, path, checkpoint_id):
96 return self._req('DELETE', url_path_join(path, name, 'checkpoints', checkpoint_id))
96 return self._req('DELETE', url_path_join(path, name, 'checkpoints', checkpoint_id))
97
97
98 class APITest(NotebookTestBase):
98 class APITest(NotebookTestBase):
99 """Test the kernels web service API"""
99 """Test the kernels web service API"""
100 dirs_nbs = [('', 'inroot'),
100 dirs_nbs = [('', 'inroot'),
101 ('Directory with spaces in', 'inspace'),
101 ('Directory with spaces in', 'inspace'),
102 (u'unicodΓ©', 'innonascii'),
102 (u'unicodΓ©', 'innonascii'),
103 ('foo', 'a'),
103 ('foo', 'a'),
104 ('foo', 'b'),
104 ('foo', 'b'),
105 ('foo', 'name with spaces'),
105 ('foo', 'name with spaces'),
106 ('foo', u'unicodΓ©'),
106 ('foo', u'unicodΓ©'),
107 ('foo/bar', 'baz'),
107 ('foo/bar', 'baz'),
108 ('ordering', 'A'),
108 ('ordering', 'A'),
109 ('ordering', 'b'),
109 ('ordering', 'b'),
110 ('ordering', 'C'),
110 ('ordering', 'C'),
111 (u'Γ₯ b', u'Γ§ d'),
111 (u'Γ₯ b', u'Γ§ d'),
112 ]
112 ]
113 hidden_dirs = ['.hidden', '__pycache__']
113 hidden_dirs = ['.hidden', '__pycache__']
114
114
115 dirs = uniq_stable([py3compat.cast_unicode(d) for (d,n) in dirs_nbs])
115 dirs = uniq_stable([py3compat.cast_unicode(d) for (d,n) in dirs_nbs])
116 del dirs[0] # remove ''
116 del dirs[0] # remove ''
117 top_level_dirs = {normalize('NFC', d.split('/')[0]) for d in dirs}
117 top_level_dirs = {normalize('NFC', d.split('/')[0]) for d in dirs}
118
118
119 @staticmethod
119 @staticmethod
120 def _blob_for_name(name):
120 def _blob_for_name(name):
121 return name.encode('utf-8') + b'\xFF'
121 return name.encode('utf-8') + b'\xFF'
122
122
123 @staticmethod
123 @staticmethod
124 def _txt_for_name(name):
124 def _txt_for_name(name):
125 return u'%s text file' % name
125 return u'%s text file' % name
126
126
127 def setUp(self):
127 def setUp(self):
128 nbdir = self.notebook_dir.name
128 nbdir = self.notebook_dir.name
129 self.blob = os.urandom(100)
129 self.blob = os.urandom(100)
130 self.b64_blob = base64.encodestring(self.blob).decode('ascii')
130 self.b64_blob = base64.encodestring(self.blob).decode('ascii')
131
131
132
132
133
133
134 for d in (self.dirs + self.hidden_dirs):
134 for d in (self.dirs + self.hidden_dirs):
135 d.replace('/', os.sep)
135 d.replace('/', os.sep)
136 if not os.path.isdir(pjoin(nbdir, d)):
136 if not os.path.isdir(pjoin(nbdir, d)):
137 os.mkdir(pjoin(nbdir, d))
137 os.mkdir(pjoin(nbdir, d))
138
138
139 for d, name in self.dirs_nbs:
139 for d, name in self.dirs_nbs:
140 d = d.replace('/', os.sep)
140 d = d.replace('/', os.sep)
141 # create a notebook
141 # create a notebook
142 with io.open(pjoin(nbdir, d, '%s.ipynb' % name), 'w',
142 with io.open(pjoin(nbdir, d, '%s.ipynb' % name), 'w',
143 encoding='utf-8') as f:
143 encoding='utf-8') as f:
144 nb = new_notebook(name=name)
144 nb = new_notebook(name=name)
145 write(nb, f, format='ipynb')
145 write(nb, f, format='ipynb')
146
146
147 # create a text file
147 # create a text file
148 with io.open(pjoin(nbdir, d, '%s.txt' % name), 'w',
148 with io.open(pjoin(nbdir, d, '%s.txt' % name), 'w',
149 encoding='utf-8') as f:
149 encoding='utf-8') as f:
150 f.write(self._txt_for_name(name))
150 f.write(self._txt_for_name(name))
151
151
152 # create a binary file
152 # create a binary file
153 with io.open(pjoin(nbdir, d, '%s.blob' % name), 'wb') as f:
153 with io.open(pjoin(nbdir, d, '%s.blob' % name), 'wb') as f:
154 f.write(self._blob_for_name(name))
154 f.write(self._blob_for_name(name))
155
155
156 self.api = API(self.base_url())
156 self.api = API(self.base_url())
157
157
158 def tearDown(self):
158 def tearDown(self):
159 nbdir = self.notebook_dir.name
159 nbdir = self.notebook_dir.name
160
160
161 for dname in (list(self.top_level_dirs) + self.hidden_dirs):
161 for dname in (list(self.top_level_dirs) + self.hidden_dirs):
162 shutil.rmtree(pjoin(nbdir, dname), ignore_errors=True)
162 shutil.rmtree(pjoin(nbdir, dname), ignore_errors=True)
163
163
164 if os.path.isfile(pjoin(nbdir, 'inroot.ipynb')):
164 if os.path.isfile(pjoin(nbdir, 'inroot.ipynb')):
165 os.unlink(pjoin(nbdir, 'inroot.ipynb'))
165 os.unlink(pjoin(nbdir, 'inroot.ipynb'))
166
166
167 def test_list_notebooks(self):
167 def test_list_notebooks(self):
168 nbs = notebooks_only(self.api.list().json())
168 nbs = notebooks_only(self.api.list().json())
169 self.assertEqual(len(nbs), 1)
169 self.assertEqual(len(nbs), 1)
170 self.assertEqual(nbs[0]['name'], 'inroot.ipynb')
170 self.assertEqual(nbs[0]['name'], 'inroot.ipynb')
171
171
172 nbs = notebooks_only(self.api.list('/Directory with spaces in/').json())
172 nbs = notebooks_only(self.api.list('/Directory with spaces in/').json())
173 self.assertEqual(len(nbs), 1)
173 self.assertEqual(len(nbs), 1)
174 self.assertEqual(nbs[0]['name'], 'inspace.ipynb')
174 self.assertEqual(nbs[0]['name'], 'inspace.ipynb')
175
175
176 nbs = notebooks_only(self.api.list(u'/unicodΓ©/').json())
176 nbs = notebooks_only(self.api.list(u'/unicodΓ©/').json())
177 self.assertEqual(len(nbs), 1)
177 self.assertEqual(len(nbs), 1)
178 self.assertEqual(nbs[0]['name'], 'innonascii.ipynb')
178 self.assertEqual(nbs[0]['name'], 'innonascii.ipynb')
179 self.assertEqual(nbs[0]['path'], u'unicodΓ©')
179 self.assertEqual(nbs[0]['path'], u'unicodΓ©')
180
180
181 nbs = notebooks_only(self.api.list('/foo/bar/').json())
181 nbs = notebooks_only(self.api.list('/foo/bar/').json())
182 self.assertEqual(len(nbs), 1)
182 self.assertEqual(len(nbs), 1)
183 self.assertEqual(nbs[0]['name'], 'baz.ipynb')
183 self.assertEqual(nbs[0]['name'], 'baz.ipynb')
184 self.assertEqual(nbs[0]['path'], 'foo/bar')
184 self.assertEqual(nbs[0]['path'], 'foo/bar')
185
185
186 nbs = notebooks_only(self.api.list('foo').json())
186 nbs = notebooks_only(self.api.list('foo').json())
187 self.assertEqual(len(nbs), 4)
187 self.assertEqual(len(nbs), 4)
188 nbnames = { normalize('NFC', n['name']) for n in nbs }
188 nbnames = { normalize('NFC', n['name']) for n in nbs }
189 expected = [ u'a.ipynb', u'b.ipynb', u'name with spaces.ipynb', u'unicodΓ©.ipynb']
189 expected = [ u'a.ipynb', u'b.ipynb', u'name with spaces.ipynb', u'unicodΓ©.ipynb']
190 expected = { normalize('NFC', name) for name in expected }
190 expected = { normalize('NFC', name) for name in expected }
191 self.assertEqual(nbnames, expected)
191 self.assertEqual(nbnames, expected)
192
192
193 nbs = notebooks_only(self.api.list('ordering').json())
193 nbs = notebooks_only(self.api.list('ordering').json())
194 nbnames = [n['name'] for n in nbs]
194 nbnames = [n['name'] for n in nbs]
195 expected = ['A.ipynb', 'b.ipynb', 'C.ipynb']
195 expected = ['A.ipynb', 'b.ipynb', 'C.ipynb']
196 self.assertEqual(nbnames, expected)
196 self.assertEqual(nbnames, expected)
197
197
198 def test_list_dirs(self):
198 def test_list_dirs(self):
199 dirs = dirs_only(self.api.list().json())
199 dirs = dirs_only(self.api.list().json())
200 dir_names = {normalize('NFC', d['name']) for d in dirs}
200 dir_names = {normalize('NFC', d['name']) for d in dirs}
201 self.assertEqual(dir_names, self.top_level_dirs) # Excluding hidden dirs
201 self.assertEqual(dir_names, self.top_level_dirs) # Excluding hidden dirs
202
202
203 def test_list_nonexistant_dir(self):
203 def test_list_nonexistant_dir(self):
204 with assert_http_error(404):
204 with assert_http_error(404):
205 self.api.list('nonexistant')
205 self.api.list('nonexistant')
206
206
207 def test_get_nb_contents(self):
207 def test_get_nb_contents(self):
208 for d, name in self.dirs_nbs:
208 for d, name in self.dirs_nbs:
209 nb = self.api.read('%s.ipynb' % name, d+'/').json()
209 nb = self.api.read('%s.ipynb' % name, d+'/').json()
210 self.assertEqual(nb['name'], u'%s.ipynb' % name)
210 self.assertEqual(nb['name'], u'%s.ipynb' % name)
211 self.assertEqual(nb['type'], 'notebook')
211 self.assertEqual(nb['type'], 'notebook')
212 self.assertIn('content', nb)
212 self.assertIn('content', nb)
213 self.assertEqual(nb['format'], 'json')
213 self.assertEqual(nb['format'], 'json')
214 self.assertIn('content', nb)
214 self.assertIn('content', nb)
215 self.assertIn('metadata', nb['content'])
215 self.assertIn('metadata', nb['content'])
216 self.assertIsInstance(nb['content']['metadata'], dict)
216 self.assertIsInstance(nb['content']['metadata'], dict)
217
217
218 def test_get_contents_no_such_file(self):
218 def test_get_contents_no_such_file(self):
219 # Name that doesn't exist - should be a 404
219 # Name that doesn't exist - should be a 404
220 with assert_http_error(404):
220 with assert_http_error(404):
221 self.api.read('q.ipynb', 'foo')
221 self.api.read('q.ipynb', 'foo')
222
222
223 def test_get_text_file_contents(self):
223 def test_get_text_file_contents(self):
224 for d, name in self.dirs_nbs:
224 for d, name in self.dirs_nbs:
225 model = self.api.read(u'%s.txt' % name, d+'/').json()
225 model = self.api.read(u'%s.txt' % name, d+'/').json()
226 self.assertEqual(model['name'], u'%s.txt' % name)
226 self.assertEqual(model['name'], u'%s.txt' % name)
227 self.assertIn('content', model)
227 self.assertIn('content', model)
228 self.assertEqual(model['format'], 'text')
228 self.assertEqual(model['format'], 'text')
229 self.assertEqual(model['type'], 'file')
229 self.assertEqual(model['type'], 'file')
230 self.assertEqual(model['content'], self._txt_for_name(name))
230 self.assertEqual(model['content'], self._txt_for_name(name))
231
231
232 # Name that doesn't exist - should be a 404
232 # Name that doesn't exist - should be a 404
233 with assert_http_error(404):
233 with assert_http_error(404):
234 self.api.read('q.txt', 'foo')
234 self.api.read('q.txt', 'foo')
235
235
236 def test_get_binary_file_contents(self):
236 def test_get_binary_file_contents(self):
237 for d, name in self.dirs_nbs:
237 for d, name in self.dirs_nbs:
238 model = self.api.read(u'%s.blob' % name, d+'/').json()
238 model = self.api.read(u'%s.blob' % name, d+'/').json()
239 self.assertEqual(model['name'], u'%s.blob' % name)
239 self.assertEqual(model['name'], u'%s.blob' % name)
240 self.assertIn('content', model)
240 self.assertIn('content', model)
241 self.assertEqual(model['format'], 'base64')
241 self.assertEqual(model['format'], 'base64')
242 self.assertEqual(model['type'], 'file')
242 self.assertEqual(model['type'], 'file')
243 b64_data = base64.encodestring(self._blob_for_name(name)).decode('ascii')
243 b64_data = base64.encodestring(self._blob_for_name(name)).decode('ascii')
244 self.assertEqual(model['content'], b64_data)
244 self.assertEqual(model['content'], b64_data)
245
245
246 # Name that doesn't exist - should be a 404
246 # Name that doesn't exist - should be a 404
247 with assert_http_error(404):
247 with assert_http_error(404):
248 self.api.read('q.txt', 'foo')
248 self.api.read('q.txt', 'foo')
249
249
250 def _check_nb_created(self, resp, name, path):
250 def _check_created(self, resp, name, path, type='notebook'):
251 self.assertEqual(resp.status_code, 201)
251 self.assertEqual(resp.status_code, 201)
252 location_header = py3compat.str_to_unicode(resp.headers['Location'])
252 location_header = py3compat.str_to_unicode(resp.headers['Location'])
253 self.assertEqual(location_header, url_escape(url_path_join(u'/api/contents', path, name)))
253 self.assertEqual(location_header, url_escape(url_path_join(u'/api/contents', path, name)))
254 self.assertEqual(resp.json()['name'], name)
254 rjson = resp.json()
255 assert os.path.isfile(pjoin(
255 self.assertEqual(rjson['name'], name)
256 self.assertEqual(rjson['path'], path)
257 self.assertEqual(rjson['type'], type)
258 isright = os.path.isdir if type == 'directory' else os.path.isfile
259 assert isright(pjoin(
256 self.notebook_dir.name,
260 self.notebook_dir.name,
257 path.replace('/', os.sep),
261 path.replace('/', os.sep),
258 name,
262 name,
259 ))
263 ))
260
264
261 def test_create_untitled(self):
265 def test_create_untitled(self):
262 resp = self.api.create_untitled(path=u'Γ₯ b')
266 resp = self.api.create_untitled(path=u'Γ₯ b')
263 self._check_nb_created(resp, 'Untitled0.ipynb', u'Γ₯ b')
267 self._check_created(resp, 'Untitled0.ipynb', u'Γ₯ b')
264
268
265 # Second time
269 # Second time
266 resp = self.api.create_untitled(path=u'Γ₯ b')
270 resp = self.api.create_untitled(path=u'Γ₯ b')
267 self._check_nb_created(resp, 'Untitled1.ipynb', u'Γ₯ b')
271 self._check_created(resp, 'Untitled1.ipynb', u'Γ₯ b')
268
272
269 # And two directories down
273 # And two directories down
270 resp = self.api.create_untitled(path='foo/bar')
274 resp = self.api.create_untitled(path='foo/bar')
271 self._check_nb_created(resp, 'Untitled0.ipynb', 'foo/bar')
275 self._check_created(resp, 'Untitled0.ipynb', 'foo/bar')
272
276
273 def test_create_untitled_txt(self):
277 def test_create_untitled_txt(self):
274 resp = self.api.create_untitled(path='foo/bar', ext='.txt')
278 resp = self.api.create_untitled(path='foo/bar', ext='.txt')
275 self._check_nb_created(resp, 'Untitled0.txt', 'foo/bar')
279 self._check_created(resp, 'Untitled0.txt', 'foo/bar', type='file')
276
280
277 resp = self.api.read(path='foo/bar', name='Untitled0.txt')
281 resp = self.api.read(path='foo/bar', name='Untitled0.txt')
278 model = resp.json()
282 model = resp.json()
279 self.assertEqual(model['type'], 'file')
283 self.assertEqual(model['type'], 'file')
280 self.assertEqual(model['format'], 'text')
284 self.assertEqual(model['format'], 'text')
281 self.assertEqual(model['content'], '')
285 self.assertEqual(model['content'], '')
282
286
283 def test_upload_untitled(self):
287 def test_upload_untitled(self):
284 nb = new_notebook(name='Upload test')
288 nb = new_notebook(name='Upload test')
285 nbmodel = {'content': nb, 'type': 'notebook'}
289 nbmodel = {'content': nb, 'type': 'notebook'}
286 resp = self.api.upload_untitled(path=u'Γ₯ b',
290 resp = self.api.upload_untitled(path=u'Γ₯ b',
287 body=json.dumps(nbmodel))
291 body=json.dumps(nbmodel))
288 self._check_nb_created(resp, 'Untitled0.ipynb', u'Γ₯ b')
292 self._check_created(resp, 'Untitled0.ipynb', u'Γ₯ b')
289
293
290 def test_upload(self):
294 def test_upload(self):
291 nb = new_notebook(name=u'ignored')
295 nb = new_notebook(name=u'ignored')
292 nbmodel = {'content': nb, 'type': 'notebook'}
296 nbmodel = {'content': nb, 'type': 'notebook'}
293 resp = self.api.upload(u'Upload tΓ©st.ipynb', path=u'Γ₯ b',
297 resp = self.api.upload(u'Upload tΓ©st.ipynb', path=u'Γ₯ b',
294 body=json.dumps(nbmodel))
298 body=json.dumps(nbmodel))
295 self._check_nb_created(resp, u'Upload tΓ©st.ipynb', u'Γ₯ b')
299 self._check_created(resp, u'Upload tΓ©st.ipynb', u'Γ₯ b')
300
301 def test_mkdir(self):
302 model = {'type': 'directory'}
303 resp = self.api.upload(u'New βˆ‚ir', path=u'Γ₯ b',
304 body=json.dumps(model))
305 self._check_created(resp, u'New βˆ‚ir', u'Γ₯ b', type='directory')
296
306
297 def test_upload_txt(self):
307 def test_upload_txt(self):
298 body = u'ΓΌnicode tΓ©xt'
308 body = u'ΓΌnicode tΓ©xt'
299 model = {
309 model = {
300 'content' : body,
310 'content' : body,
301 'format' : 'text',
311 'format' : 'text',
302 'type' : 'file',
312 'type' : 'file',
303 }
313 }
304 resp = self.api.upload(u'Upload tΓ©st.txt', path=u'Γ₯ b',
314 resp = self.api.upload(u'Upload tΓ©st.txt', path=u'Γ₯ b',
305 body=json.dumps(model))
315 body=json.dumps(model))
306
316
307 # check roundtrip
317 # check roundtrip
308 resp = self.api.read(path=u'Γ₯ b', name=u'Upload tΓ©st.txt')
318 resp = self.api.read(path=u'Γ₯ b', name=u'Upload tΓ©st.txt')
309 model = resp.json()
319 model = resp.json()
310 self.assertEqual(model['type'], 'file')
320 self.assertEqual(model['type'], 'file')
311 self.assertEqual(model['format'], 'text')
321 self.assertEqual(model['format'], 'text')
312 self.assertEqual(model['content'], body)
322 self.assertEqual(model['content'], body)
313
323
314 def test_upload_b64(self):
324 def test_upload_b64(self):
315 body = b'\xFFblob'
325 body = b'\xFFblob'
316 b64body = base64.encodestring(body).decode('ascii')
326 b64body = base64.encodestring(body).decode('ascii')
317 model = {
327 model = {
318 'content' : b64body,
328 'content' : b64body,
319 'format' : 'base64',
329 'format' : 'base64',
320 'type' : 'file',
330 'type' : 'file',
321 }
331 }
322 resp = self.api.upload(u'Upload tΓ©st.blob', path=u'Γ₯ b',
332 resp = self.api.upload(u'Upload tΓ©st.blob', path=u'Γ₯ b',
323 body=json.dumps(model))
333 body=json.dumps(model))
324
334
325 # check roundtrip
335 # check roundtrip
326 resp = self.api.read(path=u'Γ₯ b', name=u'Upload tΓ©st.blob')
336 resp = self.api.read(path=u'Γ₯ b', name=u'Upload tΓ©st.blob')
327 model = resp.json()
337 model = resp.json()
328 self.assertEqual(model['type'], 'file')
338 self.assertEqual(model['type'], 'file')
329 self.assertEqual(model['format'], 'base64')
339 self.assertEqual(model['format'], 'base64')
330 decoded = base64.decodestring(model['content'].encode('ascii'))
340 decoded = base64.decodestring(model['content'].encode('ascii'))
331 self.assertEqual(decoded, body)
341 self.assertEqual(decoded, body)
332
342
333 def test_upload_v2(self):
343 def test_upload_v2(self):
334 nb = v2.new_notebook()
344 nb = v2.new_notebook()
335 ws = v2.new_worksheet()
345 ws = v2.new_worksheet()
336 nb.worksheets.append(ws)
346 nb.worksheets.append(ws)
337 ws.cells.append(v2.new_code_cell(input='print("hi")'))
347 ws.cells.append(v2.new_code_cell(input='print("hi")'))
338 nbmodel = {'content': nb, 'type': 'notebook'}
348 nbmodel = {'content': nb, 'type': 'notebook'}
339 resp = self.api.upload(u'Upload tΓ©st.ipynb', path=u'Γ₯ b',
349 resp = self.api.upload(u'Upload tΓ©st.ipynb', path=u'Γ₯ b',
340 body=json.dumps(nbmodel))
350 body=json.dumps(nbmodel))
341 self._check_nb_created(resp, u'Upload tΓ©st.ipynb', u'Γ₯ b')
351 self._check_created(resp, u'Upload tΓ©st.ipynb', u'Γ₯ b')
342 resp = self.api.read(u'Upload tΓ©st.ipynb', u'Γ₯ b')
352 resp = self.api.read(u'Upload tΓ©st.ipynb', u'Γ₯ b')
343 data = resp.json()
353 data = resp.json()
344 self.assertEqual(data['content']['nbformat'], current.nbformat)
354 self.assertEqual(data['content']['nbformat'], current.nbformat)
345 self.assertEqual(data['content']['orig_nbformat'], 2)
355 self.assertEqual(data['content']['orig_nbformat'], 2)
346
356
347 def test_copy_untitled(self):
357 def test_copy_untitled(self):
348 resp = self.api.copy_untitled(u'Γ§ d.ipynb', path=u'Γ₯ b')
358 resp = self.api.copy_untitled(u'Γ§ d.ipynb', path=u'Γ₯ b')
349 self._check_nb_created(resp, u'Γ§ d-Copy0.ipynb', u'Γ₯ b')
359 self._check_created(resp, u'Γ§ d-Copy0.ipynb', u'Γ₯ b')
350
360
351 def test_copy(self):
361 def test_copy(self):
352 resp = self.api.copy(u'Γ§ d.ipynb', u'cΓΈpy.ipynb', path=u'Γ₯ b')
362 resp = self.api.copy(u'Γ§ d.ipynb', u'cΓΈpy.ipynb', path=u'Γ₯ b')
353 self._check_nb_created(resp, u'cΓΈpy.ipynb', u'Γ₯ b')
363 self._check_created(resp, u'cΓΈpy.ipynb', u'Γ₯ b')
354
364
355 def test_delete(self):
365 def test_delete(self):
356 for d, name in self.dirs_nbs:
366 for d, name in self.dirs_nbs:
357 resp = self.api.delete('%s.ipynb' % name, d)
367 resp = self.api.delete('%s.ipynb' % name, d)
358 self.assertEqual(resp.status_code, 204)
368 self.assertEqual(resp.status_code, 204)
359
369
360 for d in self.dirs + ['/']:
370 for d in self.dirs + ['/']:
361 nbs = notebooks_only(self.api.list(d).json())
371 nbs = notebooks_only(self.api.list(d).json())
362 self.assertEqual(len(nbs), 0)
372 self.assertEqual(len(nbs), 0)
363
373
364 def test_rename(self):
374 def test_rename(self):
365 resp = self.api.rename('a.ipynb', 'foo', 'z.ipynb')
375 resp = self.api.rename('a.ipynb', 'foo', 'z.ipynb')
366 self.assertEqual(resp.headers['Location'].split('/')[-1], 'z.ipynb')
376 self.assertEqual(resp.headers['Location'].split('/')[-1], 'z.ipynb')
367 self.assertEqual(resp.json()['name'], 'z.ipynb')
377 self.assertEqual(resp.json()['name'], 'z.ipynb')
368 assert os.path.isfile(pjoin(self.notebook_dir.name, 'foo', 'z.ipynb'))
378 assert os.path.isfile(pjoin(self.notebook_dir.name, 'foo', 'z.ipynb'))
369
379
370 nbs = notebooks_only(self.api.list('foo').json())
380 nbs = notebooks_only(self.api.list('foo').json())
371 nbnames = set(n['name'] for n in nbs)
381 nbnames = set(n['name'] for n in nbs)
372 self.assertIn('z.ipynb', nbnames)
382 self.assertIn('z.ipynb', nbnames)
373 self.assertNotIn('a.ipynb', nbnames)
383 self.assertNotIn('a.ipynb', nbnames)
374
384
375 def test_rename_existing(self):
385 def test_rename_existing(self):
376 with assert_http_error(409):
386 with assert_http_error(409):
377 self.api.rename('a.ipynb', 'foo', 'b.ipynb')
387 self.api.rename('a.ipynb', 'foo', 'b.ipynb')
378
388
379 def test_save(self):
389 def test_save(self):
380 resp = self.api.read('a.ipynb', 'foo')
390 resp = self.api.read('a.ipynb', 'foo')
381 nbcontent = json.loads(resp.text)['content']
391 nbcontent = json.loads(resp.text)['content']
382 nb = to_notebook_json(nbcontent)
392 nb = to_notebook_json(nbcontent)
383 ws = new_worksheet()
393 ws = new_worksheet()
384 nb.worksheets = [ws]
394 nb.worksheets = [ws]
385 ws.cells.append(new_heading_cell(u'Created by test Β³'))
395 ws.cells.append(new_heading_cell(u'Created by test Β³'))
386
396
387 nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb, 'type': 'notebook'}
397 nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb, 'type': 'notebook'}
388 resp = self.api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
398 resp = self.api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
389
399
390 nbfile = pjoin(self.notebook_dir.name, 'foo', 'a.ipynb')
400 nbfile = pjoin(self.notebook_dir.name, 'foo', 'a.ipynb')
391 with io.open(nbfile, 'r', encoding='utf-8') as f:
401 with io.open(nbfile, 'r', encoding='utf-8') as f:
392 newnb = read(f, format='ipynb')
402 newnb = read(f, format='ipynb')
393 self.assertEqual(newnb.worksheets[0].cells[0].source,
403 self.assertEqual(newnb.worksheets[0].cells[0].source,
394 u'Created by test Β³')
404 u'Created by test Β³')
395 nbcontent = self.api.read('a.ipynb', 'foo').json()['content']
405 nbcontent = self.api.read('a.ipynb', 'foo').json()['content']
396 newnb = to_notebook_json(nbcontent)
406 newnb = to_notebook_json(nbcontent)
397 self.assertEqual(newnb.worksheets[0].cells[0].source,
407 self.assertEqual(newnb.worksheets[0].cells[0].source,
398 u'Created by test Β³')
408 u'Created by test Β³')
399
409
400 # Save and rename
410 # Save and rename
401 nbmodel= {'name': 'a2.ipynb', 'path':'foo/bar', 'content': nb, 'type': 'notebook'}
411 nbmodel= {'name': 'a2.ipynb', 'path':'foo/bar', 'content': nb, 'type': 'notebook'}
402 resp = self.api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
412 resp = self.api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
403 saved = resp.json()
413 saved = resp.json()
404 self.assertEqual(saved['name'], 'a2.ipynb')
414 self.assertEqual(saved['name'], 'a2.ipynb')
405 self.assertEqual(saved['path'], 'foo/bar')
415 self.assertEqual(saved['path'], 'foo/bar')
406 assert os.path.isfile(pjoin(self.notebook_dir.name,'foo','bar','a2.ipynb'))
416 assert os.path.isfile(pjoin(self.notebook_dir.name,'foo','bar','a2.ipynb'))
407 assert not os.path.isfile(pjoin(self.notebook_dir.name, 'foo', 'a.ipynb'))
417 assert not os.path.isfile(pjoin(self.notebook_dir.name, 'foo', 'a.ipynb'))
408 with assert_http_error(404):
418 with assert_http_error(404):
409 self.api.read('a.ipynb', 'foo')
419 self.api.read('a.ipynb', 'foo')
410
420
411 def test_checkpoints(self):
421 def test_checkpoints(self):
412 resp = self.api.read('a.ipynb', 'foo')
422 resp = self.api.read('a.ipynb', 'foo')
413 r = self.api.new_checkpoint('a.ipynb', 'foo')
423 r = self.api.new_checkpoint('a.ipynb', 'foo')
414 self.assertEqual(r.status_code, 201)
424 self.assertEqual(r.status_code, 201)
415 cp1 = r.json()
425 cp1 = r.json()
416 self.assertEqual(set(cp1), {'id', 'last_modified'})
426 self.assertEqual(set(cp1), {'id', 'last_modified'})
417 self.assertEqual(r.headers['Location'].split('/')[-1], cp1['id'])
427 self.assertEqual(r.headers['Location'].split('/')[-1], cp1['id'])
418
428
419 # Modify it
429 # Modify it
420 nbcontent = json.loads(resp.text)['content']
430 nbcontent = json.loads(resp.text)['content']
421 nb = to_notebook_json(nbcontent)
431 nb = to_notebook_json(nbcontent)
422 ws = new_worksheet()
432 ws = new_worksheet()
423 nb.worksheets = [ws]
433 nb.worksheets = [ws]
424 hcell = new_heading_cell('Created by test')
434 hcell = new_heading_cell('Created by test')
425 ws.cells.append(hcell)
435 ws.cells.append(hcell)
426 # Save
436 # Save
427 nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb, 'type': 'notebook'}
437 nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb, 'type': 'notebook'}
428 resp = self.api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
438 resp = self.api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
429
439
430 # List checkpoints
440 # List checkpoints
431 cps = self.api.get_checkpoints('a.ipynb', 'foo').json()
441 cps = self.api.get_checkpoints('a.ipynb', 'foo').json()
432 self.assertEqual(cps, [cp1])
442 self.assertEqual(cps, [cp1])
433
443
434 nbcontent = self.api.read('a.ipynb', 'foo').json()['content']
444 nbcontent = self.api.read('a.ipynb', 'foo').json()['content']
435 nb = to_notebook_json(nbcontent)
445 nb = to_notebook_json(nbcontent)
436 self.assertEqual(nb.worksheets[0].cells[0].source, 'Created by test')
446 self.assertEqual(nb.worksheets[0].cells[0].source, 'Created by test')
437
447
438 # Restore cp1
448 # Restore cp1
439 r = self.api.restore_checkpoint('a.ipynb', 'foo', cp1['id'])
449 r = self.api.restore_checkpoint('a.ipynb', 'foo', cp1['id'])
440 self.assertEqual(r.status_code, 204)
450 self.assertEqual(r.status_code, 204)
441 nbcontent = self.api.read('a.ipynb', 'foo').json()['content']
451 nbcontent = self.api.read('a.ipynb', 'foo').json()['content']
442 nb = to_notebook_json(nbcontent)
452 nb = to_notebook_json(nbcontent)
443 self.assertEqual(nb.worksheets, [])
453 self.assertEqual(nb.worksheets, [])
444
454
445 # Delete cp1
455 # Delete cp1
446 r = self.api.delete_checkpoint('a.ipynb', 'foo', cp1['id'])
456 r = self.api.delete_checkpoint('a.ipynb', 'foo', cp1['id'])
447 self.assertEqual(r.status_code, 204)
457 self.assertEqual(r.status_code, 204)
448 cps = self.api.get_checkpoints('a.ipynb', 'foo').json()
458 cps = self.api.get_checkpoints('a.ipynb', 'foo').json()
449 self.assertEqual(cps, [])
459 self.assertEqual(cps, [])
General Comments 0
You need to be logged in to leave comments. Login now