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