##// END OF EJS Templates
MAINT: Remove unused import.
Scott Sanderson -
Show More
@@ -1,498 +1,497
1 1 # coding: utf-8
2 2 """Tests for the notebook manager."""
3 3 from __future__ import print_function
4 4
5 5 import os
6 6 import sys
7 7 import time
8 8 from contextlib import contextmanager
9 9
10 10 from nose import SkipTest
11 11 from tornado.web import HTTPError
12 12 from unittest import TestCase
13 13 from tempfile import NamedTemporaryFile
14 14
15 15 from IPython.nbformat import v4 as nbformat
16 16
17 17 from IPython.utils.tempdir import TemporaryDirectory
18 18 from IPython.utils.traitlets import TraitError
19 from IPython.html.utils import url_path_join
20 19 from IPython.testing import decorators as dec
21 20
22 21 from ..filemanager import FileContentsManager
23 22
24 23
25 24 def _make_dir(contents_manager, api_path):
26 25 """
27 26 Make a directory.
28 27 """
29 28 os_path = contents_manager._get_os_path(api_path)
30 29 try:
31 30 os.makedirs(os_path)
32 31 except OSError:
33 32 print("Directory already exists: %r" % os_path)
34 33
35 34
36 35 class TestFileContentsManager(TestCase):
37 36
38 37 @contextmanager
39 38 def assertRaisesHTTPError(self, status, msg=None):
40 39 msg = msg or "Should have raised HTTPError(%i)" % status
41 40 try:
42 41 yield
43 42 except HTTPError as e:
44 43 self.assertEqual(e.status_code, status)
45 44 else:
46 45 self.fail(msg)
47 46
48 47 def symlink(self, contents_manager, src, dst):
49 48 """Make a symlink to src from dst
50 49
51 50 src and dst are api_paths
52 51 """
53 52 src_os_path = contents_manager._get_os_path(src)
54 53 dst_os_path = contents_manager._get_os_path(dst)
55 54 print(src_os_path, dst_os_path, os.path.isfile(src_os_path))
56 55 os.symlink(src_os_path, dst_os_path)
57 56
58 57 def test_root_dir(self):
59 58 with TemporaryDirectory() as td:
60 59 fm = FileContentsManager(root_dir=td)
61 60 self.assertEqual(fm.root_dir, td)
62 61
63 62 def test_missing_root_dir(self):
64 63 with TemporaryDirectory() as td:
65 64 root = os.path.join(td, 'notebook', 'dir', 'is', 'missing')
66 65 self.assertRaises(TraitError, FileContentsManager, root_dir=root)
67 66
68 67 def test_invalid_root_dir(self):
69 68 with NamedTemporaryFile() as tf:
70 69 self.assertRaises(TraitError, FileContentsManager, root_dir=tf.name)
71 70
72 71 def test_get_os_path(self):
73 72 # full filesystem path should be returned with correct operating system
74 73 # separators.
75 74 with TemporaryDirectory() as td:
76 75 root = td
77 76 fm = FileContentsManager(root_dir=root)
78 77 path = fm._get_os_path('/path/to/notebook/test.ipynb')
79 78 rel_path_list = '/path/to/notebook/test.ipynb'.split('/')
80 79 fs_path = os.path.join(fm.root_dir, *rel_path_list)
81 80 self.assertEqual(path, fs_path)
82 81
83 82 fm = FileContentsManager(root_dir=root)
84 83 path = fm._get_os_path('test.ipynb')
85 84 fs_path = os.path.join(fm.root_dir, 'test.ipynb')
86 85 self.assertEqual(path, fs_path)
87 86
88 87 fm = FileContentsManager(root_dir=root)
89 88 path = fm._get_os_path('////test.ipynb')
90 89 fs_path = os.path.join(fm.root_dir, 'test.ipynb')
91 90 self.assertEqual(path, fs_path)
92 91
93 92 def test_checkpoint_subdir(self):
94 93 subd = u'sub βˆ‚ir'
95 94 cp_name = 'test-cp.ipynb'
96 95 with TemporaryDirectory() as td:
97 96 root = td
98 97 os.mkdir(os.path.join(td, subd))
99 98 fm = FileContentsManager(root_dir=root)
100 99 cpm = fm.checkpoints
101 100 cp_dir = cpm.checkpoint_path(
102 101 'cp', 'test.ipynb'
103 102 )
104 103 cp_subdir = cpm.checkpoint_path(
105 104 'cp', '/%s/test.ipynb' % subd
106 105 )
107 106 self.assertNotEqual(cp_dir, cp_subdir)
108 107 self.assertEqual(cp_dir, os.path.join(root, cpm.checkpoint_dir, cp_name))
109 108 self.assertEqual(cp_subdir, os.path.join(root, subd, cpm.checkpoint_dir, cp_name))
110 109
111 110 @dec.skip_win32
112 111 def test_bad_symlink(self):
113 112 with TemporaryDirectory() as td:
114 113 cm = FileContentsManager(root_dir=td)
115 114 path = 'test bad symlink'
116 115 _make_dir(cm, path)
117 116
118 117 file_model = cm.new_untitled(path=path, ext='.txt')
119 118
120 119 # create a broken symlink
121 120 self.symlink(cm, "target", '%s/%s' % (path, 'bad symlink'))
122 121 model = cm.get(path)
123 122 self.assertEqual(model['content'], [file_model])
124 123
125 124 @dec.skip_win32
126 125 def test_good_symlink(self):
127 126 with TemporaryDirectory() as td:
128 127 cm = FileContentsManager(root_dir=td)
129 128 parent = 'test good symlink'
130 129 name = 'good symlink'
131 130 path = '{0}/{1}'.format(parent, name)
132 131 _make_dir(cm, parent)
133 132
134 133 file_model = cm.new(path=parent + '/zfoo.txt')
135 134
136 135 # create a good symlink
137 136 self.symlink(cm, file_model['path'], path)
138 137 symlink_model = cm.get(path, content=False)
139 138 dir_model = cm.get(parent)
140 139 self.assertEqual(
141 140 sorted(dir_model['content'], key=lambda x: x['name']),
142 141 [symlink_model, file_model],
143 142 )
144 143
145 144 def test_403(self):
146 145 if hasattr(os, 'getuid'):
147 146 if os.getuid() == 0:
148 147 raise SkipTest("Can't test permissions as root")
149 148 if sys.platform.startswith('win'):
150 149 raise SkipTest("Can't test permissions on Windows")
151 150
152 151 with TemporaryDirectory() as td:
153 152 cm = FileContentsManager(root_dir=td)
154 153 model = cm.new_untitled(type='file')
155 154 os_path = cm._get_os_path(model['path'])
156 155
157 156 os.chmod(os_path, 0o400)
158 157 try:
159 158 with cm.open(os_path, 'w') as f:
160 159 f.write(u"don't care")
161 160 except HTTPError as e:
162 161 self.assertEqual(e.status_code, 403)
163 162 else:
164 163 self.fail("Should have raised HTTPError(403)")
165 164
166 165 def test_escape_root(self):
167 166 with TemporaryDirectory() as td:
168 167 cm = FileContentsManager(root_dir=td)
169 168 # make foo, bar next to root
170 169 with open(os.path.join(cm.root_dir, '..', 'foo'), 'w') as f:
171 170 f.write('foo')
172 171 with open(os.path.join(cm.root_dir, '..', 'bar'), 'w') as f:
173 172 f.write('bar')
174 173
175 174 with self.assertRaisesHTTPError(404):
176 175 cm.get('..')
177 176 with self.assertRaisesHTTPError(404):
178 177 cm.get('foo/../../../bar')
179 178 with self.assertRaisesHTTPError(404):
180 179 cm.delete('../foo')
181 180 with self.assertRaisesHTTPError(404):
182 181 cm.rename('../foo', '../bar')
183 182 with self.assertRaisesHTTPError(404):
184 183 cm.save(model={
185 184 'type': 'file',
186 185 'content': u'',
187 186 'format': 'text',
188 187 }, path='../foo')
189 188
190 189
191 190 class TestContentsManager(TestCase):
192 191
193 192 def setUp(self):
194 193 self._temp_dir = TemporaryDirectory()
195 194 self.td = self._temp_dir.name
196 195 self.contents_manager = FileContentsManager(
197 196 root_dir=self.td,
198 197 )
199 198
200 199 def tearDown(self):
201 200 self._temp_dir.cleanup()
202 201
203 202 def make_dir(self, api_path):
204 203 """make a subdirectory at api_path
205 204
206 205 override in subclasses if contents are not on the filesystem.
207 206 """
208 207 _make_dir(self.contents_manager, api_path)
209 208
210 209 def add_code_cell(self, nb):
211 210 output = nbformat.new_output("display_data", {'application/javascript': "alert('hi');"})
212 211 cell = nbformat.new_code_cell("print('hi')", outputs=[output])
213 212 nb.cells.append(cell)
214 213
215 214 def new_notebook(self):
216 215 cm = self.contents_manager
217 216 model = cm.new_untitled(type='notebook')
218 217 name = model['name']
219 218 path = model['path']
220 219
221 220 full_model = cm.get(path)
222 221 nb = full_model['content']
223 222 nb['metadata']['counter'] = int(1e6 * time.time())
224 223 self.add_code_cell(nb)
225 224
226 225 cm.save(full_model, path)
227 226 return nb, name, path
228 227
229 228 def test_new_untitled(self):
230 229 cm = self.contents_manager
231 230 # Test in root directory
232 231 model = cm.new_untitled(type='notebook')
233 232 assert isinstance(model, dict)
234 233 self.assertIn('name', model)
235 234 self.assertIn('path', model)
236 235 self.assertIn('type', model)
237 236 self.assertEqual(model['type'], 'notebook')
238 237 self.assertEqual(model['name'], 'Untitled.ipynb')
239 238 self.assertEqual(model['path'], 'Untitled.ipynb')
240 239
241 240 # Test in sub-directory
242 241 model = cm.new_untitled(type='directory')
243 242 assert isinstance(model, dict)
244 243 self.assertIn('name', model)
245 244 self.assertIn('path', model)
246 245 self.assertIn('type', model)
247 246 self.assertEqual(model['type'], 'directory')
248 247 self.assertEqual(model['name'], 'Untitled Folder')
249 248 self.assertEqual(model['path'], 'Untitled Folder')
250 249 sub_dir = model['path']
251 250
252 251 model = cm.new_untitled(path=sub_dir)
253 252 assert isinstance(model, dict)
254 253 self.assertIn('name', model)
255 254 self.assertIn('path', model)
256 255 self.assertIn('type', model)
257 256 self.assertEqual(model['type'], 'file')
258 257 self.assertEqual(model['name'], 'untitled')
259 258 self.assertEqual(model['path'], '%s/untitled' % sub_dir)
260 259
261 260 def test_get(self):
262 261 cm = self.contents_manager
263 262 # Create a notebook
264 263 model = cm.new_untitled(type='notebook')
265 264 name = model['name']
266 265 path = model['path']
267 266
268 267 # Check that we 'get' on the notebook we just created
269 268 model2 = cm.get(path)
270 269 assert isinstance(model2, dict)
271 270 self.assertIn('name', model2)
272 271 self.assertIn('path', model2)
273 272 self.assertEqual(model['name'], name)
274 273 self.assertEqual(model['path'], path)
275 274
276 275 nb_as_file = cm.get(path, content=True, type='file')
277 276 self.assertEqual(nb_as_file['path'], path)
278 277 self.assertEqual(nb_as_file['type'], 'file')
279 278 self.assertEqual(nb_as_file['format'], 'text')
280 279 self.assertNotIsInstance(nb_as_file['content'], dict)
281 280
282 281 nb_as_bin_file = cm.get(path, content=True, type='file', format='base64')
283 282 self.assertEqual(nb_as_bin_file['format'], 'base64')
284 283
285 284 # Test in sub-directory
286 285 sub_dir = '/foo/'
287 286 self.make_dir('foo')
288 287 model = cm.new_untitled(path=sub_dir, ext='.ipynb')
289 288 model2 = cm.get(sub_dir + name)
290 289 assert isinstance(model2, dict)
291 290 self.assertIn('name', model2)
292 291 self.assertIn('path', model2)
293 292 self.assertIn('content', model2)
294 293 self.assertEqual(model2['name'], 'Untitled.ipynb')
295 294 self.assertEqual(model2['path'], '{0}/{1}'.format(sub_dir.strip('/'), name))
296 295
297 296 # Test with a regular file.
298 297 file_model_path = cm.new_untitled(path=sub_dir, ext='.txt')['path']
299 298 file_model = cm.get(file_model_path)
300 299 self.assertDictContainsSubset(
301 300 {
302 301 'content': u'',
303 302 'format': u'text',
304 303 'mimetype': u'text/plain',
305 304 'name': u'untitled.txt',
306 305 'path': u'foo/untitled.txt',
307 306 'type': u'file',
308 307 'writable': True,
309 308 },
310 309 file_model,
311 310 )
312 311 self.assertIn('created', file_model)
313 312 self.assertIn('last_modified', file_model)
314 313
315 314 # Test getting directory model
316 315
317 316 # Create a sub-sub directory to test getting directory contents with a
318 317 # subdir.
319 318 self.make_dir('foo/bar')
320 319 dirmodel = cm.get('foo')
321 320 self.assertEqual(dirmodel['type'], 'directory')
322 321 self.assertIsInstance(dirmodel['content'], list)
323 322 self.assertEqual(len(dirmodel['content']), 3)
324 323 self.assertEqual(dirmodel['path'], 'foo')
325 324 self.assertEqual(dirmodel['name'], 'foo')
326 325
327 326 # Directory contents should match the contents of each individual entry
328 327 # when requested with content=False.
329 328 model2_no_content = cm.get(sub_dir + name, content=False)
330 329 file_model_no_content = cm.get(u'foo/untitled.txt', content=False)
331 330 sub_sub_dir_no_content = cm.get('foo/bar', content=False)
332 331 self.assertEqual(sub_sub_dir_no_content['path'], 'foo/bar')
333 332 self.assertEqual(sub_sub_dir_no_content['name'], 'bar')
334 333
335 334 for entry in dirmodel['content']:
336 335 # Order isn't guaranteed by the spec, so this is a hacky way of
337 336 # verifying that all entries are matched.
338 337 if entry['path'] == sub_sub_dir_no_content['path']:
339 338 self.assertEqual(entry, sub_sub_dir_no_content)
340 339 elif entry['path'] == model2_no_content['path']:
341 340 self.assertEqual(entry, model2_no_content)
342 341 elif entry['path'] == file_model_no_content['path']:
343 342 self.assertEqual(entry, file_model_no_content)
344 343 else:
345 344 self.fail("Unexpected directory entry: %s" % entry())
346 345
347 346 with self.assertRaises(HTTPError):
348 347 cm.get('foo', type='file')
349 348
350 349 def test_update(self):
351 350 cm = self.contents_manager
352 351 # Create a notebook
353 352 model = cm.new_untitled(type='notebook')
354 353 name = model['name']
355 354 path = model['path']
356 355
357 356 # Change the name in the model for rename
358 357 model['path'] = 'test.ipynb'
359 358 model = cm.update(model, path)
360 359 assert isinstance(model, dict)
361 360 self.assertIn('name', model)
362 361 self.assertIn('path', model)
363 362 self.assertEqual(model['name'], 'test.ipynb')
364 363
365 364 # Make sure the old name is gone
366 365 self.assertRaises(HTTPError, cm.get, path)
367 366
368 367 # Test in sub-directory
369 368 # Create a directory and notebook in that directory
370 369 sub_dir = '/foo/'
371 370 self.make_dir('foo')
372 371 model = cm.new_untitled(path=sub_dir, type='notebook')
373 372 path = model['path']
374 373
375 374 # Change the name in the model for rename
376 375 d = path.rsplit('/', 1)[0]
377 376 new_path = model['path'] = d + '/test_in_sub.ipynb'
378 377 model = cm.update(model, path)
379 378 assert isinstance(model, dict)
380 379 self.assertIn('name', model)
381 380 self.assertIn('path', model)
382 381 self.assertEqual(model['name'], 'test_in_sub.ipynb')
383 382 self.assertEqual(model['path'], new_path)
384 383
385 384 # Make sure the old name is gone
386 385 self.assertRaises(HTTPError, cm.get, path)
387 386
388 387 def test_save(self):
389 388 cm = self.contents_manager
390 389 # Create a notebook
391 390 model = cm.new_untitled(type='notebook')
392 391 name = model['name']
393 392 path = model['path']
394 393
395 394 # Get the model with 'content'
396 395 full_model = cm.get(path)
397 396
398 397 # Save the notebook
399 398 model = cm.save(full_model, path)
400 399 assert isinstance(model, dict)
401 400 self.assertIn('name', model)
402 401 self.assertIn('path', model)
403 402 self.assertEqual(model['name'], name)
404 403 self.assertEqual(model['path'], path)
405 404
406 405 # Test in sub-directory
407 406 # Create a directory and notebook in that directory
408 407 sub_dir = '/foo/'
409 408 self.make_dir('foo')
410 409 model = cm.new_untitled(path=sub_dir, type='notebook')
411 410 name = model['name']
412 411 path = model['path']
413 412 model = cm.get(path)
414 413
415 414 # Change the name in the model for rename
416 415 model = cm.save(model, path)
417 416 assert isinstance(model, dict)
418 417 self.assertIn('name', model)
419 418 self.assertIn('path', model)
420 419 self.assertEqual(model['name'], 'Untitled.ipynb')
421 420 self.assertEqual(model['path'], 'foo/Untitled.ipynb')
422 421
423 422 def test_delete(self):
424 423 cm = self.contents_manager
425 424 # Create a notebook
426 425 nb, name, path = self.new_notebook()
427 426
428 427 # Delete the notebook
429 428 cm.delete(path)
430 429
431 430 # Check that deleting a non-existent path raises an error.
432 431 self.assertRaises(HTTPError, cm.delete, path)
433 432
434 433 # Check that a 'get' on the deleted notebook raises and error
435 434 self.assertRaises(HTTPError, cm.get, path)
436 435
437 436 def test_copy(self):
438 437 cm = self.contents_manager
439 438 parent = u'Γ₯ b'
440 439 name = u'nb √.ipynb'
441 440 path = u'{0}/{1}'.format(parent, name)
442 441 self.make_dir(parent)
443 442
444 443 orig = cm.new(path=path)
445 444 # copy with unspecified name
446 445 copy = cm.copy(path)
447 446 self.assertEqual(copy['name'], orig['name'].replace('.ipynb', '-Copy1.ipynb'))
448 447
449 448 # copy with specified name
450 449 copy2 = cm.copy(path, u'Γ₯ b/copy 2.ipynb')
451 450 self.assertEqual(copy2['name'], u'copy 2.ipynb')
452 451 self.assertEqual(copy2['path'], u'Γ₯ b/copy 2.ipynb')
453 452 # copy with specified path
454 453 copy2 = cm.copy(path, u'/')
455 454 self.assertEqual(copy2['name'], name)
456 455 self.assertEqual(copy2['path'], name)
457 456
458 457 def test_trust_notebook(self):
459 458 cm = self.contents_manager
460 459 nb, name, path = self.new_notebook()
461 460
462 461 untrusted = cm.get(path)['content']
463 462 assert not cm.notary.check_cells(untrusted)
464 463
465 464 # print(untrusted)
466 465 cm.trust_notebook(path)
467 466 trusted = cm.get(path)['content']
468 467 # print(trusted)
469 468 assert cm.notary.check_cells(trusted)
470 469
471 470 def test_mark_trusted_cells(self):
472 471 cm = self.contents_manager
473 472 nb, name, path = self.new_notebook()
474 473
475 474 cm.mark_trusted_cells(nb, path)
476 475 for cell in nb.cells:
477 476 if cell.cell_type == 'code':
478 477 assert not cell.metadata.trusted
479 478
480 479 cm.trust_notebook(path)
481 480 nb = cm.get(path)['content']
482 481 for cell in nb.cells:
483 482 if cell.cell_type == 'code':
484 483 assert cell.metadata.trusted
485 484
486 485 def test_check_and_sign(self):
487 486 cm = self.contents_manager
488 487 nb, name, path = self.new_notebook()
489 488
490 489 cm.mark_trusted_cells(nb, path)
491 490 cm.check_and_sign(nb, path)
492 491 assert not cm.notary.check_signature(nb)
493 492
494 493 cm.trust_notebook(path)
495 494 nb = cm.get(path)['content']
496 495 cm.mark_trusted_cells(nb, path)
497 496 cm.check_and_sign(nb, path)
498 497 assert cm.notary.check_signature(nb)
General Comments 0
You need to be logged in to leave comments. Login now