Show More
@@ -357,6 +357,7 class FileContentsManager(ContentsManager): | |||||
357 | if model['content'] is None: |
|
357 | if model['content'] is None: | |
358 | model['content'] = base64.encodestring(bcontent).decode('ascii') |
|
358 | model['content'] = base64.encodestring(bcontent).decode('ascii') | |
359 | model['format'] = 'base64' |
|
359 | model['format'] = 'base64' | |
|
360 | if model['format'] == 'base64': | |||
360 | default_mime = 'application/octet-stream' |
|
361 | default_mime = 'application/octet-stream' | |
361 |
|
362 | |||
362 | model['mimetype'] = mimetypes.guess_type(os_path)[0] or default_mime |
|
363 | model['mimetype'] = mimetypes.guess_type(os_path)[0] or default_mime |
@@ -347,6 +347,9 class ContentsManager(LoggingConfigurable): | |||||
347 | from_path must be a full path to a file. |
|
347 | from_path must be a full path to a file. | |
348 | """ |
|
348 | """ | |
349 | path = from_path.strip('/') |
|
349 | path = from_path.strip('/') | |
|
350 | if to_path is not None: | |||
|
351 | to_path = to_path.strip('/') | |||
|
352 | ||||
350 | if '/' in path: |
|
353 | if '/' in path: | |
351 | from_dir, from_name = path.rsplit('/', 1) |
|
354 | from_dir, from_name = path.rsplit('/', 1) | |
352 | else: |
|
355 | else: | |
@@ -359,7 +362,7 class ContentsManager(LoggingConfigurable): | |||||
359 | if model['type'] == 'directory': |
|
362 | if model['type'] == 'directory': | |
360 | raise HTTPError(400, "Can't copy directories") |
|
363 | raise HTTPError(400, "Can't copy directories") | |
361 |
|
364 | |||
362 |
if |
|
365 | if to_path is None: | |
363 | to_path = from_dir |
|
366 | to_path = from_dir | |
364 | if self.dir_exists(to_path): |
|
367 | if self.dir_exists(to_path): | |
365 | name = copy_pat.sub(u'.', from_name) |
|
368 | name = copy_pat.sub(u'.', from_name) |
@@ -12,7 +12,7 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, to_os_path | |
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 read, write, from_dict |
|
17 | from IPython.nbformat import read, write, from_dict | |
18 | from IPython.nbformat.v4 import ( |
|
18 | from IPython.nbformat.v4 import ( | |
@@ -73,9 +73,6 class API(object): | |||||
73 | def upload(self, path, body): |
|
73 | def upload(self, path, body): | |
74 | return self._req('PUT', path, body) |
|
74 | return self._req('PUT', path, body) | |
75 |
|
75 | |||
76 | def mkdir_untitled(self, path='/'): |
|
|||
77 | return self._req('POST', path, json.dumps({'type': 'directory'})) |
|
|||
78 |
|
||||
79 | def mkdir(self, path='/'): |
|
76 | def mkdir(self, path='/'): | |
80 | return self._req('PUT', path, json.dumps({'type': 'directory'})) |
|
77 | return self._req('PUT', path, json.dumps({'type': 'directory'})) | |
81 |
|
78 | |||
@@ -122,8 +119,8 class APITest(NotebookTestBase): | |||||
122 | ] |
|
119 | ] | |
123 | hidden_dirs = ['.hidden', '__pycache__'] |
|
120 | hidden_dirs = ['.hidden', '__pycache__'] | |
124 |
|
121 | |||
125 | dirs = uniq_stable([py3compat.cast_unicode(d) for (d,n) in dirs_nbs]) |
|
122 | # Don't include root dir. | |
126 | del dirs[0] # remove '' |
|
123 | dirs = uniq_stable([py3compat.cast_unicode(d) for (d,n) in dirs_nbs[1:]]) | |
127 | top_level_dirs = {normalize('NFC', d.split('/')[0]) for d in dirs} |
|
124 | top_level_dirs = {normalize('NFC', d.split('/')[0]) for d in dirs} | |
128 |
|
125 | |||
129 | @staticmethod |
|
126 | @staticmethod | |
@@ -133,44 +130,77 class APITest(NotebookTestBase): | |||||
133 | @staticmethod |
|
130 | @staticmethod | |
134 | def _txt_for_name(name): |
|
131 | def _txt_for_name(name): | |
135 | return u'%s text file' % name |
|
132 | return u'%s text file' % name | |
136 |
|
133 | |||
|
134 | def to_os_path(self, api_path): | |||
|
135 | return to_os_path(api_path, root=self.notebook_dir.name) | |||
|
136 | ||||
|
137 | def make_dir(self, api_path): | |||
|
138 | """Create a directory at api_path""" | |||
|
139 | os_path = self.to_os_path(api_path) | |||
|
140 | try: | |||
|
141 | os.makedirs(os_path) | |||
|
142 | except OSError: | |||
|
143 | print("Directory already exists: %r" % os_path) | |||
|
144 | ||||
|
145 | def make_txt(self, api_path, txt): | |||
|
146 | """Make a text file at a given api_path""" | |||
|
147 | os_path = self.to_os_path(api_path) | |||
|
148 | with io.open(os_path, 'w', encoding='utf-8') as f: | |||
|
149 | f.write(txt) | |||
|
150 | ||||
|
151 | def make_blob(self, api_path, blob): | |||
|
152 | """Make a binary file at a given api_path""" | |||
|
153 | os_path = self.to_os_path(api_path) | |||
|
154 | with io.open(os_path, 'wb') as f: | |||
|
155 | f.write(blob) | |||
|
156 | ||||
|
157 | def make_nb(self, api_path, nb): | |||
|
158 | """Make a notebook file at a given api_path""" | |||
|
159 | os_path = self.to_os_path(api_path) | |||
|
160 | ||||
|
161 | with io.open(os_path, 'w', encoding='utf-8') as f: | |||
|
162 | write(nb, f, version=4) | |||
|
163 | ||||
|
164 | def delete_dir(self, api_path): | |||
|
165 | """Delete a directory at api_path, removing any contents.""" | |||
|
166 | os_path = self.to_os_path(api_path) | |||
|
167 | shutil.rmtree(os_path, ignore_errors=True) | |||
|
168 | ||||
|
169 | def delete_file(self, api_path): | |||
|
170 | """Delete a file at the given path if it exists.""" | |||
|
171 | if self.isfile(api_path): | |||
|
172 | os.unlink(self.to_os_path(api_path)) | |||
|
173 | ||||
|
174 | def isfile(self, api_path): | |||
|
175 | return os.path.isfile(self.to_os_path(api_path)) | |||
|
176 | ||||
|
177 | def isdir(self, api_path): | |||
|
178 | return os.path.isdir(self.to_os_path(api_path)) | |||
|
179 | ||||
137 | def setUp(self): |
|
180 | def setUp(self): | |
138 | nbdir = self.notebook_dir.name |
|
|||
139 | self.blob = os.urandom(100) |
|
|||
140 | self.b64_blob = base64.encodestring(self.blob).decode('ascii') |
|
|||
141 |
|
181 | |||
142 | for d in (self.dirs + self.hidden_dirs): |
|
182 | for d in (self.dirs + self.hidden_dirs): | |
143 | d.replace('/', os.sep) |
|
183 | self.make_dir(d) | |
144 | if not os.path.isdir(pjoin(nbdir, d)): |
|
|||
145 | os.mkdir(pjoin(nbdir, d)) |
|
|||
146 |
|
184 | |||
147 | for d, name in self.dirs_nbs: |
|
185 | for d, name in self.dirs_nbs: | |
148 | d = d.replace('/', os.sep) |
|
|||
149 | # create a notebook |
|
186 | # create a notebook | |
150 | with io.open(pjoin(nbdir, d, '%s.ipynb' % name), 'w', |
|
187 | nb = new_notebook() | |
151 | encoding='utf-8') as f: |
|
188 | self.make_nb(u'{}/{}.ipynb'.format(d, name), nb) | |
152 | nb = new_notebook() |
|
189 | ||
153 | write(nb, f, version=4) |
|
|||
154 |
|
||||
155 | # create a text file |
|
190 | # create a text file | |
156 | with io.open(pjoin(nbdir, d, '%s.txt' % name), 'w', |
|
191 | txt = self._txt_for_name(name) | |
157 | encoding='utf-8') as f: |
|
192 | self.make_txt(u'{}/{}.txt'.format(d, name), txt) | |
158 | f.write(self._txt_for_name(name)) |
|
193 | ||
159 |
|
||||
160 | # create a binary file |
|
194 | # create a binary file | |
161 | with io.open(pjoin(nbdir, d, '%s.blob' % name), 'wb') as f: |
|
195 | blob = self._blob_for_name(name) | |
162 | f.write(self._blob_for_name(name)) |
|
196 | self.make_blob(u'{}/{}.blob'.format(d, name), blob) | |
163 |
|
197 | |||
164 | self.api = API(self.base_url()) |
|
198 | self.api = API(self.base_url()) | |
165 |
|
199 | |||
166 | def tearDown(self): |
|
200 | def tearDown(self): | |
167 | nbdir = self.notebook_dir.name |
|
|||
168 |
|
||||
169 | for dname in (list(self.top_level_dirs) + self.hidden_dirs): |
|
201 | for dname in (list(self.top_level_dirs) + self.hidden_dirs): | |
170 | shutil.rmtree(pjoin(nbdir, dname), ignore_errors=True) |
|
202 | self.delete_dir(dname) | |
171 |
|
203 | self.delete_file('inroot.ipynb') | ||
172 | if os.path.isfile(pjoin(nbdir, 'inroot.ipynb')): |
|
|||
173 | os.unlink(pjoin(nbdir, 'inroot.ipynb')) |
|
|||
174 |
|
204 | |||
175 | def test_list_notebooks(self): |
|
205 | def test_list_notebooks(self): | |
176 | nbs = notebooks_only(self.api.list().json()) |
|
206 | nbs = notebooks_only(self.api.list().json()) | |
@@ -204,11 +234,8 class APITest(NotebookTestBase): | |||||
204 | self.assertEqual(nbnames, expected) |
|
234 | self.assertEqual(nbnames, expected) | |
205 |
|
235 | |||
206 | def test_list_dirs(self): |
|
236 | def test_list_dirs(self): | |
207 | print(self.api.list().json()) |
|
|||
208 | dirs = dirs_only(self.api.list().json()) |
|
237 | dirs = dirs_only(self.api.list().json()) | |
209 | dir_names = {normalize('NFC', d['name']) for d in dirs} |
|
238 | dir_names = {normalize('NFC', d['name']) for d in dirs} | |
210 | print(dir_names) |
|
|||
211 | print(self.top_level_dirs) |
|
|||
212 | self.assertEqual(dir_names, self.top_level_dirs) # Excluding hidden dirs |
|
239 | self.assertEqual(dir_names, self.top_level_dirs) # Excluding hidden dirs | |
213 |
|
240 | |||
214 | def test_list_nonexistant_dir(self): |
|
241 | def test_list_nonexistant_dir(self): | |
@@ -261,8 +288,10 class APITest(NotebookTestBase): | |||||
261 | self.assertIn('content', model) |
|
288 | self.assertIn('content', model) | |
262 | self.assertEqual(model['format'], 'base64') |
|
289 | self.assertEqual(model['format'], 'base64') | |
263 | self.assertEqual(model['type'], 'file') |
|
290 | self.assertEqual(model['type'], 'file') | |
264 | b64_data = base64.encodestring(self._blob_for_name(name)).decode('ascii') |
|
291 | self.assertEqual( | |
265 | self.assertEqual(model['content'], b64_data) |
|
292 | base64.decodestring(model['content'].encode('ascii')), | |
|
293 | self._blob_for_name(name), | |||
|
294 | ) | |||
266 |
|
295 | |||
267 | # Name that doesn't exist - should be a 404 |
|
296 | # Name that doesn't exist - should be a 404 | |
268 | with assert_http_error(404): |
|
297 | with assert_http_error(404): | |
@@ -283,11 +312,8 class APITest(NotebookTestBase): | |||||
283 | self.assertEqual(rjson['name'], path.rsplit('/', 1)[-1]) |
|
312 | self.assertEqual(rjson['name'], path.rsplit('/', 1)[-1]) | |
284 | self.assertEqual(rjson['path'], path) |
|
313 | self.assertEqual(rjson['path'], path) | |
285 | self.assertEqual(rjson['type'], type) |
|
314 | self.assertEqual(rjson['type'], type) | |
286 |
isright = |
|
315 | isright = self.isdir if type == 'directory' else self.isfile | |
287 |
assert isright(p |
|
316 | assert isright(path) | |
288 | self.notebook_dir.name, |
|
|||
289 | path.replace('/', os.sep), |
|
|||
290 | )) |
|
|||
291 |
|
317 | |||
292 | def test_create_untitled(self): |
|
318 | def test_create_untitled(self): | |
293 | resp = self.api.create_untitled(path=u'å b') |
|
319 | resp = self.api.create_untitled(path=u'å b') | |
@@ -451,7 +477,7 class APITest(NotebookTestBase): | |||||
451 | self.assertEqual(resp.headers['Location'].split('/')[-1], 'z.ipynb') |
|
477 | self.assertEqual(resp.headers['Location'].split('/')[-1], 'z.ipynb') | |
452 | self.assertEqual(resp.json()['name'], 'z.ipynb') |
|
478 | self.assertEqual(resp.json()['name'], 'z.ipynb') | |
453 | self.assertEqual(resp.json()['path'], 'foo/z.ipynb') |
|
479 | self.assertEqual(resp.json()['path'], 'foo/z.ipynb') | |
454 |
assert |
|
480 | assert self.isfile('foo/z.ipynb') | |
455 |
|
481 | |||
456 | nbs = notebooks_only(self.api.list('foo').json()) |
|
482 | nbs = notebooks_only(self.api.list('foo').json()) | |
457 | nbnames = set(n['name'] for n in nbs) |
|
483 | nbnames = set(n['name'] for n in nbs) | |
@@ -471,11 +497,6 class APITest(NotebookTestBase): | |||||
471 | nbmodel= {'content': nb, 'type': 'notebook'} |
|
497 | nbmodel= {'content': nb, 'type': 'notebook'} | |
472 | resp = self.api.save('foo/a.ipynb', body=json.dumps(nbmodel)) |
|
498 | resp = self.api.save('foo/a.ipynb', body=json.dumps(nbmodel)) | |
473 |
|
499 | |||
474 | nbfile = pjoin(self.notebook_dir.name, 'foo', 'a.ipynb') |
|
|||
475 | with io.open(nbfile, 'r', encoding='utf-8') as f: |
|
|||
476 | newnb = read(f, as_version=4) |
|
|||
477 | self.assertEqual(newnb.cells[0].source, |
|
|||
478 | u'Created by test ³') |
|
|||
479 | nbcontent = self.api.read('foo/a.ipynb').json()['content'] |
|
500 | nbcontent = self.api.read('foo/a.ipynb').json()['content'] | |
480 | newnb = from_dict(nbcontent) |
|
501 | newnb = from_dict(nbcontent) | |
481 | self.assertEqual(newnb.cells[0].source, |
|
502 | self.assertEqual(newnb.cells[0].source, |
@@ -2,7 +2,6 | |||||
2 | """Tests for the notebook manager.""" |
|
2 | """Tests for the notebook manager.""" | |
3 | from __future__ import print_function |
|
3 | from __future__ import print_function | |
4 |
|
4 | |||
5 | import logging |
|
|||
6 | import os |
|
5 | import os | |
7 |
|
6 | |||
8 | from tornado.web import HTTPError |
|
7 | from tornado.web import HTTPError | |
@@ -17,11 +16,31 from IPython.html.utils import url_path_join | |||||
17 | from IPython.testing import decorators as dec |
|
16 | from IPython.testing import decorators as dec | |
18 |
|
17 | |||
19 | from ..filemanager import FileContentsManager |
|
18 | from ..filemanager import FileContentsManager | |
20 | from ..manager import ContentsManager |
|
19 | ||
|
20 | ||||
|
21 | def _make_dir(contents_manager, api_path): | |||
|
22 | """ | |||
|
23 | Make a directory. | |||
|
24 | """ | |||
|
25 | os_path = contents_manager._get_os_path(api_path) | |||
|
26 | try: | |||
|
27 | os.makedirs(os_path) | |||
|
28 | except OSError: | |||
|
29 | print("Directory already exists: %r" % os_path) | |||
21 |
|
30 | |||
22 |
|
31 | |||
23 | class TestFileContentsManager(TestCase): |
|
32 | class TestFileContentsManager(TestCase): | |
24 |
|
33 | |||
|
34 | def symlink(self, contents_manager, src, dst): | |||
|
35 | """Make a symlink to src from dst | |||
|
36 | ||||
|
37 | src and dst are api_paths | |||
|
38 | """ | |||
|
39 | src_os_path = contents_manager._get_os_path(src) | |||
|
40 | dst_os_path = contents_manager._get_os_path(dst) | |||
|
41 | print(src_os_path, dst_os_path, os.path.isfile(src_os_path)) | |||
|
42 | os.symlink(src_os_path, dst_os_path) | |||
|
43 | ||||
25 | def test_root_dir(self): |
|
44 | def test_root_dir(self): | |
26 | with TemporaryDirectory() as td: |
|
45 | with TemporaryDirectory() as td: | |
27 | fm = FileContentsManager(root_dir=td) |
|
46 | fm = FileContentsManager(root_dir=td) | |
@@ -69,10 +88,44 class TestFileContentsManager(TestCase): | |||||
69 | self.assertNotEqual(cp_dir, cp_subdir) |
|
88 | self.assertNotEqual(cp_dir, cp_subdir) | |
70 | self.assertEqual(cp_dir, os.path.join(root, fm.checkpoint_dir, cp_name)) |
|
89 | self.assertEqual(cp_dir, os.path.join(root, fm.checkpoint_dir, cp_name)) | |
71 | self.assertEqual(cp_subdir, os.path.join(root, subd, fm.checkpoint_dir, cp_name)) |
|
90 | self.assertEqual(cp_subdir, os.path.join(root, subd, fm.checkpoint_dir, cp_name)) | |
|
91 | ||||
|
92 | @dec.skip_win32 | |||
|
93 | def test_bad_symlink(self): | |||
|
94 | with TemporaryDirectory() as td: | |||
|
95 | cm = FileContentsManager(root_dir=td) | |||
|
96 | path = 'test bad symlink' | |||
|
97 | _make_dir(cm, path) | |||
72 |
|
98 | |||
|
99 | file_model = cm.new_untitled(path=path, ext='.txt') | |||
|
100 | ||||
|
101 | # create a broken symlink | |||
|
102 | self.symlink(cm, "target", '%s/%s' % (path, 'bad symlink')) | |||
|
103 | model = cm.get(path) | |||
|
104 | self.assertEqual(model['content'], [file_model]) | |||
|
105 | ||||
|
106 | @dec.skip_win32 | |||
|
107 | def test_good_symlink(self): | |||
|
108 | with TemporaryDirectory() as td: | |||
|
109 | cm = FileContentsManager(root_dir=td) | |||
|
110 | parent = 'test good symlink' | |||
|
111 | name = 'good symlink' | |||
|
112 | path = '{0}/{1}'.format(parent, name) | |||
|
113 | _make_dir(cm, parent) | |||
|
114 | ||||
|
115 | file_model = cm.new(path=parent + '/zfoo.txt') | |||
|
116 | ||||
|
117 | # create a good symlink | |||
|
118 | self.symlink(cm, file_model['path'], path) | |||
|
119 | symlink_model = cm.get(path, content=False) | |||
|
120 | dir_model = cm.get(parent) | |||
|
121 | self.assertEqual( | |||
|
122 | sorted(dir_model['content'], key=lambda x: x['name']), | |||
|
123 | [symlink_model, file_model], | |||
|
124 | ) | |||
73 |
|
125 | |||
74 | class TestContentsManager(TestCase): |
|
|||
75 |
|
126 | |||
|
127 | class TestContentsManager(TestCase): | |||
|
128 | ||||
76 | def setUp(self): |
|
129 | def setUp(self): | |
77 | self._temp_dir = TemporaryDirectory() |
|
130 | self._temp_dir = TemporaryDirectory() | |
78 | self.td = self._temp_dir.name |
|
131 | self.td = self._temp_dir.name | |
@@ -83,15 +136,12 class TestContentsManager(TestCase): | |||||
83 | def tearDown(self): |
|
136 | def tearDown(self): | |
84 | self._temp_dir.cleanup() |
|
137 | self._temp_dir.cleanup() | |
85 |
|
138 | |||
86 |
def make_dir(self, a |
|
139 | def make_dir(self, api_path): | |
87 |
"""make subdirectory |
|
140 | """make a subdirectory at api_path | |
88 | to that directory from the location where the server started""" |
|
141 | ||
89 | os_path = os.path.join(abs_path, rel_path) |
|
142 | override in subclasses if contents are not on the filesystem. | |
90 |
|
|
143 | """ | |
91 | os.makedirs(os_path) |
|
144 | _make_dir(self.contents_manager, api_path) | |
92 | except OSError: |
|
|||
93 | print("Directory already exists: %r" % os_path) |
|
|||
94 | return os_path |
|
|||
95 |
|
145 | |||
96 | def add_code_cell(self, nb): |
|
146 | def add_code_cell(self, nb): | |
97 | output = nbformat.new_output("display_data", {'application/javascript': "alert('hi');"}) |
|
147 | output = nbformat.new_output("display_data", {'application/javascript': "alert('hi');"}) | |
@@ -169,7 +219,7 class TestContentsManager(TestCase): | |||||
169 |
|
219 | |||
170 | # Test in sub-directory |
|
220 | # Test in sub-directory | |
171 | sub_dir = '/foo/' |
|
221 | sub_dir = '/foo/' | |
172 |
self.make_dir( |
|
222 | self.make_dir('foo') | |
173 | model = cm.new_untitled(path=sub_dir, ext='.ipynb') |
|
223 | model = cm.new_untitled(path=sub_dir, ext='.ipynb') | |
174 | model2 = cm.get(sub_dir + name) |
|
224 | model2 = cm.get(sub_dir + name) | |
175 | assert isinstance(model2, dict) |
|
225 | assert isinstance(model2, dict) | |
@@ -179,46 +229,59 class TestContentsManager(TestCase): | |||||
179 | self.assertEqual(model2['name'], 'Untitled.ipynb') |
|
229 | self.assertEqual(model2['name'], 'Untitled.ipynb') | |
180 | self.assertEqual(model2['path'], '{0}/{1}'.format(sub_dir.strip('/'), name)) |
|
230 | self.assertEqual(model2['path'], '{0}/{1}'.format(sub_dir.strip('/'), name)) | |
181 |
|
231 | |||
|
232 | # Test with a regular file. | |||
|
233 | file_model_path = cm.new_untitled(path=sub_dir, ext='.txt')['path'] | |||
|
234 | file_model = cm.get(file_model_path) | |||
|
235 | self.assertDictContainsSubset( | |||
|
236 | { | |||
|
237 | 'content': u'', | |||
|
238 | 'format': u'text', | |||
|
239 | 'mimetype': u'text/plain', | |||
|
240 | 'name': u'untitled.txt', | |||
|
241 | 'path': u'foo/untitled.txt', | |||
|
242 | 'type': u'file', | |||
|
243 | 'writable': True, | |||
|
244 | }, | |||
|
245 | file_model, | |||
|
246 | ) | |||
|
247 | self.assertIn('created', file_model) | |||
|
248 | self.assertIn('last_modified', file_model) | |||
|
249 | ||||
182 | # Test getting directory model |
|
250 | # Test getting directory model | |
|
251 | ||||
|
252 | # Create a sub-sub directory to test getting directory contents with a | |||
|
253 | # subdir. | |||
|
254 | self.make_dir('foo/bar') | |||
183 | dirmodel = cm.get('foo') |
|
255 | dirmodel = cm.get('foo') | |
184 | self.assertEqual(dirmodel['type'], 'directory') |
|
256 | self.assertEqual(dirmodel['type'], 'directory') | |
|
257 | self.assertIsInstance(dirmodel['content'], list) | |||
|
258 | self.assertEqual(len(dirmodel['content']), 3) | |||
|
259 | self.assertEqual(dirmodel['path'], 'foo') | |||
|
260 | self.assertEqual(dirmodel['name'], 'foo') | |||
|
261 | ||||
|
262 | # Directory contents should match the contents of each individual entry | |||
|
263 | # when requested with content=False. | |||
|
264 | model2_no_content = cm.get(sub_dir + name, content=False) | |||
|
265 | file_model_no_content = cm.get(u'foo/untitled.txt', content=False) | |||
|
266 | sub_sub_dir_no_content = cm.get('foo/bar', content=False) | |||
|
267 | self.assertEqual(sub_sub_dir_no_content['path'], 'foo/bar') | |||
|
268 | self.assertEqual(sub_sub_dir_no_content['name'], 'bar') | |||
|
269 | ||||
|
270 | for entry in dirmodel['content']: | |||
|
271 | # Order isn't guaranteed by the spec, so this is a hacky way of | |||
|
272 | # verifying that all entries are matched. | |||
|
273 | if entry['path'] == sub_sub_dir_no_content['path']: | |||
|
274 | self.assertEqual(entry, sub_sub_dir_no_content) | |||
|
275 | elif entry['path'] == model2_no_content['path']: | |||
|
276 | self.assertEqual(entry, model2_no_content) | |||
|
277 | elif entry['path'] == file_model_no_content['path']: | |||
|
278 | self.assertEqual(entry, file_model_no_content) | |||
|
279 | else: | |||
|
280 | self.fail("Unexpected directory entry: %s" % entry()) | |||
185 |
|
281 | |||
186 | with self.assertRaises(HTTPError): |
|
282 | with self.assertRaises(HTTPError): | |
187 | cm.get('foo', type='file') |
|
283 | cm.get('foo', type='file') | |
188 |
|
284 | |||
189 |
|
||||
190 | @dec.skip_win32 |
|
|||
191 | def test_bad_symlink(self): |
|
|||
192 | cm = self.contents_manager |
|
|||
193 | path = 'test bad symlink' |
|
|||
194 | os_path = self.make_dir(cm.root_dir, path) |
|
|||
195 |
|
||||
196 | file_model = cm.new_untitled(path=path, ext='.txt') |
|
|||
197 |
|
||||
198 | # create a broken symlink |
|
|||
199 | os.symlink("target", os.path.join(os_path, "bad symlink")) |
|
|||
200 | model = cm.get(path) |
|
|||
201 | self.assertEqual(model['content'], [file_model]) |
|
|||
202 |
|
||||
203 | @dec.skip_win32 |
|
|||
204 | def test_good_symlink(self): |
|
|||
205 | cm = self.contents_manager |
|
|||
206 | parent = 'test good symlink' |
|
|||
207 | name = 'good symlink' |
|
|||
208 | path = '{0}/{1}'.format(parent, name) |
|
|||
209 | os_path = self.make_dir(cm.root_dir, parent) |
|
|||
210 |
|
||||
211 | file_model = cm.new(path=parent + '/zfoo.txt') |
|
|||
212 |
|
||||
213 | # create a good symlink |
|
|||
214 | os.symlink(file_model['name'], os.path.join(os_path, name)) |
|
|||
215 | symlink_model = cm.get(path, content=False) |
|
|||
216 | dir_model = cm.get(parent) |
|
|||
217 | self.assertEqual( |
|
|||
218 | sorted(dir_model['content'], key=lambda x: x['name']), |
|
|||
219 | [symlink_model, file_model], |
|
|||
220 | ) |
|
|||
221 |
|
||||
222 | def test_update(self): |
|
285 | def test_update(self): | |
223 | cm = self.contents_manager |
|
286 | cm = self.contents_manager | |
224 | # Create a notebook |
|
287 | # Create a notebook | |
@@ -240,9 +303,8 class TestContentsManager(TestCase): | |||||
240 | # Test in sub-directory |
|
303 | # Test in sub-directory | |
241 | # Create a directory and notebook in that directory |
|
304 | # Create a directory and notebook in that directory | |
242 | sub_dir = '/foo/' |
|
305 | sub_dir = '/foo/' | |
243 |
self.make_dir( |
|
306 | self.make_dir('foo') | |
244 | model = cm.new_untitled(path=sub_dir, type='notebook') |
|
307 | model = cm.new_untitled(path=sub_dir, type='notebook') | |
245 | name = model['name'] |
|
|||
246 | path = model['path'] |
|
308 | path = model['path'] | |
247 |
|
309 | |||
248 | # Change the name in the model for rename |
|
310 | # Change the name in the model for rename | |
@@ -279,7 +341,7 class TestContentsManager(TestCase): | |||||
279 | # Test in sub-directory |
|
341 | # Test in sub-directory | |
280 | # Create a directory and notebook in that directory |
|
342 | # Create a directory and notebook in that directory | |
281 | sub_dir = '/foo/' |
|
343 | sub_dir = '/foo/' | |
282 |
self.make_dir( |
|
344 | self.make_dir('foo') | |
283 | model = cm.new_untitled(path=sub_dir, type='notebook') |
|
345 | model = cm.new_untitled(path=sub_dir, type='notebook') | |
284 | name = model['name'] |
|
346 | name = model['name'] | |
285 | path = model['path'] |
|
347 | path = model['path'] | |
@@ -301,6 +363,9 class TestContentsManager(TestCase): | |||||
301 | # Delete the notebook |
|
363 | # Delete the notebook | |
302 | cm.delete(path) |
|
364 | cm.delete(path) | |
303 |
|
365 | |||
|
366 | # Check that deleting a non-existent path raises an error. | |||
|
367 | self.assertRaises(HTTPError, cm.delete, path) | |||
|
368 | ||||
304 | # Check that a 'get' on the deleted notebook raises and error |
|
369 | # Check that a 'get' on the deleted notebook raises and error | |
305 | self.assertRaises(HTTPError, cm.get, path) |
|
370 | self.assertRaises(HTTPError, cm.get, path) | |
306 |
|
371 | |||
@@ -309,9 +374,9 class TestContentsManager(TestCase): | |||||
309 | parent = u'å b' |
|
374 | parent = u'å b' | |
310 | name = u'nb √.ipynb' |
|
375 | name = u'nb √.ipynb' | |
311 | path = u'{0}/{1}'.format(parent, name) |
|
376 | path = u'{0}/{1}'.format(parent, name) | |
312 | os.mkdir(os.path.join(cm.root_dir, parent)) |
|
377 | self.make_dir(parent) | |
313 | orig = cm.new(path=path) |
|
|||
314 |
|
378 | |||
|
379 | orig = cm.new(path=path) | |||
315 | # copy with unspecified name |
|
380 | # copy with unspecified name | |
316 | copy = cm.copy(path) |
|
381 | copy = cm.copy(path) | |
317 | self.assertEqual(copy['name'], orig['name'].replace('.ipynb', '-Copy1.ipynb')) |
|
382 | self.assertEqual(copy['name'], orig['name'].replace('.ipynb', '-Copy1.ipynb')) | |
@@ -366,3 +431,4 class TestContentsManager(TestCase): | |||||
366 | cm.mark_trusted_cells(nb, path) |
|
431 | cm.mark_trusted_cells(nb, path) | |
367 | cm.check_and_sign(nb, path) |
|
432 | cm.check_and_sign(nb, path) | |
368 | assert cm.notary.check_signature(nb) |
|
433 | assert cm.notary.check_signature(nb) | |
|
434 |
@@ -30,6 +30,7 class NotebookTestBase(TestCase): | |||||
30 | """ |
|
30 | """ | |
31 |
|
31 | |||
32 | port = 12341 |
|
32 | port = 12341 | |
|
33 | config = None | |||
33 |
|
34 | |||
34 | @classmethod |
|
35 | @classmethod | |
35 | def wait_until_alive(cls): |
|
36 | def wait_until_alive(cls): | |
@@ -65,6 +66,7 class NotebookTestBase(TestCase): | |||||
65 | open_browser=False, |
|
66 | open_browser=False, | |
66 | ipython_dir=cls.ipython_dir.name, |
|
67 | ipython_dir=cls.ipython_dir.name, | |
67 | notebook_dir=cls.notebook_dir.name, |
|
68 | notebook_dir=cls.notebook_dir.name, | |
|
69 | config=cls.config, | |||
68 | ) |
|
70 | ) | |
69 |
|
71 | |||
70 | # clear log handlers and propagate to root for nose to capture it |
|
72 | # clear log handlers and propagate to root for nose to capture it |
General Comments 0
You need to be logged in to leave comments.
Login now